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