super_awesome_resource_serializer 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,2 @@
1
+ = Super Awesome Resource Serializer
2
+
data/Rakefile ADDED
@@ -0,0 +1,35 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ desc 'Default: run unit tests'
5
+ task :default => :test
6
+
7
+ begin
8
+ require 'rspec'
9
+ require 'rspec/core/rake_task'
10
+ desc 'Run the unit tests'
11
+ RSpec::Core::RakeTask.new(:test)
12
+ rescue LoadError
13
+ task :test do
14
+ raise "You must have rspec 2.0 installed to run the tests"
15
+ end
16
+ end
17
+
18
+ begin
19
+ require 'jeweler'
20
+ Jeweler::Tasks.new do |gem|
21
+ gem.name = "super_awesome_resource_serializer"
22
+ gem.summary = %Q{Quickly create custom serializations for resources}
23
+ gem.description = %Q{Quickly create custom serializations for resources rather than relying on the generated ones.}
24
+ gem.authors = ["Brian Durand"]
25
+ gem.email = ["mdobrota@tribune.com", "ddpr@tribune.com"]
26
+ gem.files = FileList["lib/**/*", "spec/**/*", "README.rdoc", "Rakefile", "License.txt"].to_a
27
+ gem.has_rdoc = true
28
+ gem.rdoc_options << '--line-numbers' << '--inline-source' << '--main' << 'README.rdoc'
29
+ gem.extra_rdoc_files = ["README.rdoc"]
30
+ gem.add_dependency('activesupport')
31
+ gem.add_development_dependency('rspec', '>= 2.0.0')
32
+ end
33
+ Jeweler::RubygemsDotOrgTasks.new
34
+ rescue LoadError
35
+ end
@@ -0,0 +1,349 @@
1
+ require 'active_support'
2
+ require 'active_support/core_ext'
3
+ require 'active_support/json'
4
+
5
+ # This class provides the base functionality for serializing Activeobject objects to XML, JSON, or YAML. It is intended to be
6
+ # used with web services where tighter control is desired than is provided by ActiveResource out of the box. For instance, you may have
7
+ # some sensitive fields in your Activeobject that should not be made available through a public web service API unless the user
8
+ # is a trusted user.
9
+ #
10
+ # To create a serializer for you model, you simply need to make a new class named ModelNameSerializer where ModelName is the
11
+ # class name of your model. The serializer class itself then needs to define how each field should be serialized using the serialize
12
+ # method.
13
+ class SuperAwesomeResourceSerializer
14
+
15
+ STATIC_CLASSES = [String, Symbol, Fixnum, Float, NilClass, TrueClass, FalseClass, Object].inject({}){|hash, klass| hash[klass] = true; hash}
16
+
17
+ class << self
18
+ # Get the serializer class for a given object.
19
+ def for_object (object, options = {}, klass = object.class)
20
+ serializer_class = "#{klass.name}Serializer".constantize
21
+ return serializer_class.new(object, options)
22
+ rescue
23
+ if klass.superclass.nil?
24
+ raise NameError.new("no known serializers for class #{object.class.name}")
25
+ else
26
+ return for_object(object, options, klass.superclass)
27
+ end
28
+ end
29
+
30
+ # Define how to serialize and deserialize a field. The following options can be passed in to define how to serialize an object.
31
+ # * :element - The name of the field in the serialized output. This can be used to give a object a prettier name on output.
32
+ # * :getter - The getter to get the field value.
33
+ # * :setter - The setter to set the field value.
34
+ # * :exclude - Indicate that this field should be excluded unless specifically asked for. Values can be :getter, :setter, or true.
35
+ #
36
+ # By default, getters and setters will call the field accessor on the serializer (if it is defined) or on the object.
37
+ # This can be overridden by specifying a different method name to call, or a proc that will be yielded to with the object
38
+ # (and value for a setter). Finally, getters and setters can be disabled by setting them to false.
39
+ def serialize (field, options = {})
40
+ serialized_field_info << SerializedFieldInfo.new(field, options)
41
+ end
42
+
43
+ # Merge the values of two field lists as used in :include, :exclude, and :only options.
44
+ def merge_field_lists (list_1, list_2)
45
+ if list_1.blank?
46
+ if list_2.blank?
47
+ return nil
48
+ else
49
+ return list_2
50
+ end
51
+ elsif list_2.blank?
52
+ return list_1
53
+ else
54
+ list_1 = normalize_field_list(list_1)
55
+ list_2 = normalize_field_list(list_2)
56
+ merge_hashes(list_1, list_2)
57
+ end
58
+ end
59
+
60
+ # Turn an :include, :exclude, or :only field list into a hash for consistent access.
61
+ def normalize_field_list (field_list)
62
+ case field_list
63
+ when nil
64
+ return {}
65
+ when true
66
+ return {}
67
+ when String
68
+ return {field_list.to_sym => true}
69
+ when Symbol
70
+ return {field_list => true}
71
+ when Array
72
+ hash = {}
73
+ field_list.each do |value|
74
+ if value.is_a?(Hash)
75
+ hash.merge!(normalize_field_list(value))
76
+ else
77
+ hash[value.to_sym] = true
78
+ end
79
+ end
80
+ return hash
81
+ when Hash
82
+ hash = {}
83
+ field_list.each_pair do |key, value|
84
+ value = normalize_field_list(value) unless value == true
85
+ hash[key.to_sym] = value
86
+ end
87
+ return hash
88
+ else
89
+ raise ArgumentError.new("illegal type in field list: #{field_list.class.name}")
90
+ end
91
+ end
92
+
93
+ # Get a list of serialized field information for preforming an action (either :getter or :setter) on the class.
94
+ # Option can pass in values for :include, :exclude, and :only to indicate filters on what fields are allowed.
95
+ # Include can be used to add fields that have been defined but excluded by default, exclude can be used to remove
96
+ # fields included by default, while only is used to specify the exact list of fields to use.
97
+ def serializable_fields (action, options = {})
98
+ if self == SuperAwesomeResourceSerializer
99
+ return []
100
+ else
101
+ exclude_fields = options[:exclude] || {}
102
+ include_fields = options[:include] || {}
103
+ only_fields = options[:only] || {}
104
+
105
+ all_field_info = superclass.serializable_fields(options) + serialized_field_info
106
+
107
+ if only_fields.empty?
108
+ excluded_field_names = exclude_fields.collect{|k,v| k if v == true}.compact
109
+ excluded_field_names = (excluded_field_names + all_field_info.select{|field| field.excluded?(action)}.collect{|field| field.name}) - include_fields.keys
110
+ all_field_info.delete_if{|field| excluded_field_names.include?(field.name)} unless excluded_field_names.empty?
111
+ else
112
+ all_field_info.delete_if{|field| not only_fields.include?(field.name)}
113
+ end
114
+
115
+ return all_field_info
116
+ end
117
+ end
118
+
119
+ # Get the field name with the unique key used to lookup the object
120
+ def param_key
121
+ if @param_key
122
+ @param_key
123
+ elsif superclass < SuperAwesomeResourceSerializer
124
+ superclass.param_key
125
+ else
126
+ :id
127
+ end
128
+ end
129
+
130
+ # Set the field name with the unique key used to lookup the object
131
+ def param_key= (val)
132
+ @param_key = val
133
+ end
134
+
135
+ protected
136
+
137
+ # Merge two hashes recursively
138
+ def merge_hashes (hash_1, hash_2)
139
+ hash_1.each_pair do |key, value_1|
140
+ value_2 = hash_2.delete(key)
141
+ if value_2.is_a?(Hash)
142
+ hash_1[key] = merge_hashes(value_1, value_2)
143
+ end
144
+ end
145
+ hash_1.merge(hash_2)
146
+ end
147
+
148
+ def serialized_field_info
149
+ @serialized_field_info ||= []
150
+ end
151
+ end
152
+
153
+ attr_reader :object, :include_fields, :exclude_fields, :only_fields, :root_element
154
+
155
+ # Create a new serializer. Options can be passed in for :exclude, :include, or :only to determine
156
+ # which fields will be used.
157
+ #
158
+ # To indicate a field should be included or excluded on a serializer returned by a field, prefix it with the field name.
159
+ # For example, if you have a field :venue and this field returns a VenueSerializer object and you only want the id of the
160
+ # venue, you can specify :only => "venue.id".
161
+ def initialize (object, serialize_options = {})
162
+ @object = object
163
+ @include_fields = self.class.normalize_field_list(serialize_options[:include])
164
+ @exclude_fields = self.class.normalize_field_list(serialize_options[:exclude])
165
+ @only_fields = self.class.normalize_field_list(serialize_options[:only])
166
+ @root_element = serialize_options[:root_element] ? serialize_options[:root_element].to_s : object.class.name.underscore.gsub("/", "-")
167
+ end
168
+
169
+ # Set the attributes on the underlying object by calling the available setters. All keys in the hash (and in any hashes in the values)
170
+ # will be converted to symbols.
171
+ def set_attributes (attributes)
172
+ return unless attributes
173
+ attributes = normalize_attribute_keys(attributes)
174
+ serializable_fields(:setter).each do |field|
175
+ if attributes.include?(field.name)
176
+ field.set(self, attributes[field.name])
177
+ end
178
+ end
179
+ end
180
+
181
+ # Update the available fields in the object with a serialized representation of the object in XML.
182
+ def update_with_xml (xml)
183
+ set_attributes(Hash.from_xml(xml)[root_element])
184
+ end
185
+
186
+ # Update the available fields in the object with a serialized representation of the object in JSON.
187
+ def update_with_json (json)
188
+ set_attributes(ActiveSupport::JSON.decode(json))
189
+ end
190
+
191
+ # Update the available fields in the object with a serialized representation of the object in YAML.
192
+ def update_with_yaml (yaml)
193
+ set_attributes(YAML.load(yaml))
194
+ end
195
+
196
+ # Serialize the object to a hash.
197
+ def to_hash
198
+ hash = HashWithIndifferentAccess.new
199
+ serializable_fields(:getter).each do |field|
200
+ if field.getter?
201
+ hash[field.element] = wrap_object_for_hash(field.get(self), field.name)
202
+ end
203
+ end
204
+ return hash
205
+ end
206
+
207
+ # Serialize the object to XML.
208
+ def to_xml (xml_options = {})
209
+ to_hash.to_xml({:root => root_element, :dasherize => false}.reverse_merge(xml_options))
210
+ end
211
+
212
+ # Serialize the object to JSON.
213
+ def to_json (json_options = nil)
214
+ to_hash.to_json(json_options)
215
+ end
216
+
217
+ # Serialize the object to YAML.
218
+ def to_yaml (yaml_options = {})
219
+ remove_hash_indifferent_access(to_hash).to_yaml(yaml_options)
220
+ end
221
+
222
+ # Get the unique key used for lookups
223
+ def to_param
224
+ object.send(self.class.param_key || :id)
225
+ end
226
+
227
+ def == (val)
228
+ return val.is_a?(self.class) && val.object == object && val.include_fields == include_fields && val.exclude_fields == exclude_fields && val.only_fields == only_fields && val.root_element == root_element
229
+ end
230
+
231
+ private
232
+
233
+ # Convert a hash and all contained hashes to use symbols for keys.
234
+ def normalize_attribute_keys (attributes)
235
+ attributes = HashWithIndifferentAccess.new(attributes) unless attributes.is_a?(HashWithIndifferentAccess)
236
+ attributes.each_pair do |key, value|
237
+ attributes[key] = normalize_attribute_keys(value) if value.is_a?(Hash) and not value.is_a?(HashWithIndifferentAccess)
238
+ end
239
+ end
240
+
241
+ # Get the list of serializable fields for an action (either :getter or :setter)
242
+ def serializable_fields (action)
243
+ return self.class.serializable_fields(action, :include => include_fields, :exclude => exclude_fields, :only => only_fields)
244
+ end
245
+
246
+ # Convert an object to a hash using serializers if they exist.
247
+ def wrap_object_for_hash (object, field)
248
+ if STATIC_CLASSES.include?(object.class)
249
+ return object
250
+ elsif object.is_a?(Array)
251
+ return object.collect{|element| wrap_object_for_hash(element, field.to_s.singularize)}
252
+ elsif object.is_a?(Hash)
253
+ hash = {}
254
+ object.each_pair{|key, value| hash[key] = wrap_object_for_hash(value, field)}
255
+ return hash
256
+ elsif object.is_a?(SuperAwesomeResourceSerializer)
257
+ return object.to_hash
258
+ else
259
+ serializer_class = "#{object.class.name}Serializer".constantize rescue nil
260
+ if serializer_class
261
+ serialize_options = {:include => include_fields[:field], :exclude => exclude_fields[field], :only => only_fields[field]}
262
+ return serializer_class.new(object, serialize_options).to_hash
263
+ else
264
+ return object
265
+ end
266
+ end
267
+ end
268
+
269
+ # Change a HashWithIndifferentAccess into a regular hash including all hashes in the values
270
+ def remove_hash_indifferent_access (value)
271
+ if value.is_a?(Hash)
272
+ hash = {}
273
+ value.each_pair do |key, value|
274
+ hash[key] = remove_hash_indifferent_access(value)
275
+ end
276
+ return hash
277
+ elsif value.is_a?(Array)
278
+ return value.collect{|v| remove_hash_indifferent_access(v)}
279
+ else
280
+ return value
281
+ end
282
+ end
283
+
284
+ # Encapulation of information about a serialized field.
285
+ class SerializedFieldInfo
286
+ attr_reader :name, :element
287
+
288
+ def initialize (name, options)
289
+ @name = name.to_sym
290
+ @getter = options[:getter]
291
+ @setter = options[:setter]
292
+ @element = (options[:element] || @name).to_sym
293
+ @exclude = options[:exclude] || []
294
+ end
295
+
296
+ # Get the field value in the specified serializer.
297
+ def get (serializer)
298
+ if getter?
299
+ if @getter.is_a?(Proc)
300
+ @getter.call(serializer.object) if @getter.is_a?(Proc)
301
+ else
302
+ method_name = @getter || name
303
+ if serializer.respond_to?(method_name)
304
+ serializer.send(method_name)
305
+ else
306
+ serializer.object.send(method_name)
307
+ end
308
+ end
309
+ end
310
+ end
311
+
312
+ # Set the field value in the specified serializer.
313
+ def set (serializer, value)
314
+ if setter?
315
+ if @setter.is_a?(Proc)
316
+ @setter.call(serializer.object, value) if @setter.is_a?(Proc)
317
+ else
318
+ method_name = @setter || "#{name}="
319
+ if serializer.respond_to?(method_name)
320
+ serializer.send(method_name, value)
321
+ else
322
+ serializer.object.send(method_name, value)
323
+ end
324
+ end
325
+ end
326
+ end
327
+
328
+ # Check if the field has a getter.
329
+ def getter?
330
+ return @getter != false
331
+ end
332
+
333
+ # Check if the field has a setter.
334
+ def setter?
335
+ return @setter != false
336
+ end
337
+
338
+ # Return true if the field is excluded from the specified action by default.
339
+ def excluded? (action)
340
+ if @exclude == true
341
+ return true
342
+ elsif @exclude.is_a?(Array)
343
+ return @exclude.include?(action)
344
+ else
345
+ return @exclude == action
346
+ end
347
+ end
348
+ end
349
+ end
@@ -0,0 +1,285 @@
1
+ require 'rubygems'
2
+ begin
3
+ require 'simplecov'
4
+ SimpleCov.start do
5
+ add_filter "/spec/"
6
+ end
7
+ rescue LoadError
8
+ # simplecov not installed
9
+ end
10
+
11
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'super_awesome_resource_serializer'))
12
+
13
+ describe SuperAwesomeResourceSerializer do
14
+
15
+ class SuperAwesomeResourceSerializer
16
+ class Tester
17
+ attr_accessor :field_1, :field_2, :field_3, :field_4, :field_5
18
+ def initialize (values = {})
19
+ values.each_pair{|key, value| self.send("#{key}=", value)}
20
+ end
21
+ end
22
+
23
+ class TesterSubclass < Tester
24
+ end
25
+
26
+ class TesterSerializer < SuperAwesomeResourceSerializer
27
+ serialize :field_1
28
+ serialize :field_2, :exclude => :setter
29
+ serialize :field_3, :exclude => :getter
30
+ serialize :field_4, :exclude => true
31
+ serialize :field_5, :element => :field_five, :exclude => true
32
+ serialize :field_one, :getter => :field_1, :setter => :field_1=, :exclude => true
33
+ serialize :virtual_1, :getter => lambda{|object| object.field_1.upcase}, :setter => false, :exclude => true
34
+ serialize :virtual_2, :getter => false, :setter => lambda{|object, value| object.field_1 = value.upcase}, :exclude => true
35
+
36
+ def field_4
37
+ object.field_4.upcase if object.field_4
38
+ end
39
+
40
+ def field_4= (value)
41
+ value = value.upcase if value
42
+ object.field_4 = value
43
+ end
44
+ end
45
+
46
+ class TesterWithParamKeySerializer < SuperAwesomeResourceSerializer
47
+ self.param_key = :slug
48
+ end
49
+
50
+ class Tester2
51
+ attr_accessor :value
52
+ def initialize (value)
53
+ @value = value
54
+ end
55
+ end
56
+ end
57
+
58
+ it "should serialize just the default fields and a HashWithIndifferentAccess" do
59
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one", :field_2 => "two")
60
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj)
61
+ hash = serializer.to_hash
62
+ hash.should == {"field_1" => "one", "field_2" => "two"}
63
+ hash[:field_1].should == "one"
64
+ end
65
+
66
+ it "should be able to add fields to the serialization" do
67
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one", :field_3 => "three")
68
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :include => :field_3)
69
+ serializer.to_hash.should == {"field_1" => "one", "field_2" => nil, "field_3" => "three"}
70
+ end
71
+
72
+ it "should be able to exclude default fields from the serialization" do
73
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one", :field_2 => "two")
74
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :exclude => [:field_2])
75
+ serializer.to_hash.should == {"field_1" => "one"}
76
+ end
77
+
78
+ it "should be able to define the exact fields to serialize" do
79
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one", :field_2 => "two", :field_3 => "three")
80
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :only => [:field_1, :field_3])
81
+ serializer.to_hash.should == {"field_1" => "one", "field_3" => "three"}
82
+ end
83
+
84
+ it "should be able to specify a hash key for serialization with the :element option" do
85
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_5 => "five")
86
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :only => :field_5)
87
+ serializer.to_hash.should == {"field_five" => "five"}
88
+ end
89
+
90
+ it "should be able to define a getter with a symbol" do
91
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one")
92
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :only => :field_one)
93
+ serializer.to_hash.should == {"field_one" => "one"}
94
+ end
95
+
96
+ it "should call getters on the serializer if they are defined" do
97
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_4 => "four")
98
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :only => :field_4)
99
+ serializer.to_hash.should == {"field_4" => "FOUR"}
100
+ end
101
+
102
+ it "should be able to define a getter with a proc" do
103
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one")
104
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :only => :virtual_1)
105
+ serializer.to_hash.should == {"virtual_1" => "ONE"}
106
+ end
107
+
108
+ it "should not be able to get values without getters" do
109
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one")
110
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :only => :virtual_2)
111
+ serializer.to_hash.should == {}
112
+ end
113
+
114
+ it "should use an object's serializer on field values if it exists" do
115
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one", :field_2 => "two"))
116
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :only => :field_1)
117
+ serializer.to_hash.should == {"field_1" => {"field_1" => "one", "field_2" => "two"}}
118
+ end
119
+
120
+ it "should not use an object's serializer if it doesn't exist" do
121
+ value = SuperAwesomeResourceSerializer::Tester2.new("value")
122
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => value)
123
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :only => :field_1)
124
+ serializer.to_hash.should == {"field_1" => value}
125
+ end
126
+
127
+ it "should wrap an array's elements with serializers if the exist" do
128
+ element_1 = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "a", :field_2 => "b")
129
+ element_2 = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "c", :field_2 => "d")
130
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => [element_1, element_2])
131
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :only => :field_1)
132
+ serializer.to_hash.should == {"field_1" => [{"field_1" => "a", "field_2" => "b"}, {"field_1" => "c", "field_2" => "d"}]}
133
+ end
134
+
135
+ it "should wrap a hash's values with serializers if the exist" do
136
+ element_1 = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "a", :field_2 => "b")
137
+ element_2 = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "c", :field_2 => "d")
138
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => {:a => element_1, :b => element_2})
139
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :only => :field_1)
140
+ serializer.to_hash.should == {"field_1" => {"a" => {"field_1" => "a", "field_2" => "b"}, "b" => {"field_1" => "c", "field_2" => "d"}}}
141
+ end
142
+
143
+ it "should normalize field lists" do
144
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one")
145
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :include => [:field_1, :field_2], :exclude => {:field_1 => [:field_1, :field_2], :field_2 => true}, :only => [{:field_1 => "field_1"}, "field_2"])
146
+ serializer.include_fields.should == {:field_1 => true, :field_2 => true}
147
+ serializer.exclude_fields.should == {:field_1=>{:field_1=>true, :field_2=>true}, :field_2=>true}
148
+ serializer.only_fields.should == {:field_1 => {:field_1 => true}, :field_2 => true}
149
+ SuperAwesomeResourceSerializer.normalize_field_list(:field_1).should == {:field_1 => true}
150
+ end
151
+
152
+ it "should merge field lists" do
153
+ SuperAwesomeResourceSerializer.merge_field_lists(nil, "").should == nil
154
+ SuperAwesomeResourceSerializer.merge_field_lists(:slug, "").should == :slug
155
+ SuperAwesomeResourceSerializer.merge_field_lists(nil, :slug).should == :slug
156
+ SuperAwesomeResourceSerializer.merge_field_lists(:slug, "title").should == {:slug => true, :title => true}
157
+ SuperAwesomeResourceSerializer.merge_field_lists([:slug, {:assoc => :f3}], ["title", {:assoc => [:f1, :f2]}]).should == {:slug => true, :title => true, :assoc => {:f1 => true, :f2 => true, :f3 => true}}
158
+ end
159
+
160
+ it "should be able to specify filters on object returned by a field that have serializers" do
161
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one", :field_2 => "two"), :field_2 => "two")
162
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :only => [{:field_1 => "field_1"}, "field_2"])
163
+ serializer.to_hash.should == {"field_1" => {"field_1" => "one"}, "field_2" => "two"}
164
+ end
165
+
166
+ it "should be able to specify exclude filters on object returned by a field that have serializers and only exclude the subobject fields" do
167
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one", :field_2 => "two"), :field_2 => "two")
168
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :exclude => {:field_1 => "field_1", :field_2 => true})
169
+ serializer.to_hash.should == {"field_1" => {"field_2" => "two"}}
170
+ end
171
+
172
+ it "should be able to serialize to xml" do
173
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one", :field_2 => "two")
174
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj)
175
+ Hash.from_xml(serializer.to_xml).should == {"super_awesome_resource_serializer_tester" => {"field_1" => "one", "field_2" => "two"}}
176
+ end
177
+
178
+ it "should be able to serialize to json" do
179
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one", :field_2 => "two")
180
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj)
181
+ ActiveSupport::JSON.decode(serializer.to_json).should == {"field_1" => "one", "field_2" => "two"}
182
+ end
183
+
184
+ it "should be able to serialize to yaml" do
185
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one", :field_2 => "two")
186
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj)
187
+ YAML.load(serializer.to_yaml).should == {"field_1" => "one", "field_2" => "two"}
188
+ end
189
+
190
+ it "should be able to set attribute values" do
191
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one", :field_2 => "two")
192
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj)
193
+ serializer.set_attributes(:field_1 => 1)
194
+ obj.field_1.should == 1
195
+ obj.field_2.should == "two"
196
+ end
197
+
198
+ it "should not be able to set attribute values on fields not included" do
199
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one", :field_2 => "two")
200
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :exclude => :field_1, :include => :field_2)
201
+ serializer.set_attributes(:field_1 => 1, :field_2 => 2)
202
+ obj.field_1.should == "one"
203
+ obj.field_2.should == 2
204
+ end
205
+
206
+ it "should call setters on the serializer if they are defined" do
207
+ obj = SuperAwesomeResourceSerializer::Tester.new
208
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :include => :field_4)
209
+ serializer.set_attributes(:field_4 => "forty")
210
+ obj.field_4.should == "FORTY"
211
+ end
212
+
213
+ it "should be able to define setters as procs" do
214
+ obj = SuperAwesomeResourceSerializer::Tester.new
215
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :include => :virtual_2)
216
+ serializer.set_attributes(:virtual_2 => "one")
217
+ obj.field_1.should == "ONE"
218
+ end
219
+
220
+ it "should not be able to set values without setters" do
221
+ obj = SuperAwesomeResourceSerializer::Tester.new
222
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :include => :virtual_1)
223
+ serializer.set_attributes(:virtual_1 => "one")
224
+ obj.field_1.should == nil
225
+ end
226
+
227
+ it "should be able to use a default root element of the XML document" do
228
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one", :field_2 => "two")
229
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj)
230
+ serializer.root_element.should == "super_awesome_resource_serializer-tester"
231
+ end
232
+
233
+ it "should be able to use a custom root element of the XML document" do
234
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one", :field_2 => "two")
235
+ serializer = SuperAwesomeResourceSerializer::TesterSerializer.new(obj, :root_element => "test")
236
+ serializer.root_element.should == "test"
237
+ end
238
+
239
+ it "should be able to create a serializer for an object" do
240
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one", :field_2 => "two")
241
+ serializer = SuperAwesomeResourceSerializer.for_object(obj, :include => :test)
242
+ serializer.class.should == SuperAwesomeResourceSerializer::TesterSerializer
243
+ serializer.object.should == obj
244
+ serializer.include_fields.should == {:test => true}
245
+ end
246
+
247
+ it "should be able to create a serializer for an object whose superclass has a serializer" do
248
+ obj = SuperAwesomeResourceSerializer::TesterSubclass.new(:field_1 => "one", :field_2 => "two")
249
+ serializer = SuperAwesomeResourceSerializer.for_object(obj, :include => :test)
250
+ serializer.class.should == SuperAwesomeResourceSerializer::TesterSerializer
251
+ serializer.object.should == obj
252
+ serializer.include_fields.should == {:test => true}
253
+ end
254
+
255
+ it "should raise an error if an object doesn't have a serializer" do
256
+ lambda{SuperAwesomeResourceSerializer.for_object(1)}.should raise_error(NameError)
257
+ end
258
+
259
+ it "should be able to turn a serializer into a param" do
260
+ obj = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one", :field_2 => "two")
261
+ serializer = SuperAwesomeResourceSerializer.for_object(obj, :include => :test)
262
+ obj.should_receive(:id).and_return(5)
263
+ serializer.to_param.should == 5
264
+ end
265
+
266
+ it "should be able to customize the param key used" do
267
+ SuperAwesomeResourceSerializer::TesterSerializer.param_key.should == :id
268
+ SuperAwesomeResourceSerializer::TesterWithParamKeySerializer.param_key.should == :slug
269
+ end
270
+
271
+ it "should test equality" do
272
+ obj_1 = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "one")
273
+ obj_2 = SuperAwesomeResourceSerializer::Tester.new(:field_1 => "ONE")
274
+ SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1).should == SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1)
275
+ SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1).should_not == SuperAwesomeResourceSerializer::TesterSerializer.new(obj_2)
276
+ SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1, :include => :field_1).should_not == SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1)
277
+ SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1, :include => :field_1).should == SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1, :include => :field_1)
278
+ SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1, :exclude => :field_1).should_not == SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1)
279
+ SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1, :exclude => :field_1).should == SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1, :exclude => :field_1)
280
+ SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1, :only => :field_1).should_not == SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1)
281
+ SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1, :only => :field_1).should == SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1, :only => :field_1)
282
+ SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1, :root_element => "test").should_not == SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1)
283
+ SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1, :root_element => "test").should == SuperAwesomeResourceSerializer::TesterSerializer.new(obj_1, :root_element => "test")
284
+ end
285
+ end