Serializable_hash and serializable_add_includes

I’m trying to write a helper method similar to attr_accessible but
instead it will be used to whitelist which attributes are available for
serialization (attr_serializable). My first attempt at this was to
override serialized_hash and modify the :only option to include nothing
but serializable attributes. I’m facing a problem when nested models
are ‘included’ using the :include option. The :only options for the
parent model are also getting sent to the nested model(s). Here’s an
example:

model1.to_json(:include => model2, :only => [:id])

First serializable_hash gets called for model1 and is passed an options
hash with :only set to an array including :id. This makes sense to me
since the :only option was specified as a top level options. Next
serializable_hash gets called for model2 and it is also passed an
options hash that include :id in the :only option. Why would :only =>
[:id] be an option for the included model (model2)? Same goes for
:methods and :except options set at the top level. I would expect the
:only , :except, and :methods options to be removed if they are not
specified directly on the included model as such:

model1.to_json(:include => {:model2 => {:only =>
[:some_other_property]}, :only => [:id])

Can someone please explain to me why it works this way.

On Wed, Jan 25, 2012 at 7:35 AM, Jacob H. [email protected] wrote:

model1.to_json(:include => {:model2 => {:only =>
[:some_other_property]}, :only => [:id])

Can someone please explain to me why it works this way.

The code in active_model/serialization.rb was changed significantly
between 3.1.3 and 3.2.0. On which version are you working? In
my case, the code in 3.2.0 did the right thing :slight_smile:

:only => [] resulted (correctly) in an empty list in 3.2.0, while in
3.1.3
it resulted in all attributes included.

These 2 commits seem to be most relevant:

HTH,

Peter

Peter V. wrote in post #1042445:

On Wed, Jan 25, 2012 at 7:35 AM, Jacob H. [email protected] wrote:

model1.to_json(:include => {:model2 => {:only =>
[:some_other_property]}, :only => [:id])

Can someone please explain to me why it works this way.

The code in active_model/serialization.rb was changed significantly
between 3.1.3 and 3.2.0. On which version are you working? In
my case, the code in 3.2.0 did the right thing :slight_smile:

:only => [] resulted (correctly) in an empty list in 3.2.0, while in
3.1.3
it resulted in all attributes included.

These 2 commits seem to be most relevant:

Revert "Implement ArraySerializer and move old serialization API to a… · rails/rails@5b2eb64 · GitHub
Remove Array.wrap call in ActiveModel · rails/rails@2a663dc · GitHub

HTH,

Peter

Thanks for the reply and the links. I am running on 3.1.3 which may be
the problem but after walking through the commit you sent I believe
3.2.0 is handling it the same way. My problem is the same options are
being used for the parent and included models.

book.to_json( :only => [:id] )

{ “id”: 1 } ← expected

book.to_json(:only => [:id], :include => :chapters)

{ “id”: 1, “chapters”:[ { “id” : 1 } ] } ← sorta expected but shouldn’t
be

book.to_json(:only => [:id, :name], :include => :chapters)

{ “id”: 1, “name”:“The Ruby Way”, “chapters”:[ { “id” : 1,
“name”:“serialization” } ] } ← don’t understand

Notice how name is included for the chapters as well? The reason this
happens is because :only => [:id, :name] is passed to the
serializable_hash call for both book and chapters. My argument is that
the options should only be passed to the parent model (books) but not
the included association (chapters). Let’s expand upon the previous
example and say we added one more attribute to the only option.

book.to_json(:only => [:id, :name, :price], :include => :chapters)

{ “id”: 1, “name”:“The Ruby Way”, “price”:2000, “chapters”:[ { “id” : 1,
“name”:“serialization” } ] }

In the example above, the “price” attribute was added as an :only
option. Since book has a price the value is included in the json. The
problem is chapters doesn’t have a price but the option is still passed
in during serialization anyway. Why not start clean for each included
relationship? Instead of passing the same options along, exclude the
top level options for :only, :except, and :methods when serializing
included associations.

What are your thoughts

On Wed, Jan 25, 2012 at 4:55 PM, Peter V.
[email protected]wrote:

Could it be that is actually solved in Rails 3.2.0 ?
Did you effectively test with 3.2.0?

I just tested now in Rails 3.2.0 and found this:

1.9.3-p0 :012 > p.to_xml(:only => :name, :include => :child)
=> “<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n\n
dad\n \n <created-at
type="datetime">2012-01-24T10:05:43Z\n <id
type="integer">1\n Sarah\n <parent-id
type="integer">1\n <updated-at
type="datetime">2012-01-24T10:05:43Z\n
\n\n”

This seems to indicate that for the child, all attributes are shown,
independent of the :only on the parent.

There is recent discussion about this on

In the example above, the “price” attribute was added as an :only
I agree (the current behavior seems a bug to me). I did not test this
specific behavior in Rails 3.2.0

I have the impression now that the issue is resolved in 3.2.0.

Peter

On Wed, Jan 25, 2012 at 4:03 PM, Jacob H. [email protected] wrote:

The code in active_model/serialization.rb was changed significantly
Remove Array.wrap call in ActiveModel · rails/rails@2a663dc · GitHub
book.to_json( :only => [:id] )
{ “id”: 1, “name”:“The Ruby Way”, “chapters”:[ { “id” : 1,
“name”:“serialization” } ] } ← don’t understand

Notice how name is included for the chapters as well? The reason this
happens is because :only => [:id, :name] is passed to the
serializable_hash call for both book and chapters.

I can confirm your observation (Rails 3.1.3):

Calling user.to_xml with the options:

{:include=>{:address=>{}}, :only=>[:city, :first_name]}

where :city is only in address and :first_name is only in user, I get

<first_name>Jan</first_name>

AND

\n Brussel\n

in the result. The latter was not expected (I did not
specify any "only options in the included :address.

The reason I never bumped into this, is that for each
to_xml of main object and associated objects, I run
through a white_list (that explicitly sets the :only list),
to protect what I will show in the API.

Specifically, this options set

Rails 3.1.3 :

book.to_json(:only => [:id], :include => {:chapters => {:only =>
:nothing}})

yes, the :nothing looks funny …

Rails 3.2.0 :

book.to_json(:only => [:id], :include => {:chapters => {:only => []}})

would be a work-around for your case (if you wanted nothing shown
from the chapters attributes).

In the example above, the “price” attribute was added as an :only
option. Since book has a price the value is included in the json. The
problem is chapters doesn’t have a price but the option is still passed
in during serialization anyway. Why not start clean for each included
relationship? Instead of passing the same options along, exclude the
top level options for :only, :except, and :methods when serializing
included associations.

What are your thoughts

I agree (the current behavior seems a bug to me). I did not test this
specific behavior in Rails 3.2.0

HTH,

Peter