easy_serializer 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +154 -17
- data/easy_serializer.gemspec +3 -1
- data/lib/easy_serializer.rb +2 -0
- data/lib/easy_serializer/base.rb +32 -47
- data/lib/easy_serializer/cacher.rb +60 -0
- data/lib/easy_serializer/helpers.rb +17 -0
- data/lib/easy_serializer/version.rb +1 -1
- metadata +8 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c4bcd2510b26fd9bc5bce354fa24b8063ab3cd7
|
4
|
+
data.tar.gz: 6ad7cac70735ddc9ba2b5cec6285a04a8ef62f70
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 8d7bedfffb05a5a67d7f6719cc6fdf2caea443d71506b227cdf10e840602516b1d8f92dcf86bcfcef5207953b42b60edc250e25b4cfd3f5cf7b5fc233139942e
|
7
|
+
data.tar.gz: 39425cec8ea178f8e7123483dceeac92b5790e277b7c7befa09feb4c11d950946459edcab9dc92ac1797444a7e372fa12b4299cc1e40ef1f973da20b0177a21a
|
data/README.md
CHANGED
@@ -5,8 +5,11 @@
|
|
5
5
|
[![Test Coverage](https://codeclimate.com/github/arturictus/easy_serializer/badges/coverage.svg)](https://codeclimate.com/github/arturictus/easy_serializer/coverage)
|
6
6
|
|
7
7
|
Semantic serializer for making easy serializing objects.
|
8
|
+
EasySerializer is inspired in [ActiveModel Serializer > 0.10] (https://github.com/rails-api/active_model_serializers/tree/v0.10.0.rc3) it's a
|
9
|
+
simple solution for a day to day work with APIs.
|
10
|
+
It tries to give you a serializer with flexibility, full of features and important capabilities for caching.
|
8
11
|
|
9
|
-
|
12
|
+
Features:
|
10
13
|
- Nice and simple serialization DSL.
|
11
14
|
- Cache helpers to use with your favorite adapter like rails cache.
|
12
15
|
|
@@ -76,6 +79,8 @@ UserSerializer.call(user)
|
|
76
79
|
```
|
77
80
|
**Using blocks:**
|
78
81
|
|
82
|
+
Object being serialized is pass in the block as a first argument.
|
83
|
+
|
79
84
|
```ruby
|
80
85
|
class UserSerializer < EasySerializer::Base
|
81
86
|
attribute(:name) { |user| user.name.capitalize }
|
@@ -83,16 +88,70 @@ class UserSerializer < EasySerializer::Base
|
|
83
88
|
end
|
84
89
|
```
|
85
90
|
|
91
|
+
**Using helpers in blocks:**
|
92
|
+
|
93
|
+
Blocks are executed in the serializer instance, this way you can build your helpers and use them inside the blocks.
|
94
|
+
|
95
|
+
```ruby
|
96
|
+
class BlockExample < EasySerializer::Base
|
97
|
+
attribute :name do |object|
|
98
|
+
upcase object.name
|
99
|
+
end
|
100
|
+
|
101
|
+
def upcase(str)
|
102
|
+
str.upcase
|
103
|
+
end
|
104
|
+
end
|
105
|
+
```
|
106
|
+
|
86
107
|
**Changing keys:**
|
87
108
|
|
88
109
|
```ruby
|
89
110
|
class UserSerializer < EasySerializer::Base
|
90
|
-
attribute :name, key: :
|
91
|
-
attribute(:surname, key: :
|
111
|
+
attribute :name, key: :first_name
|
112
|
+
attribute(:surname, key: :last_name) { |user| user.surname.capitalize }
|
92
113
|
end
|
93
114
|
```
|
94
115
|
|
95
|
-
|
116
|
+
**Using defaults:**
|
117
|
+
|
118
|
+
Default will only be triggered when value is `nil`
|
119
|
+
|
120
|
+
```ruby
|
121
|
+
obj = OpenStruct.new(name: 'Jack', boolean: nil, missing: nil)
|
122
|
+
|
123
|
+
class DefaultLiteral < EasySerializer::Base
|
124
|
+
attribute :name
|
125
|
+
attribute :boolean, default: true
|
126
|
+
attribute(:missing, default: 'anything') { |obj| obj.missing }
|
127
|
+
end
|
128
|
+
|
129
|
+
output = DefaultLiteral.call(obj)
|
130
|
+
output.fetch(:name) #=> 'Jack'
|
131
|
+
output.fetch(:boolean) #=> true
|
132
|
+
output.fetch(:missing) #=> 'anything'
|
133
|
+
```
|
134
|
+
|
135
|
+
Using blocks:
|
136
|
+
|
137
|
+
```ruby
|
138
|
+
obj = OpenStruct.new(name: 'Jack', boolean: nil, missing: nil)
|
139
|
+
|
140
|
+
class DefaultBlock < EasySerializer::Base
|
141
|
+
attribute :name
|
142
|
+
attribute :boolean, default: proc { |obj| obj.name == 'Jack' }
|
143
|
+
attribute :missing, default: proc { |obj| "#{obj.name}-missing" } do |obj|
|
144
|
+
obj.missing
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
output = DefaultBlock.call(obj)
|
149
|
+
output.fetch(:name) #=> 'Jack'
|
150
|
+
output.fetch(:boolean) #=> true
|
151
|
+
output.fetch(:missing) #=> 'Jack-missing'
|
152
|
+
```
|
153
|
+
|
154
|
+
### Serializing nested objects
|
96
155
|
|
97
156
|
```ruby
|
98
157
|
user = OpenStruct.new(
|
@@ -110,7 +169,7 @@ end
|
|
110
169
|
|
111
170
|
class UserSerializer < EasySerializer::Base
|
112
171
|
attributes :name, :surname
|
113
|
-
|
172
|
+
attribute :address, serializer: AddressSerializer
|
114
173
|
end
|
115
174
|
|
116
175
|
UserSerializer.call(user)
|
@@ -144,16 +203,39 @@ UserSerializer.call(user)
|
|
144
203
|
}
|
145
204
|
```
|
146
205
|
|
147
|
-
**Serializer option accepts a
|
206
|
+
**Serializer option accepts a Block:**
|
207
|
+
|
208
|
+
The block will be executed in the Serializer instance.
|
148
209
|
|
149
210
|
```ruby
|
150
|
-
class
|
151
|
-
|
152
|
-
attribute :
|
153
|
-
|
154
|
-
|
211
|
+
class DynamicSerializer < EasySerializer::Base
|
212
|
+
attribute :thing, serializer: proc { serializer_for_object }
|
213
|
+
attribute :d_name
|
214
|
+
|
215
|
+
def serializer_for_object
|
216
|
+
"#{object.class.name}Serializer".classify
|
217
|
+
end
|
155
218
|
end
|
156
219
|
```
|
220
|
+
Inside the block is yielded the value of the method
|
221
|
+
|
222
|
+
```ruby
|
223
|
+
thing = OpenStruct.new(name: 'rigoverto', serializer: 'ThingSerializer')
|
224
|
+
obj = OpenStruct.new(d_name: 'a name', thing: thing)
|
225
|
+
|
226
|
+
class DynamicWithContentSerializer < EasySerializer::Base
|
227
|
+
attribute :thing,
|
228
|
+
serializer: proc { |value| to_const value.serializer }
|
229
|
+
# => block will output ThingSerializer
|
230
|
+
attribute :d_name
|
231
|
+
|
232
|
+
def to_const(str)
|
233
|
+
Class.const_get str.classify
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
DynamicWithContentSerializer.call(obj)
|
238
|
+
```
|
157
239
|
|
158
240
|
### Collection Example:
|
159
241
|
|
@@ -190,7 +272,7 @@ UserSerializer.call(user)
|
|
190
272
|
|
191
273
|
**Caching the serialized object:**
|
192
274
|
|
193
|
-
Serialization will happen only once and the
|
275
|
+
Serialization will happen only once and the resulting hash will be stored in the cache.
|
194
276
|
|
195
277
|
```ruby
|
196
278
|
class UserSerializer < EasySerializer::Base
|
@@ -210,7 +292,7 @@ class UserSerializer < EasySerializer::Base
|
|
210
292
|
end
|
211
293
|
```
|
212
294
|
|
213
|
-
|
295
|
+
Of course it works with blocks:
|
214
296
|
|
215
297
|
```ruby
|
216
298
|
class UserSerializer < EasySerializer::Base
|
@@ -221,6 +303,62 @@ class UserSerializer < EasySerializer::Base
|
|
221
303
|
end
|
222
304
|
```
|
223
305
|
|
306
|
+
Passing cache key:
|
307
|
+
|
308
|
+
```ruby
|
309
|
+
class UserSerializer < EasySerializer::Base
|
310
|
+
attribute(:costly_query, cache: true, cache_key: 'hello') do |user|
|
311
|
+
user.best_friends
|
312
|
+
end
|
313
|
+
end
|
314
|
+
```
|
315
|
+
|
316
|
+
Passing cache key block:
|
317
|
+
|
318
|
+
```ruby
|
319
|
+
class UserSerializer < EasySerializer::Base
|
320
|
+
attribute(
|
321
|
+
:costly_query,
|
322
|
+
cache: true,
|
323
|
+
cache_key: proc { |object| [object, 'costly_query'] }
|
324
|
+
) do |user|
|
325
|
+
user.best_friends
|
326
|
+
end
|
327
|
+
end
|
328
|
+
```
|
329
|
+
|
330
|
+
Passing options to the cache:
|
331
|
+
|
332
|
+
Any option passed in the cache method not specified for EasySerializer will be
|
333
|
+
forwarded as options to the set Cache as options for the fetch method.
|
334
|
+
|
335
|
+
example:
|
336
|
+
|
337
|
+
```ruby
|
338
|
+
class OptionForRootCache < EasySerializer::Base
|
339
|
+
cache true, expires_in: 10.minutes, another_option: true
|
340
|
+
attribute :name
|
341
|
+
end
|
342
|
+
```
|
343
|
+
|
344
|
+
Cache fetch will receive:
|
345
|
+
|
346
|
+
```ruby
|
347
|
+
EasySerializer.cache.fetch(
|
348
|
+
key,# object or defined key
|
349
|
+
expires_in: 10.minutes,
|
350
|
+
another_option: true
|
351
|
+
)
|
352
|
+
```
|
353
|
+
|
354
|
+
Use **cache_options** in attributes
|
355
|
+
|
356
|
+
```ruby
|
357
|
+
class OptionForAttributeCache < EasySerializer::Base
|
358
|
+
attribute :name, cache: true, cache_options: { expires_in: 10.minutes }
|
359
|
+
end
|
360
|
+
```
|
361
|
+
|
224
362
|
**Caching Collections:**
|
225
363
|
|
226
364
|
Cache will try to fetch the cached object in the collection **one by one, the whole collection is not cached**.
|
@@ -232,7 +370,7 @@ class UserSerializer < EasySerializer::Base
|
|
232
370
|
end
|
233
371
|
```
|
234
372
|
|
235
|
-
### Complex example using all features
|
373
|
+
### Complex example using all features
|
236
374
|
|
237
375
|
```ruby
|
238
376
|
class PolymorphicSerializer < EasySerializer::Base
|
@@ -253,9 +391,8 @@ class PolymorphicSerializer < EasySerializer::Base
|
|
253
391
|
collection :elements, serializer: ElementsSerializer, cache: true
|
254
392
|
|
255
393
|
def serializer_for_subject
|
256
|
-
|
257
|
-
object_name
|
258
|
-
"#{namespace}#{object_name}Serializer".constantize
|
394
|
+
object_name = object.subject_type.demodulize
|
395
|
+
"#{object_name}Serializer".constantize
|
259
396
|
end
|
260
397
|
end
|
261
398
|
```
|
data/easy_serializer.gemspec
CHANGED
@@ -10,7 +10,9 @@ Gem::Specification.new do |spec|
|
|
10
10
|
spec.email = ["arturictus@gmail.com"]
|
11
11
|
|
12
12
|
spec.summary = %q{Semantic serializer for making easy serializing objects.}
|
13
|
-
spec.description = %q{
|
13
|
+
spec.description = %q{EasySerializer is inspired in ActiveModel Serializer > 0.10 it's a
|
14
|
+
simple solution for a day to day work with APIs.
|
15
|
+
It tries to give you a serializer with flexibility, full of features and important capabilities for caching.}
|
14
16
|
spec.homepage = "https://github.com/arturictus/easy_serializer"
|
15
17
|
spec.license = "MIT"
|
16
18
|
|
data/lib/easy_serializer.rb
CHANGED
data/lib/easy_serializer/base.rb
CHANGED
@@ -1,9 +1,11 @@
|
|
1
1
|
module EasySerializer
|
2
2
|
class Base
|
3
|
+
include Helpers
|
4
|
+
|
3
5
|
delegate :to_json, :[], to: :serialize
|
4
|
-
attr_reader :
|
5
|
-
def initialize(
|
6
|
-
@
|
6
|
+
attr_reader :object
|
7
|
+
def initialize(object)
|
8
|
+
@object = object
|
7
9
|
end
|
8
10
|
|
9
11
|
class << self
|
@@ -48,12 +50,12 @@ module EasySerializer
|
|
48
50
|
private
|
49
51
|
|
50
52
|
def _serialize
|
51
|
-
__serializable_attributes.each_with_object(HashWithIndifferentAccess.new) do |setup,
|
53
|
+
__serializable_attributes.each_with_object(HashWithIndifferentAccess.new) do |setup, hash|
|
52
54
|
if setup[:key] === false
|
53
|
-
|
55
|
+
hash.merge!(value_or_default(setup))
|
54
56
|
else
|
55
57
|
key = (setup[:key] ? setup[:key] : setup[:name])
|
56
|
-
|
58
|
+
hash[key] = value_or_default(setup)
|
57
59
|
end
|
58
60
|
end
|
59
61
|
end
|
@@ -62,17 +64,7 @@ module EasySerializer
|
|
62
64
|
return false unless EasySerializer.perform_caching
|
63
65
|
cache = __cache
|
64
66
|
return false unless cache
|
65
|
-
|
66
|
-
cache[:block].call(klass_ins, self)
|
67
|
-
else
|
68
|
-
key = if cache[:key]
|
69
|
-
cache[:key].call(klass_ins)
|
70
|
-
else
|
71
|
-
[klass_ins, 'EasySerialized']
|
72
|
-
end
|
73
|
-
fail "[Serializer] No key for cache" unless key
|
74
|
-
EasySerializer.cache.fetch(key) { _serialize }
|
75
|
-
end
|
67
|
+
Cacher.root_call(self, cache, object) { _serialize }
|
76
68
|
end
|
77
69
|
|
78
70
|
def __cache
|
@@ -83,53 +75,46 @@ module EasySerializer
|
|
83
75
|
self.class.instance_variable_get(:@__serializable_attributes) || []
|
84
76
|
end
|
85
77
|
|
78
|
+
def value_or_default(setup)
|
79
|
+
value = attr_serializer(setup)
|
80
|
+
if value.nil? && setup[:default]
|
81
|
+
return option_to_value(setup[:default], object)
|
82
|
+
end
|
83
|
+
value
|
84
|
+
end
|
85
|
+
|
86
86
|
def attr_serializer(setup)
|
87
|
-
|
88
|
-
return
|
87
|
+
value = cache_or_attribute(setup)
|
88
|
+
return value unless serializer = setup[:serializer]
|
89
89
|
if setup[:collection]
|
90
|
-
Array.wrap(
|
90
|
+
Array.wrap(value).map { |o| cache_or_serialize(serializer, o, setup) }
|
91
91
|
else
|
92
|
-
cache_or_serialize(serializer,
|
92
|
+
cache_or_serialize(serializer, value, setup)
|
93
93
|
end
|
94
94
|
end
|
95
95
|
|
96
|
-
def cache_or_attribute(
|
96
|
+
def cache_or_attribute(setup)
|
97
97
|
execute = setup[:block] || proc { |o| o.send(setup[:name]) }
|
98
|
-
if EasySerializer.perform_caching && setup[:cache]
|
99
|
-
|
98
|
+
if EasySerializer.perform_caching && setup[:cache]
|
99
|
+
Cacher.call(self, setup, nil, &execute)
|
100
100
|
else
|
101
|
-
execute
|
101
|
+
instance_exec object, &execute
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
|
-
def cache_or_serialize(serializer,
|
106
|
-
return unless
|
105
|
+
def cache_or_serialize(serializer, value, opts)
|
106
|
+
return unless value
|
107
107
|
if EasySerializer.perform_caching && opts[:cache]
|
108
|
-
|
109
|
-
opts[:cache_key].call(content)
|
110
|
-
else
|
111
|
-
[content, 'EasySerialized']
|
112
|
-
end
|
113
|
-
# Be Aware
|
114
|
-
# We are caching the serialized object
|
115
|
-
EasySerializer.cache.fetch(key) { send_to_serializer(serializer, content) }
|
108
|
+
Cacher.call(self, opts, value)
|
116
109
|
else
|
117
|
-
send_to_serializer(serializer,
|
110
|
+
send_to_serializer(serializer, value)
|
118
111
|
end
|
119
112
|
end
|
120
113
|
|
121
|
-
def from_setup_serializer(serializer, content)
|
122
|
-
case serializer
|
123
|
-
when Proc
|
124
|
-
serializer.call(self, klass_ins, content)
|
125
|
-
else
|
126
|
-
serializer
|
127
|
-
end
|
128
|
-
end
|
129
114
|
|
130
|
-
def send_to_serializer(serializer,
|
131
|
-
return unless
|
132
|
-
|
115
|
+
def send_to_serializer(serializer, value)
|
116
|
+
return unless value
|
117
|
+
option_to_value(serializer, value).call(value)
|
133
118
|
end
|
134
119
|
end
|
135
120
|
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module EasySerializer
|
2
|
+
Cacher = Struct.new(:serializer) do
|
3
|
+
include Helpers
|
4
|
+
|
5
|
+
def self.call(serializer, options, value, &block)
|
6
|
+
Cacher.new(serializer)
|
7
|
+
.set(options: options, block: block, value: value)
|
8
|
+
.execute
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.root_call(serializer, options, value, &block)
|
12
|
+
options[:cache_key] = options[:key]
|
13
|
+
options[:root_call] = true
|
14
|
+
call(serializer, options, value, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_accessor :object, :options, :block
|
18
|
+
attr_writer :value
|
19
|
+
|
20
|
+
delegate :object, to: :serializer
|
21
|
+
|
22
|
+
def set(options)
|
23
|
+
options.each { |k, v| send("#{k}=", v) }
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
def value
|
28
|
+
return unless object && block
|
29
|
+
@value ||= serializer.instance_exec object, &block
|
30
|
+
end
|
31
|
+
|
32
|
+
|
33
|
+
def key
|
34
|
+
@key ||= if options[:cache_key]
|
35
|
+
option_to_value(options[:cache_key], value, serializer)
|
36
|
+
elsif options[:serializer] || options[:root_call]
|
37
|
+
[value, 'EasySerialized'].flatten
|
38
|
+
else
|
39
|
+
[value, options[:name], 'EasySerialized'].flatten
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def options_for_cache
|
44
|
+
if options[:root_call]
|
45
|
+
options.except(:block, :cache_key, :root_call, :serializer, :key)
|
46
|
+
else
|
47
|
+
options[:cache_options]
|
48
|
+
end || {}
|
49
|
+
end
|
50
|
+
|
51
|
+
def execute
|
52
|
+
to_execute = if options[:serializer]
|
53
|
+
proc { serializer.send_to_serializer(options[:serializer], value) }
|
54
|
+
elsif !options[:serializer]
|
55
|
+
proc { serializer.instance_exec object, &block }
|
56
|
+
end
|
57
|
+
EasySerializer.cache.fetch(key, options_for_cache, &to_execute)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module EasySerializer
|
2
|
+
module Helpers
|
3
|
+
def option_to_value(option, value, instance = nil)
|
4
|
+
inst = instance || self
|
5
|
+
case option
|
6
|
+
when Proc
|
7
|
+
inst.instance_exec value, &option
|
8
|
+
# TODO
|
9
|
+
# Will be nice to be able to add classes in the options responding to call
|
10
|
+
# for complex algorithms.
|
11
|
+
# when ->(opt) { opt.respond_to?(:call) }
|
12
|
+
else
|
13
|
+
option
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: easy_serializer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Artur Pañach
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-01-
|
11
|
+
date: 2016-01-31 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -66,7 +66,10 @@ dependencies:
|
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '4.2'
|
69
|
-
description:
|
69
|
+
description: |-
|
70
|
+
EasySerializer is inspired in ActiveModel Serializer > 0.10 it's a
|
71
|
+
simple solution for a day to day work with APIs.
|
72
|
+
It tries to give you a serializer with flexibility, full of features and important capabilities for caching.
|
70
73
|
email:
|
71
74
|
- arturictus@gmail.com
|
72
75
|
executables: []
|
@@ -86,6 +89,8 @@ files:
|
|
86
89
|
- easy_serializer.gemspec
|
87
90
|
- lib/easy_serializer.rb
|
88
91
|
- lib/easy_serializer/base.rb
|
92
|
+
- lib/easy_serializer/cacher.rb
|
93
|
+
- lib/easy_serializer/helpers.rb
|
89
94
|
- lib/easy_serializer/version.rb
|
90
95
|
homepage: https://github.com/arturictus/easy_serializer
|
91
96
|
licenses:
|