For unify data formats to transfer to clients.
gem install client-data-adapter
or in Gemfile
gem 'client-data-adapter'
Include library to model and use define_adapter
to define the adapter.
# book.rb
include ClientDataAdapter
define_adapter do
# define your adapter here.
# ...
end
adapter
method define the main adapter, should return a Hash
.
# ...
define_adapter do
adapter do
{
id: id,
title: title,
}
end
end
In elsewhere
# ...
@book = Book.new(id: 1, title: 'My Book')
@book.adapter # => { id: 1, title: 'My Book' }
And you probably need some complex calculation or related some other class, they maybe need some cost and is unnecessary to load everywhere.
So we need use them on-demand.
# ...
define_adapter do
adapter do
{
id: id,
title: title,
}
end
with :my_complex_calc do
'something complex'
end
end
Then
# ...
@book = Book.new(id: 1, title: 'My Book')
@book.adapter(:my_complex_calc)
# => { id: 1, title: 'My Book', my_complex_calc: 'something complex' }
And you can merge any instance method of original class to the adapter result, such as
# book.rb
def full_title
"<#{title}>"
end
Then you can use adapter like this
# ...
@book.adapter(:full_title)
# => { id: 1, title: 'My Book', full_title: '<My Book>' }
It will set the method name
as the key
, and returns
as the value
.
But notice that if method name is repeated, will trigger the one inside the adapter.
Sometimes we need pass some arguments to deal different case, you can write like this
@book.adapter(foo: [:bar, :baz])
Arguments with Hash
will consider the key
as the method name
,
and values
as arguments
.
To work with the method like this
def foo(*args)
args.join(',')
end
or this
with :foo do |*args|
args.join(',')
end
The result will be
@book.adapter(foo: [:bar, :baz])
# => { id: 1, title: 'My Book', foo: 'bar,baz' }
You can use return in adapter block to flow control.
# ...
with :id do
return 'i have no id' unless id
id
end
It is works.
If you define something inside the adapter, you can't read it directly.
# book.rb
define_adapter do
# ...
with :my_method do
'invoke me please!'
end
end
@book.my_method # => ERROR! No Method
Since that adapter differentiate the namespace with the original class to make naming more flexible.
It create a wrapper for the adapter named AdapterWrapper
,
and exposed by the instance method adapter_wrapper
.
@book.adapter_wrapper.class # => Book::AdapterWrapper
And you can read the adapter internal method via it.
@book.adapter_wrapper.my_method # => works
hmm... but i'm not very recommend you to use it by this way, if you need some method works alone, maybe you should define it in the original class.
This method is a syntactic sugar of with
, seems like
with :my_link do |*args|
my_link.adapter(*args)
end
is equal with
link_one :my_link
It usual used in one-to-one relationship like belongs_to
in rails
.
For example
# book.rb
belongs_to :book_shelf
define_adapter do
link_one :book_shelf
# ...
end
# book_shelf.rb
define_adapter do
adapter do
{
id: id,
desc: desc,
}
end
end
Then
@book.adapter(:book_shelf)
# => { id: 1, title: 'My Book', book_shelf: @book.book_shelf.adapter }
Of course you can pass some arguments, and if you have several links, can also used in nested.
# ...
@book.adapter(book_shelf: [:foo, library: :bar])
# => { ..., book_shelf: { ..., foo: ..., library: { ..., bar: ... } } }
And can define many links once.
# ...
link_one :my_link1, :my_link2
Okay, this method is similar with link_one
but multiple.
If use with
to implement likes
with :my_links do |*args|
my_links.map { |link| link.adapter(*args) }
end
Usual used in one-to-many relationship, such as has_many
in rails
.
For example
# book.rb
has_many :categories
define_adapter do
link_many :categories
# ...
end
@book.adapter(:categories)
# => { id: 1, title: 'My Book', categories: @book.categories.map(&:adapter) }
Support multiple arguments and other usage, detail above.