Skip to content
This repository
Browse code

to_xml should also rely on serializable hash.

  • Loading branch information...
commit 51bef9d8fb0b4da7a104425ab8545e9331387743 1 parent cb0dbe3
José Valim josevalim authored
46 activemodel/lib/active_model/serializers/xml.rb
@@ -15,10 +15,10 @@ class Serializer #:nodoc:
15 15 class Attribute #:nodoc:
16 16 attr_reader :name, :value, :type
17 17
18   - def initialize(name, serializable, raw_value=nil)
  18 + def initialize(name, serializable, value)
19 19 @name, @serializable = name, serializable
20   - raw_value = raw_value.in_time_zone if raw_value.respond_to?(:in_time_zone)
21   - @value = raw_value || @serializable.send(name)
  20 + value = value.in_time_zone if value.respond_to?(:in_time_zone)
  21 + @value = value
22 22 @type = compute_type
23 23 end
24 24
@@ -49,40 +49,24 @@ class MethodAttribute < Attribute #:nodoc:
49 49 def initialize(serializable, options = nil)
50 50 @serializable = serializable
51 51 @options = options ? options.dup : {}
52   -
53   - @options[:only] = Array.wrap(@options[:only]).map { |n| n.to_s }
54   - @options[:except] = Array.wrap(@options[:except]).map { |n| n.to_s }
55 52 end
56 53
57   - # To replicate the behavior in ActiveRecord#attributes, <tt>:except</tt>
58   - # takes precedence over <tt>:only</tt>. If <tt>:only</tt> is not set
59   - # for a N level model but is set for the N+1 level models,
60   - # then because <tt>:except</tt> is set to a default value, the second
61   - # level model can have both <tt>:except</tt> and <tt>:only</tt> set. So if
62   - # <tt>:only</tt> is set, always delete <tt>:except</tt>.
63   - def attributes_hash
64   - attributes = @serializable.attributes
65   - if options[:only].any?
66   - attributes.slice(*options[:only])
67   - elsif options[:except].any?
68   - attributes.except(*options[:except])
69   - else
70   - attributes
71   - end
  54 + def serializable_hash
  55 + @serializable.serializable_hash(@options.except(:include))
72 56 end
73 57
74   - def serializable_attributes
75   - attributes_hash.map do |name, value|
76   - self.class::Attribute.new(name, @serializable, value)
  58 + def serializable_collection
  59 + methods = Array.wrap(options[:methods]).map(&:to_s)
  60 + serializable_hash.map do |name, value|
  61 + name = name.to_s
  62 + if methods.include?(name)
  63 + self.class::MethodAttribute.new(name, @serializable, value)
  64 + else
  65 + self.class::Attribute.new(name, @serializable, value)
  66 + end
77 67 end
78 68 end
79 69
80   - def serializable_methods
81   - Array.wrap(options[:methods]).map do |name|
82   - self.class::MethodAttribute.new(name.to_s, @serializable) if @serializable.respond_to?(name.to_s)
83   - end.compact
84   - end
85   -
86 70 def serialize
87 71 require 'builder' unless defined? ::Builder
88 72
@@ -114,7 +98,7 @@ def add_extra_behavior
114 98 end
115 99
116 100 def add_attributes_and_methods
117   - (serializable_attributes + serializable_methods).each do |attribute|
  101 + serializable_collection.each do |attribute|
118 102 key = ActiveSupport::XmlMini.rename_key(attribute.name, options)
119 103 ActiveSupport::XmlMini.to_tag(key, attribute.value,
120 104 options.merge(attribute.decorations))
17 activemodel/test/cases/serializers/xml_serialization_test.rb
@@ -33,6 +33,12 @@ def attributes
33 33 end
34 34 end
35 35
  36 +class SerializableContact < Contact
  37 + def serializable_hash(options={})
  38 + super(options.merge(:only => [:name, :age]))
  39 + end
  40 +end
  41 +
36 42 class XmlSerializationTest < ActiveModel::TestCase
37 43 def setup
38 44 @contact = Contact.new
@@ -96,6 +102,17 @@ def setup
96 102 assert_match %r{<createdAt}, @xml
97 103 end
98 104
  105 + test "should use serialiable hash" do
  106 + @contact = SerializableContact.new
  107 + @contact.name = 'aaron stack'
  108 + @contact.age = 25
  109 +
  110 + @xml = @contact.to_xml
  111 + assert_match %r{<name>aaron stack</name>}, @xml
  112 + assert_match %r{<age type="integer">25</age>}, @xml
  113 + assert_no_match %r{<awesome>}, @xml
  114 + end
  115 +
99 116 test "should allow skipped types" do
100 117 @xml = @contact.to_xml :skip_types => true
101 118 assert_match %r{<age>25</age>}, @xml
2  activerecord/lib/active_record/serializers/xml_serializer.rb
@@ -179,7 +179,7 @@ def to_xml(options = {}, &block)
179 179 class XmlSerializer < ActiveModel::Serializers::Xml::Serializer #:nodoc:
180 180 def initialize(*args)
181 181 super
182   - options[:except] |= Array.wrap(@serializable.class.inheritance_column)
  182 + options[:except] = Array.wrap(options[:except]) | Array.wrap(@serializable.class.inheritance_column)
183 183 end
184 184
185 185 class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:

4 comments on commit 51bef9d

Deepak Prasanna

Hi,

This commit has broken a test.

1) Failure:
test_to_xml_with_private_method_name_as_attribute(BaseTest) [/home/shikeb/rails/activeresource/test/cases/base_test.rb:1007]:
Exception raised:
ArgumentError: wrong number of arguments (0 for 2..3).

289 tests, 892 assertions, 1 failures, 0 errors, 0 skips

Test run options: --seed 46552

Thanks.

cc @josevalim @spastorino

Jon Leighton

@josevalim I have fixed this in a15424b, please could you check that you are happy with the fix?

José Valim
Owner
Jon Leighton

Yes, I agree with you. I also think that our reliance on method_missing is a bit problematic, for example consider this:

def foo
  "bar"
end
class Person < ActiveResource::Base; end
Person.new(:foo => "baz").foo # => "bar"

It's not straightforward to avoid though. For example, in Active Record you can "piggyback" attributes onto a model: Person.select("people.*, 'foo' as bar") - this either requires using method_missing, or adding instance-specific behaviour, which is very slow as it requires creating a singleton class.

Please sign in to comment.
Something went wrong with that request. Please try again.