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/s开发者_如何转开发erializers/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:
-
Don't serialize
instrument_id
(good idea).render :xml => @musicians.to_xml( :include => { :instruments => { :except => :instrument_id } } )
- 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
精彩评论