Rails Nested Resource Issues

Tomas Sedovic tsedovic at redhat.com
Fri Sep 21 11:25:54 UTC 2012


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.

[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



More information about the aeolus-devel mailing list