super_awesome_resource_serializer 1.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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