简体   繁体   中英

to_xml include doesn't work in rails 3.0.6 ruby 1.9.2

I've updated my server to ruby 1.9.2 and this stopped working (rails 3.0.6):

def index  
  @musicians = Musician.includes(:instruments)
  render :xml => @musicians.to_xml( :include => :instruments )
end

And the models:

class Musician < ActiveRecord::Base
  has_and_belongs_to_many :instruments
end

class Instrument < ActiveRecord::Base
  has_and_belongs_to_many :musicians
end

I'm getting this error:

undefined method `type' for nil:NilClass

Framework trace:

activesupport (3.0.6) lib/active_support/whiny_nil.rb:48:in `method_missing'
activerecord (3.0.6) lib/active_record/serializers/xml_serializer.rb:230:in `compute_type'
activemodel (3.0.6) lib/active_model/serializers/xml.rb:22:in `initialize'
activemodel (3.0.6) lib/active_model/serializers/xml.rb:75:in `new'
activemodel (3.0.6) lib/active_model/serializers/xml.rb:75:in `block in serializable_attributes'

Any clue what I'm doing wrong?

Maybe this is related to: https://rails.lighthouseapp.com/projects/8994/tickets/4840-to_xml-doesnt-work-in-such-case-eventselecttitle-as-tto_xml

This is a core issue with Rails. What's happening is that when Instruments are being included, an instrument_id attribute is getting added. Then, when each Instrument is serialized, the XmlSerializer class determines the type of that attribute based on the Instrument class' definition, using the type attribute for each column. Since the instrument_id attribute does not exist in the class definition, a nil object is returned which, as of Ruby 1.9, does not have a type attribute, which Rails is depending on.

(I don't think the patch in the thread you linked to works -- but the one I've provided below does.)

There are two ways to fix this:

  1. Don't serialize instrument_id (good idea).
     render :xml => @musicians.to_xml( :include => { :instruments => { :except => :instrument_id } } ) 
  2. Or patch Rails core (bad idea).
     --- a/activerecord/lib/active_record/serializers/xml_serializer.rb 2011-04-20 15:01:10.000000000 -0700 +++ b/activerecord/lib/active_record/serializers/xml_serializer.rb 2011-04-20 15:00:42.000000000 -0700 @@ -226,8 +226,10 @@ class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc: def compute_type + Rails.logger.info("key: #{name}, hash: #{@serializable.class.columns_hash[name]}") type = @serializable.class.serialized_attributes.has_key?(name) ? - super : @serializable.class.columns_hash[name].type + super : @serializable.class.columns_hash[name].nil? ? + NilClass : @serializable.class.columns_hash[name].type case type when :text 

I guess if you are using HABTM association you should have a join table called musicians_instruments you have to include that nested as well.

render :xml => @musicians.to_xml( :include => {:musicians_instruments =>{:include=>:instruments}} )

Note that the include is different than in ActiveRecord associations.

Update

If you create a model for the join table, you can change the has_and_belongs_to_many to a has_many :through , its used more often and does the same.

#musician.rb
has_many :musicians_instruments
has_many :instruments, :through=>:musicians_instruments

#instrument.rb
has_many :musicians_instruments
has_many :musicians, :through=>:musicians_instruments

This one should work properly with the to_xml nested includes.

Here is a workaround that did the trick for me.

I converted all the active record objects to hashes using the attributes call which fixed the issue for me

@musicians = @musicians.map{|a| a.attributes}

@musicians.to_xml

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM