Open mrbongiolo opened 9 years ago
Interesting problem! :grimacing:
What about this?
class CustomerRepresenter < Representable::Decorator
property :name
property :address, decorator: AddressRepresenter
def to_hash(*)
super(confirmed: confirmed?)
end
end
:getter
in your case as this is the only way to achieve the format you desire given your object graph. However, I don't like using this option at all. You should create an object that represents the document structure and then feed this to the representer. Remember: Representable is not a data mapping framework.
The easiest is to create an OpenStruct object that represents the properties as you want them in the document. You might also be interested in Disposable which actually is a data mapper and not a representation gem. You would then feed the Twin
to the representer.
I am guessing you're working with Rails which makes you believe that one object should do everything in an acts_as_awesome_fu way. This is wrong.
Let your models be persisting objects. Let the intermediate object graph (OpenStruct, twin) be your data mapping. Let the representer render and parse this graph. Don't try to achieve all of that in one asset. If you don't want your controller to be doing this, consider using operations from Trailblazer. I'm here to assist you, either way! :stuck_out_tongue_winking_eye: :
Thanks for you answers.
I'll try your first suggestion and see how it goes.
Right now I'm using Representable just to render my json data, I had it all in json views using jbuilder. I know that it can also be used to parse data from json to my objects, but didn't used it yet.
So as you suggest I could create a CustomerAddress < Disposable::Twin
right? This object would be responsible for the compositions and mapping them correctly to the models.
class CustomerAddressRepresenter < Representable::Decorator
property :district
property :street, if: ->(opts) { opts[:confirmed] }
property :confirmed
end
class CustomerAddressTwin < Disposable::Twin
include Composition
property :district, on: :address
property :street, on: :address
property :confirmed, on: :customer_address
end
# and when rendering it should be called like that?
customer_address_twin = CustomerAddressTwin
.new(customer_address: customer_address, address: customer_address.address)
CustomerAddressRepresenter.new( customer_address_twin ).to_json
Or am I missing something? And BTW I'm already using operations on most of my endpoints, still have a few left to refactor :smile:
Awesome, good to hear you are using operations! :laughing:
What you pasted is 100% correct. You can actually infer twins from representers and vice-versa, but in your case, this looks fine to me. Let me know how it goes.
BTW, we are planning a Crystal-port of Trb where we can define data types on the language level (fast, in C). And one of the extensions will be twins, so you have twins just like you use integers and strings.
1 - Passing Options when re-using Representers
Maybe this is possible, but I couldn't find it out. I'm re-using a Representer within another Representer, and I would like to pass "user options" to it, just like I can do when calling
MyRepresenter.new(model).to_json(foo: :bar)
.Something like this:
I tried it this way, but the
:confirmed
option does not show up in theopts
.Right now I solved my issue by creating 2 different Representers and caling the correct one within a lambda, this way:
decorator: ->(*) { confirmed? ? ConfirmedAddressRepresenter : UnconfirmedAddressRepresenter }
. This seems ok, but for some cases it would be simpler to just pass options from one Representer to another.2 - Non-wrapping property
Another case that I faced when re-using a Representer is this one:
When I render a JSON collection from the
CustomerAddressRepresenter
I get this:and what I really need is this:
In this case the CustomerAddress is a model used to link the Customer to an Address, and store some extra fields, like the
:confirmed
. But for the API response this should be transparent, a CustomerAddress should be just like an Address, but with the extra fields.That's how I "solved" this:
It's pretty ugly, but worked for me, maybe my case is very edgy and no one else needed something like this.
I was thinking in two ways to solve this, the first one is to use a
non_wrapping: true
or something like this, but this could get confused with thewrap: false
option used to disable the representation_wrap of a Representer. Ex:The second way is to add all properties manually in the Representer, but have an option to set the correct represented model. Ex:
What are your thoughts on those issues? If needed just point me where I could start and I might be able to send a PR.