fast_serializer 1.1.2 → 1.1.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +52 -0
- data/{MIT_LICENSE → MIT_LICENSE.txt} +1 -1
- data/README.md +32 -3
- data/VERSION +1 -1
- data/fast_serializer.gemspec +27 -19
- data/lib/fast_serializer/array_serializer.rb +11 -5
- data/lib/fast_serializer/cache/active_support_cache.rb +6 -6
- data/lib/fast_serializer/cache.rb +12 -0
- data/lib/fast_serializer/serialization_context.rb +15 -2
- data/lib/fast_serializer/serialized_field.rb +24 -11
- data/lib/fast_serializer/serializer.rb +37 -18
- data/lib/fast_serializer.rb +12 -12
- metadata +14 -79
- data/.gitignore +0 -6
- data/.travis.yml +0 -7
- data/Gemfile +0 -2
- data/Gemfile.lock +0 -50
- data/HISTORY.md +0 -30
- data/Rakefile +0 -6
- data/spec/array_serializer_spec.rb +0 -67
- data/spec/cache/active_support_cache_spec.rb +0 -24
- data/spec/fast_serializer_spec.rb +0 -40
- data/spec/serialization_context_spec.rb +0 -32
- data/spec/serialized_field_spec.rb +0 -82
- data/spec/serializer_spec.rb +0 -177
- data/spec/spec_helper.rb +0 -18
- data/spec/support/test_models.rb +0 -74
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a466a168d1e4a3e346fca8549c08eef80b4f524d5db975197129f23ce8614b16
|
4
|
+
data.tar.gz: 07113eca188e090a2ab948fc9740ac71de8af3d74e76748a368a4e3acc9d3da1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dce0f676f3cb5dc3591b9f28ef09ad662b8203839b96a27f30043fd000399fff0c03799ee642a085afecaba60d7221bc97ae5bc3dc044bdcca21d95bba3cbc97
|
7
|
+
data.tar.gz: c4486052e66f392022e8524cb3fa201087248c82ed6444b9b08ff4ee3d1292795a9f6657324213e32791f572751745a05f58b7c896aba8550f0212a1589debd4
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,52 @@
|
|
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.4
|
8
|
+
|
9
|
+
### Fixed
|
10
|
+
|
11
|
+
- Removed unneeded dependency from gemspec.
|
12
|
+
|
13
|
+
## 1.1.3
|
14
|
+
|
15
|
+
### Added
|
16
|
+
- Optimize object shapes for the Ruby interpreter by declaring instance variables in constructors.
|
17
|
+
|
18
|
+
## 1.1.2
|
19
|
+
|
20
|
+
### Added
|
21
|
+
- Sanity check for unsupported options
|
22
|
+
|
23
|
+
### Fixed
|
24
|
+
- Handle converting ActiveSupport::TimeWithZone to a Time so it can be better dumped to non-JSON formats.
|
25
|
+
|
26
|
+
## 1.1.1
|
27
|
+
|
28
|
+
### Added
|
29
|
+
- Add `array` class method to serializers.
|
30
|
+
|
31
|
+
## 1.1.0
|
32
|
+
|
33
|
+
### Added
|
34
|
+
- Add helper method for scope option.
|
35
|
+
- Pass serialization options to child serializers.
|
36
|
+
- Add `if` option to conditionally include fields.
|
37
|
+
- Better cache keys handling for more complex objects.
|
38
|
+
|
39
|
+
## 1.0.2
|
40
|
+
|
41
|
+
### Added
|
42
|
+
- Better integration with ActiveSupport caching.
|
43
|
+
|
44
|
+
## 1.0.1
|
45
|
+
|
46
|
+
### Fixed
|
47
|
+
- Compatibility with change to fetch_multi in ActiveSupport 4.2.
|
48
|
+
|
49
|
+
## 1.0.0
|
50
|
+
|
51
|
+
### Added
|
52
|
+
- Initial release
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
|
-
[![
|
2
|
-
[![
|
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
|
+
[![Gem Version](https://badge.fury.io/rb/fast_serializer.svg)](https://badge.fury.io/rb/fast_serializer)
|
3
5
|
|
4
6
|
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.
|
5
7
|
|
@@ -232,7 +234,34 @@ You can also use the `array` helper class method on a serializer to do the same
|
|
232
234
|
PersonSerializer.array([a, b, c, d])
|
233
235
|
```
|
234
236
|
|
235
|
-
|
236
237
|
## Performance
|
237
238
|
|
238
239
|
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.
|
240
|
+
|
241
|
+
## Installation
|
242
|
+
|
243
|
+
Add this line to your application's Gemfile:
|
244
|
+
|
245
|
+
```ruby
|
246
|
+
gem 'fast_serializer'
|
247
|
+
```
|
248
|
+
|
249
|
+
And then execute:
|
250
|
+
```bash
|
251
|
+
$ bundle
|
252
|
+
```
|
253
|
+
|
254
|
+
Or install it yourself as:
|
255
|
+
```bash
|
256
|
+
$ gem install fast_serializer
|
257
|
+
```
|
258
|
+
|
259
|
+
## Contributing
|
260
|
+
|
261
|
+
Open a pull request on GitHub.
|
262
|
+
|
263
|
+
Please use the [standardrb](https://github.com/testdouble/standard) syntax and lint your code with `standardrb --fix` before submitting.
|
264
|
+
|
265
|
+
## License
|
266
|
+
|
267
|
+
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.4
|
data/fast_serializer.gemspec
CHANGED
@@ -1,24 +1,32 @@
|
|
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
|
7
|
-
spec.version
|
8
|
-
spec.authors
|
9
|
-
spec.email
|
10
|
-
|
11
|
-
spec.summary
|
12
|
-
spec.homepage
|
13
|
-
spec.license
|
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.
|
21
|
-
|
22
|
-
spec.add_development_dependency "
|
23
|
-
spec.add_development_dependency "activesupport", ">=4.0"
|
29
|
+
spec.required_ruby_version = ">= 2.5"
|
30
|
+
|
31
|
+
spec.add_development_dependency "bundler"
|
24
32
|
end
|
@@ -10,9 +10,11 @@ module FastSerializer
|
|
10
10
|
serialize :array
|
11
11
|
|
12
12
|
def initialize(object, options = nil)
|
13
|
+
@_array = nil
|
13
14
|
super(Array(object), options)
|
14
15
|
end
|
15
16
|
|
17
|
+
# @return [String]
|
16
18
|
def cache_key
|
17
19
|
if option(:serializer)
|
18
20
|
array.collect(&:cache_key)
|
@@ -21,6 +23,7 @@ module FastSerializer
|
|
21
23
|
end
|
22
24
|
end
|
23
25
|
|
26
|
+
# @return [Boolean]
|
24
27
|
def cacheable?
|
25
28
|
if option(:cacheable) || self.class.cacheable?
|
26
29
|
true
|
@@ -31,6 +34,7 @@ module FastSerializer
|
|
31
34
|
end
|
32
35
|
end
|
33
36
|
|
37
|
+
# @return [Numeric, Boolean]
|
34
38
|
def cache_ttl
|
35
39
|
if option(:cache_ttl)
|
36
40
|
true
|
@@ -41,6 +45,7 @@ module FastSerializer
|
|
41
45
|
end
|
42
46
|
end
|
43
47
|
|
48
|
+
# @return [FastSerializer::Cache, Boolean]
|
44
49
|
def cache
|
45
50
|
if option(:cache)
|
46
51
|
true
|
@@ -51,6 +56,7 @@ module FastSerializer
|
|
51
56
|
end
|
52
57
|
end
|
53
58
|
|
59
|
+
# @return [Hash]
|
54
60
|
def as_json(*args)
|
55
61
|
if array.nil?
|
56
62
|
nil
|
@@ -63,14 +69,14 @@ module FastSerializer
|
|
63
69
|
|
64
70
|
undef :to_hash
|
65
71
|
undef :to_h
|
66
|
-
|
72
|
+
alias_method :to_a, :as_json
|
67
73
|
|
68
74
|
protected
|
69
75
|
|
70
76
|
def load_from_cache
|
71
77
|
if cache
|
72
|
-
values = cache.fetch_all(array, cache_ttl){|serializer| serializer.as_json}
|
73
|
-
{:
|
78
|
+
values = cache.fetch_all(array, cache_ttl) { |serializer| serializer.as_json }
|
79
|
+
{array: values}
|
74
80
|
else
|
75
81
|
load_hash
|
76
82
|
end
|
@@ -79,11 +85,11 @@ module FastSerializer
|
|
79
85
|
private
|
80
86
|
|
81
87
|
def array
|
82
|
-
|
88
|
+
if @_array.nil?
|
83
89
|
serializer = option(:serializer)
|
84
90
|
if serializer
|
85
91
|
serializer_options = option(:serializer_options)
|
86
|
-
@_array = object.collect{|obj| serializer.new(obj, serializer_options)}
|
92
|
+
@_array = object.collect { |obj| serializer.new(obj, serializer_options) }
|
87
93
|
else
|
88
94
|
@_array = object
|
89
95
|
end
|
@@ -4,21 +4,21 @@ module FastSerializer
|
|
4
4
|
# ActiveSupport compatible cache implementation.
|
5
5
|
class Cache::ActiveSupportCache < Cache
|
6
6
|
attr_reader :cache
|
7
|
-
|
7
|
+
|
8
8
|
def initialize(cache)
|
9
9
|
@cache = cache
|
10
10
|
end
|
11
|
-
|
11
|
+
|
12
12
|
def fetch(serializer, ttl)
|
13
|
-
@cache.fetch(serializer.cache_key, :
|
13
|
+
@cache.fetch(serializer.cache_key, expires_in: ttl) do
|
14
14
|
yield(serializer)
|
15
15
|
end
|
16
16
|
end
|
17
|
-
|
17
|
+
|
18
18
|
def fetch_all(serializers, ttl)
|
19
|
-
results = @cache.fetch_multi(*serializers){|serializer| yield(serializer)}
|
19
|
+
results = @cache.fetch_multi(*serializers) { |serializer| yield(serializer) }
|
20
20
|
if results.is_a?(Hash)
|
21
|
-
serializers.collect{|serializer| results[serializer]}
|
21
|
+
serializers.collect { |serializer| results[serializer] }
|
22
22
|
else
|
23
23
|
results
|
24
24
|
end
|
@@ -4,6 +4,13 @@ module FastSerializer
|
|
4
4
|
# Base class for cache implementations for storing cacheable serializers.
|
5
5
|
# Implementations must implement the +fetch+ method.
|
6
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.
|
7
14
|
def fetch(serializer, ttl, &block)
|
8
15
|
raise NotImplementedError
|
9
16
|
end
|
@@ -13,6 +20,11 @@ module FastSerializer
|
|
13
20
|
# if the cache can return multiple values at once.
|
14
21
|
#
|
15
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.
|
16
28
|
def fetch_all(serializers, ttl)
|
17
29
|
serializers.collect do |serializer|
|
18
30
|
fetch(serializer, ttl) do
|
@@ -9,6 +9,8 @@ module FastSerializer
|
|
9
9
|
# Use a context or create one for use within a block. Any serializers
|
10
10
|
# based on the same object with the same options within the block will be
|
11
11
|
# re-used instead of creating duplicates.
|
12
|
+
#
|
13
|
+
# @return [Object] The return value of the block.
|
12
14
|
def use
|
13
15
|
if Thread.current[:fast_serializer_context]
|
14
16
|
yield
|
@@ -23,6 +25,8 @@ module FastSerializer
|
|
23
25
|
end
|
24
26
|
|
25
27
|
# Return the current context or nil if none is in use.
|
28
|
+
#
|
29
|
+
# @return [FastSerializer::SerializationContext, nil]
|
26
30
|
def current
|
27
31
|
Thread.current[:fast_serializer_context]
|
28
32
|
end
|
@@ -33,9 +37,14 @@ module FastSerializer
|
|
33
37
|
@references = nil
|
34
38
|
end
|
35
39
|
|
36
|
-
# Returns a serializer from the context cache if
|
40
|
+
# Returns a serializer from the context cache if one has already
|
37
41
|
# been created. Otherwise creates the serializer and adds it to the
|
38
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.
|
39
48
|
def load(serializer_class, object, options = nil)
|
40
49
|
key = [serializer_class, object, options]
|
41
50
|
serializer = nil
|
@@ -46,7 +55,7 @@ module FastSerializer
|
|
46
55
|
unless serializer
|
47
56
|
serializer = serializer_class.allocate
|
48
57
|
serializer.send(:initialize, object, options)
|
49
|
-
@cache
|
58
|
+
@cache = {}
|
50
59
|
@cache[key] = serializer
|
51
60
|
end
|
52
61
|
|
@@ -54,6 +63,10 @@ module FastSerializer
|
|
54
63
|
end
|
55
64
|
|
56
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.
|
57
70
|
def with_reference(object)
|
58
71
|
if @references
|
59
72
|
raise CircularReferenceError.new(object) if @references.include?(object)
|
@@ -5,8 +5,16 @@ module FastSerializer
|
|
5
5
|
class SerializedField
|
6
6
|
attr_reader :name, :condition
|
7
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
|
8
16
|
def initialize(name, optional: false, serializer: nil, serializer_options: nil, enumerable: false, condition: nil)
|
9
|
-
@name = name
|
17
|
+
@name = name.to_sym
|
10
18
|
@optional = !!optional
|
11
19
|
@condition = condition
|
12
20
|
if serializer
|
@@ -16,22 +24,27 @@ module FastSerializer
|
|
16
24
|
end
|
17
25
|
end
|
18
26
|
|
27
|
+
# @return [Boolean] true if the field is optional
|
19
28
|
def optional?
|
20
29
|
@optional
|
21
30
|
end
|
22
31
|
|
23
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
|
24
37
|
def serialize(value, options = nil)
|
25
38
|
if value && @serializer
|
26
39
|
serializer = nil
|
27
|
-
if @enumerable
|
28
|
-
|
40
|
+
serializer = if @enumerable
|
41
|
+
ArraySerializer.new(value, serializer: @serializer, serializer_options: serializer_options(options))
|
29
42
|
else
|
30
|
-
|
43
|
+
@serializer.new(value, serializer_options(options))
|
31
44
|
end
|
32
45
|
context = SerializationContext.current
|
33
46
|
if context
|
34
|
-
context.with_reference(value){ serializer.as_json }
|
47
|
+
context.with_reference(value) { serializer.as_json }
|
35
48
|
else
|
36
49
|
serializer.as_json
|
37
50
|
end
|
@@ -58,10 +71,10 @@ module FastSerializer
|
|
58
71
|
retval = {}
|
59
72
|
merge_hash.each do |key, merge_value|
|
60
73
|
value = hash[key]
|
61
|
-
if value.is_a?(Hash) && merge_value.is_a?(Hash)
|
62
|
-
|
74
|
+
retval[key] = if value.is_a?(Hash) && merge_value.is_a?(Hash)
|
75
|
+
deep_merge(value, merge_value)
|
63
76
|
else
|
64
|
-
|
77
|
+
merge_value
|
65
78
|
end
|
66
79
|
end
|
67
80
|
retval
|
@@ -69,7 +82,7 @@ module FastSerializer
|
|
69
82
|
|
70
83
|
# Convert the value to primitive data types: string, number, boolean, symbol, time, date, array, hash.
|
71
84
|
def serialize_value(value)
|
72
|
-
if value.is_a?(String) || value.is_a?(Numeric) || value
|
85
|
+
if value.is_a?(String) || value.is_a?(Numeric) || value.nil? || value == true || value == false || value.is_a?(Symbol)
|
73
86
|
value
|
74
87
|
elsif value.is_a?(Time) || value.is_a?(Date)
|
75
88
|
if defined?(ActiveSupport::TimeWithZone) && value.is_a?(ActiveSupport::TimeWithZone)
|
@@ -97,7 +110,7 @@ module FastSerializer
|
|
97
110
|
value.each do |k, v|
|
98
111
|
val = serialize_value(v)
|
99
112
|
if val.object_id != v.object_id
|
100
|
-
hash
|
113
|
+
hash ||= value.dup
|
101
114
|
hash[k] = val
|
102
115
|
end
|
103
116
|
end
|
@@ -109,7 +122,7 @@ module FastSerializer
|
|
109
122
|
value.each_with_index do |v, i|
|
110
123
|
val = serialize_value(v)
|
111
124
|
if val.object_id != v.object_id
|
112
|
-
array
|
125
|
+
array ||= value.dup
|
113
126
|
array[i] = val
|
114
127
|
end
|
115
128
|
end
|
@@ -57,7 +57,6 @@ module FastSerializer
|
|
57
57
|
#
|
58
58
|
# Serializing a nil object will result in nil rather than an empty hash.
|
59
59
|
module Serializer
|
60
|
-
|
61
60
|
def self.included(base)
|
62
61
|
base.extend(ClassMethods)
|
63
62
|
base.extend(ArrayHelper) unless base.is_a?(FastSerializer::ArraySerializer)
|
@@ -102,6 +101,10 @@ module FastSerializer
|
|
102
101
|
#
|
103
102
|
# Subclasses will inherit all of their parent classes serialized fields. Subclasses can override fields
|
104
103
|
# defined on the parent class by simply defining them again.
|
104
|
+
#
|
105
|
+
# @param fields [Array<Symbol, Hash>] the fields to serialize. If the last argument is a hash, it will be
|
106
|
+
# treated as options for the serialized fields.
|
107
|
+
# @return [void]
|
105
108
|
def serialize(*fields)
|
106
109
|
options = {}
|
107
110
|
if fields.size > 1 && fields.last.is_a?(Hash)
|
@@ -119,7 +122,7 @@ module FastSerializer
|
|
119
122
|
condition = options.delete(:if)
|
120
123
|
|
121
124
|
unless options.empty?
|
122
|
-
raise ArgumentError.new("Unsupported serialize options: #{options.keys.join(
|
125
|
+
raise ArgumentError.new("Unsupported serialize options: #{options.keys.join(", ")}")
|
123
126
|
end
|
124
127
|
|
125
128
|
if as && fields.size > 1
|
@@ -128,8 +131,8 @@ module FastSerializer
|
|
128
131
|
|
129
132
|
fields.each do |field|
|
130
133
|
name = as
|
131
|
-
if name.nil? && field.to_s.end_with?("?"
|
132
|
-
name = field.to_s.chomp("?"
|
134
|
+
if name.nil? && field.to_s.end_with?("?")
|
135
|
+
name = field.to_s.chomp("?")
|
133
136
|
end
|
134
137
|
|
135
138
|
field = field.to_sym
|
@@ -144,6 +147,8 @@ module FastSerializer
|
|
144
147
|
|
145
148
|
# Remove a field from being serialized. This can be useful in subclasses if they need to remove a
|
146
149
|
# field defined by the parent class.
|
150
|
+
#
|
151
|
+
# @param fields [Array<Symbol>] the fields to remove
|
147
152
|
def remove(*fields)
|
148
153
|
remove_fields = fields.collect(&:to_sym)
|
149
154
|
field_list = []
|
@@ -161,6 +166,10 @@ module FastSerializer
|
|
161
166
|
#
|
162
167
|
# You can also specify the cache time to live (ttl) in seconds and the cache implementation to use.
|
163
168
|
# Both of these values are inherited on subclasses.
|
169
|
+
#
|
170
|
+
# @param cacheable [Boolean] pass false if the serializer is not cacheable
|
171
|
+
# @param ttl [Numeric] the time to live in seconds for a cacheable serializer
|
172
|
+
# @param cache [FastSerializer::Cache] the cache implementation to use for a cacheable serializer
|
164
173
|
def cacheable(cacheable = true, ttl: nil, cache: nil)
|
165
174
|
@cacheable = cacheable
|
166
175
|
self.cache_ttl = ttl if ttl
|
@@ -168,6 +177,8 @@ module FastSerializer
|
|
168
177
|
end
|
169
178
|
|
170
179
|
# Return true if the serializer class is cacheable.
|
180
|
+
#
|
181
|
+
# @return [Boolean]
|
171
182
|
def cacheable?
|
172
183
|
unless defined?(@cacheable)
|
173
184
|
@cacheable = superclass.cacheable? if superclass.respond_to?(:cacheable?)
|
@@ -176,22 +187,27 @@ module FastSerializer
|
|
176
187
|
end
|
177
188
|
|
178
189
|
# Return the time to live in seconds for a cacheable serializer.
|
190
|
+
#
|
191
|
+
# @return [Numeric]
|
179
192
|
def cache_ttl
|
180
193
|
if defined?(@cache_ttl)
|
181
194
|
@cache_ttl
|
182
195
|
elsif superclass.respond_to?(:cache_ttl)
|
183
196
|
superclass.cache_ttl
|
184
|
-
else
|
185
|
-
nil
|
186
197
|
end
|
187
198
|
end
|
188
199
|
|
189
200
|
# Set the time to live on a cacheable serializer.
|
201
|
+
#
|
202
|
+
# @param value [Numeric] the time to live in seconds
|
203
|
+
# @return [void]
|
190
204
|
def cache_ttl=(value)
|
191
205
|
@cache_ttl = value
|
192
206
|
end
|
193
207
|
|
194
208
|
# Get the cache implemtation used to store cacheable serializers.
|
209
|
+
#
|
210
|
+
# @return [FastSerializer::Cache]
|
195
211
|
def cache
|
196
212
|
if defined?(@cache)
|
197
213
|
@cache
|
@@ -203,6 +219,9 @@ module FastSerializer
|
|
203
219
|
end
|
204
220
|
|
205
221
|
# Set the cache implementation used to store cacheable serializers.
|
222
|
+
#
|
223
|
+
# @param cache [FastSerializer::Cache]
|
224
|
+
# @return [void]
|
206
225
|
def cache=(cache)
|
207
226
|
if defined?(ActiveSupport::Cache::Store) && cache.is_a?(ActiveSupport::Cache::Store)
|
208
227
|
cache = Cache::ActiveSupportCache.new(cache)
|
@@ -222,6 +241,8 @@ module FastSerializer
|
|
222
241
|
end
|
223
242
|
|
224
243
|
# Return a list of the SerializedFields defined for the class.
|
244
|
+
#
|
245
|
+
# @return [Array<FastSerializer::SerializedField>]
|
225
246
|
def serializable_fields
|
226
247
|
unless defined?(@serializable_fields) && @serializable_fields
|
227
248
|
fields = superclass.send(:serializable_fields).dup if superclass.respond_to?(:serializable_fields)
|
@@ -249,10 +270,10 @@ module FastSerializer
|
|
249
270
|
field_list = []
|
250
271
|
added = false
|
251
272
|
serializable_fields.each do |existing_field|
|
252
|
-
if existing_field.name == name
|
253
|
-
|
273
|
+
field_list << if existing_field.name == name
|
274
|
+
field
|
254
275
|
else
|
255
|
-
|
276
|
+
existing_field
|
256
277
|
end
|
257
278
|
end
|
258
279
|
field_list << field unless added
|
@@ -261,14 +282,14 @@ module FastSerializer
|
|
261
282
|
|
262
283
|
# Define a delegate method name +attribute+ that invokes the +field+ method on the wrapped object.
|
263
284
|
def define_delegate(attribute, field)
|
264
|
-
define_method(attribute){ object.send(field) }
|
285
|
+
define_method(attribute) { object.send(field) }
|
265
286
|
end
|
266
287
|
end
|
267
288
|
|
268
289
|
module ArrayHelper
|
269
290
|
# Helper method to serialize an array of values using this serializer.
|
270
291
|
def array(values, options = nil)
|
271
|
-
options = (options ? options.merge(:
|
292
|
+
options = (options ? options.merge(serializer: self) : {serializer: self})
|
272
293
|
FastSerializer::ArraySerializer.new(values, options)
|
273
294
|
end
|
274
295
|
end
|
@@ -295,14 +316,12 @@ module FastSerializer
|
|
295
316
|
# Serialize the wrapped object into a format suitable for passing to a JSON parser.
|
296
317
|
def as_json(*args)
|
297
318
|
return nil unless object
|
298
|
-
|
299
|
-
@_serialized = (cacheable? ? load_from_cache : load_hash).freeze
|
300
|
-
end
|
319
|
+
@_serialized ||= (cacheable? ? load_from_cache : load_hash).freeze
|
301
320
|
@_serialized
|
302
321
|
end
|
303
322
|
|
304
|
-
|
305
|
-
|
323
|
+
alias_method :to_hash, :as_json
|
324
|
+
alias_method :to_h, :as_json
|
306
325
|
|
307
326
|
# Convert the wrapped object to JSON format.
|
308
327
|
def to_json(options = {})
|
@@ -363,7 +382,7 @@ module FastSerializer
|
|
363
382
|
name = field.name
|
364
383
|
|
365
384
|
if field.optional?
|
366
|
-
next unless include_fields
|
385
|
+
next unless include_fields&.include?(name)
|
367
386
|
end
|
368
387
|
next if excluded_fields && excluded_fields[name] == true
|
369
388
|
condition = field.condition
|
@@ -419,7 +438,7 @@ module FastSerializer
|
|
419
438
|
end
|
420
439
|
hash_key
|
421
440
|
elsif options.is_a?(Enumerable)
|
422
|
-
options.collect{|option| options_cache_key(option)}
|
441
|
+
options.collect { |option| options_cache_key(option) }
|
423
442
|
else
|
424
443
|
options
|
425
444
|
end
|
data/lib/fast_serializer.rb
CHANGED
@@ -1,24 +1,22 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require "json"
|
4
|
+
require "time"
|
5
|
+
require "date"
|
6
6
|
|
7
7
|
module FastSerializer
|
8
|
-
require_relative
|
9
|
-
require_relative
|
10
|
-
require_relative
|
11
|
-
require_relative
|
12
|
-
require_relative
|
13
|
-
require_relative
|
8
|
+
require_relative "fast_serializer/cache"
|
9
|
+
require_relative "fast_serializer/cache/active_support_cache"
|
10
|
+
require_relative "fast_serializer/serialization_context"
|
11
|
+
require_relative "fast_serializer/serialized_field"
|
12
|
+
require_relative "fast_serializer/serializer"
|
13
|
+
require_relative "fast_serializer/array_serializer"
|
14
14
|
|
15
15
|
class << self
|
16
16
|
@cache = nil
|
17
17
|
|
18
18
|
# Get the global cache implementation used for storing cacheable serializers.
|
19
|
-
|
20
|
-
@cache
|
21
|
-
end
|
19
|
+
attr_reader :cache
|
22
20
|
|
23
21
|
# Set the global cache implementation used for storing cacheable serializers.
|
24
22
|
# The cache implementation should implement the +fetch+ method as defined in
|
@@ -26,6 +24,8 @@ module FastSerializer
|
|
26
24
|
#
|
27
25
|
# In a Rails app, you can initialize the cache by simply passing in the value :rails
|
28
26
|
# to use the default Rails.cache. You can also directly pass in an ActiveSupportCache::Store.
|
27
|
+
#
|
28
|
+
# @param cache [FastSerializer::Cache, ActiveSupport::Cache::Store, Symbol] the cache to use
|
29
29
|
def cache=(cache)
|
30
30
|
if cache == :rails
|
31
31
|
cache = Cache::ActiveSupportCache.new(Rails.cache)
|