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/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
|