Closed summera closed 10 years ago
You can use the :deserialize
option from representable.
property :bowl, deserialize: lambda { |object, fragment, *| Bowl.find id_from_url(fragment) }
Awesome! That's easy enough. Thanks :+1:
How does this work when it is a link
in a HAL representer instead of a property
?
I don't see a way to change how links are deserialized so I can interpret them into ActiveRecord relations.
@kookster Have you tried sending in your request with the url relation nested under _links
?
Also I created this gem https://github.com/sweatshirtio/actionback for deserializing urls to ActiveRecord instances. There is an example in the README using roar-rails. The gem still needs work but it's on its way. :)
Hope this helps.
@kookster I don't think you can deserialize with link
@summera thanks for the info, but that isn't what I am trying to figure out.
When the representer has a url value for a property
, then you can specify the deserialize
option on the property with a lambda to look up the model based on the url value. I see how your actionback gem helps with that.
The problem is that I see no way to specify the deserialize
option for a link
definition in the representer. This might look like the following:
link rel: :bowl do
{
href: bowl_url(represented.bowl)
}
end
No way to specify in a deserialize
option. Additional options besides rel
are considered as attributes, not options for the Definition.
When specifying a link using ::link
, the representer will deserialise Hyperlink
objects into the model's link
field (with representer modules). https://github.com/apotonick/roar#consuming-hypermedia
In case you're using decorators, this is deserialised into the decorator's link
field. If you still want it on the model, include HypermediaConsumer
into your decorator: https://github.com/apotonick/roar/blob/master/lib/roar/decorator.rb
This will, when deserialising, call model.links= [link, link, link, ...]
.
@apotonick this is really, really, helpful. I am using decorators, I'll try that.
I won't get to use those links
in the consume!
call to immediately populate model relations, but at least I'll be able to access the link data, and I can do something with it after that.
We can think about a block for #consume!
that gives you the populated model, @kookster ?
@apotonick I would like to confine serialization logic to the representer, so adding a block to #consume!
, while it would make this possible, wouldn't be my choice of where to put that logic.
If I pursue using hal-json links to add AR relations, my next line of thinking is to override links_definition_options
or create_links_definition
so I can define deserializer
callbacks that will allow me to set the relations on the represented object.
I'm also thinking about abandoning using hal-json links for this, and treating them as read-only. That would basically make it so my API provides hal-json content for reading and navigating the data, but to add or change data it would only support regular json. Not the end of the world - perhaps hal-json just works better as a read-only standard.
One way to do this would be to do something like what @summera did above, and have write-only properties for any links that can be created or updated by the client, and add deserializer lambdas for those as per above. I could also then use actionback
in the lambdas to generically handle this.
I wouldn't need to add these properties for all links; it seems to be a good pattern for (forgive my UML terminology) aggregation relationships where I need to have a way to express the relationship of the child to the parent, but not composition, where the parent is responsible for creating/managing the child.
With composition, I can express the link to the parent via the url that the new object is posted to:
/parent/#{parent_id}/child
For aggregation, I can express it with a url
attribute on the child, where the same parent_url from links (i.e. _links{ parent: { href: 'parent_url'}}
) would be a write-only property of the child:
class ChildRepresenter < Roar::Decorator
property :parent_url, readable: false
end
I would want to continue to have the parent link expressed as a url rather than the id, so the client wouldn't be parsing the url string to pull out the database key integer.
In talking with the engineer here who is working on the API client code, that seems to be their preferred approach.
Cool, interesting thoughts!
Am I right assuming that you want to do the following?
represented.title= "Roxanne"
(so far, this is normal representable/Roar behaviour).I see two ways for that.
This is pure pseudo-code.
model.from_json("{..}") do |mdl, links|
mdl.items = Item.find links[:items].ids
end
Here, you could partly hook into deserialisation process and change state without having to worry about parsing shizzle.
link :items, parse_filter: lambda { .. } do
items_url
end
In this example, a new option :parse_filter
could allow you to define parsing code per link. Is that what you're after?
The reason this is not implemented, yet, in Roar is because I wanted to wait for users to bring in some requirements. It's your chance to work something out with me now! :grimacing:
When updating or creating a resource in RESTful hypermedia APIs I have seen them designed in such a way where you relate resources via URLs instead of id's. To make up an example and follow your Fruit Bowl analogy, if I wanted to create a fruit as a part of a bowl I would do something along the lines of:
Behind the scenes, this would deserialize the passed in bowl url (http://bowls/1) to the bowl object with id 1 and relate the new fruit to the existing bowl.
Would really appreciate some insight on how I would go about this in roar-rails. :)