fast_serializer 1.1.1 → 1.1.3

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
2
  SHA256:
3
- metadata.gz: 9c038967d22d51bae0cd7f9993f625dbc3d1b747590ec3db6826cf4ac3c4c4ba
4
- data.tar.gz: aeffd7e6274da8733f26556e6be03799fc182e05e8888245b4fe3a62df5c881c
3
+ metadata.gz: a7cb104ff4ece3439d2fcd7b6276866359a9ead6a511765fbef327d4bd33e168
4
+ data.tar.gz: dfff11c222e5cf0dc6352097fc3de8603f437fdc92636030c0aef4576b697445
5
5
  SHA512:
6
- metadata.gz: 1d21207437c1ea463715ee99cd354de62c27a455df3f58b3ec56ed8b282276cdd1c6b632870750dbe2946d00aecd17e9457a12e1008e49d0d35cf500a05c973e
7
- data.tar.gz: bf2888c605c80312f7fdfb457a258b86ac4c3b4810527e21b5e959c16aaec42933414fefb9121e15bde5a00a941c9cdb63b3d650070788f5712a6a73fac4976f
6
+ metadata.gz: 891c9cfde95aa08e4e2f73646f3cde78c4a1730673e9e74d518356dda869ff4848d1c9fc0d81bbcda936147faedd5e32775870a006e99679d34918f554252c70
7
+ data.tar.gz: 68814c806dd3b6a3b9e519b8ed5bf8c948ca2d12cb143a64a25c71aaac93690a66f5d85ea2ff71953d1bade1479f619b29c400ebab8137de947fb55053928d21
data/CHANGELOG.md ADDED
@@ -0,0 +1,46 @@
1
+ # Changelog
2
+ All notable changes to this project will be documented in this file.
3
+
4
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
+
7
+ ## 1.1.3
8
+
9
+ ### Added
10
+ - Optimize object shapes for the Ruby interpreter by declaring instance variables in constructors.
11
+
12
+ ## 1.1.2
13
+
14
+ ### Added
15
+ - Sanity check for unsupported options
16
+
17
+ ### Fixed
18
+ - Handle converting ActiveSupport::TimeWithZone to a Time so it can be better dumped to non-JSON formats.
19
+
20
+ ## 1.1.1
21
+
22
+ ### Added
23
+ - Add `array` class method to serializers.
24
+
25
+ ## 1.1.0
26
+
27
+ ### Added
28
+ - Add helper method for scope option.
29
+ - Pass serialization options to child serializers.
30
+ - Add `if` option to conditionally include fields.
31
+ - Better cache keys handling for more complex objects.
32
+
33
+ ## 1.0.2
34
+
35
+ ### Added
36
+ - Better integration with ActiveSupport caching.
37
+
38
+ ## 1.0.1
39
+
40
+ ### Fixed
41
+ - Compatibility with change to fetch_multi in ActiveSupport 4.2.
42
+
43
+ ## 1.0.0
44
+
45
+ ### Added
46
+ - Initial release
data/MIT_LICENSE CHANGED
@@ -1,4 +1,4 @@
1
- Copyright (c) 2016 We Heart It
1
+ Copyright (c) 2016 Brian Durand
2
2
 
3
3
  Permission is hereby granted, free of charge, to any person obtaining
