Rails Nested Resource Issues

Dmitri Dolguikh dmitri at redhat.com
Fri Sep 21 11:49:37 UTC 2012


On 21/09/12 12:25 PM, Tomas Sedovic wrote:
> On 09/20/2012 02:20 PM, Martyn Taylor wrote:
>> Gents.
>>
>> I'm having some trouble with getting nested resources working properly
>> from bespoke XML/JSON in RESTful API.  There seems to be some
>> fundamental problems with the way rails works, which means we have to
>> write a lot of bespoke code to get it working, possibly even having to
>> monkey patch Rails itself.
>>
>> I've written an example rails app that highlights the problem and put a
>> detailed description in the README you can find it here:
>>
>> https://github.com/mtaylor/Rails-Nested-Resource-Issues
>>
>> I've ran into this issue on IME.  But it's going to affect us across all
>> rails projects with REST APIs, so we could do with discussing this issue
>> and coming up with the best possible solution that results in as little
>> replication as possible.
>>
>> Please read through the problem and let me know if you have any better
>> ideas that the solutions I have proposed.
>>
>> Regards
>>
>> Martyn
>
>
> I'm beginning to lean towards the custom serialization and 
> deserialization. We'll have explicit control over the input/output 
> formats which is a good thing for API stability.
>
> However, there may be another issue that's reasonably close to what we 
> expected the default behaviour would be.
>
> If I understand this correctly, the issue isn't really behaviour of 
> the `attribute=` setters but of `Model.new`[1] and 
> `@model.update_attributes`[2] methods.
>
> We could insert a base class into the model inheritance chain and 
> either "fix" the methods there or add new ones that do bulk attribute 
> operations correctly.
>
> Pseudocode:
>
> class NestedModels < ActiveRecord::Base
>   def initialize(attributes = nil, options = {})
>     if attributes
>       attributes = attributes.clone
>       get_nested_attribute_names.each do |name|
>         attributes[name + '_attributes'] = attributes[name]
>     attributes.delete name
>       end
>     end
>     super.initialize(attributes, options)
>   end
>
>   def update_attributes(attributes, options = {})
>     # TODO, similar to initialize
>   end
>
>   def update_attributes!(attributes, options = {})
>     # TODO, similar to update_attributes
>   end
>
>   def get_nested_attributes
>     # TODO: use introspection to figure out attribute names passed to 
> accepts_nested_attributes_for in the model declaration
>   end
> end
>
> class User < NestedModels
>   has_many :addresses
>
>   accepts_nested_attributes_for :addresses
>   attr_accessible :name, :addresses, :addresses_attributes
> end
>
>
> now calling:
>
>     User.new({"name"=>"Joe Bloggs", "addresses"=>[{"street"=>"Church 
> Street"}]})
>
> would work because ActiveRecord::Base would receive:
>
>     {"name"=>"Joe Bloggs", "addresses_attributes"=>[{"street"=>"Church 
> Street"}]}
>
>
> This is similar to the monkeypatching proposal but it's safer. We 
> would not be modifying any of the Rails' internals. Rather, we'd 
> explicitly specify which models should have this modified behaviour.
>
> We could probably also do this by including a module with the 
> overrides instead of using inheritance.

Keep in mind that ActiveRecord provides builder methods for associations 
[3]. In the example you used:

    User.address.build(:street => "Church street")

or if you already have an instance of User:

    @user.address.build(:street => "Church street")


There are similar methods for one-to-many associations, which have the 
benefit of elements of collection not being persisted immediately (as 
opposed to adding an element to a collection directly).


If you are inclined to solve the problem on the model level, you could 
add a factory method that accepts a hash, and returns a fully-assembled 
instance, nested resources and all, with no monkey patching involved.


-d



>
> [1]: 
> https://github.com/rails/rails/blob/9b5309fb6819d8f2a1b31e44ba61e682272c7aa3/activerecord/lib/active_record/base.rb#L481
> [2]: 
> https://github.com/rails/rails/blob/81542f95d25825a7d3eff87d6f706661bf553b18/activerecord/lib/active_record/persistence.rb#L211
[3]: 
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html 

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://lists.fedorahosted.org/pipermail/aeolus-devel/attachments/20120921/17683e14/attachment.html>


More information about the aeolus-devel mailing list