fast_serializer 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 476d327b3e3f93b51fa1e27425a9617d43335292
4
- data.tar.gz: b589d9d3834f931a694511d4d353bb4cfd005c4a
2
+ SHA256:
3
+ metadata.gz: 9aa9a0f0945690089e7b2811fc8a7a2bd7d5d391a44d6ffd5da46fde8cde178a
4
+ data.tar.gz: 5c7767c57d03ec466b8b0b8a241ecaaf1190b04115db5bbe63a04884e302320e
5
5
  SHA512:
6
- metadata.gz: e16e1f88ab40764e761d905435c45622ad4388945a97daddf5138b6a1dc4a544f43a49e6d357b99ece70bcfe7f3af7b328dce71d7062be3cea2ac565c90b2424
7
- data.tar.gz: 785755326eda08ccb27a4d758f7ddc6c0783229fecab2e9029362af1375a5e21deb43feec983aa3698b7672f61e308f3794be3051aa960392941078d87043d54
6
+ metadata.gz: 6b385a08ca0c432e8f8660e19f95ad0f33b43372084dcd9910a10410f3f7cd168158e1138bfae101a76a216e210b50dfdea7a8207864182ab96ef65f099cef53
7
+ data.tar.gz: 3b39610d1a17bece32360aac268b5fd508e0fe46bb2fd2b29fa22f6e422e93fa2c8ae7b11bdd926f848b30e8c40f68d508e6ffe57c04eeea8cbbeb77e06fcceb
data/HISTORY.md ADDED
@@ -0,0 +1,11 @@
1
+ ### 1.0.2
2
+
3
+ * Better integration with ActiveSupport caching.
4
+
5
+ ### 1.0.1
6
+
7
+ * Compatibility with change to fetch_multi in ActiveSupport 4.2.
8
+
9
+ ### 1.0.0
10
+
11
+ * Initial release
data/README.md CHANGED
@@ -7,7 +7,7 @@ For these examples we'll assume we have a simple Person class.
7
7
  ```ruby
8
8
  class Person
9
9
  attr_accessor :id, :first_name, :last_name, :parents, :children
10
-
10
+
11
11
  def intitialize(attributes = {})
12
12
  @id = attributes[:id]
13
13
  @first_name = attributes[:first_name]
@@ -16,7 +16,7 @@ class Person
16
16
  @parent = attributes[:parents]
17
17
  @children = attributes[:children] || {}
18
18
  end
19
-
19
+
20
20
  def ==(other)
21
21
  other.instance_of?(self.class) && other.id == id
22
22
  end
@@ -31,7 +31,7 @@ ruby```
31
31
  class PersonSerializer
32
32
  include FastSerializer::Serializer
33
33
  serialize :id, :name
34
-
34
+
35
35
  def name
36
36
  "#{object.first_name} #{object.last_name}"
37
37
  end
@@ -47,7 +47,7 @@ class PersonSerializer
47
47
  include FastSerializer::Serializer
48
48
  serialize :id, as: :person_id
49
49
  serialize :name, :delegate => false
50
-
50
+
51
51
  def name
52
52
  "#{object.first_name} #{object.last_name}"
53
53
  end
@@ -65,7 +65,7 @@ class PersonSerializer
65
65
  serialize :name, :delegate => false
66
66
  serialize :parent, serializer: PersonSerializer
67
67
  serialize :children, serializer: PersonSerializer, enumerable: true
68
-
68
+
69
69
  def name
70
70
  "#{object.first_name} #{object.last_name}"
71
71
  end
@@ -81,6 +81,8 @@ PersonSerializer.new(person).as_json # => {
81
81
  # }
82
82
  ```
83
83
 
84
+ ### Optional and excluding fields
85
+
84
86
  Serializer can have optional fields. You can also specify fields to exclude.
85
87
 
86
88
  ```ruby
@@ -89,7 +91,7 @@ class PersonSerializer
89
91
  serialize :id
90
92
  serialize :name, :delegate => false
91
93
  serialize :gender, optional: true
92
-
94
+
93
95
  def name
94
96
  "#{object.first_name} #{object.last_name}"
95
97
  end
@@ -100,6 +102,8 @@ PersonSerializer.new(person, :include => [:gender]).as_json # => {:id => 1, :nam
100
102
  PersonSerializer.new(person, :exclude => [:id]).as_json # => {:name => "John Doe"}
101
103
  ```
102
104
 
105
+ ### Serializer options
106
+
103
107
  You can specify custom options that control how the object is serialized.
104
108
 
105
109
  ```ruby
@@ -107,7 +111,7 @@ class PersonSerializer
107
111
  include FastSerializer::Serializer
108
112
  serialize :id
109
113
  serialize :name, :delegate => false
110
-
114
+
111
115
  def name
112
116
  if option(:last_first)
113
117
  "#{object.last_name}, #{object.first_name}"
@@ -118,9 +122,11 @@ class PersonSerializer
118
122
  end
119
123
 
120
124
  PersonSerializer.new(person).as_json # => {:id => 1, :name => "John Doe"}
121
- PersonSerializer.new(person, :last_first).as_json # => {:id => 1, :name => "Doe, John"}
125
+ PersonSerializer.new(person, :last_first => true).as_json # => {:id => 1, :name => "Doe, John"}
122
126
  ```
123
127
 
128
+ ### Caching
129
+
124
130
  You can make serializers cacheable so that the serialized value can be stored and fetched from a cache.
125
131
 
126
132
  ```ruby
@@ -128,9 +134,9 @@ class PersonSerializer
128
134
  include FastSerializer::Serializer
129
135
  serialize :id
130
136
  serialize :name, :delegate => false
131
-
137
+
132
138
  cacheable true, ttl: 60
133
-
139
+
134
140
  def name
