cinnamon_serial 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.editorconfig +8 -0
- data/.gitignore +2 -0
- data/.rubocop.yml +7 -0
- data/.rubocop_todo.yml +56 -0
- data/.ruby-version +1 -0
- data/.travis.yml +8 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +56 -0
- data/LICENSE +7 -0
- data/README.md +212 -0
- data/cinnamon_serial.gemspec +27 -0
- data/lib/cinnamon_serial.rb +10 -0
- data/lib/cinnamon_serial/base.rb +91 -0
- data/lib/cinnamon_serial/cinnamon_serial.rb +16 -0
- data/lib/cinnamon_serial/dsl.rb +53 -0
- data/lib/cinnamon_serial/formatting.rb +45 -0
- data/lib/cinnamon_serial/resolver.rb +174 -0
- data/lib/cinnamon_serial/specification.rb +40 -0
- data/lib/cinnamon_serial/version.rb +12 -0
- data/spec/base_spec.rb +123 -0
- data/spec/dsl_spec.rb +81 -0
- data/spec/examples.rb +175 -0
- metadata +111 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 82d270da4eb17b6f19b8bc07991f497001b624ff
|
4
|
+
data.tar.gz: e6066491403152ead4a08d7523c9a867cfb5de82
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9f23a5d532c75e3dc676a947fb27abd1497601f269376a5c65a775cb9128edecad6ae803f28e2bb617a39e33fedb51da8ae7d6366674c9e359a465661d37639c
|
7
|
+
data.tar.gz: 1cf6043f141a31c2021ee2168bcb032f57990ce44e05a5bd9be9935afc34a1e72eb548bee619c8c344cd87c4062579e0c68243dfe277bf84ddaaa19b4525672e
|
data/.editorconfig
ADDED
data/.gitignore
ADDED
data/.rubocop.yml
ADDED
data/.rubocop_todo.yml
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
# This configuration was generated by
|
2
|
+
# `rubocop --auto-gen-config`
|
3
|
+
# on 2018-10-03 13:08:39 -0500 using RuboCop version 0.59.2.
|
4
|
+
# The point is for the user to remove these configuration records
|
5
|
+
# one by one as the offenses are removed from the code base.
|
6
|
+
# Note that changes in the inspected code, or installation of new
|
7
|
+
# versions of RuboCop, may require this file to be generated again.
|
8
|
+
|
9
|
+
# Offense count: 2
|
10
|
+
# Cop supports --auto-correct.
|
11
|
+
# Configuration parameters: EnforcedStyle.
|
12
|
+
# SupportedStyles: empty_lines, empty_lines_except_namespace, empty_lines_special, no_empty_lines, beginning_only, ending_only
|
13
|
+
Layout/EmptyLinesAroundClassBody:
|
14
|
+
Exclude:
|
15
|
+
- 'spec/examples.rb'
|
16
|
+
|
17
|
+
# Offense count: 4
|
18
|
+
Lint/BooleanSymbol:
|
19
|
+
Exclude:
|
20
|
+
- 'lib/cinnamon_serial/resolver.rb'
|
21
|
+
- 'spec/examples.rb'
|
22
|
+
|
23
|
+
# Offense count: 4
|
24
|
+
Metrics/AbcSize:
|
25
|
+
Max: 26
|
26
|
+
|
27
|
+
# Offense count: 2
|
28
|
+
# Configuration parameters: CountComments, ExcludedMethods.
|
29
|
+
# ExcludedMethods: refine
|
30
|
+
Metrics/BlockLength:
|
31
|
+
Exclude:
|
32
|
+
- 'spec/base_spec.rb'
|
33
|
+
- 'spec/dsl_spec.rb'
|
34
|
+
|
35
|
+
# Offense count: 1
|
36
|
+
# Configuration parameters: CountComments.
|
37
|
+
Metrics/ClassLength:
|
38
|
+
Max: 111
|
39
|
+
|
40
|
+
# Offense count: 2
|
41
|
+
Metrics/CyclomaticComplexity:
|
42
|
+
Max: 15
|
43
|
+
|
44
|
+
# Offense count: 1
|
45
|
+
# Configuration parameters: CountKeywordArgs.
|
46
|
+
Metrics/ParameterLists:
|
47
|
+
Max: 8
|
48
|
+
|
49
|
+
# Offense count: 1
|
50
|
+
Metrics/PerceivedComplexity:
|
51
|
+
Max: 16
|
52
|
+
|
53
|
+
# Offense count: 1
|
54
|
+
Style/DoubleNegation:
|
55
|
+
Exclude:
|
56
|
+
- 'lib/cinnamon_serial/formatting.rb'
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.7
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,56 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
cinnamon_serial (1.0.0)
|
5
|
+
|
6
|
+
GEM
|
7
|
+
remote: https://rubygems.org/
|
8
|
+
specs:
|
9
|
+
ast (2.4.0)
|
10
|
+
coderay (1.1.2)
|
11
|
+
diff-lcs (1.3)
|
12
|
+
jaro_winkler (1.5.1)
|
13
|
+
method_source (0.9.0)
|
14
|
+
parallel (1.12.1)
|
15
|
+
parser (2.5.1.2)
|
16
|
+
ast (~> 2.4.0)
|
17
|
+
powerpack (0.1.2)
|
18
|
+
pry (0.11.3)
|
19
|
+
coderay (~> 1.1.0)
|
20
|
+
method_source (~> 0.9.0)
|
21
|
+
rainbow (3.0.0)
|
22
|
+
rspec (3.8.0)
|
23
|
+
rspec-core (~> 3.8.0)
|
24
|
+
rspec-expectations (~> 3.8.0)
|
25
|
+
rspec-mocks (~> 3.8.0)
|
26
|
+
rspec-core (3.8.0)
|
27
|
+
rspec-support (~> 3.8.0)
|
28
|
+
rspec-expectations (3.8.1)
|
29
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
30
|
+
rspec-support (~> 3.8.0)
|
31
|
+
rspec-mocks (3.8.0)
|
32
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
33
|
+
rspec-support (~> 3.8.0)
|
34
|
+
rspec-support (3.8.0)
|
35
|
+
rubocop (0.59.2)
|
36
|
+
jaro_winkler (~> 1.5.1)
|
37
|
+
parallel (~> 1.10)
|
38
|
+
parser (>= 2.5, != 2.5.1.1)
|
39
|
+
powerpack (~> 0.1)
|
40
|
+
rainbow (>= 2.2.2, < 4.0)
|
41
|
+
ruby-progressbar (~> 1.7)
|
42
|
+
unicode-display_width (~> 1.0, >= 1.0.1)
|
43
|
+
ruby-progressbar (1.10.0)
|
44
|
+
unicode-display_width (1.4.0)
|
45
|
+
|
46
|
+
PLATFORMS
|
47
|
+
ruby
|
48
|
+
|
49
|
+
DEPENDENCIES
|
50
|
+
cinnamon_serial!
|
51
|
+
pry
|
52
|
+
rspec
|
53
|
+
rubocop (~> 0.59.2)
|
54
|
+
|
55
|
+
BUNDLED WITH
|
56
|
+
1.16.3
|
data/LICENSE
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
Copyright 2018 Blue Marble Payroll, LLC
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
4
|
+
|
5
|
+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
6
|
+
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
# Cinnamon Serial
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/bluemarblepayroll/cinnamon_serial.svg?branch=master)](https://travis-ci.org/bluemarblepayroll/cinnamon_serial)
|
4
|
+
|
5
|
+
A common issue is that we typically want different data going
|
6
|
+
outbound than what we have available server-side. Some example motivations could be:
|
7
|
+
|
8
|
+
* I have too much data, I want to slim down the outbound request.
|
9
|
+
* I do not have enough data, I need to get more and send that as well.
|
10
|
+
* I do not want to expose some data due to security/authorization concerns.
|
11
|
+
|
12
|
+
Having a separate layer that specializes in this type of materialization is important no matter
|
13
|
+
what the reasons are. This library provides a simple domain-specific language that makes creating
|
14
|
+
serializers or serialization layers declarative and easy.
|
15
|
+
|
16
|
+
## Installation
|
17
|
+
|
18
|
+
To install through Rubygems:
|
19
|
+
|
20
|
+
````
|
21
|
+
gem install install cinnamon_serial
|
22
|
+
````
|
23
|
+
|
24
|
+
You can also add this to your Gemfile:
|
25
|
+
|
26
|
+
````
|
27
|
+
bundle add cinnamon_serial
|
28
|
+
````
|
29
|
+
|
30
|
+
## Examples
|
31
|
+
|
32
|
+
### Getting Started
|
33
|
+
|
34
|
+
Consider the following class:
|
35
|
+
|
36
|
+
```
|
37
|
+
class Employee
|
38
|
+
attr_accessor :id, :first_name, :last_name, :active, :account, :progress, :start_date
|
39
|
+
end
|
40
|
+
```
|
41
|
+
|
42
|
+
We could create a simple 1:1 serializer like so:
|
43
|
+
|
44
|
+
```
|
45
|
+
class EmployeeSerializer < CinnamonSerial::Base
|
46
|
+
serialize :id, :first_name, :last_name, :active, :account, :progress, :start_date
|
47
|
+
end
|
48
|
+
```
|
49
|
+
|
50
|
+
To use this serializer:
|
51
|
+
|
52
|
+
```
|
53
|
+
employee = Employee.new
|
54
|
+
# populate employee data...
|
55
|
+
serializer = EmployeeSerializer.new(employee)
|
56
|
+
data = serializer.as_json
|
57
|
+
```
|
58
|
+
|
59
|
+
The 'data' variable above is now a hash with only data as specified by the serializer.
|
60
|
+
|
61
|
+
### Dynamic Attributes
|
62
|
+
|
63
|
+
Serialized keys do not have to match the composed object. Using our examples above we could
|
64
|
+
create another serializer:
|
65
|
+
|
66
|
+
```
|
67
|
+
class EmployeeListSerializer < CinnamonSerial::Base
|
68
|
+
serialize :id
|
69
|
+
serialize :proper_name, to: :last_name
|
70
|
+
end
|
71
|
+
```
|
72
|
+
|
73
|
+
In this case the serialized data will contain a key 'proper_name' instead of 'last_name'.
|
74
|
+
|
75
|
+
### Calling Methods
|
76
|
+
|
77
|
+
serialized keys are not limited to just attributes, in fact, it will just test the model to see if the
|
78
|
+
composed object responds to the key and if it does it will send to the object. For example:
|
79
|
+
|
80
|
+
```
|
81
|
+
class FormalEmployee < Employee
|
82
|
+
def proper_name
|
83
|
+
"#{last_name}, #{first_name}"
|
84
|
+
end
|
85
|
+
end
|
86
|
+
```
|
87
|
+
|
88
|
+
```
|
89
|
+
class EmployeeListSerializer < CinnamonSerial::Base
|
90
|
+
serialize :id, :proper_name
|
91
|
+
end
|
92
|
+
```
|
93
|
+
|
94
|
+
### Value Aliasing
|
95
|
+
|
96
|
+
Note: Internationalization is an incredibly complex problem that this library will not try to solve but it does provide the ability to override resolved values. In the future the aliasing and formatting abilities should be extracted and plugged-in as to provide internationalization support.
|
97
|
+
|
98
|
+
You are allowed to override the value if a value has been resolved to one of the following:
|
99
|
+
|
100
|
+
1. true
|
101
|
+
2. false
|
102
|
+
3. nil
|
103
|
+
4. 'present'
|
104
|
+
5. 'blank'
|
105
|
+
|
106
|
+
Say you want to show Yes/No/Unknown for a boolean value. Building on our previous Employee examples we could modify our EmployeeSerializer:
|
107
|
+
|
108
|
+
```
|
109
|
+
class EmployeeSerializer < CinnamonSerial::Base
|
110
|
+
serialize :id, :first_name, :last_name
|
111
|
+
serialize :active, true_alias: 'Yes', false_alias: 'No', null: 'Unknown'
|
112
|
+
end
|
113
|
+
```
|
114
|
+
|
115
|
+
Now the value of active will be 'Yes', 'No', or 'Unknown' instead of true, false, or null.
|
116
|
+
|
117
|
+
### Value Formatting
|
118
|
+
|
119
|
+
Two basic formatters that come included with this library are:
|
120
|
+
|
121
|
+
1. Masking (defaults to masking all but last 4 characters with character X)
|
122
|
+
2. Percent Formatting (two decimal places)
|
123
|
+
|
124
|
+
An example of custom formatters would be:
|
125
|
+
|
126
|
+
```
|
127
|
+
class EmployeeSerializer < CinnamonSerial::Base
|
128
|
+
serialize :id, :first_name, :last_name
|
129
|
+
serialize :account, mask: true
|
130
|
+
serialize :progress, percent: true
|
131
|
+
end
|
132
|
+
```
|
133
|
+
|
134
|
+
Account will be formatted as a masked string and progress will be converted to a percent formatted string.
|
135
|
+
|
136
|
+
### Custom Methods
|
137
|
+
|
138
|
+
There are two ways to specify to execute a method on the serializer:
|
139
|
+
|
140
|
+
1. Method - call an instance method with no arguments.
|
141
|
+
2. Transform - Resolve the value first then pass it into an instance method.
|
142
|
+
|
143
|
+
For example:
|
144
|
+
|
145
|
+
```
|
146
|
+
class EmployeeSerializer < CinnamonSerial::Base
|
147
|
+
serialize :id, :first_name, :last_name, :start_date
|
148
|
+
serialize :renewal_date, for: :start_date, transform: true
|
149
|
+
serialize :user_id, method: true
|
150
|
+
|
151
|
+
private
|
152
|
+
|
153
|
+
def renewal_date(date)
|
154
|
+
# Two years out
|
155
|
+
date + (60 * 60 * 24 * 24)
|
156
|
+
end
|
157
|
+
|
158
|
+
def user_id
|
159
|
+
dig_opt(:user, :id)
|
160
|
+
end
|
161
|
+
end
|
162
|
+
```
|
163
|
+
|
164
|
+
Some notes about the above example:
|
165
|
+
|
166
|
+
* You can either pass in true (it will call the method named as the key) or the explicit name of the method.
|
167
|
+
* dig_opt is a convenience method that will call dig on the serializer options (second argument in serializer constructor.)
|
168
|
+
|
169
|
+
### Custom Code
|
170
|
+
|
171
|
+
In you need full control over serialization you can create hydrate blocks of code that will execute (in order of declaration.) For example:
|
172
|
+
|
173
|
+
```
|
174
|
+
class EmployeeSerializer < CinnamonSerial::Base
|
175
|
+
serialize :id, :first_name, :last_name, :start_date
|
176
|
+
serialize :active, manual: true
|
177
|
+
|
178
|
+
hydrate do
|
179
|
+
set_active(obj.start_date >= Date.today)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
```
|
183
|
+
|
184
|
+
some notes about the above example:
|
185
|
+
|
186
|
+
* setting manual to true declares that no mapping should be automatically performed. Instead, you must set it within the hydrate block.
|
187
|
+
* set_* are magic methods that will set the value to the value passed in. In this context we will set the 'active' value to true if the start_date is either today or earlier than today.
|
188
|
+
* obj is the composed object (in this context it would be the Employee instance.) You are allowed to access it using the obj getter. In the same vain, you are also allowed to access the serializer options using 'opts' getter.
|
189
|
+
|
190
|
+
## Contributing
|
191
|
+
|
192
|
+
### Development Environment Configuration
|
193
|
+
|
194
|
+
Basic steps to take to get this repository compiling:
|
195
|
+
|
196
|
+
1. Install [Ruby](https://www.ruby-lang.org/en/documentation/installation/) (check cinnamon_serial.gemspec for versions supported)
|
197
|
+
2. Install bundler (gem install bundler)
|
198
|
+
3. Clone the repository (git clone git@github.com:bluemarblepayroll/cinnamon_serial.git)
|
199
|
+
4. Navigate to the root folder (cd cinnamon_serial)
|
200
|
+
5. Install dependencies (bundle)
|
201
|
+
|
202
|
+
### Running Tests
|
203
|
+
|
204
|
+
To execute the test suite run:
|
205
|
+
|
206
|
+
````
|
207
|
+
rspec
|
208
|
+
````
|
209
|
+
|
210
|
+
## License
|
211
|
+
|
212
|
+
This project is MIT Licensed.
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require './lib/cinnamon_serial/version'
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = 'cinnamon_serial'
|
7
|
+
s.version = CinnamonSerial::VERSION
|
8
|
+
s.summary = 'Domain-specific language for serialization specification.'
|
9
|
+
|
10
|
+
s.description = <<-DESCRIPTION
|
11
|
+
Domain-specific language for serialization specification.
|
12
|
+
DESCRIPTION
|
13
|
+
|
14
|
+
s.authors = ['Matthew Ruggio']
|
15
|
+
s.email = ['mruggio@bluemarblepayroll.com']
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
18
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map { |f| File.basename(f) }
|
19
|
+
s.homepage = 'https://github.com/bluemarblepayroll/cinnamon_serial'
|
20
|
+
s.license = 'MIT'
|
21
|
+
|
22
|
+
s.required_ruby_version = '>= 2.3.1'
|
23
|
+
|
24
|
+
s.add_development_dependency('pry')
|
25
|
+
s.add_development_dependency('rspec')
|
26
|
+
s.add_development_dependency('rubocop', '~> 0.59.2')
|
27
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2018-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
require_relative 'cinnamon_serial/cinnamon_serial'
|
@@ -0,0 +1,91 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2018-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module CinnamonSerial
|
11
|
+
# This is the main parent class that all serializers must inherit from.
|
12
|
+
class Base
|
13
|
+
extend Dsl
|
14
|
+
|
15
|
+
class << self
|
16
|
+
def map(enumerable, opts = {})
|
17
|
+
enumerable.map { |e| new(e, opts) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
attr_reader :data,
|
22
|
+
:obj,
|
23
|
+
:opts,
|
24
|
+
:klasses
|
25
|
+
|
26
|
+
def initialize(obj, opts = {}, klasses = Set.new)
|
27
|
+
@obj = obj
|
28
|
+
@opts = opts || {}
|
29
|
+
@klasses = klasses
|
30
|
+
|
31
|
+
materialize_data
|
32
|
+
execute_hydrate_blocks
|
33
|
+
end
|
34
|
+
|
35
|
+
def dig_opt(*keys)
|
36
|
+
opts.dig(*keys)
|
37
|
+
end
|
38
|
+
|
39
|
+
def as_json(_options = {})
|
40
|
+
data
|
41
|
+
end
|
42
|
+
|
43
|
+
def respond_to_missing?(method_sym)
|
44
|
+
data.key?(method_sym.to_s) || super
|
45
|
+
end
|
46
|
+
|
47
|
+
def method_missing(method_sym, *arguments, &block)
|
48
|
+
key = method_sym.to_s.sub('set_', '')
|
49
|
+
|
50
|
+
if data.key?(method_sym.to_s)
|
51
|
+
data[method_sym.to_s]
|
52
|
+
elsif data.key?(key)
|
53
|
+
@data[key] = arguments[0]
|
54
|
+
else
|
55
|
+
super
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def [](attr)
|
60
|
+
send(attr)
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def inherited_cinnamon_serial_specification
|
66
|
+
self.class.inherited_cinnamon_serial_specification
|
67
|
+
end
|
68
|
+
|
69
|
+
def materialize_data
|
70
|
+
@data = {}
|
71
|
+
|
72
|
+
inherited_cinnamon_serial_specification.attribute_map.each do |key, options|
|
73
|
+
@data[key.to_s] = options.resolve(self, key)
|
74
|
+
end
|
75
|
+
|
76
|
+
nil
|
77
|
+
end
|
78
|
+
|
79
|
+
def execute_hydrate_blocks
|
80
|
+
inherited_cinnamon_serial_specification.hydrate_blocks.each do |block|
|
81
|
+
if block && block.arity == 1
|
82
|
+
block.call(self)
|
83
|
+
elsif block
|
84
|
+
instance_eval(&block)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
nil
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2018-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'set'
|
11
|
+
|
12
|
+
require_relative 'formatting'
|
13
|
+
require_relative 'resolver'
|
14
|
+
require_relative 'specification'
|
15
|
+
require_relative 'dsl'
|
16
|
+
require_relative 'base'
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2018-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module CinnamonSerial
|
11
|
+
# This module includes all the class-level methods used to specify serializers.
|
12
|
+
module Dsl
|
13
|
+
def cinnamon_serial_specification
|
14
|
+
@cinnamon_serial_specification ||= Specification.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def serialize(*keys)
|
18
|
+
cinnamon_serial_specification.set(keys)
|
19
|
+
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
# <b>DEPRECATED:</b> Please use <tt>serialize</tt> instead.
|
23
|
+
alias present serialize
|
24
|
+
|
25
|
+
def hydrate(&block)
|
26
|
+
cinnamon_serial_specification.hydrate(block)
|
27
|
+
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def inherited_cinnamon_serial_specification
|
32
|
+
return @inherited_cinnamon_serial_specification if @inherited_cinnamon_serial_specification
|
33
|
+
|
34
|
+
attribute_map = {}
|
35
|
+
hydrate_blocks = []
|
36
|
+
|
37
|
+
# We need to reverse this so parents go first.
|
38
|
+
ancestors.reverse_each do |ancestor|
|
39
|
+
next unless ancestor.respond_to?(:cinnamon_serial_specification)
|
40
|
+
|
41
|
+
specification = ancestor.cinnamon_serial_specification
|
42
|
+
|
43
|
+
attribute_map.merge!(specification.attribute_map)
|
44
|
+
hydrate_blocks += specification.hydrate_blocks
|
45
|
+
end
|
46
|
+
|
47
|
+
@inherited_cinnamon_serial_specification = Specification.new(
|
48
|
+
attribute_map: attribute_map,
|
49
|
+
hydrate_blocks: hydrate_blocks
|
50
|
+
)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2018-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module CinnamonSerial
|
11
|
+
# Static utility methods for general use.
|
12
|
+
class Formatting
|
13
|
+
class << self
|
14
|
+
# Only show the last N positions in a string, replace the
|
15
|
+
# rest with the mask_with value.
|
16
|
+
# Example:
|
17
|
+
# - 123-45-6789 becomes: XXXXXXX6789
|
18
|
+
# - ABCDEFG becomes: XXXDEFG
|
19
|
+
def mask(value, keep_last = 4, mask_with = 'X')
|
20
|
+
string_value = value.to_s
|
21
|
+
return string_value if blank?(string_value) || string_value.size <= keep_last
|
22
|
+
|
23
|
+
(mask_with.to_s * (string_value.size - keep_last)) + string_value[-keep_last..-1]
|
24
|
+
end
|
25
|
+
|
26
|
+
def percent(num)
|
27
|
+
present?(num) ? format('%.2f %', num) : ''
|
28
|
+
end
|
29
|
+
|
30
|
+
def present?(value)
|
31
|
+
!blank?(value)
|
32
|
+
end
|
33
|
+
|
34
|
+
def blank?(value)
|
35
|
+
if value.respond_to?(:blank?)
|
36
|
+
value.blank?
|
37
|
+
elsif value.respond_to?(:empty?)
|
38
|
+
!!value.empty?
|
39
|
+
else
|
40
|
+
!value
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,174 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2018-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module CinnamonSerial
|
11
|
+
# Class that allows an engineer to specify what to do about mapping a key for a serializer.
|
12
|
+
class Resolver
|
13
|
+
attr_accessor :as,
|
14
|
+
:blank,
|
15
|
+
# <b>DEPRECATED:</b> Please use <tt>false_alias</tt> instead.
|
16
|
+
:false,
|
17
|
+
:false_alias,
|
18
|
+
:for,
|
19
|
+
:manual,
|
20
|
+
:mask,
|
21
|
+
:mask_char,
|
22
|
+
:mask_len,
|
23
|
+
:method,
|
24
|
+
:null,
|
25
|
+
:percent,
|
26
|
+
:present,
|
27
|
+
:through,
|
28
|
+
:transform,
|
29
|
+
# <b>DEPRECATED:</b> Please use <tt>true_alias</tt> instead.
|
30
|
+
:true,
|
31
|
+
:true_alias
|
32
|
+
|
33
|
+
def initialize(options = {})
|
34
|
+
@option_keys = options.keys.map(&:to_s).to_set
|
35
|
+
|
36
|
+
options.each do |key, value|
|
37
|
+
raise ArgumentError, "Illegal option: #{key}" unless respond_to?(key)
|
38
|
+
|
39
|
+
send("#{key}=", value)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def resolve(presenter, key)
|
44
|
+
raise ArgumentError, 'Presenter is required' unless presenter
|
45
|
+
|
46
|
+
return if manual
|
47
|
+
|
48
|
+
# Get the value
|
49
|
+
value = resolve_value(presenter, key)
|
50
|
+
|
51
|
+
# Transform the value
|
52
|
+
value = resolve_transform(presenter, key, value)
|
53
|
+
value = resolve_alias(value)
|
54
|
+
value = resolve_as(presenter, value)
|
55
|
+
|
56
|
+
# Format the value
|
57
|
+
value = resolve_percent(value)
|
58
|
+
resolve_mask(value)
|
59
|
+
end
|
60
|
+
|
61
|
+
private
|
62
|
+
|
63
|
+
# (method) and (for/through) are mutually exlusive use-cases.
|
64
|
+
# Example: you would never use for and method.
|
65
|
+
def resolve_value(presenter, key)
|
66
|
+
# If you pass in something that is not true boolean value then use that as a method name
|
67
|
+
# to call on the presenter.
|
68
|
+
return presenter.send(key) if method.is_a?(TrueClass)
|
69
|
+
return presenter.send(method) if method.to_s.length.positive?
|
70
|
+
|
71
|
+
# User for/through
|
72
|
+
model_key = self.for || key
|
73
|
+
model = presenter.obj
|
74
|
+
|
75
|
+
Array(through).each do |association|
|
76
|
+
model = model.respond_to?(association) ? model.send(association) : nil
|
77
|
+
|
78
|
+
break unless model
|
79
|
+
end
|
80
|
+
|
81
|
+
model&.respond_to?(model_key) ? model.send(model_key) : nil
|
82
|
+
end
|
83
|
+
|
84
|
+
def resolve_transform(presenter, key, value)
|
85
|
+
return presenter.send(key, value) if transform.is_a?(TrueClass)
|
86
|
+
return presenter.send(transform, value) if Formatting.present?(transform)
|
87
|
+
|
88
|
+
value
|
89
|
+
end
|
90
|
+
|
91
|
+
def resolve_alias(value)
|
92
|
+
if @option_keys.include?('true_alias') && value.is_a?(TrueClass)
|
93
|
+
true_alias
|
94
|
+
# <b>DEPRECATED:</b> Please use <tt>true_alias</tt> instead.
|
95
|
+
elsif @option_keys.include?('true') && value.is_a?(TrueClass)
|
96
|
+
self.true
|
97
|
+
elsif @option_keys.include?('false_alias') && value.is_a?(FalseClass)
|
98
|
+
false_alias
|
99
|
+
# <b>DEPRECATED:</b> Please use <tt>false_alias</tt> instead.
|
100
|
+
elsif @option_keys.include?('false') && value.is_a?(FalseClass)
|
101
|
+
self.false
|
102
|
+
elsif @option_keys.include?('null') && value.nil?
|
103
|
+
null
|
104
|
+
elsif @option_keys.include?('blank') && Formatting.blank?(value)
|
105
|
+
blank
|
106
|
+
elsif @option_keys.include?('present') && Formatting.present?(value)
|
107
|
+
present
|
108
|
+
else
|
109
|
+
value
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def resolve_mask(value)
|
114
|
+
mask ? Formatting.mask(value, mask_len || 4, mask_char || 'X') : value
|
115
|
+
end
|
116
|
+
|
117
|
+
def resolve_percent(value)
|
118
|
+
percent ? Formatting.percent(value) : value
|
119
|
+
end
|
120
|
+
|
121
|
+
def resolve_as(presenter, value)
|
122
|
+
return value unless as
|
123
|
+
return nil unless value
|
124
|
+
|
125
|
+
class_constant = as_class_constant
|
126
|
+
|
127
|
+
# If we already serialized this type, lets not do it again.
|
128
|
+
# This will prevent endless cycles / loops.
|
129
|
+
return nil if presenter.klasses.include?(class_constant.to_s)
|
130
|
+
|
131
|
+
# We do not want to create a hard dependency on ActiveRecord/Rails in this gem,
|
132
|
+
# but we can still create a soft dependency in case it was included as a peer.
|
133
|
+
value = value.to_a if value.class.name == 'ActiveRecord::Relation'
|
134
|
+
|
135
|
+
new_klasses = presenter.klasses + Set[class_constant.to_s]
|
136
|
+
|
137
|
+
if value.is_a?(Array)
|
138
|
+
value.map { |v| class_constant.new(v, presenter.opts, new_klasses) }
|
139
|
+
else
|
140
|
+
class_constant.new(value, presenter.opts, new_klasses)
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def as_class_name
|
145
|
+
return nil unless as
|
146
|
+
|
147
|
+
non_constant_types = %w[String Symbol]
|
148
|
+
|
149
|
+
# If we have a peer dependency for ActiveSupport then lets use it.
|
150
|
+
if non_constant_types.include?(as.class.name) && as.to_s.respond_to?(:classify)
|
151
|
+
as.to_s.classify
|
152
|
+
elsif non_constant_types.include?(as.class.name)
|
153
|
+
as.to_s
|
154
|
+
else
|
155
|
+
as
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def as_class_constant
|
160
|
+
return nil unless as
|
161
|
+
|
162
|
+
class_name = as_class_name
|
163
|
+
|
164
|
+
# If we have a peer dependency for ActiveSupport then lets use it.
|
165
|
+
if class_name.is_a?(String) && class_name.respond_to?(:constantize)
|
166
|
+
class_name.constantize
|
167
|
+
elsif class_name.is_a?(String)
|
168
|
+
Object.const_get(class_name)
|
169
|
+
else
|
170
|
+
class_name
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2018-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module CinnamonSerial
|
11
|
+
# A Specification is a group of attribute mappings and custom code blocks to execute
|
12
|
+
# for a serializer.
|
13
|
+
class Specification
|
14
|
+
attr_reader :attribute_map, :hydrate_blocks
|
15
|
+
|
16
|
+
def initialize(attribute_map: {}, hydrate_blocks: [])
|
17
|
+
@attribute_map = attribute_map
|
18
|
+
@hydrate_blocks = hydrate_blocks
|
19
|
+
end
|
20
|
+
|
21
|
+
def set(*keys)
|
22
|
+
keys = keys.flatten
|
23
|
+
|
24
|
+
# We have been sent options
|
25
|
+
options = Resolver.new(keys.last.is_a?(Hash) ? keys.pop : {})
|
26
|
+
|
27
|
+
raise ArgumentError, 'keys cannot be empty' if keys.empty?
|
28
|
+
|
29
|
+
keys.each { |key| @attribute_map[key.to_s] = options }
|
30
|
+
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def hydrate(block)
|
35
|
+
@hydrate_blocks << block
|
36
|
+
|
37
|
+
nil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2018-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
module CinnamonSerial
|
11
|
+
VERSION = '1.0.0'
|
12
|
+
end
|
data/spec/base_spec.rb
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2018-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'date'
|
11
|
+
require 'pry'
|
12
|
+
require './lib/cinnamon_serial'
|
13
|
+
require './spec/examples'
|
14
|
+
|
15
|
+
describe CinnamonSerial::Base do
|
16
|
+
let(:employee_list_keys) do
|
17
|
+
%w[
|
18
|
+
active
|
19
|
+
id
|
20
|
+
name
|
21
|
+
user_id
|
22
|
+
user_name
|
23
|
+
other_id
|
24
|
+
manager_name
|
25
|
+
renewal_date
|
26
|
+
notify_date
|
27
|
+
true_value
|
28
|
+
true_alias_value
|
29
|
+
false_value
|
30
|
+
false_alias_value
|
31
|
+
null
|
32
|
+
present
|
33
|
+
blank
|
34
|
+
manager
|
35
|
+
employees
|
36
|
+
account
|
37
|
+
progress
|
38
|
+
]
|
39
|
+
end
|
40
|
+
|
41
|
+
let(:employee_keys) do
|
42
|
+
employee_list_keys + %w[
|
43
|
+
start_date
|
44
|
+
job
|
45
|
+
founder
|
46
|
+
owner
|
47
|
+
]
|
48
|
+
end
|
49
|
+
|
50
|
+
let(:nick) do
|
51
|
+
Employee.new(
|
52
|
+
id: 2,
|
53
|
+
name: 'nick',
|
54
|
+
start_date: Date.new(1350, 12, 12),
|
55
|
+
job: 'electrician',
|
56
|
+
account: '1234567890',
|
57
|
+
progress: 55.5,
|
58
|
+
employees: [
|
59
|
+
Employee.new(
|
60
|
+
id: 1,
|
61
|
+
name: 'matt',
|
62
|
+
start_date:
|
63
|
+
Date.new(1750, 12, 12),
|
64
|
+
job: 'plumber',
|
65
|
+
account: '98765443322111',
|
66
|
+
progress: 10.98
|
67
|
+
)
|
68
|
+
]
|
69
|
+
)
|
70
|
+
end
|
71
|
+
|
72
|
+
let(:matt) do
|
73
|
+
Employee.new(
|
74
|
+
id: 1,
|
75
|
+
name: 'matt',
|
76
|
+
start_date:
|
77
|
+
Date.new(1750, 12, 12),
|
78
|
+
job: 'plumber',
|
79
|
+
account: '98765443322111',
|
80
|
+
progress: 10.98,
|
81
|
+
manager: nick
|
82
|
+
)
|
83
|
+
end
|
84
|
+
|
85
|
+
let(:opts) { { user: { id: 100, name: 'Frank Rizzo' }, active: true } }
|
86
|
+
let(:employee_serializer) { EmployeeSerializer.new(matt, opts) }
|
87
|
+
let(:data) { employee_serializer.data }
|
88
|
+
|
89
|
+
it 'should materialize_data and execute hydrate blocks for superclass' do
|
90
|
+
expect(data['id']).to eq(matt.id)
|
91
|
+
expect(data['name']).to eq(matt.name)
|
92
|
+
expect(data['user_id']).to eq(opts[:user][:id])
|
93
|
+
expect(data['user_name']).to eq(opts[:user][:name])
|
94
|
+
expect(data['other_id']).to eq(matt.id)
|
95
|
+
expect(data['manager_name']).to eq(nick.name)
|
96
|
+
expect(data['renewal_date']).to eq(matt.start_date + (60 * 60 * 24 * 24))
|
97
|
+
expect(data['notify_date']).to eq(matt.start_date + (60 * 60 * 24 * 23))
|
98
|
+
|
99
|
+
expect(data['true_value']).to eq('I am true.')
|
100
|
+
expect(data['true_alias_value']).to eq('I am true alias.')
|
101
|
+
expect(data['false_value']).to eq('I am false.')
|
102
|
+
expect(data['false_alias_value']).to eq('I am false alias.')
|
103
|
+
expect(data['null']).to eq('I am null.')
|
104
|
+
expect(data['present']).to eq('I am present.')
|
105
|
+
expect(data['blank']).to eq('I am blank.')
|
106
|
+
expect(data['manager']).to be_a_kind_of(EmployeeSerializer)
|
107
|
+
|
108
|
+
expect(data['account']).to eq('XXXXXXXXXX2111')
|
109
|
+
expect(data['progress']).to eq('10.98 %')
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should materialize_data and execute hydrate blocks for subclass' do
|
113
|
+
expect(data['active']).to eq(opts[:active])
|
114
|
+
expect(data['start_date']).to eq(matt.start_date)
|
115
|
+
expect(data['job']).to eq(matt.job)
|
116
|
+
expect(data['founder']).to eq(matt.id < 10)
|
117
|
+
expect(data['owner']).to eq(matt.id == 1)
|
118
|
+
end
|
119
|
+
|
120
|
+
it 'should not create cycles when flattening out presenters' do
|
121
|
+
expect(data['manager'].employees).to be nil
|
122
|
+
end
|
123
|
+
end
|
data/spec/dsl_spec.rb
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2018-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
require 'date'
|
11
|
+
require 'pry'
|
12
|
+
require './lib/cinnamon_serial'
|
13
|
+
require './spec/examples'
|
14
|
+
|
15
|
+
describe CinnamonSerial::Dsl do
|
16
|
+
let(:employee_list_keys) do
|
17
|
+
%w[
|
18
|
+
active
|
19
|
+
id
|
20
|
+
name
|
21
|
+
user_id
|
22
|
+
user_name
|
23
|
+
other_id
|
24
|
+
manager_name
|
25
|
+
renewal_date
|
26
|
+
notify_date
|
27
|
+
true_value
|
28
|
+
true_alias_value
|
29
|
+
false_value
|
30
|
+
false_alias_value
|
31
|
+
null
|
32
|
+
present
|
33
|
+
blank
|
34
|
+
manager
|
35
|
+
employees
|
36
|
+
account
|
37
|
+
progress
|
38
|
+
]
|
39
|
+
end
|
40
|
+
|
41
|
+
let(:employee_keys) do
|
42
|
+
employee_list_keys + %w[
|
43
|
+
start_date
|
44
|
+
job
|
45
|
+
founder
|
46
|
+
owner
|
47
|
+
]
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should include all attribute mappings' do
|
51
|
+
specification = EmployeeListSerializer.cinnamon_serial_specification
|
52
|
+
attribute_map = specification.attribute_map
|
53
|
+
keys = attribute_map.keys
|
54
|
+
|
55
|
+
expect(specification).to be_a_kind_of(CinnamonSerial::Specification)
|
56
|
+
expect(keys.count).to eq(employee_list_keys.count)
|
57
|
+
expect(keys).to eq(employee_list_keys)
|
58
|
+
end
|
59
|
+
|
60
|
+
context 'with inheritance' do
|
61
|
+
it 'should include only its immediate attribute mappings' do
|
62
|
+
specification = EmployeeSerializer.cinnamon_serial_specification
|
63
|
+
attribute_map = specification.attribute_map
|
64
|
+
keys = attribute_map.keys
|
65
|
+
|
66
|
+
expect(specification).to be_a_kind_of(CinnamonSerial::Specification)
|
67
|
+
expect(keys.count).to eq(4)
|
68
|
+
expect(keys).to eq(%w[start_date job founder owner])
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should include its immediate and ancestor attribute mappings' do
|
72
|
+
specification = EmployeeSerializer.inherited_cinnamon_serial_specification
|
73
|
+
attribute_map = specification.attribute_map
|
74
|
+
keys = attribute_map.keys
|
75
|
+
|
76
|
+
expect(specification).to be_a_kind_of(CinnamonSerial::Specification)
|
77
|
+
expect(keys.count).to eq(employee_keys.count)
|
78
|
+
expect(keys).to eq(employee_keys)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/spec/examples.rb
ADDED
@@ -0,0 +1,175 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
#
|
4
|
+
# Copyright (c) 2018-present, Blue Marble Payroll, LLC
|
5
|
+
#
|
6
|
+
# This source code is licensed under the MIT license found in the
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
8
|
+
#
|
9
|
+
|
10
|
+
class Employee
|
11
|
+
attr_accessor :id,
|
12
|
+
:name,
|
13
|
+
:start_date,
|
14
|
+
:job,
|
15
|
+
:account,
|
16
|
+
:progress,
|
17
|
+
:manager,
|
18
|
+
:employees
|
19
|
+
|
20
|
+
def initialize(
|
21
|
+
id:,
|
22
|
+
name:,
|
23
|
+
start_date:,
|
24
|
+
job:,
|
25
|
+
account:,
|
26
|
+
progress:,
|
27
|
+
manager: nil,
|
28
|
+
employees: []
|
29
|
+
)
|
30
|
+
@id = id
|
31
|
+
@name = name
|
32
|
+
@start_date = start_date
|
33
|
+
@job = job
|
34
|
+
@account = account
|
35
|
+
@progress = progress
|
36
|
+
@manager = manager
|
37
|
+
@employees = employees
|
38
|
+
end
|
39
|
+
|
40
|
+
def true_alias_value
|
41
|
+
true
|
42
|
+
end
|
43
|
+
alias true_value true_alias_value
|
44
|
+
|
45
|
+
def false_alias_value
|
46
|
+
false
|
47
|
+
end
|
48
|
+
alias false_value false_alias_value
|
49
|
+
|
50
|
+
def null
|
51
|
+
nil
|
52
|
+
end
|
53
|
+
|
54
|
+
def blank
|
55
|
+
''
|
56
|
+
end
|
57
|
+
|
58
|
+
def present
|
59
|
+
'abc123.'
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
# ######################################################
|
64
|
+
# A Class that exemplifies all possible mapping options.
|
65
|
+
# ######################################################
|
66
|
+
class EmployeeListSerializer < CinnamonSerial::Base
|
67
|
+
# Test skipping mapping using 'manual'
|
68
|
+
serialize :active, manual: true
|
69
|
+
|
70
|
+
# ################
|
71
|
+
# Value Resolution
|
72
|
+
# ################
|
73
|
+
|
74
|
+
# Test basic 1:1 mapping
|
75
|
+
serialize :id, :name
|
76
|
+
|
77
|
+
# Test presenter 'method'
|
78
|
+
serialize :user_id, method: true
|
79
|
+
serialize :user_name, method: :formatted_user_name
|
80
|
+
|
81
|
+
# Test 'for'
|
82
|
+
serialize :other_id, for: :id
|
83
|
+
|
84
|
+
# Test 'for' and 'through'
|
85
|
+
serialize :manager_name, for: :name, through: :manager
|
86
|
+
|
87
|
+
# ################
|
88
|
+
# Value Aliasing
|
89
|
+
# ################
|
90
|
+
|
91
|
+
# Test presenter 'transform' method
|
92
|
+
serialize :renewal_date, for: :start_date, transform: true
|
93
|
+
serialize :notify_date, for: :start_date, transform: :notification_date
|
94
|
+
|
95
|
+
# Test 'true' alias
|
96
|
+
|
97
|
+
# <b>DEPRECATED:</b> Please use <tt>true_alias</tt> instead.
|
98
|
+
serialize :true_value, true: 'I am true.'
|
99
|
+
serialize :true_alias_value, true_alias: 'I am true alias.'
|
100
|
+
|
101
|
+
# Test 'false' alias
|
102
|
+
|
103
|
+
# <b>DEPRECATED:</b> Please use <tt>false_alias</tt> instead.
|
104
|
+
serialize :false_value, false: 'I am false.'
|
105
|
+
serialize :false_alias_value, false_alias: 'I am false alias.'
|
106
|
+
|
107
|
+
# Test 'null' alias
|
108
|
+
serialize :null, null: 'I am null.'
|
109
|
+
|
110
|
+
# Test 'present' alias
|
111
|
+
serialize :present, present: 'I am present.'
|
112
|
+
|
113
|
+
# Test 'blank' alias
|
114
|
+
serialize :blank, blank: 'I am blank.'
|
115
|
+
|
116
|
+
# Test 'as' conversion
|
117
|
+
serialize :manager, as: 'EmployeeSerializer'
|
118
|
+
serialize :employees, as: 'EmployeeSerializer'
|
119
|
+
|
120
|
+
# ################
|
121
|
+
# Value Formatting
|
122
|
+
# ################
|
123
|
+
|
124
|
+
serialize :account, mask: true
|
125
|
+
serialize :progress, percent: true
|
126
|
+
|
127
|
+
# #######################
|
128
|
+
# Manual Hydration Blocks
|
129
|
+
# #######################
|
130
|
+
hydrate do
|
131
|
+
set_active(dig_opt(:active))
|
132
|
+
end
|
133
|
+
|
134
|
+
# ##############
|
135
|
+
# Custom Methods
|
136
|
+
# ##############
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def user_id
|
141
|
+
dig_opt(:user, :id)
|
142
|
+
end
|
143
|
+
|
144
|
+
def formatted_user_name
|
145
|
+
dig_opt(:user, :name)
|
146
|
+
end
|
147
|
+
|
148
|
+
def renewal_date(date)
|
149
|
+
# Two years out
|
150
|
+
date + (60 * 60 * 24 * 24)
|
151
|
+
end
|
152
|
+
|
153
|
+
def notification_date(date)
|
154
|
+
# 23 months out
|
155
|
+
date + (60 * 60 * 24 * 23)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
# #############################
|
160
|
+
# Subclass to test inheritance.
|
161
|
+
# #############################
|
162
|
+
class EmployeeSerializer < EmployeeListSerializer
|
163
|
+
present :start_date, :job
|
164
|
+
|
165
|
+
present :founder,
|
166
|
+
:owner, manual: true
|
167
|
+
|
168
|
+
hydrate do
|
169
|
+
set_founder(obj.id < 10)
|
170
|
+
end
|
171
|
+
|
172
|
+
hydrate do
|
173
|
+
set_owner(obj.id == 1)
|
174
|
+
end
|
175
|
+
end
|
metadata
ADDED
@@ -0,0 +1,111 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: cinnamon_serial
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matthew Ruggio
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-10-03 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pry
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rubocop
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 0.59.2
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 0.59.2
|
55
|
+
description: " Domain-specific language for serialization specification.\n"
|
56
|
+
email:
|
57
|
+
- mruggio@bluemarblepayroll.com
|
58
|
+
executables: []
|
59
|
+
extensions: []
|
60
|
+
extra_rdoc_files: []
|
61
|
+
files:
|
62
|
+
- ".editorconfig"
|
63
|
+
- ".gitignore"
|
64
|
+
- ".rubocop.yml"
|
65
|
+
- ".rubocop_todo.yml"
|
66
|
+
- ".ruby-version"
|
67
|
+
- ".travis.yml"
|
68
|
+
- Gemfile
|
69
|
+
- Gemfile.lock
|
70
|
+
- LICENSE
|
71
|
+
- README.md
|
72
|
+
- cinnamon_serial.gemspec
|
73
|
+
- lib/cinnamon_serial.rb
|
74
|
+
- lib/cinnamon_serial/base.rb
|
75
|
+
- lib/cinnamon_serial/cinnamon_serial.rb
|
76
|
+
- lib/cinnamon_serial/dsl.rb
|
77
|
+
- lib/cinnamon_serial/formatting.rb
|
78
|
+
- lib/cinnamon_serial/resolver.rb
|
79
|
+
- lib/cinnamon_serial/specification.rb
|
80
|
+
- lib/cinnamon_serial/version.rb
|
81
|
+
- spec/base_spec.rb
|
82
|
+
- spec/dsl_spec.rb
|
83
|
+
- spec/examples.rb
|
84
|
+
homepage: https://github.com/bluemarblepayroll/cinnamon_serial
|
85
|
+
licenses:
|
86
|
+
- MIT
|
87
|
+
metadata: {}
|
88
|
+
post_install_message:
|
89
|
+
rdoc_options: []
|
90
|
+
require_paths:
|
91
|
+
- lib
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - ">="
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: 2.3.1
|
97
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
requirements: []
|
103
|
+
rubyforge_project:
|
104
|
+
rubygems_version: 2.5.2.3
|
105
|
+
signing_key:
|
106
|
+
specification_version: 4
|
107
|
+
summary: Domain-specific language for serialization specification.
|
108
|
+
test_files:
|
109
|
+
- spec/base_spec.rb
|
110
|
+
- spec/dsl_spec.rb
|
111
|
+
- spec/examples.rb
|