4
4
  a copy of this software and associated documentation files (the
data/README.md CHANGED
@@ -1,3 +1,7 @@
1
+ [![Continuous Integration](https://github.com/bdurand/fast_serializer/actions/workflows/continuous_integration.yml/badge.svg)](https://github.com/bdurand/fast_serializer/actions/workflows/continuous_integration.yml)
2
+ [![Regression Test](https://github.com/bdurand/fast_serializer/actions/workflows/regression_test.yml/badge.svg)](https://github.com/bdurand/fast_serializer/actions/workflows/regression_test.yml)
3
+ [![Ruby Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://github.com/testdouble/standard)
4
+
1
5
  This gem provides a highly optimized framework for serializing Ruby objects into hashes suitable for serialization to some other format (i.e. JSON). It provides many of the same features as other serialization frameworks like active_model_serializers, but it is designed to emphasize code efficiency over feature set and syntactic surgar.
2
6
 
3
7
  ## Examples
@@ -229,7 +233,33 @@ You can also use the `array` helper class method on a serializer to do the same
229
233
  PersonSerializer.array([a, b, c, d])
230
234
  ```
231
235
 
232
-
233
236
  ## Performance
234
237
 
235
238
  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.
239
+ ## Installation
240
+
241
+ Add this line to your application's Gemfile:
242
+
243
+ ```ruby
244
+ gem 'fast_serializer'
245
+ ```
246
+
247
+ And then execute:
248
+ ```bash
249
+ $ bundle
250
+ ```
251
+
252
+ Or install it yourself as:
253
+ ```bash
254
+ $ gem install fast_serializer
255
+ ```
256
+
257
+ ## Contributing
258
+
259
+ Open a pull request on GitHub.
260
+
261
+ Please use the [standardrb](https://github.com/testdouble/standard) syntax and lint your code with `standardrb --fix` before submitting.
262
+
263
+ ## License
264
+
265
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.1.1
1
+ 1.1.3
@@ -1,24 +1,34 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
-
5
1
  Gem::Specification.new do |spec|
6
- spec.name = "fast_serializer"
7
- spec.version = File.read(File.expand_path("../VERSION", __FILE__)).chomp
8
- spec.authors = ["We Heart It", "Brian Durand"]
9
- spec.email = ["dev@weheartit.com", "bbdurand@gmail.com"]
10
- spec.description = %q{Super fast object serialization for API's combining a simple DSL with many optimizations under the hood.}
11
- spec.summary = %q{Super fast object serialization for API's.}
12
- spec.homepage = "https://github.com/weheartit/fast_serializer"
13
- spec.license = "MIT"
2
+ spec.name = "fast_serializer"
3
+ spec.version = File.read(File.expand_path("VERSION", __dir__)).strip
4
+ spec.authors = ["Brian Durand"]
5
+ spec.email = ["bbdurand@gmail.com"]
6
+
7
+ spec.summary = "Super fast object serialization for API's combining a simple DSL with many optimizations under the hood."
8
+ spec.homepage = "https://github.com/bdurand/fast_serializer"
9
+ spec.license = "MIT"
10
+
11
+ # Specify which files should be added to the gem when it is released.
12
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
13
+ ignore_files = %w[
14
+ .
15
+ Appraisals
16
+ Gemfile
17
+ Gemfile.lock
18
+ Rakefile
19
+ bin/
20
+ gemfiles/
21
+ spec/
22
+ ]
23
+ spec.files = Dir.chdir(__dir__) do
24
+ `git ls-files -z`.split("\x0").reject { |f| ignore_files.any? { |path| f.start_with?(path) } }
25
+ end
14
26
 
15
- spec.files = `git ls-files`.split($/)
16
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
27
  spec.require_paths = ["lib"]
19
28
 
20
- spec.add_development_dependency "bundler", "~>1.3"
21
- spec.add_development_dependency "rake"
22
- spec.add_development_dependency "rspec", "~>3.0"
23
- spec.add_development_dependency "active_support", ">=4.0"
29
+ spec.required_ruby_version = ">= 2.5"
30
+
31
+ spec.add_dependency("redis")
32
+
33
+ spec.add_development_dependency "bundler"
24
34
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FastSerializer
2
4
  # Serializer implementation for serializing an array of objects.
3
5
  # This class allows taking advantage of a single SerializationContext
@@ -8,9 +10,11 @@ module FastSerializer
8
10
  serialize :array
9
11
 
10
12
  def initialize(object, options = nil)
13
+ @_array = nil
11
14
  super(Array(object), options)
12
15
  end
13
16
 
17
+ # @return [String]
14
18
  def cache_key
15
19
  if option(:serializer)
16
20
  array.collect(&:cache_key)
@@ -19,6 +23,7 @@ module FastSerializer
19
23
  end
20
24
  end
21
25
 
26
+ # @return [Boolean]
22
27
  def cacheable?
23
28
  if option(:cacheable) || self.class.cacheable?
24
29
  true
@@ -29,6 +34,7 @@ module FastSerializer
29
34
  end
30
35
  end
31
36
 
37
+ # @return [Numeric, Boolean]
32
38
  def cache_ttl
33
39
  if option(:cache_ttl)
34
40
  true
@@ -39,6 +45,7 @@ module FastSerializer
39
45
  end
40
46
  end
41
47
 
48
+ # @return [FastSerializer::Cache, Boolean]
42
49
  def cache
43
50
  if option(:cache)
44
51
  true
@@ -49,6 +56,7 @@ module FastSerializer
49
56
  end
50
57
  end
51
58
 
59
+ # @return [Hash]
52
60
  def as_json(*args)
53
61
  if array.nil?
54
62
  nil
@@ -61,14 +69,14 @@ module FastSerializer
61
69
 
62
70
  undef :to_hash
63
71
  undef :to_h
64
- alias :to_a :as_json
72
+ alias_method :to_a, :as_json
65
73
 
66
74
  protected
67
75
 
68
76
  def load_from_cache
69
77
  if cache
70
- values = cache.fetch_all(array, cache_ttl){|serializer| serializer.as_json}
71
- {:array => values}
78
+ values = cache.fetch_all(array, cache_ttl) { |serializer| serializer.as_json }
79
+ {array: values}
72
80
  else
73
81
  load_hash
74
82
  end
@@ -77,11 +85,11 @@ module FastSerializer
77
85
  private
78
86
 
79
87
  def array
80
- unless defined?(@_array)
88
+ if @_array.nil?
81
89
  serializer = option(:serializer)
82
90
  if serializer
83
91
  serializer_options = option(:serializer_options)
84
- @_array = object.collect{|obj| serializer.new(obj, serializer_options)}
92
+ @_array = object.collect { |obj| serializer.new(obj, serializer_options) }
85
93
  else
86
94
  @_array = object
87
95
  end
@@ -1,22 +1,24 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FastSerializer
2
4
  # ActiveSupport compatible cache implementation.
3
5
  class Cache::ActiveSupportCache < Cache
4
6
  attr_reader :cache
5
-
7
+
6
8
  def initialize(cache)
7
9
  @cache = cache
8
10
  end
9
-
11
+
10
12
  def fetch(serializer, ttl)
11
- @cache.fetch(serializer.cache_key, :expires_in => ttl) do
13
+ @cache.fetch(serializer.cache_key, expires_in: ttl) do
12
14
  yield(serializer)
13
15
  end
14
16
  end
15
-
17
+
16
18
  def fetch_all(serializers, ttl)
17
- results = @cache.fetch_multi(*serializers){|serializer| yield(serializer)}
19
+ results = @cache.fetch_multi(*serializers) { |serializer| yield(serializer) }
18
20
  if results.is_a?(Hash)
19
- serializers.collect{|serializer| results[serializer]}
21
+ serializers.collect { |serializer| results[serializer] }
20
22
  else
21
23
  results
22
24
  end
@@ -1,7 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FastSerializer
2
4
  # Base class for cache implementations for storing cacheable serializers.
3
5
  # Implementations must implement the +fetch+ method.
4
6
  class Cache
7
+ # Fetch a serialized value from the cache. If the value is not cached, the
8
+ # block will be yielded to to generate the value.
9
+ #
10
+ # @param serializer [FastSerializer::Serializer] The serializer to fetch the value for.
11
+ # @param ttl [Numeric] The time to live for the cached value.
12
+ # @yieldparam serializer [FastSerializer::Serializer] The serializer to generate the value for.
13
+ # @return [Object] The serialized value.
5
14
  def fetch(serializer, ttl, &block)
6
15
  raise NotImplementedError
7
16
  end
@@ -11,6 +20,11 @@ module FastSerializer
11
20
  # if the cache can return multiple values at once.
12
21
  #
13
22
  # The block to this method will be yielded to with each uncached serializer.
23
+ #
24
+ # @param serializers [Array<FastSerializer::Serializer>] The serializers to fetch the values for.
25
+ # @param ttl [Numeric] The time to live for the cached values.
26
+ # @yieldparam serializer [FastSerializer::Serializer] A serializer to generate the value for.
27
+ # @return [Array<Object>] The serialized values.
14
28
  def fetch_all(serializers, ttl)
15
29
  serializers.collect do |serializer|
16
30
  fetch(serializer, ttl) do
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FastSerializer
2
4
  # This class provides a context for creating serializers that allows
3
5
  # duplicate serializers to be re-used within the context. This then
@@ -7,6 +9,8 @@ module FastSerializer
7
9
  # Use a context or create one for use within a block. Any serializers
8
10
  # based on the same object with the same options within the block will be
9
11
  # re-used instead of creating duplicates.
12
+ #
13
+ # @return [Object] The return value of the block.
10
14
  def use
11
15
  if Thread.current[:fast_serializer_context]
12
16
  yield
@@ -21,6 +25,8 @@ module FastSerializer
21
25
  end
22
26
 
23
27
  # Return the current context or nil if none is in use.
28
+ #
29
+ # @return [FastSerializer::SerializationContext, nil]
24
30
  def current
25
31
  Thread.current[:fast_serializer_context]
26
32
  end
@@ -31,9 +37,14 @@ module FastSerializer
31
37
  @references = nil
32
38
  end
33
39
 
34
- # Returns a serializer from the context cache if a duplicate has already
40
+ # Returns a serializer from the context cache if one has already
35
41
  # been created. Otherwise creates the serializer and adds it to the
36
42
  # cache.
43
+ #
44
+ # @param serializer_class [Class] The serializer class to create.
45
+ # @param object [Object] The object to serialize.
46
+ # @param options [Hash] The options to pass to the serializer.
47
+ # @return [FastSerializer::Serializer] The serializer.
37
48
  def load(serializer_class, object, options = nil)
38
49
  key = [serializer_class, object, options]
39
50
  serializer = nil
@@ -44,7 +55,7 @@ module FastSerializer
44
55
  unless serializer
45
56
  serializer = serializer_class.allocate
46
57
  serializer.send(:initialize, object, options)
47
- @cache ||= {}
58
+ @cache = {}
48
59
  @cache[key] = serializer
49
60
  end
50
61
 
@@ -52,6 +63,10 @@ module FastSerializer
52
63
  end
53
64
 
54
65
  # Maintain reference stack to avoid circular references.
66
+ #
67
+ # @param object [Object] The object to check for circular references.
68
+ # @yield The block to execute.
69
+ # @return The return value of the block.
55
70
  def with_reference(object)
56
71
  if @references
57
72
  raise CircularReferenceError.new(object) if @references.include?(object)
@@ -1,10 +1,20 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module FastSerializer
2
4
  # Data structure used internally for maintaining a field to be serialized.
3
5
  class SerializedField
4
6
  attr_reader :name, :condition
5
7
 
8
+ # Create a new serialized field.
9
+ #
10
+ # @param name [Symbol] the name of the field
11
+ # @param optional [Boolean] whether the field is optional
12
+ # @param serializer [Class] the serializer to use for the field
13
+ # @param serializer_options [Hash] the options to pass to the serializer
14
+ # @param enumerable [Boolean] whether the field is enumerable
15
+ # @param condition [Proc] a condition to determine whether the field should be serialized
6
16
  def initialize(name, optional: false, serializer: nil, serializer_options: nil, enumerable: false, condition: nil)
7
- @name = name
17
+ @name = name.to_sym
8
18
  @optional = !!optional
9
19
  @condition = condition
10
20
  if serializer
@@ -14,22 +24,27 @@ module FastSerializer
14
24
  end
15
25
  end
16
26
 
27
+ # @return [Boolean] true if the field is optional
17
28
  def optional?
18
29
  @optional
19
30
  end
20
31
 
21
32
  # Wrap a value in the serializer if one has been set. Otherwise just returns the raw value.
33
+ #
34
+ # @param value [Object] the value to serialize
35
+ # @param options [Hash] the options to pass to the serializer
36
+ # @return [Object] the serialized value
22
37
  def serialize(value, options = nil)
23
38
  if value && @serializer
24
39
  serializer = nil
25
- if @enumerable
26
- serializer = ArraySerializer.new(value, :serializer => @serializer, :serializer_options => serializer_options(options))
40
+ serializer = if @enumerable
41
+ ArraySerializer.new(value, serializer: @serializer, serializer_options: serializer_options(options))
27
42
  else
28
- serializer = @serializer.new(value, serializer_options(options))
43
+ @serializer.new(value, serializer_options(options))
29
44
  end
30
45
  context = SerializationContext.current
31
46
  if context
32
- context.with_reference(value){ serializer.as_json }
47
+ context.with_reference(value) { serializer.as_json }
33
48
  else
34
49
  serializer.as_json
35
50
  end
@@ -56,10 +71,10 @@ module FastSerializer
56
71
  retval = {}
57
72
  merge_hash.each do |key, merge_value|
58
73
  value = hash[key]
59
- if value.is_a?(Hash) && merge_value.is_a?(Hash)
60
- retval[key] = deep_merge(value, merge_value)
74
+ retval[key] = if value.is_a?(Hash) && merge_value.is_a?(Hash)
75
+ deep_merge(value, merge_value)
61
76
  else
62
- retval[key] = merge_value
77
+ merge_value
63
78
  end
64
79
  end
65
80
  retval
@@ -67,8 +82,14 @@ module FastSerializer
67
82
 
68
83
  # Convert the value to primitive data types: string, number, boolean, symbol, time, date, array, hash.
69
84
  def serialize_value(value)
70
- 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)
85
+ if value.is_a?(String) || value.is_a?(Numeric) || value.nil? || value == true || value == false || value.is_a?(Symbol)
71
86
  value
87
+ elsif value.is_a?(Time) || value.is_a?(Date)
88
+ if defined?(ActiveSupport::TimeWithZone) && value.is_a?(ActiveSupport::TimeWithZone)
89
+ value.to_time
90
+ else
91
+ value
92
+ end
72
93
  elsif value.is_a?(Hash)
73
94
  serialize_hash(value)
74
95
  elsif value.is_a?(Enumerable)
@@ -89,7 +110,7 @@ module FastSerializer
89
110
  value.each do |k, v|
90
111
  val = serialize_value(v)
91
112
  if val.object_id != v.object_id
92
- hash = value.dup unless hash
113
+ hash ||= value.dup
93
114
  hash[k] = val
94
115
  end
95
116
  end
@@ -101,7 +122,7 @@ module FastSerializer
101
122
  value.each_with_index do |v, i|
102
123
  val = serialize_value(v)
103
124
  if val.object_id != v.object_id
104
- array = value.dup unless array
125
+ array ||= value.dup
105
126
  array[i] = val
106
127
  end
107
128
  end