On Sunday, May 1, 2016 at 10:35:24 PM UTC-7, Patrick Kuykendall wrote:
>
> Hello,
>
> I'm running into a little hiccup with the json_serializer plug-in. If I 
> try to include an association that is an array, the to_json call doesn't 
> process the items in the array, just the array itself. I added the 
> activesupport fixes you mention in the documentation, but I'm still having 
> this issue.
>
> Something like this should be able to reproduce it:
>
> class User < Sequel::Model(DB[:users])
>   one_to_many :addresses
> end
>
> class Address < Sequel::Model(DB[:addresses])
> end
>
> User.first.to_json(
>     only: [:first_name, :last_name],
>     include: {
>       addresses: { only: [:line_1, :line_2, :city, :state, :zip_code] } 
>     }
>   )
> #=> {first_name: 'John', last_name: 'Doe', addresses: [{address_id: 1, 
> address_type_id: 1, line_1: 'XXX' line_2: nil, city 'XXX', state: 'XXX', 
> zip_code: '12345', created_on: 2016-05-01T01:30:00, updated_on: nil, 
> deleted_on: nil}]}
>
>
> I dove into it a little and I found that InstanceMethods#to_json performs 
> a check to see if the included association is an Array. It seems this isn't 
> catching for me, and it's processing the array object as a whole.
>
> Here's the section: [direct link](
> https://github.com/jeremyevans/sequel/blob/master/lib/sequel/plugins/json_serializer.rb#L303
> )
>
> cols.each{|c| h[c.to_s] = get_column_value(c)}
> if inc = opts[:include]
>   if inc.is_a?(Hash)
>     inc.each do |k, v|
>     v = v.empty? ? [] : [v]
>     h[k.to_s]  = case objs = send(k)
>       when 'Array'
>         objs.map{|obj| Literal.new(Sequel.object_to_json(obj, *v))}
>       else
>         Literal.new(Sequel.object_to_json(objs, *v))
>       end
>     end
>   else
>     Array(inc).each{|c| h[c.to_s] = send(c)}
>   end
> end
>
> I was able to have it properly render the objects contained in the array 
> with this. (Pardon the dirty hack, I was just testing it out)
>
> cols.each{|c| h[c.to_s] = get_column_value(c)}
> if inc = opts[:include]
>   if inc.is_a?(Hash)
>     inc.each do |k, v|
>     v = v.empty? ? [] : [v]
>     objs = send(k)
>     h[k.to_s] = case objs.class.to_s
>       when 'Array'
>         objs.map{|obj| Literal.new(Sequel.object_to_json(obj, *v))}
>       else
>         Literal.new(Sequel.object_to_json(objs, *v))
>       end
>     end
>   else
>     Array(inc).each{|c| h[c.to_s] = send(c)}
>   end
> end
>
> Let me know if I might be doing something wrong here.
> Thank you.
>

Unfortunately, this isn't a self contained example, so at most I can give 
an educated guess.  From the change your making it looks like you are just 
changing the case statement (please use a diff in the future to make the 
change more obvious).  The only situation I can think of where that would 
solve the problem is if you were using the association_proxies plugin, in 
which case the association method returns a proxy and not an array, but 
calling class on the object will return Array.

I think one possible way to get what you want without modifying the 
json_serializer plugin would be to preprocess the addresses:

u = User.first
u.addresses.each{|a| a.json_serializer_opts(only: [:line_1, :line_2, :city, 
:state, :zip_code])}
u.to_json(only: [:first_name, :last_name], include: :addresses)

In terms of modifying the json_serializer plugin, we could see if the 
:include value is also an association, and if so if the association returns 
an array, falling back to the current code for detecting arrays if the 
value is not an association.  Something like:

diff --git a/lib/sequel/plugins/json_serializer.rb 
b/lib/sequel/plugins/json_serializer.rb
index c953295..8215548 100644
--- a/lib/sequel/plugins/json_serializer.rb
+++ b/lib/sequel/plugins/json_serializer.rb
@@ -300,8 +300,16 @@ module Sequel
             if inc.is_a?(Hash)
               inc.each do |k, v|
                 v = v.empty? ? [] : [v]
-                h[k.to_s] = case objs = send(k)
-                when Array
+
+                objs = send(k)
+
+                is_array = if r = model.association_reflection(k)
+                  r.returns_array?
+                else
+                  objs.is_a?(Array)
+                end
+
+                h[k.to_s] = if is_array
                   objs.map{|obj| Literal.new(Sequel.object_to_json(obj, 
*v))}
                 else
                   Literal.new(Sequel.object_to_json(objs, *v))

Can you try that out and see if it works for you?

Thanks,
Jeremy

-- 
You received this message because you are subscribed to the Google Groups 
"sequel-talk" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/sequel-talk.
For more options, visit https://groups.google.com/d/optout.

Reply via email to