blueprinter 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +77 -0
- data/Rakefile +1 -1
- data/lib/blueprinter.rb +0 -1
- data/lib/blueprinter/base.rb +31 -10
- data/lib/blueprinter/extractor.rb +0 -3
- data/lib/blueprinter/extractors/association_extractor.rb +2 -2
- data/lib/blueprinter/extractors/auto_extractor.rb +6 -1
- data/lib/blueprinter/extractors/block_extractor.rb +1 -1
- data/lib/blueprinter/field.rb +4 -4
- data/lib/blueprinter/version.rb +1 -1
- data/lib/blueprinter/view.rb +14 -4
- data/lib/blueprinter/view_collection.rb +6 -0
- metadata +32 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7305adc0699ba1c10026b7dbfc2de138d85b7e05f19de91d70fd10069bf274b1
|
4
|
+
data.tar.gz: a15a1d19a578a472c3cc91228070ff32441137e57b241571578e72ba89a5f892
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f93a3442dd563f3f929e8d1964a320823819a2ef8bc4aa292751caa35bd33c2dfe74716e49f38bf856d594ebe12251fa4ca0f9ecde01ea7fe2a564f3c9e2a375
|
7
|
+
data.tar.gz: 3ffea695ddd219bd1ec1721853c9a519159d883c31259545440511b8700b82dddc1b9e55595cd886c808f2b09aacf9e47f41e6b65efd7f8034b512c3576d0061
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## 0.7.0 - 2018/10/17
|
2
|
+
|
3
|
+
* [FEATURE] Allow associations to be defined with a block. Please see pr #106. Thanks to @hugopeixoto.
|
4
|
+
* [FEATURE] Inherit view definition when using inheritance. Please see pr #105. Thanks to @hugopeixoto.
|
5
|
+
|
6
|
+
|
1
7
|
## 0.6.0 - 2018/06/05
|
2
8
|
|
3
9
|
* [FEATURE] Add `date_time` format as an option to `field`. Please see pr #68. Thanks to @njbbaer.
|
data/README.md
CHANGED
@@ -41,6 +41,30 @@ And the output would look like:
|
|
41
41
|
}
|
42
42
|
```
|
43
43
|
|
44
|
+
### Renaming
|
45
|
+
|
46
|
+
You can rename the resulting JSON keys in both fields and associations by using the `name` option.
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
class UserBlueprint < Blueprinter::Base
|
50
|
+
identifier :uuid
|
51
|
+
|
52
|
+
field :email, name: :login
|
53
|
+
|
54
|
+
association :user_projects, name: :projects
|
55
|
+
end
|
56
|
+
```
|
57
|
+
|
58
|
+
This will result in JSON that looks something like this:
|
59
|
+
|
60
|
+
```json
|
61
|
+
{
|
62
|
+
"uuid": "92a5c732-2874-41e4-98fc-4123cd6cfa86",
|
63
|
+
"login": "my@email.com",
|
64
|
+
"projects": []
|
65
|
+
}
|
66
|
+
```
|
67
|
+
|
44
68
|
### Views
|
45
69
|
You may define different outputs by utilizing views:
|
46
70
|
```ruby
|
@@ -162,6 +186,44 @@ Output:
|
|
162
186
|
}
|
163
187
|
```
|
164
188
|
|
189
|
+
#### Defining an association directly in the Blueprint
|
190
|
+
|
191
|
+
You can also pass a block to an association:
|
192
|
+
|
193
|
+
```ruby
|
194
|
+
class ProjectBlueprint < Blueprinter::Base
|
195
|
+
identifier :uuid
|
196
|
+
field :name
|
197
|
+
end
|
198
|
+
|
199
|
+
class UserBlueprint < Blueprinter::Base
|
200
|
+
identifier :uuid
|
201
|
+
|
202
|
+
association :projects, blueprint: ProjectBlueprint do |user|
|
203
|
+
user.projects + user.company.projects
|
204
|
+
end
|
205
|
+
end
|
206
|
+
```
|
207
|
+
|
208
|
+
Usage:
|
209
|
+
|
210
|
+
```ruby
|
211
|
+
puts UserBlueprint.render(user)
|
212
|
+
```
|
213
|
+
|
214
|
+
Output:
|
215
|
+
|
216
|
+
```json
|
217
|
+
{
|
218
|
+
"uuid": "733f0758-8f21-4719-875f-262c3ec743af",
|
219
|
+
"projects": [
|
220
|
+
{"uuid": "b426a1e6-ac41-45ab-bfef-970b9a0b4289", "name": "query-console"},
|
221
|
+
{"uuid": "5bd84d6c-4fd2-4e36-ae31-c137e39be542", "name": "blueprinter"},
|
222
|
+
{"uuid": "785f5cd4-7d8d-4779-a6dd-ec5eab440eff", "name": "uncontrollable"}
|
223
|
+
]
|
224
|
+
}
|
225
|
+
```
|
226
|
+
|
165
227
|
### Passing additional properties to `render`
|
166
228
|
|
167
229
|
`render` takes an options hash which you can pass additional properties, allowing you to utilize those additional properties in the `field` block. For example:
|
@@ -190,6 +252,18 @@ Output:
|
|
190
252
|
}
|
191
253
|
```
|
192
254
|
|
255
|
+
### Conditional field
|
256
|
+
|
257
|
+
`field` supports `:if` and `:unless` options argument that can be used to serialize the field conditionally.
|
258
|
+
|
259
|
+
```ruby
|
260
|
+
class UserBlueprint < Blueprinter::Base
|
261
|
+
identifier :uuid
|
262
|
+
field :last_name, if: ->(user, options) { user.first_name != options[:first_name] }
|
263
|
+
field :age, unless: ->(user, _options) { user.age < 18 }
|
264
|
+
end
|
265
|
+
```
|
266
|
+
|
193
267
|
### Custom formatting for dates and times
|
194
268
|
To define a custom format for a Date or DateTime field, include the option `datetime_format` with the associated `strptime` format.
|
195
269
|
|
@@ -267,5 +341,8 @@ We use Yard for documentation. Here are the following documentation rules:
|
|
267
341
|
- Document all public methods we expect to be utilized by the end developers.
|
268
342
|
- Methods that are not set to private due to ruby visibility rule limitations should be marked with `@api private`.
|
269
343
|
|
344
|
+
### Releasing a New Version
|
345
|
+
To release a new version, change the version number in `version.rb`, and update the `CHANGELOG.md`. Finally, maintainers need to run `bundle exec rake release`, which will automatically create a git tag for the version, push git commits and tags to Github, and push the `.gem` file to rubygems.org.
|
346
|
+
|
270
347
|
## License
|
271
348
|
The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
|
data/Rakefile
CHANGED
data/lib/blueprinter.rb
CHANGED
data/lib/blueprinter/base.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require_relative 'blueprinter_error'
|
2
|
-
require_relative '
|
2
|
+
require_relative 'configuration'
|
3
3
|
require_relative 'extractor'
|
4
4
|
require_relative 'extractors/association_extractor'
|
5
5
|
require_relative 'extractors/auto_extractor'
|
@@ -7,6 +7,7 @@ require_relative 'extractors/block_extractor'
|
|
7
7
|
require_relative 'extractors/hash_extractor'
|
8
8
|
require_relative 'extractors/public_send_extractor'
|
9
9
|
require_relative 'field'
|
10
|
+
require_relative 'helpers/active_record_helpers'
|
10
11
|
require_relative 'view'
|
11
12
|
require_relative 'view_collection'
|
12
13
|
|
@@ -36,10 +37,14 @@ module Blueprinter
|
|
36
37
|
# end
|
37
38
|
#
|
38
39
|
# @return [Field] A Field object
|
39
|
-
def self.identifier(method, name: method, extractor: AutoExtractor)
|
40
|
+
def self.identifier(method, name: method, extractor: AutoExtractor.new)
|
40
41
|
view_collection[:identifier] << Field.new(method, name, extractor, self)
|
41
42
|
end
|
42
43
|
|
44
|
+
def self.inherited(subclass)
|
45
|
+
subclass.send(:view_collection).inherit(view_collection)
|
46
|
+
end
|
47
|
+
|
43
48
|
# Specify a field or method name to be included for serialization.
|
44
49
|
# Takes a required method and an option.
|
45
50
|
#
|
@@ -92,9 +97,9 @@ module Blueprinter
|
|
92
97
|
# @return [Field] A Field object
|
93
98
|
def self.field(method, options = {}, &block)
|
94
99
|
options = if block_given?
|
95
|
-
{name: method, extractor: BlockExtractor, block:
|
100
|
+
{name: method, extractor: BlockExtractor.new, block: block}
|
96
101
|
else
|
97
|
-
{name: method, extractor: AutoExtractor}
|
102
|
+
{name: method, extractor: AutoExtractor.new}
|
98
103
|
end.merge(options)
|
99
104
|
current_view << Field.new(method,
|
100
105
|
options[:name],
|
@@ -114,6 +119,8 @@ module Blueprinter
|
|
114
119
|
# JSON output.
|
115
120
|
# @option options [Symbol] :view Specify the view to use or fall back to
|
116
121
|
# to the :default view.
|
122
|
+
# @yield [Object] The object passed to `render` is also passed to the
|
123
|
+
# block.
|
117
124
|
#
|
118
125
|
# @example Specifying an association
|
119
126
|
# class UserBlueprint < Blueprinter::Base
|
@@ -122,15 +129,29 @@ module Blueprinter
|
|
122
129
|
# # code
|
123
130
|
# end
|
124
131
|
#
|
132
|
+
# @example Passing a block to be evaluated as the value.
|
133
|
+
# class UserBlueprint < Blueprinter::Base
|
134
|
+
# association :vehicles, blueprint: VehiclesBlueprint do |user|
|
135
|
+
# user.vehicles + user.company.vehicles
|
136
|
+
# end
|
137
|
+
# end
|
138
|
+
#
|
125
139
|
# @return [Field] A Field object
|
126
|
-
def self.association(method, options = {})
|
140
|
+
def self.association(method, options = {}, &block)
|
127
141
|
raise BlueprinterError, 'blueprint required' unless options[:blueprint]
|
128
142
|
name = options.delete(:name) || method
|
143
|
+
|
144
|
+
options = if block_given?
|
145
|
+
options.merge(extractor: BlockExtractor.new, block: block)
|
146
|
+
else
|
147
|
+
options.merge(extractor: AutoExtractor.new)
|
148
|
+
end
|
149
|
+
|
129
150
|
current_view << Field.new(method,
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
151
|
+
name,
|
152
|
+
AssociationExtractor.new,
|
153
|
+
self,
|
154
|
+
options.merge(association: true))
|
134
155
|
end
|
135
156
|
|
136
157
|
# Generates a JSON formatted String.
|
@@ -215,7 +236,7 @@ module Blueprinter
|
|
215
236
|
# @return [Array<Symbol>] an array of field names
|
216
237
|
def self.fields(*field_names)
|
217
238
|
field_names.each do |field_name|
|
218
|
-
|
239
|
+
field(field_name)
|
219
240
|
end
|
220
241
|
end
|
221
242
|
|
@@ -1,8 +1,8 @@
|
|
1
1
|
module Blueprinter
|
2
2
|
class AssociationExtractor < Extractor
|
3
3
|
def extract(association_name, object, local_options, options={})
|
4
|
-
value =
|
5
|
-
return
|
4
|
+
value = options[:extractor].extract(association_name, object, local_options, options)
|
5
|
+
return options[:default] if value.nil?
|
6
6
|
view = options[:view] || :default
|
7
7
|
options[:blueprint].prepare(value, view_name: view, local_options: local_options)
|
8
8
|
end
|
@@ -1,7 +1,12 @@
|
|
1
1
|
module Blueprinter
|
2
2
|
class AutoExtractor < Extractor
|
3
|
+
def initialize
|
4
|
+
@hash_extractor = HashExtractor.new
|
5
|
+
@public_send_extractor = PublicSendExtractor.new
|
6
|
+
end
|
7
|
+
|
3
8
|
def extract(field_name, object, local_options, options = {})
|
4
|
-
extractor = object.is_a?(Hash) ?
|
9
|
+
extractor = object.is_a?(Hash) ? @hash_extractor : @public_send_extractor
|
5
10
|
extraction = extractor.extract(field_name, object, local_options, options)
|
6
11
|
options.key?(:datetime_format) ? format_datetime(extraction, options[:datetime_format]) : extraction
|
7
12
|
end
|
data/lib/blueprinter/field.rb
CHANGED
@@ -21,13 +21,13 @@ class Blueprinter::Field
|
|
21
21
|
private
|
22
22
|
|
23
23
|
def if_callable
|
24
|
-
return @if_callable
|
25
|
-
@if_callable
|
24
|
+
return @if_callable if defined?(@if_callable)
|
25
|
+
@if_callable = callable_from(:if)
|
26
26
|
end
|
27
27
|
|
28
28
|
def unless_callable
|
29
|
-
return @unless_callable
|
30
|
-
@unless_callable
|
29
|
+
return @unless_callable if defined?(@unless_callable)
|
30
|
+
@unless_callable = callable_from(:unless)
|
31
31
|
end
|
32
32
|
|
33
33
|
def callable_from(option_name)
|
data/lib/blueprinter/version.rb
CHANGED
data/lib/blueprinter/view.rb
CHANGED
@@ -10,6 +10,20 @@ module Blueprinter
|
|
10
10
|
@excluded_field_names = excluded_view_names
|
11
11
|
end
|
12
12
|
|
13
|
+
def inherit(view)
|
14
|
+
view.fields.values.each do |field|
|
15
|
+
self << field
|
16
|
+
end
|
17
|
+
|
18
|
+
view.included_view_names.each do |view_name|
|
19
|
+
include_view(view_name)
|
20
|
+
end
|
21
|
+
|
22
|
+
view.excluded_field_names.each do |field_name|
|
23
|
+
exclude_field(field_name)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
13
27
|
def include_view(view_name)
|
14
28
|
included_view_names << view_name
|
15
29
|
end
|
@@ -19,10 +33,6 @@ module Blueprinter
|
|
19
33
|
end
|
20
34
|
|
21
35
|
def <<(field)
|
22
|
-
if fields.has_key?(field.name)
|
23
|
-
raise BlueprinterError,
|
24
|
-
"Field #{field.name} already defined on #{name}"
|
25
|
-
end
|
26
36
|
fields[field.name] = field
|
27
37
|
end
|
28
38
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: blueprinter
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.7.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Hess
|
@@ -9,8 +9,22 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2018-
|
12
|
+
date: 2018-10-17 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: factory_bot
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
17
|
+
requirements:
|
18
|
+
- - ">="
|
19
|
+
- !ruby/object:Gem::Version
|
20
|
+
version: '0'
|
21
|
+
type: :development
|
22
|
+
prerelease: false
|
23
|
+
version_requirements: !ruby/object:Gem::Requirement
|
24
|
+
requirements:
|
25
|
+
- - ">="
|
26
|
+
- !ruby/object:Gem::Version
|
27
|
+
version: '0'
|
14
28
|
- !ruby/object:Gem::Dependency
|
15
29
|
name: nokogiri
|
16
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -54,7 +68,21 @@ dependencies:
|
|
54
68
|
- !ruby/object:Gem::Version
|
55
69
|
version: '0'
|
56
70
|
- !ruby/object:Gem::Dependency
|
57
|
-
name:
|
71
|
+
name: rake
|
72
|
+
requirement: !ruby/object:Gem::Requirement
|
73
|
+
requirements:
|
74
|
+
- - ">="
|
75
|
+
- !ruby/object:Gem::Version
|
76
|
+
version: '0'
|
77
|
+
type: :development
|
78
|
+
prerelease: false
|
79
|
+
version_requirements: !ruby/object:Gem::Requirement
|
80
|
+
requirements:
|
81
|
+
- - ">="
|
82
|
+
- !ruby/object:Gem::Version
|
83
|
+
version: '0'
|
84
|
+
- !ruby/object:Gem::Dependency
|
85
|
+
name: activerecord
|
58
86
|
requirement: !ruby/object:Gem::Requirement
|
59
87
|
requirements:
|
60
88
|
- - "~>"
|
@@ -159,7 +187,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
159
187
|
version: '0'
|
160
188
|
requirements: []
|
161
189
|
rubyforge_project:
|
162
|
-
rubygems_version: 2.7.
|
190
|
+
rubygems_version: 2.7.6
|
163
191
|
signing_key:
|
164
192
|
specification_version: 4
|
165
193
|
summary: Simple Fast Declarative Serialization Library
|