135
141
  if option(:last_first)
136
142
  "#{object.last_name}, #{object.first_name}"
@@ -143,6 +149,21 @@ end
143
149
  FastSerializer.cache = MyCache.new # Must be an implementation of FastSerializer::Cache
144
150
  ```
145
151
 
152
+ For Rails application, you can run this in an initializer to tell `FastSerializer` to use `Rails.cache`
153
+
154
+ ```ruby
155
+ FastSerializer.cache = :rails
156
+ ```
157
+
158
+ You can also pass a cache to a serializer using the `:cache` option.
159
+
160
+ ### Collections
161
+
162
+ If you have a collection of objects to serialize, you can use the `FastSerializer::ArraySerializer` to serialize an enumeration of objects.
163
+
164
+ ```ruby
165
+ ```
166
+
146
167
  ## Performance
147
168
 
148
169
  Your mileage may vary. In many cases the performance of the serialization code doesn't particularly matter and this gem performs just about as well as other solutions. However, if you do have high throughput API or can utilize the caching features or have heavily nested models in your JSON responses, then the performance increase may be noticeable.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.1
1
+ 1.0.2
@@ -20,4 +20,5 @@ Gem::Specification.new do |spec|
20
20
  spec.add_development_dependency "bundler", "~>1.3"
21
21
  spec.add_development_dependency "rake"
22
22
  spec.add_development_dependency "rspec", "~>3.0"
23
+ spec.add_development_dependency "active_support", "~>4.0"
23
24
  end
@@ -9,25 +9,34 @@ module FastSerializer
9
9
  require_relative 'fast_serializer/serialized_field'
10
10
  require_relative 'fast_serializer/serializer'
11
11
  require_relative 'fast_serializer/array_serializer'
12
-
12
+
13
13
  class << self
14
+ @cache = nil
15
+
14
16
  # Get the global cache implementation used for storing cacheable serializers.
15
17
  def cache
16
- @cache if defined?(@cache)
18
+ @cache
17
19
  end
18
-
20
+
19
21
  # Set the global cache implementation used for storing cacheable serializers.
20
- # The cache implementation should implement the +fetch+ method as defined in
22
+ # The cache implementation should implement the +fetch+ method as defined in
21
23
  # FastSerializer::Cache. By default no cache is set so caching won't do anything.
22
24
  #
23
25
  # In a Rails app, you can initialize the cache by simply passing in the value :rails
24
- # to use the default Rails.cache.
26
+ # to use the default Rails.cache. You can also directly pass in an ActiveSupportCache::Store.
25
27
  def cache=(cache)
26
- cache = Cache::ActiveSupportCache.new(Rails.cache) if cache == :rails
28
+ if cache == :rails
29
+ cache = Cache::ActiveSupportCache.new(Rails.cache)
30
+ elsif defined?(ActiveSupport::Cache::Store) && cache.is_a?(ActiveSupport::Cache::Store)
31
+ cache = Cache::ActiveSupportCache.new(cache)
32
+ end
33
+ if cache && !cache.is_a?(FastSerializer::Cache)
34
+ raise ArgumentError.new("The cache must be a FastSerializer::Cache or ActiveSupport::Cache::Store")
35
+ end
27
36
  @cache = cache
28
37
  end
29
38
  end
30
-
39
+
31
40
  # Exception raised when there is a circular reference serializing a model dependent on itself.
32
41
  class CircularReferenceError < StandardError
33
42
  def initialize(model)
@@ -4,13 +4,13 @@ module FastSerializer
4
4
  # for caching duplicate serializers.
5
5
  class ArraySerializer
6
6
  include Serializer
7
-
7
+
8
8
  serialize :array
9
-
9
+
10
10
  def initialize(object, options = nil)
11
11
  super(Array(object), options)
12
12
  end
13
-
13
+
14
14
  def cache_key
15
15
  if option(:serializer)
16
16
  array.collect(&:cache_key)
@@ -18,7 +18,7 @@ module FastSerializer
18
18
  super
19
19
  end
20
20
  end
21
-
21
+
22
22
  def cacheable?
23
23
  if option(:cacheable) || self.class.cacheable?
24
24
  true
@@ -28,7 +28,7 @@ module FastSerializer
28
28
  super
29
29
  end
30
30
  end
31
-
31
+
32
32
  def cache_ttl
33
33
  if option(:cache_ttl)
34
34
  true
@@ -38,7 +38,7 @@ module FastSerializer
38
38
  super
39
39
  end
40
40
  end
41
-
41
+
42
42
  def cache
43
43
  if option(:cache)
44
44
  true
@@ -48,7 +48,7 @@ module FastSerializer
48
48
  super
49
49
  end
50
50
  end
51
-
51
+
52
52
  def as_json(*args)
53
53
  if array.nil?
54
54
  nil
@@ -58,13 +58,13 @@ module FastSerializer
58
58
  super[:array]
59
59
  end
60
60
  end
61
-
61
+
62
62
  undef :to_hash
63
63
  undef :to_h
64
64
  alias :to_a :as_json
65
-
65
+
66
66
  protected
67
-
67
+
68
68
  def load_from_cache
69
69
  if cache
70
70
  values = cache.fetch_all(array, cache_ttl){|serializer| serializer.as_json}
@@ -73,9 +73,9 @@ module FastSerializer
73
73
  load_hash
74
74
  end
75
75
  end
76
-
76
+
77
77
  private
78
-
78
+
79
79
  def array
80
80
  unless defined?(@_array)
81
81
  serializer = option(:serializer)
@@ -5,11 +5,11 @@ module FastSerializer
5
5
  def fetch(serializer, ttl, &block)
6
6
  raise NotImplementedError
7
7
  end
8
-
8
+
9
9
  # Fetch multiple serializers from the cache. The default behavior is just
10
10
  # to call +fetch+ with each serializer. Implementations may optimize this
11
11
  # if the cache can return multiple values at once.
12
- #
12
+ #
13
13
  # The block to this method will be yielded to with each uncached serializer.
14
14
  def fetch_all(serializers, ttl)
15
15
  serializers.collect do |serializer|
@@ -7,9 +7,10 @@ module FastSerializer
7
7
  @cache = cache
8
8
  end
9
9
 
10
- def fetch(serializer, ttl, &block)
11
- exists = !!@cache.read(serializer.cache_key)
12
- @cache.fetch(serializer.cache_key, :expires_in => ttl, &block)
10
+ def fetch(serializer, ttl)
11
+ @cache.fetch(serializer.cache_key, :expires_in => ttl) do
12
+ yield(serializer)
13
+ end
13
14
  end
14
15
 
15
16
  def fetch_all(serializers, ttl)
@@ -19,18 +19,18 @@ module FastSerializer
19
19
  end
20
20
  end
21
21
  end
22
-
22
+
23
23
  # Return the current context or nil if none is in use.
24
24
  def current
25
25
  Thread.current[:fast_serializer_context]
26
26
  end
27
27
  end
28
-
28
+
29
29
  def initialize
30
30
  @cache = nil
31
31
  @references = nil
32
32
  end
33
-
33
+
34
34
  # Returns a serializer from the context cache if a duplicate has already
35
35
  # been created. Otherwise creates the serializer and adds it to the
36
36
  # cache.
@@ -40,17 +40,17 @@ module FastSerializer
40
40
  if @cache
41
41
  serializer = @cache[key]
42
42
  end
43
-
43
+
44
44
  unless serializer
45
45
  serializer = serializer_class.allocate
46
46
  serializer.send(:initialize, object, options)
47
47
  @cache ||= {}
48
48
  @cache[key] = serializer
49
49
  end
50
-
50
+
51
51
  serializer
52
52
  end
53
-
53
+
54
54
  # Maintain reference stack to avoid circular references.
55
55
  def with_reference(object)
56
56
  if @references
@@ -58,7 +58,7 @@ module FastSerializer
58
58
  else
59
59
  @references = []
60
60
  end
61
-
61
+
62
62
  begin
63
63
  @references.push(object)
64
64
  yield
@@ -2,7 +2,7 @@ module FastSerializer
2
2
  # Data structure used internally for maintaining a field to be serialized.
3
3
  class SerializedField
4
4
  attr_reader :name
5
-
5
+
6
6
  def initialize(name, optional: false, serializer: nil, serializer_options: nil, enumerable: false)
7
7
  @name = name
8
8
  @optional = !!optional
@@ -12,11 +12,11 @@ module FastSerializer
12
12
  @enumerable = enumerable
13
13
  end
14
14
  end
15
-
15
+
16
16
  def optional?
17
17
  @optional
18
18
  end
19
-
19
+
20
20
  # Wrap a value in the serializer if one has been set. Otherwise just returns the raw value.
21
21
  def serialize(value)
22
22
  if value && @serializer
@@ -36,9 +36,9 @@ module FastSerializer
36
36
  serialize_value(value)
37
37
  end
38
38
  end
39
-
39
+
40
40
  private
41
-
41
+
42
42
  # Convert the value to primitive data types: string, number, boolean, symbol, time, date, array, hash.
43
43
  def serialize_value(value)
44
44
  if value.is_a?(String) || value.is_a?(Numeric) || value == nil || value == true || value == false || value.is_a?(Time) || value.is_a?(Date) || value.is_a?(Symbol)
@@ -73,6 +73,6 @@ module FastSerializer
73
73
  value
74
74
  end
75
75
  end
76
-
76
+
77
77
  end
78
78
  end
@@ -55,17 +55,17 @@ module FastSerializer
55
55
  #
56
56
  # Serializing a nil object will result in nil rather than an empty hash.
57
57
  module Serializer
58
-
58
+
59
59
  def self.included(base)
60
60
  base.extend(ClassMethods)
61
61
  end
62
-
62
+
63
63
  # Return the wrapped object that is being serialized.
64
64
  attr_reader :object
65
-
65
+
66
66
  # Return the options hash (if any) that specified optional details about how to serialize the object.
67
67
  attr_reader :options
68
-
68
+
69
69
  module ClassMethods
70
70
  # Define one or more fields to include in the serialized object. Field values will be gotten
71
71
  # by calling the method of the same name on class including this module.
@@ -100,23 +100,23 @@ module FastSerializer
100
100
  if as && fields.size > 1
101
101
  raise ArgumentError.new("Cannot specify :as argument with multiple fields to serialize")
102
102
  end
103
-
103
+
104
104
  fields.each do |field|
105
105
  name = as
106
106
  if name.nil? && field.to_s.end_with?("?".freeze)
107
107
  name = field.to_s.chomp("?".freeze)
108
108
  end
109
-
109
+
110
110
  field = field.to_sym
111
111
  attribute = (name || field).to_sym
112
112
  add_field(attribute, optional: optional, serializer: serializer, serializer_options: serializer_options, enumerable: enumerable)
113
-
113
+
114
114
  if delegate && !method_defined?(attribute)
115
115
  define_delegate(attribute, field)
116
116
  end
117
117
  end
118
118
  end
119
-
119
+
120
120
  # Remove a field from being serialized. This can be useful in subclasses if they need to remove a
121
121
  # field defined by the parent class.
122
122
  def remove(*fields)
@@ -127,11 +127,11 @@ module FastSerializer
127
127
  end
128
128
  @serializable_fields = field_list.freeze
129
129
  end
130
-
130
+
131
131
  # Specify the cacheability of the serializer.
132
132
  #
133
133
  # You can specify the cacheable state (defaults to true) of the class. Subclasses will inherit the
134
- # cacheable state of their parent class, so if you have non-cacheable serializer subclassing a
134
+ # cacheable state of their parent class, so if you have non-cacheable serializer subclassing a
135
135
  # cacheable parent class, you can call <tt>cacheable false</tt> to override the parent behavior.
136
136
  #
137
137
  # You can also specify the cache time to live (ttl) in seconds and the cache implementation to use.
@@ -141,7 +141,7 @@ module FastSerializer
141
141
  self.cache_ttl = ttl if ttl
142
142
  self.cache = cache if cache
143
143
  end
144
-
144
+
145
145
  # Return true if the serializer class is cacheable.
146
146
  def cacheable?
147
147
  unless defined?(@cacheable)
@@ -149,7 +149,7 @@ module FastSerializer
149
149
  end
150
150
  !!@cacheable
151
151
  end
152
-
152
+
153
153
  # Return the time to live in seconds for a cacheable serializer.
154
154
  def cache_ttl
155
155
  if defined?(@cache_ttl)
@@ -160,12 +160,12 @@ module FastSerializer
160
160
  nil
161
161
  end
162
162
  end
163
-
163
+
164
164
  # Set the time to live on a cacheable serializer.
165
165
  def cache_ttl=(value)
166
166
  @cache_ttl = value
167
167
  end
168
-
168
+
169
169
  # Get the cache implemtation used to store cacheable serializers.
170
170
  def cache
171
171
  if defined?(@cache)
@@ -176,12 +176,15 @@ module FastSerializer
176
176
  FastSerializer.cache
177
177
  end
178
178
  end
179
-
179
+
180
180
  # Set the cache implementation used to store cacheable serializers.
181
181
  def cache=(cache)
182
+ if defined?(ActiveSupport::Cache::Store) && cache.is_a?(ActiveSupport::Cache::Store)
183
+ cache = Cache::ActiveSupportCache.new(cache)
184
+ end
182
185
  @cache = cache
183
186
  end
184
-
187
+
185
188
  # :nodoc:
186
189
  def new(object, options = nil)
187
190
  context = SerializationContext.current
@@ -192,7 +195,7 @@ module FastSerializer
192
195
  super
193
196
  end
194
197
  end
195
-
198
+
196
199
  # Return a list of the SerializedFields defined for the class.
197
200
  def serializable_fields
198
201
  unless defined?(@serializable_fields) && @serializable_fields
@@ -202,14 +205,14 @@ module FastSerializer
202
205
  end
203
206
  @serializable_fields
204
207
  end
205
-
208
+
206
209
  private
207
-
210
+
208
211
  # Add a field to be serialized.
209
212
  def add_field(name, optional:, serializer:, serializer_options:, enumerable:)
210
213
  name = name.to_sym
211
214
  field = SerializedField.new(name, optional: optional, serializer: serializer, serializer_options: serializer_options, enumerable: enumerable)
212
-
215
+
213
216
  # Add the field to the frozen list of fields.
214
217
  field_list = []
215
218
  added = false
@@ -223,13 +226,13 @@ module FastSerializer
223
226
  field_list << field unless added
224
227
  @serializable_fields = field_list.freeze
225
228
  end
226
-
229
+
227
230
  # Define a delegate method name +attribute+ that invokes the +field+ method on the wrapped object.
228
231
  def define_delegate(attribute, field)
229
- define_method(attribute){ object.send(field) }
232
+ define_method(attribute){ object.send(field) }
230
233
  end
231
234
  end
232
-
235
+
233
236
  # Create a new serializer for the specified object.
234
237
  #
235
238
  # Options can be passed in to control how the object is serialized. Options supported by all Serializers:
@@ -242,9 +245,13 @@ module FastSerializer
242
245
  def initialize(object, options = nil)
243
246
  @object = object
244
247
  @options = options
248
+ @cache = options[:cache] if options
249
+ if @cache && defined?(ActiveSupport::Cache::Store) && cache.is_a?(ActiveSupport::Cache::Store)
250
+ @cache = Cache::ActiveSupportCache.new(@cache)
251
+ end
245
252
  @_serialized = nil
246
253
  end
247
-
254
+
248
255
  # Serialize the wrapped object into a format suitable for passing to a JSON parser.
249
256
  def as_json(*args)
250
257
  return nil unless object
@@ -253,54 +260,54 @@ module FastSerializer
253
260
  end
254
261
  @_serialized
255
262
  end
256
-
263
+
257
264
  alias :to_hash :as_json
258
265
  alias :to_h :as_json
259
-
266
+
260
267
  # Convert the wrapped object to JSON format.
261
- def to_json(options = nil)
268
+ def to_json(options = {})
262
269
  if defined?(MultiJson)
263
- MultiJson.dump(as_json)
270
+ MultiJson.dump(as_json, options)
264
271
  else
265
272
  JSON.dump(as_json)
266
273
  end
267
274
  end
268
-
275
+
269
276
  # Fetch the specified option from the options hash.
270
277
  def option(name)
271
278
  @options[name] if @options
272
279
  end
273
-
280
+
274
281
  # Return true if this serializer is cacheable.
275
282
  def cacheable?
276
283
  option(:cacheable) || self.class.cacheable?
277
284
  end
278
-
285
+
279
286
  # Return the cache implementation where this serializer can be stored.
280
287
  def cache
281
- option(:cache) || self.class.cache
288
+ @cache || self.class.cache
282
289
  end
283
-
290
+
284
291
  # Return the time to live in seconds this serializer can be cached for.
285
292
  def cache_ttl
286
293
  option(:cache_ttl) || self.class.cache_ttl
287
294
  end
288
-
295
+
289
296
  # Returns a array of the elements that make this serializer unique. The
290
297
  # key is an array made up of the serializer class name, wrapped object, and
291
298
  # serialization options hash.
292
299
  def cache_key
293
300
  [self.class.name, object, options]
294
301
  end
295
-
302
+
296
303
  # :nodoc:
297
304
  def ==(other)
298
305
  other.instance_of?(self.class) && @object == other.object && @options == other.options
299
306
  end
300
307
  alias_method :eql?, :==
301
-
308
+
302
309
  protected
303
-
310
+
304
311
  # Load the hash that will represent the wrapped object as a serialized object.
305
312
  def load_hash
306
313
  hash = {}
@@ -319,7 +326,7 @@ module FastSerializer
319
326
  end
320
327
  hash
321
328
  end
322
-
329
+
323
330
  # Load the hash that will represent the wrapped object as a serialized object from a cache.
324
331
  def load_from_cache
325
332
  if cache
@@ -330,9 +337,9 @@ module FastSerializer
330
337
  load_hash
331
338
  end
332
339
  end
333
-
340
+
334
341
  private
335
-
342
+
336
343
  # Return a list of optional fields to be included in the output from the :include option.
337
344
  def included_optional_fields
338
345
  included_fields = option(:include)
@@ -342,7 +349,7 @@ module FastSerializer
342
349
  nil
343
350
  end
344
351
  end
345
-
352
+
346
353
  # Return a list of fields to be excluded from the output from the :exclude option.
347
354
  def excluded_regular_fields
348
355
  excluded_fields = option(:exclude)
@@ -1,19 +1,19 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe FastSerializer::ArraySerializer do
4
-
4
+
5
5
  it "should serialize an array of regular objects" do
6
6
  array = [1, 2, 3]
7
7
  serializer = FastSerializer::ArraySerializer.new(array)
8
8
  expect(serializer.as_json).to eq array
9
9
  end
10
-
10
+
11
11
  it "should serialize any Enumerable" do
12
12
  hash = {:a => 1, :b => 2}
13
13
  serializer = FastSerializer::ArraySerializer.new(hash)
14
14
  expect(serializer.as_json).to eq hash.to_a
15
15
  end
16
-
16
+
17
17
  it "should serializer an array of objects using a specific serializer" do
18
18
  model_1 = SimpleModel.new(:id => 1, :name => "foo")
19
19
  model_2 = SimpleModel.new(:id => 2, :name => "bar")
@@ -23,7 +23,7 @@ describe FastSerializer::ArraySerializer do
23
23
  {"id" => 2, "name" => "bar", "validated" => false}
24
24
  ]
25
25
  end
26
-
26
+
27
27
  it "should serializer an array of objects using a specific serializer with options" do
28
28
  model_1 = SimpleModel.new(:id => 1, :name => "foo")
29
29
  model_2 = SimpleModel.new(:id => 2, :name => "bar")
@@ -33,20 +33,20 @@ describe FastSerializer::ArraySerializer do
33
33
  {"id" => 2, "name" => "bar", "validated" => false, "description" => nil}
34
34
  ]
35
35
  end
36
-
36
+
37
37
  it "should not respond to_hash methods" do
38
38
  array = [1, 2, 3]
39
39
  serializer = FastSerializer::ArraySerializer.new(array)
40
40
  expect(serializer.respond_to?(:to_hash)).to eq false
41
41
  expect(serializer.respond_to?(:to_h)).to eq false
42
42
  end
43
-
43
+
44
44
  it "should respond to to_a" do
45
45
  array = [1, 2, 3]
46
46
  serializer = FastSerializer::ArraySerializer.new(array)
47
47
  expect(serializer.to_a).to eq array
48
48
  end
49
-
49
+
50
50
  it "should pull cacheable serializers from a cache" do
51
51
  model_1 = SimpleModel.new(:id => 1, :name => "foo")
52
52
  model_2 = SimpleModel.new(:id => 2, :name => "bar")
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ describe FastSerializer::Cache::ActiveSupportCache do
4
+
5
+ it "should fetch from an ActiveSupport cache store" do
6
+ cache_store = ActiveSupport::Cache::MemoryStore.new
7
+ cache = FastSerializer::Cache::ActiveSupportCache.new(cache_store)
8
+ serializer = SimpleSerializer.new(SimpleModel.new(:id => 1))
9
+
10
+ expect(cache.fetch(serializer, 60){|s| s.as_json} ).to eq serializer.as_json
11
+ expect(cache.fetch(serializer, 60){ raise "boom" }).to eq serializer.as_json
12
+ end
13
+
14
+ it "should fetch multiple from an ActiveSupport cache store" do
15
+ cache_store = ActiveSupport::Cache::MemoryStore.new
16
+ cache = FastSerializer::Cache::ActiveSupportCache.new(cache_store)
17
+ s1 = SimpleSerializer.new(SimpleModel.new(:id => 1))
18
+ s2 = SimpleSerializer.new(SimpleModel.new(:id => 2))
19
+
20
+ expect(cache.fetch_all([s1, s2], 60){|s| s.as_json} ).to eq [s1.as_json, s2.as_json]
21
+ expect(cache.fetch_all([s1, s2], 60){ raise "boom" }).to eq [s1.as_json, s2.as_json]
22
+ end
23
+
24
+ end
@@ -1,18 +1,19 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe FastSerializer do
4
-
4
+
5
5
  it "should be able to set and get a global cache" do
6
6
  expect(FastSerializer.cache).to eq nil
7
7
  begin
8
- FastSerializer.cache = :mock
9
- expect(FastSerializer.cache).to eq :mock
8
+ cache = TestCache.new
9
+ FastSerializer.cache = cache
10
+ expect(FastSerializer.cache).to eq cache
10
11
  ensure
11
12
  FastSerializer.cache = nil
12
13
  end
13
14
  expect(FastSerializer.cache).to eq nil
14
15
  end
15
-
16
+
16
17
  it "should set the cache to Rails.cache with the value :rails" do
17
18
  begin
18
19
  rails = double(:cache => :rails_cache)
@@ -24,5 +25,16 @@ describe FastSerializer do
24
25
  FastSerializer.cache = nil
25
26
  end
26
27
  end
27
-
28
+
29
+ it "should set the cache with an ActiveSupport cache" do
30
+ begin
31
+ cache_store = ActiveSupport::Cache::MemoryStore.new
32
+ FastSerializer.cache = cache_store
33
+ expect(FastSerializer.cache).to be_a FastSerializer::Cache::ActiveSupportCache
34
+ expect(FastSerializer.cache.cache).to eq cache_store
35
+ ensure
36
+ FastSerializer.cache = nil
37
+ end
38
+ end
39
+
28
40
  end
@@ -1,7 +1,7 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe FastSerializer::SerializationContext do
4
-
4
+
5
5
  it "should get a single context only within a block" do
6
6
  expect(FastSerializer::SerializationContext.current).to eq nil
7
7
  FastSerializer::SerializationContext.use do
@@ -14,11 +14,11 @@ describe FastSerializer::SerializationContext do
14
14
  end
15
15
  expect(FastSerializer::SerializationContext.current).to eq nil
16
16
  end
17
-
17
+
18
18
  it "should create serializers and reload them from cache with the same object and options" do
19
19
  context = FastSerializer::SerializationContext.new
20
20
  object = SimpleModel.new(:id => 1, :name => "foo")
21
-
21
+
22
22
  serializer = context.load(SimpleSerializer, object, :count => 1)
23
23
  expect(serializer).to be_a SimpleSerializer
24
24
  expect(serializer.object).to eq object
@@ -28,5 +28,5 @@ describe FastSerializer::SerializationContext do
28
28
  expect(context.load(SimpleSerializer, SimpleModel.new(:id => 2, :name => "bar"), :count => 1).object_id).to_not eq serializer.object_id
29
29
  expect(context.load(SimpleSerializer, object, :count => 2).object_id).to_not eq serializer.object_id
30
30
  end
31
-
31
+
32
32
  end
@@ -1,66 +1,66 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe FastSerializer::SerializedField do
4
-
4
+
5
5
  let(:field){ FastSerializer::SerializedField.new(:test) }
6
6
  let(:model){ SimpleModel.new(:id => 1, :name => "foo") }
7
-
7
+
8
8
  it "should integers" do
9
9
  expect(field.serialize(1)).to eq 1
10
10
  end
11
-
11
+
12
12
  it "should serialize floats" do
13
13
  expect(field.serialize(1.5)).to eq 1.5
14
14
  end
15
-
15
+
16
16
  it "should serialize strings" do
17
17
  expect(field.serialize("foo")).to eq "foo"
18
18
  end
19
-
19
+
20
20
  it "should serialize symbols" do
21
21
  expect(field.serialize(:foo)).to eq :foo
22
22
  end
23
-
23
+
24
24
  it "should serialize nil" do
25
25
  expect(field.serialize(nil)).to eq nil
26
26
  end
27
-
27
+
28
28
  it "should serialize booleans" do
29
29
  expect(field.serialize(true)).to eq true
30
30
  expect(field.serialize(false)).to eq false
31
31
  end
32
-
32
+
33
33
  it "should serialize times" do
34
34
  time = Time.now
35
35
  expect(field.serialize(time)).to eq time
36
36
  end
37
-
37
+
38
38
  it "should serialize dates" do
39
39
  date = Date.today
40
40
  expect(field.serialize(date)).to eq date
41
41
  end
42
-
42
+
43
43
  it "should serialize a field using a specified serializer" do
44
44
  field = FastSerializer::SerializedField.new(:test, serializer: SimpleSerializer)
45
45
  expect(field.serialize(model)).to eq({:id => 1, :name => "foo", :validated => false})
46
46
  end
47
-
47
+
48
48
  it "should serialize an enumerable field using a specified serializer" do
49
49
  field = FastSerializer::SerializedField.new(:test, serializer: SimpleSerializer, enumerable: true)
50
50
  expect(field.serialize(model)).to eq([{:id => 1, :name => "foo", :validated => false}])
51
51
  end
52
-
52
+
53
53
  it "should serialize a field value by calling as_json on the field" do
54
54
  expect(field.serialize(model)).to eq({:id => 1, :name => "foo", :description => nil, :number => nil})
55
55
  end
56
-
56
+
57
57
  it "should serialize a hash of objects" do
58
58
  expect(field.serialize(:name => "Test", :object => model)).to eq({
59
59
  :name => "Test",
60
60
  :object => {:id => 1, :name => "foo", :description => nil, :number => nil}
61
61
  })
62
62
  end
63
-
63
+
64
64
  it "should serialize an array of objects" do
65
65
  expect(field.serialize([model, model])).to eq([
66
66
  {:id => 1, :name => "foo", :description => nil, :number => nil},
@@ -1,93 +1,93 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe FastSerializer::Serializer do
4
-
4
+
5
5
  let(:model){ SimpleModel.new(:id => 1, :name => "foo", :description => "foobar") }
6
-
6
+
7
7
  it "should serialize object to JSON compatible format" do
8
8
  serializer = SimpleSerializer.new(model)
9
9
  expect(serializer.as_json).to eq({:id => 1, :name => "foo", :validated => false})
10
10
  expect(serializer.to_hash).to eq({:id => 1, :name => "foo", :validated => false})
11
11
  expect(serializer.to_h).to eq({:id => 1, :name => "foo", :validated => false})
12
12
  end
13
-
13
+
14
14
  it "should serialize nil as nil" do
15
15
  expect(SimpleSerializer.new(nil).as_json).to eq nil
16
16
  end
17
-
17
+
18
18
  it "should include optional fields only if included in the options" do
19
19
  expect(SimpleSerializer.new(model).as_json).to eq({:id => 1, :name => "foo", :validated => false})
20
20
  expect(SimpleSerializer.new(model, :include => :nothing).as_json).to eq({:id => 1, :name => "foo", :validated => false})
21
21
  expect(SimpleSerializer.new(model, :include => :description).as_json).to eq({:id => 1, :name => "foo", :validated => false, :description => "foobar"})
22
22
  expect(SimpleSerializer.new(model, :include => ["description"]).as_json).to eq({:id => 1, :name => "foo", :validated => false, :description => "foobar"})
23
23
  end
24
-
24
+
25
25
  it "should exclude specified fields" do
26
26
  expect(SimpleSerializer.new(model, :exclude => :name).as_json).to eq({:id => 1, :validated => false})
27
27
  expect(SimpleSerializer.new(model, :exclude => ["id", "validated"]).as_json).to eq({:name => "foo"})
28
28
  end
29
-
29
+
30
30
  it "should allow aliasing fields" do
31
31
  number_model = SimpleModel.new(:number => 50.5)
32
32
  expect(SimpleSerializer.new(number_model, :include => :amount).as_json).to eq({:id => nil, :name => nil, :validated => false, :amount => 50.5})
33
33
  end
34
-
34
+
35
35
  it "should pull cached serializers from a cache" do
36
36
  serializer = SimpleSerializer.new(model)
37
37
  cached_serializer = CachedSerializer.new(model)
38
-
38
+
39
39
  expect(serializer.cacheable?).to eq false
40
40
  expect(cached_serializer.cacheable?).to eq true
41
-
41
+
42
42
  expect(serializer.cache_ttl).to eq nil
43
43
  expect(cached_serializer.cache_ttl).to eq 2
44
-
44
+
45
45
  expect(cached_serializer.as_json).to eq serializer.as_json
46
46
  expect(cached_serializer.as_json.object_id).to eq cached_serializer.as_json.object_id
47
47
  end
48
-
48
+
49
49
  it "should allow setting cache and ttl on parent serializers" do
50
50
  class SubCacheSerializer1 < CachedSerializer
51
51
  self.cache_ttl = 5
52
52
  self.cache = :mock
53
53
  end
54
-
54
+
55
55
  class SubCacheSerializer2 < CachedSerializer
56
56
  end
57
-
57
+
58
58
  expect(SubCacheSerializer1.cacheable?).to eq true
59
59
  expect(SubCacheSerializer2.cacheable?).to eq true
60
-
60
+
61
61
  expect(SubCacheSerializer1.cache).to eq :mock
62
62
  expect(SubCacheSerializer2.cache).to eq CachedSerializer.cache
63
-
63
+
64
64
  expect(SubCacheSerializer1.cache_ttl).to eq 5
65
65
  expect(SubCacheSerializer2.cache_ttl).to eq 2
66
66
  end
67
-
67
+
68
68
  it "should allow setting cache and ttl on instances" do
69
69
  serializer = SimpleSerializer.new(model, :cacheable => true, :cache => :mock, :cache_ttl => 10)
70
70
  expect(serializer.cacheable?).to eq true
71
71
  expect(serializer.cache).to eq :mock
72
72
  expect(serializer.cache_ttl).to eq 10
73
73
  end
74
-
74
+
75
75
  it "should get the cache from the global setting by default" do
76
76
  class SubCacheSerializerGlobalInheritTest
77
77
  include FastSerializer::Serializer
78
78
  self.cacheable
79
79
  end
80
-
81
- expect(SubCacheSerializerGlobalInheritTest.cache).to eq nil
80
+
81
+ expect(SubCacheSerializerGlobalInheritTest.cache).to eq nil
82
82
  allow(FastSerializer).to receive_messages(:cache => :mock)
83
83
  expect(SubCacheSerializerGlobalInheritTest.cache).to eq :mock
84
84
  end
85
-
85
+
86
86
  it "should not break on cached serializers if no cache is set" do
87
87
  serializer = CachedSerializer.new(model, :cache => nil)
88
88
  expect(serializer.as_json).to eq({:id => 1, :name => "foo", :validated => false})
89
89
  end
90
-
90
+
91
91
  it "should serialize complex objects" do
92
92
  other_model = SimpleModel.new(:id => 3, :name => "other")
93
93
  complex = SimpleModel.new(:id => 2, :name => :complex, :parent => model, :associations => [model, other_model])
@@ -101,7 +101,7 @@ describe FastSerializer::Serializer do
101
101
  :parent => {:id => 1, :name => "foo", :validated => false}
102
102
  })
103
103
  end
104
-
104
+
105
105
  it "should return identical serialized values for serializers on the same object and options inside the same context" do
106
106
  other_model = SimpleModel.new(:id => 3, :name => "other")
107
107
  complex = SimpleModel.new(:id => 2, :name => "complex", :associations => [model, other_model, model])
@@ -110,7 +110,7 @@ describe FastSerializer::Serializer do
110
110
  expect(json[:associations][0].object_id).to_not eq json[:associations][1].object_id
111
111
  expect(json[:associations][0].object_id).to eq json[:associations][2].object_id
112
112
  end
113
-
113
+
114
114
  it "should dump the object to JSON" do
115
115
  complex = SimpleModel.new(:id => 2, :name => "complex", :parent => model, :associations => [model])
116
116
  serializer = ComplexSerializer.new(complex)
@@ -125,18 +125,24 @@ describe FastSerializer::Serializer do
125
125
  "parent" => {"id" => 1, "name" => "foo", "validated" => false}
126
126
  })
127
127
  end
128
-
128
+
129
+ it "should allow passing options for JSON dumping" do
130
+ complex = SimpleModel.new(:id => 2, :name => "complex", :parent => model, :associations => [model])
131
+ serializer = ComplexSerializer.new(complex)
132
+ expect(serializer.to_json(:pretty => true)).to be_a(String)
133
+ end
134
+
129
135
  it "should expose options from the options hash by name" do
130
136
  serializer = CachedSerializer.new(model, :foo => "bar")
131
137
  expect(serializer.option(:foo)).to eq "bar"
132
138
  expect(serializer.option(:other)).to eq nil
133
139
  end
134
-
140
+
135
141
  it "should return nil value for options if none are set" do
136
142
  serializer = CachedSerializer.new(model, nil)
137
143
  expect(serializer.option(:foo)).to eq nil
138
144
  end
139
-
145
+
140
146
  it "should not get into infinite loops" do
141
147
  model = SimpleModel.new(:id => 1)
142
148
  model.parent = model
data/spec/spec_helper.rb CHANGED
@@ -1,4 +1,6 @@
1
1
  require 'rspec'
2
+ require 'active_support/cache'
3
+ require 'active_support/cache/memory_store'
2
4
  require_relative "../lib/fast_serializer"
3
5
  require_relative "support/test_models"
4
6
 
@@ -11,6 +13,6 @@ RSpec.configure do |config|
11
13
  # the seed, which is printed after each run.
12
14
  # --seed 1234
13
15
  config.order = 'random'
14
-
16
+
15
17
  config.expect_with(:rspec) { |c| c.syntax = [:should, :expect] }
16
18
  end
@@ -1,7 +1,7 @@
1
1
  class SimpleModel
2
2
  attr_reader :id, :name, :description, :associations, :number
3
3
  attr_accessor :parent
4
-
4
+
5
5
  def initialize(attributes = {})
6
6
  @id = attributes[:id]
7
7
  @name = attributes[:name]
@@ -11,11 +11,11 @@ class SimpleModel
11
11
  @parent = attributes[:parent]
12
12
  @number = attributes[:number]
13
13
  end
14
-
14
+
15
15
  def validated?
16
16
  !!@validated
17
17
  end
18
-
18
+
19
19
  def as_json(*args)
20
20
  {:id => @id, :name => @name, :description => @description, :number => @number}
21
21
  end
@@ -25,7 +25,7 @@ class TestCache < FastSerializer::Cache
25
25
  def initialize
26
26
  @cache = {}
27
27
  end
28
-
28
+
29
29
  def fetch(serializer, ttl)
30
30
  val = @cache[serializer.cache_key]
31
31
  unless val
@@ -38,7 +38,7 @@ end
38
38
 
39
39
  class SimpleSerializer
40
40
  include FastSerializer::Serializer
41
-
41
+
42
42
  serialize :id, :name, :validated?
43
43
  serialize :description, optional: true
44
44
  serialize :number, as: :amount, optional: true
@@ -52,7 +52,7 @@ class ComplexSerializer < SimpleSerializer
52
52
  serialize :serial_number, delegate: false
53
53
  serialize :associations, delegate: true, serializer: CachedSerializer, enumerable: true
54
54
  serialize :parent, delegate: true, serializer: SimpleSerializer
55
-
55
+
56
56
  def serial_number
57
57
  option(:serial_number)
58
58
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fast_serializer
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.1
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - We Heart It
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2017-12-23 00:00:00.000000000 Z
12
+ date: 2018-08-06 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -53,6 +53,20 @@ dependencies:
53
53
  - - "~>"
54
54
  - !ruby/object:Gem::Version
55
55
  version: '3.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: active_support
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '4.0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '4.0'
56
70
  description: Super fast object serialization for API's combining a simple DSL with
57
71
  many optimizations under the hood.
58
72
  email:
@@ -63,7 +77,7 @@ extensions: []
63
77
  extra_rdoc_files: []
64
78
  files:
65
79
  - ".gitignore"
66
- - HISTORY.txt
80
+ - HISTORY.md
67
81
  - MIT_LICENSE
68
82
  - README.md
69
83
  - Rakefile
@@ -77,6 +91,7 @@ files:
77
91
  - lib/fast_serializer/serialized_field.rb
78
92
  - lib/fast_serializer/serializer.rb
79
93
  - spec/array_serializer_spec.rb
94
+ - spec/cache/active_support_cache_spec.rb
80
95
  - spec/fast_serializer_spec.rb
81
96
  - spec/serialization_context_spec.rb
82
97
  - spec/serialized_field_spec.rb
@@ -103,12 +118,13 @@ required_rubygems_version: !ruby/object:Gem::Requirement
103
118
  version: '0'
104
119
  requirements: []
105
120
  rubyforge_project:
106
- rubygems_version: 2.6.12
121
+ rubygems_version: 2.7.6
107
122
  signing_key:
108
123
  specification_version: 4
109
124
  summary: Super fast object serialization for API's.
110
125
  test_files:
111
126
  - spec/array_serializer_spec.rb
127
+ - spec/cache/active_support_cache_spec.rb
112
128
  - spec/fast_serializer_spec.rb
113
129
  - spec/serialization_context_spec.rb
114
130
  - spec/serialized_field_spec.rb
data/HISTORY.txt DELETED
@@ -1,7 +0,0 @@
1
- 1.1.0
2
-
3
- Compatibility with change to fetch_multi in ActiveSupport 4.2.
4
-
5
- 1.0.0
6
-
7
- * Initial release