On Tue, Feb 21, 2012 at 3:02 PM, Dimas Cyriaco <[email protected]>wrote:

> Hello,
>
> I'm trying to make ActiveResource work with nested attributes and the
> fields_for tag.
>
> I started with a typical Rails application, with two ActiveRecord models
> with one to many relationship (one band has many members).
>
> In view 'bands/new.html.erb' I have something like this:
>
> <%= form_for @band do |f| %>
>   <%= f.label: name %>
>   <%= f.text_field: name %>
>
>   <% f.fields_for: members do |member_form| %>
>     <%= member_form.label: name %>
>     <%= member_form.text_field: name %>
>
>     <%= member_form.label: Instrument %>
>     <%= member_form.text_field: Instrument %>
>   <% end %>
>
>   <%= f.submit >
> <% end %>
>
> In the controller I get this data as follows (in the params hash):
>
> {
> band: {
> name: 'band name',
> members_attributes: [
> {name: 'member name' instrument: 'some instrument'}
> ]
> }
> }
>
> When I send this params to ActiveRecord's new or create it creates the
> band and members. So far so good.
>
> So I moved the ActiveRecord models into a service and replaced them with
> ActiveResource models.
>
> However when I send the request to the service ActiveResource change these
> parameters in a strange way. He turns the 'members_attributes: [...]' in
> something like this:
>
> members_attributes: [
>     { members_attribute: { name: 'member name' instrument: 'some
> instrument' }}
> ]
>
> And the ActiveRecord on the other side cannot treat this.
>
> Does anyone have any idea how to prevent this behavior (or why it happens)?
>
> In a second attempt I replaced the Band.create by Band.post(nil, {},
> params[:band].to_json) (which makes a request directly to the service
> without going through the ActiveResource::Base#load that appears to be
> source of the problem), but ActiveResource does a post to '/bands/.json'
> instead of '/bands.json'. I patched ActiveResource's
> 'custom_method_collection_url' method, so the post goes to the right url,
> but i have not yet submitted a push request because I don't know if this
> will be usefull for everyone.
>
> Actually I'm more concerned with understanding why ActiveResource's
> default behavior is so strange.
>
> Anyone know what the purpose of the method
> ActiveRecord::Base#find_or_create_resource_for_collection? (I know what it
> does, i just don't understand why it does it). Wouldn't it be easier if
> ActiveResource simply passes my parameters for the service?
>
> If someone can help me I would be grateful.
>

Sorry, was a bit overloaded ...

What I did to accept XML input for such a case was:

* put back the "has_many" objects in an array of objects without the
_attributes attachment.

The hash would then be:

{
band: {
name: 'band name',
members: [
{name: 'member name' instrument: 'some instrument'}
]
}
}

for XML that would be:

<band>
  <name>band name</name>
  <members type="array">
<name>
      member name
    </name>
    <instrument>
      some instrument
    </instrument>
  </members>
</band>

and similar for JSON.

Then to create a band and it's member with the assignment:

  band = Band.new(params[:band])

which needs a fix to the members= function in the Band class.

class Band
  # members
  has_many :members, :inverse_of => :band
  accepts_nested_attributes_for :members
  include Xml::FromXmlMembers
end

module Xml
  module FromXmlMembers
    extend ActiveSupport::Concern
    included do
      # for xml  (on band.members=)
      def members=(members)
        case members
        when Array
          super(
            members.map |member|
              case member
              when Hash
                Member.new(member)
              else
                member
              end
            end)
        else
          raise 'members MUST be an array; maybe <members type="array"> was
forgotten'
        end
      end
    end
  end
end


The catch is that the function `members= ` that is created by the has_many
relationship
* expects an array of Member objects
* but the hash I typically got when parsing incoming XML data
  (and you seem to have here with ActiveResource)
is an array of attributes hashes.

HTH (not entirely sure ...),

Peter

-- 
You received this message because you are subscribed to the Google Groups "Ruby 
on Rails: Talk" group.
To post to this group, send email to [email protected].
To unsubscribe from this group, send email to 
[email protected].
For more options, visit this group at 
http://groups.google.com/group/rubyonrails-talk?hl=en.

Reply via email to