diff options
author | John Firebaugh <john_firebaugh@us.ibm.com> | 2011-02-21 22:55:49 -0800 |
---|---|---|
committer | John Firebaugh <john_firebaugh@us.ibm.com> | 2011-07-17 11:34:07 -0700 |
commit | 4860143ee4ccafef474f14f40b8f70c2b6b54656 (patch) | |
tree | 1fa5a120a4b1d6f79feaf4b7dc18c3c41de6e523 /activemodel/lib/active_model | |
parent | 1723a7a6c6098eaa61ce964bebca2ed5f8f947b7 (diff) | |
download | rails-4860143ee4ccafef474f14f40b8f70c2b6b54656.tar.gz rails-4860143ee4ccafef474f14f40b8f70c2b6b54656.tar.bz2 rails-4860143ee4ccafef474f14f40b8f70c2b6b54656.zip |
ActiveModel support for the :include serialization option
This commit moves support for the :include serialization option for
serializing associated objects out of ActiveRecord in into ActiveModel.
The following methods support the :include option:
* serializable_hash
* to_json
* to_xml
Instances must respond to methods named by the values of the :includes
array (or keys of the :includes hash). If an association method returns
an object that is_a?(Enumerable) (which AR has_many associations do), it
is assumed to be a collection association, and its elements must respond
to :serializable_hash. Otherwise it must respond to :serializable_hash
itself.
While here, fix #858, XmlSerializer should not singularize already
singular association names.
Diffstat (limited to 'activemodel/lib/active_model')
-rw-r--r-- | activemodel/lib/active_model/serialization.rb | 33 | ||||
-rw-r--r-- | activemodel/lib/active_model/serializers/xml.rb | 40 |
2 files changed, 72 insertions, 1 deletions
diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb index 0b4067257e..9260c5082d 100644 --- a/activemodel/lib/active_model/serialization.rb +++ b/activemodel/lib/active_model/serialization.rb @@ -77,7 +77,38 @@ module ActiveModel end method_names = Array.wrap(options[:methods]).select { |n| respond_to?(n) } - Hash[(attribute_names + method_names).map { |n| [n, send(n)] }] + hash = Hash[(attribute_names + method_names).map { |n| [n, send(n)] }] + + serializable_add_includes(options) do |association, records, opts| + hash[association] = if records.is_a?(Enumerable) + records.map { |a| a.serializable_hash(opts) } + else + records.serializable_hash(opts) + end + end + + hash end + + private + # Add associations specified via the <tt>:include</tt> option. + # + # Expects a block that takes as arguments: + # +association+ - name of the association + # +records+ - the association record(s) to be serialized + # +opts+ - options for the association records + def serializable_add_includes(options = {}) + return unless include = options[:include] + + unless include.is_a?(Hash) + include = Hash[Array.wrap(include).map { |n| [n, {}] }] + end + + include.each do |association, opts| + if records = send(association) + yield association, records, opts + end + end + end end end diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb index 9812af43d6..64dda3bcee 100644 --- a/activemodel/lib/active_model/serializers/xml.rb +++ b/activemodel/lib/active_model/serializers/xml.rb @@ -101,6 +101,7 @@ module ActiveModel @builder.tag!(*args) do add_attributes_and_methods + add_includes add_extra_behavior add_procs yield @builder if block_given? @@ -120,6 +121,45 @@ module ActiveModel end end + def add_includes + @serializable.send(:serializable_add_includes, options) do |association, records, opts| + add_associations(association, records, opts) + end + end + + # TODO This can likely be cleaned up to simple use ActiveSupport::XmlMini.to_tag as well. + def add_associations(association, records, opts) + merged_options = opts.merge(options.slice(:builder, :indent)) + merged_options[:skip_instruct] = true + + if records.is_a?(Enumerable) + tag = ActiveSupport::XmlMini.rename_key(association.to_s, options) + type = options[:skip_types] ? { } : {:type => "array"} + association_name = association.to_s.singularize + merged_options[:root] = association_name + + if records.empty? + @builder.tag!(tag, type) + else + @builder.tag!(tag, type) do + records.each do |record| + if options[:skip_types] + record_type = {} + else + record_class = (record.class.to_s.underscore == association_name) ? nil : record.class.name + record_type = {:type => record_class} + end + + record.to_xml merged_options.merge(record_type) + end + end + end + else + merged_options[:root] = association.to_s + records.to_xml(merged_options) + end + end + def add_procs if procs = options.delete(:procs) Array.wrap(procs).each do |proc| |