Skip to content
Newer
Older
100644 239 lines (203 sloc) 7.45 KB
5b2eb64 @josevalim Revert "Implement ArraySerializer and move old serialization API to a…
josevalim authored
1 require 'active_support/core_ext/class/attribute_accessors'
2 require 'active_support/core_ext/array/conversions'
3 require 'active_support/core_ext/hash/conversions'
4 require 'active_support/core_ext/hash/slice'
4dd5635 @pixeltrix Only call `in_time_zone` on Time or DateTime instances
pixeltrix authored
5 require 'active_support/core_ext/time/acts_like'
5b2eb64 @josevalim Revert "Implement ArraySerializer and move old serialization API to a…
josevalim authored
6
d2b78b3 @josh Initial extraction of AMo xml serializer
josh authored
7 module ActiveModel
8 module Serializers
10d9cb2 fix AM::Serializers titles [ci skip]
Francesco Rodriguez authored
9 # == Active Model XML Serializer
d2b78b3 @josh Initial extraction of AMo xml serializer
josh authored
10 module Xml
11 extend ActiveSupport::Concern
5b2eb64 @josevalim Revert "Implement ArraySerializer and move old serialization API to a…
josevalim authored
12 include ActiveModel::Serialization
13
0703295 AM::Serializers::Xml depends on AM::Naming
Francesco Rodriguez authored
14 included do
15 extend ActiveModel::Naming
16 end
17
5b2eb64 @josevalim Revert "Implement ArraySerializer and move old serialization API to a…
josevalim authored
18 class Serializer #:nodoc:
19 class Attribute #:nodoc:
20 attr_reader :name, :value, :type
21
22 def initialize(name, serializable, value)
23 @name, @serializable = name, serializable
4dd5635 @pixeltrix Only call `in_time_zone` on Time or DateTime instances
pixeltrix authored
24
25 if value.acts_like?(:time) && value.respond_to?(:in_time_zone)
26 value = value.in_time_zone
27 end
28
5b2eb64 @josevalim Revert "Implement ArraySerializer and move old serialization API to a…
josevalim authored
29 @value = value
30 @type = compute_type
31 end
32
33 def decorations
34 decorations = {}
35 decorations[:encoding] = 'base64' if type == :binary
36 decorations[:type] = (type == :string) ? nil : type
37 decorations[:nil] = true if value.nil?
38 decorations
39 end
40
41 protected
42
43 def compute_type
44 return if value.nil?
45 type = ActiveSupport::XmlMini::TYPE_NAMES[value.class.name]
46 type ||= :string if value.respond_to?(:to_str)
47 type ||= :yaml
48 type
49 end
50 end
51
52 class MethodAttribute < Attribute #:nodoc:
53 end
54
55 attr_reader :options
56
57 def initialize(serializable, options = nil)
58 @serializable = serializable
59 @options = options ? options.dup : {}
60 end
61
62 def serializable_hash
63 @serializable.serializable_hash(@options.except(:include))
64 end
65
66 def serializable_collection
2a663dc @rafaelfranca Remove Array.wrap call in ActiveModel
rafaelfranca authored
67 methods = Array(options[:methods]).map(&:to_s)
5b2eb64 @josevalim Revert "Implement ArraySerializer and move old serialization API to a…
josevalim authored
68 serializable_hash.map do |name, value|
69 name = name.to_s
70 if methods.include?(name)
71 self.class::MethodAttribute.new(name, @serializable, value)
72 else
73 self.class::Attribute.new(name, @serializable, value)
74 end
75 end
76 end
77
78 def serialize
79 require 'builder' unless defined? ::Builder
80
81 options[:indent] ||= 2
82 options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
83
84 @builder = options[:builder]
85 @builder.instruct! unless options[:skip_instruct]
c6bc8e6 @josh Break up concerns for choosing what attributes should be serialized a…
josh authored
86
5b2eb64 @josevalim Revert "Implement ArraySerializer and move old serialization API to a…
josevalim authored
87 root = (options[:root] || @serializable.class.model_name.element).to_s
88 root = ActiveSupport::XmlMini.rename_key(root, options)
89
90 args = [root]
91 args << {:xmlns => options[:namespace]} if options[:namespace]
92 args << {:type => options[:type]} if options[:type] && !options[:skip_types]
93
94 @builder.tag!(*args) do
95 add_attributes_and_methods
96 add_includes
97 add_extra_behavior
98 add_procs
99 yield @builder if block_given?
100 end
101 end
102
103 private
104
105 def add_extra_behavior
106 end
107
108 def add_attributes_and_methods
109 serializable_collection.each do |attribute|
110 key = ActiveSupport::XmlMini.rename_key(attribute.name, options)
111 ActiveSupport::XmlMini.to_tag(key, attribute.value,
112 options.merge(attribute.decorations))
113 end
114 end
115
116 def add_includes
117 @serializable.send(:serializable_add_includes, options) do |association, records, opts|
118 add_associations(association, records, opts)
119 end
120 end
121
d67b289 update ActiveModel::Serializers documentation [ci skip]
Francesco Rodriguez authored
122 # TODO: This can likely be cleaned up to simple use ActiveSupport::XmlMini.to_tag as well.
5b2eb64 @josevalim Revert "Implement ArraySerializer and move old serialization API to a…
josevalim authored
123 def add_associations(association, records, opts)
124 merged_options = opts.merge(options.slice(:builder, :indent))
125 merged_options[:skip_instruct] = true
126
78f5874 @anthonyalberto Following the false issue reporting I did here : #6958
anthonyalberto authored
127 [:skip_types, :dasherize, :camelize].each do |key|
128 merged_options[key] = options[key] if merged_options[key].nil? && !options[key].nil?
129 end
130
a8637cf @jonleighton Use respond_to?(:to_ary) rather than is_a?(Enumerable) to detect coll…
jonleighton authored
131 if records.respond_to?(:to_ary)
132 records = records.to_ary
133
5b2eb64 @josevalim Revert "Implement ArraySerializer and move old serialization API to a…
josevalim authored
134 tag = ActiveSupport::XmlMini.rename_key(association.to_s, options)
135 type = options[:skip_types] ? { } : {:type => "array"}
136 association_name = association.to_s.singularize
137 merged_options[:root] = association_name
138
139 if records.empty?
140 @builder.tag!(tag, type)
141 else
142 @builder.tag!(tag, type) do
143 records.each do |record|
144 if options[:skip_types]
145 record_type = {}
146 else
147 record_class = (record.class.to_s.underscore == association_name) ? nil : record.class.name
148 record_type = {:type => record_class}
149 end
150
151 record.to_xml merged_options.merge(record_type)
152 end
153 end
154 end
155 else
156 merged_options[:root] = association.to_s
9504b44 @steveklabnik Specify type of singular association during serialization
steveklabnik authored
157
158 unless records.class.to_s.underscore == association.to_s
159 merged_options[:type] = records.class.name
160 end
161
162 records.to_xml merged_options
5b2eb64 @josevalim Revert "Implement ArraySerializer and move old serialization API to a…
josevalim authored
163 end
164 end
165
166 def add_procs
167 if procs = options.delete(:procs)
2a663dc @rafaelfranca Remove Array.wrap call in ActiveModel
rafaelfranca authored
168 Array(procs).each do |proc|
5b2eb64 @josevalim Revert "Implement ArraySerializer and move old serialization API to a…
josevalim authored
169 if proc.arity == 1
170 proc.call(options)
171 else
172 proc.call(options, @serializable)
173 end
174 end
175 end
176 end
177 end
178
179 # Returns XML representing the model. Configuration can be
180 # passed through +options+.
181 #
d67b289 update ActiveModel::Serializers documentation [ci skip]
Francesco Rodriguez authored
182 # Without any +options+, the returned XML string will include all the
183 # model's attributes.
5b2eb64 @josevalim Revert "Implement ArraySerializer and move old serialization API to a…
josevalim authored
184 #
185 # user = User.find(1)
186 # user.to_xml
187 #
188 # <?xml version="1.0" encoding="UTF-8"?>
189 # <user>
190 # <id type="integer">1</id>
191 # <name>David</name>
192 # <age type="integer">16</age>
5646d65 @acapilleri changed xml type datetime to dateTime, fixes #6328
acapilleri authored
193 # <created-at type="dateTime">2011-01-30T22:29:23Z</created-at>
5b2eb64 @josevalim Revert "Implement ArraySerializer and move old serialization API to a…
josevalim authored
194 # </user>
195 #
d67b289 update ActiveModel::Serializers documentation [ci skip]
Francesco Rodriguez authored
196 # The <tt>:only</tt> and <tt>:except</tt> options can be used to limit the
197 # attributes included, and work similar to the +attributes+ method.
5b2eb64 @josevalim Revert "Implement ArraySerializer and move old serialization API to a…
josevalim authored
198 #
199 # To include the result of some method calls on the model use <tt>:methods</tt>.
200 #
201 # To include associations use <tt>:include</tt>.
202 #
b512012 @vijaydev copy edits [ci skip]
vijaydev authored
203 # For further documentation, see <tt>ActiveRecord::Serialization#to_xml</tt>
5b2eb64 @josevalim Revert "Implement ArraySerializer and move old serialization API to a…
josevalim authored
204 def to_xml(options = {}, &block)
205 Serializer.new(self, options).serialize(&block)
206 end
d2b78b3 @josh Initial extraction of AMo xml serializer
josh authored
207
d67b289 update ActiveModel::Serializers documentation [ci skip]
Francesco Rodriguez authored
208 # Sets the model +attributes+ from a JSON string. Returns +self+.
209 #
210 # class Person
211 # include ActiveModel::Serializers::Xml
212 #
213 # attr_accessor :name, :age, :awesome
214 #
215 # def attributes=(hash)
216 # hash.each do |key, value|
217 # instance_variable_set("@#{key}", value)
218 # end
219 # end
220 #
221 # def attributes
222 # instance_values
223 # end
224 # end
225 #
226 # xml = { name: 'bob', age: 22, awesome:true }.to_xml
227 # person = Person.new
228 # person.from_xml(xml) # => #<Person:0x007fec5e3b3c40 @age=22, @awesome=true, @name="bob">
229 # person.name # => "bob"
230 # person.age # => 22
231 # person.awesome # => true
5b2eb64 @josevalim Revert "Implement ArraySerializer and move old serialization API to a…
josevalim authored
232 def from_xml(xml)
233 self.attributes = Hash.from_xml(xml).values.first
234 self
d2b78b3 @josh Initial extraction of AMo xml serializer
josh authored
235 end
236 end
237 end
5b2eb64 @josevalim Revert "Implement ArraySerializer and move old serialization API to a…
josevalim authored
238 end
Something went wrong with that request. Please try again.