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/License.txt +674 -0
- data/README.rdoc +2 -0
- data/Rakefile +35 -0
- data/lib/super_awesome_resource_serializer.rb +349 -0
- data/spec/super_awesome_resource_serializer_spec.rb +285 -0
- metadata +79 -0
data/README.rdoc
ADDED
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
|