Making ActiveResource work with nested attributes and fields_for

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.

Realy? No one?
Maybe here is not the place to ask such things. I’ll try somewhere else.

I’m having the same problem. Did you manage to solve it?

On 28.02.2012, at 19:34, Esti Alvarez wrote:

I’m having the same problem. Did you manage to solve it?

2) If nothing happens, customize view code:

“_member_fields.html.erb”:
<%= builder.label: name %>
<%= builder.text_field: name %>

<%= builder.label: Instrument %>
<%= builder.text_field: Instrument %>

“_form.html_erb”:
<% if @band.new_record? %>
<% f.fields_for :members_attributes do |member_form| %>
<%= render "member_fields, :builder => member_form %>
<% end %>
<% else %>
<% @band.members.each.with_index do |index, member| %>
<% f.fields_for member, “members_attributes[#{index}]” do
|member_form| %>
<%= render "member_fields, :builder => member_form %>
<% end %>
<% end %>
<% end %>

I could write something inaccurate, but like that.

On Tue, Feb 21, 2012 at 3:02 PM, Dimas C.
[email protected]wrote:

<%= form_for @band do |f| %>

]
parameters in a strange way. He turns the ‘members_attributes: […]’ in

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 member name some instrument

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
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

Which version of ActiveResource are you using? I also encounter what you
are encountering while using ActiveResource 3.2.1.

In 3.0.* there was “ActiveResource::Base.include_root_in_json” which you
could set to false. I am not positive if that’s the exact stumbling
point,
but it feels like it is.

Put a new file in your initializers with this code below and see if it
helps:

module ActiveResource
class Base
self.include_root_in_json = false
end
module Formats
module JsonFormat
def decode(json)
ActiveSupport::JSON.decode(json)
end
end
end
end

module ActiveModel
module Serializers
module JSON
def as_json(options = nil)
hash = serializable_hash(options)
if include_root_in_json
custom_root = options && options[:root]
hash = { custom_root || self.class.model_name.element => hash
}
end
hash
end

  def from_json(json)
    hash = ActiveSupport::JSON.decode(json)
    hash = hash.values.first if include_root_in_json
    self.attributes = hash
    self
  end
end

end
end

There’s a difference between ActiveModel 3.0.* and ActiveModel 3.2.*

ActiveModel 3.0.*

module ActiveModel
module Serializers
module JSON
def as_json(options = nil)
hash = serializable_hash(options)

    if include_root_in_json
      custom_root = options && options[:root]
      hash = { custom_root || self.class.model_name.element => hash 

}
end

    hash
  end
end

end
end

ActiveModel 3.2.*

module ActiveModel
module Serializers
module JSON
def as_json(options = nil)
root = include_root_in_json
root = options[:root] if options.try(:key?, :root)
if root
root = self.class.model_name.element if root == true
{ root => serializable_hash(options) }
else
serializable_hash(options)
end
end
end
end
end

I think this has something to do with it. Apologies if I set you on a
wild
goose chase.

I am creating application which requires User management of various
levels for authorization of different level of users.

I want several models:
Admin_user
Project Manager
Company
Clients
Account : have many users of all level.Every user have account.

See below the relations between our models:

  1. Admin_user : have many project managers and can give rights to
    project manager.It can add and delete Project managers.Admin can
    access or manage any level of this application i.e. Its a SUPER USER.

2.Project manager : have many companies.It can create and delete many
companies. Project Manager can also add new project manager if
Admin_user give right to him to create new project manager.its all
depends on Admin_user to give or take rights from PM.

  1. Companies : can create and delete many client_users but cannot
    create any company in th same level.Company also start or stop rights
    of client.

4.Client :can have account login and use services provided by company
nothing more than that.

This is my model Association. But i m confused what can i make first a
Account model or a Admin_user model.

Thanks

On 3 March 2012 10:12, Anushank L. [email protected] wrote:

I am creating application which requires User management of various
levels for authorization of different level of users.

I want several models:
Admin_user
Project Manager
Company
Clients
Account : have many users of all level.Every user have account.

Don’t have all those different models. Just have model User and use
Roles to limit capabilities of different user types. Have a look at
the cancan gem.

Colin

depends on Admin_user to give or take rights from PM.


You received this message because you are subscribed to the Google G. “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.


gplus.to/clanlaw