blueprinter 0.12.1 → 0.13.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 +5 -5
- data/CHANGELOG.md +5 -0
- data/README.md +98 -0
- data/lib/blueprinter/base.rb +20 -78
- data/lib/blueprinter/helpers/base_helpers.rb +103 -0
- data/lib/blueprinter/version.rb +1 -1
- metadata +8 -9
- data/lib/blueprinter/helpers/active_record_helpers.rb +0 -18
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 00bf7f1cfbdc877cc258ddc862d695f7ed202a956c1088e3d3f762e84556fa49
|
4
|
+
data.tar.gz: 761ccae943b7f909584d9db53a4025fff1e23a2602ce1b564fa69bd2821e6388
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 48dcabf90713cf47a2631ccbcc9b43699921ef9404e6d0c7009a51d1fae3c39256a0b1f869b4762ca0f681ed8617340eeda704d5faa8c65b1a99aa917c648300
|
7
|
+
data.tar.gz: 21c47ce365b89e103ab49dcd9655ba25f892730e7a7ae022d3fd73a1aa2be521b1b57a85335332bdbeaae78f2711c03da38a368b19890e981a6ad162f2d75706
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,8 @@
|
|
1
|
+
## 0.13.0 - 2019/02/07
|
2
|
+
|
3
|
+
* 🚀 [FEATURE] Added an option to render with a root key. [#135](https://github.com/procore/blueprinter/pull/135). Thanks to [@ritikesh](https://github.com/ritikesh).
|
4
|
+
* 🚀 [FEATURE] Added an option to render with a top-level meta attribute. [#135](https://github.com/procore/blueprinter/pull/135). Thanks to [@ritikesh](https://github.com/ritikesh).
|
5
|
+
|
1
6
|
## 0.12.1 - 2019/1/24
|
2
7
|
|
3
8
|
* 🐛 [BUGFIX] Fix boolean `false` values getting serialized as `null`. Please see PR [#132](https://github.com/procore/blueprinter/pull/132).
|
data/README.md
CHANGED
@@ -42,6 +42,33 @@ And the output would look like:
|
|
42
42
|
}
|
43
43
|
```
|
44
44
|
|
45
|
+
### Collections
|
46
|
+
|
47
|
+
You can also pass a collection object or an array to the render method.
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
puts UserBlueprint.render(User.all)
|
51
|
+
```
|
52
|
+
|
53
|
+
This will result in JSON that looks something like this:
|
54
|
+
|
55
|
+
```json
|
56
|
+
[
|
57
|
+
{
|
58
|
+
"uuid": "733f0758-8f21-4719-875f-262c3ec743af",
|
59
|
+
"email": "john.doe@some.fake.email.domain",
|
60
|
+
"first_name": "John",
|
61
|
+
"last_name": "Doe"
|
62
|
+
},
|
63
|
+
{
|
64
|
+
"uuid": "733f0758-8f21-4719-875f-743af262c3ec",
|
65
|
+
"email": "john.doe.2@some.fake.email.domain",
|
66
|
+
"first_name": "John",
|
67
|
+
"last_name": "Doe 2"
|
68
|
+
}
|
69
|
+
]
|
70
|
+
```
|
71
|
+
|
45
72
|
### Renaming
|
46
73
|
|
47
74
|
You can rename the resulting JSON keys in both fields and associations by using the `name` option.
|
@@ -101,6 +128,77 @@ Output:
|
|
101
128
|
}
|
102
129
|
```
|
103
130
|
|
131
|
+
### Root
|
132
|
+
You can also optionally pass in a root key to wrap your resulting json in:
|
133
|
+
```ruby
|
134
|
+
class UserBlueprint < Blueprinter::Base
|
135
|
+
identifier :uuid
|
136
|
+
field :email, name: :login
|
137
|
+
|
138
|
+
view :normal do
|
139
|
+
fields :first_name, :last_name
|
140
|
+
end
|
141
|
+
end
|
142
|
+
```
|
143
|
+
|
144
|
+
Usage:
|
145
|
+
```ruby
|
146
|
+
puts UserBlueprint.render(user, view: :normal, root: :user)
|
147
|
+
```
|
148
|
+
|
149
|
+
Output:
|
150
|
+
```json
|
151
|
+
{
|
152
|
+
"user": {
|
153
|
+
"uuid": "733f0758-8f21-4719-875f-262c3ec743af",
|
154
|
+
"first_name": "John",
|
155
|
+
"last_name": "Doe",
|
156
|
+
"login": "john.doe@some.fake.email.domain"
|
157
|
+
}
|
158
|
+
}
|
159
|
+
```
|
160
|
+
|
161
|
+
### Meta attributes
|
162
|
+
You can additionally add meta-data to the json as well:
|
163
|
+
```ruby
|
164
|
+
class UserBlueprint < Blueprinter::Base
|
165
|
+
identifier :uuid
|
166
|
+
field :email, name: :login
|
167
|
+
|
168
|
+
view :normal do
|
169
|
+
fields :first_name, :last_name
|
170
|
+
end
|
171
|
+
end
|
172
|
+
```
|
173
|
+
|
174
|
+
Usage:
|
175
|
+
```ruby
|
176
|
+
json = UserBlueprint.render(user, view: :normal, root: :user, meta: {links: [
|
177
|
+
'https://app.mydomain.com',
|
178
|
+
'https://alternate.mydomain.com'
|
179
|
+
]})
|
180
|
+
puts json
|
181
|
+
```
|
182
|
+
|
183
|
+
Output:
|
184
|
+
```json
|
185
|
+
{
|
186
|
+
"user": {
|
187
|
+
"uuid": "733f0758-8f21-4719-875f-262c3ec743af",
|
188
|
+
"first_name": "John",
|
189
|
+
"last_name": "Doe",
|
190
|
+
"login": "john.doe@some.fake.email.domain"
|
191
|
+
},
|
192
|
+
"meta": {
|
193
|
+
"links": [
|
194
|
+
"https://app.mydomain.com",
|
195
|
+
"https://alternate.mydomain.com"
|
196
|
+
]
|
197
|
+
}
|
198
|
+
}
|
199
|
+
```
|
200
|
+
Note: For meta attributes, a [root](#root) is mandatory.
|
201
|
+
|
104
202
|
### Exclude fields
|
105
203
|
You can specifically choose to exclude certain fields for specific views
|
106
204
|
```ruby
|
data/lib/blueprinter/base.rb
CHANGED
@@ -7,13 +7,13 @@ 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/
|
10
|
+
require_relative 'helpers/base_helpers'
|
11
11
|
require_relative 'view'
|
12
12
|
require_relative 'view_collection'
|
13
13
|
|
14
14
|
module Blueprinter
|
15
15
|
class Base
|
16
|
-
include
|
16
|
+
include BaseHelpers
|
17
17
|
|
18
18
|
# Specify a field or method name used as an identifier. Usually, this is
|
19
19
|
# something like :id
|
@@ -167,6 +167,11 @@ module Blueprinter
|
|
167
167
|
# @option options [Symbol] :view Defaults to :default.
|
168
168
|
# The view name that corresponds to the group of
|
169
169
|
# fields to be serialized.
|
170
|
+
# @option options [Symbol|String] :root Defaults to nil.
|
171
|
+
# Render the json/hash with a root key if provided.
|
172
|
+
# @option options [Any] :meta Defaults to nil.
|
173
|
+
# Render the json/hash with a meta attribute with provided value
|
174
|
+
# if both root and meta keys are provided in the options hash.
|
170
175
|
#
|
171
176
|
# @example Generating JSON with an extended view
|
172
177
|
# post = Post.all
|
@@ -187,6 +192,11 @@ module Blueprinter
|
|
187
192
|
# @option options [Symbol] :view Defaults to :default.
|
188
193
|
# The view name that corresponds to the group of
|
189
194
|
# fields to be serialized.
|
195
|
+
# @option options [Symbol|String] :root Defaults to nil.
|
196
|
+
# Render the json/hash with a root key if provided.
|
197
|
+
# @option options [Any] :meta Defaults to nil.
|
198
|
+
# Render the json/hash with a meta attribute with provided value
|
199
|
+
# if both root and meta keys are provided in the options hash.
|
190
200
|
#
|
191
201
|
# @example Generating a hash with an extended view
|
192
202
|
# post = Post.all
|
@@ -207,6 +217,11 @@ module Blueprinter
|
|
207
217
|
# @option options [Symbol] :view Defaults to :default.
|
208
218
|
# The view name that corresponds to the group of
|
209
219
|
# fields to be serialized.
|
220
|
+
# @option options [Symbol|String] :root Defaults to nil.
|
221
|
+
# Render the json/hash with a root key if provided.
|
222
|
+
# @option options [Any] :meta Defaults to nil.
|
223
|
+
# Render the json/hash with a meta attribute with provided value
|
224
|
+
# if both root and meta keys are provided in the options hash.
|
210
225
|
#
|
211
226
|
# @example Generating a hash with an extended view
|
212
227
|
# post = Post.all
|
@@ -225,22 +240,12 @@ module Blueprinter
|
|
225
240
|
# so we rename it for clarity
|
226
241
|
#
|
227
242
|
# @api private
|
228
|
-
def self.prepare(object, view_name:, local_options:)
|
243
|
+
def self.prepare(object, view_name:, local_options:, root: nil, meta: nil)
|
229
244
|
unless view_collection.has_view? view_name
|
230
245
|
raise BlueprinterError, "View '#{view_name}' is not defined"
|
231
246
|
end
|
232
|
-
|
233
|
-
|
234
|
-
prepared_object.map do |obj|
|
235
|
-
object_to_hash(obj,
|
236
|
-
view_name: view_name,
|
237
|
-
local_options: local_options)
|
238
|
-
end
|
239
|
-
else
|
240
|
-
object_to_hash(prepared_object,
|
241
|
-
view_name: view_name,
|
242
|
-
local_options: local_options)
|
243
|
-
end
|
247
|
+
data = prepare_data(object, view_name, local_options)
|
248
|
+
prepend_root_and_meta(data, root, meta)
|
244
249
|
end
|
245
250
|
|
246
251
|
# Specify one or more field/method names to be included for serialization.
|
@@ -325,68 +330,5 @@ module Blueprinter
|
|
325
330
|
yield
|
326
331
|
@current_view = view_collection[:default]
|
327
332
|
end
|
328
|
-
|
329
|
-
# Begin private class methods
|
330
|
-
def self.prepare_for_render(object, options)
|
331
|
-
view_name = options.delete(:view) || :default
|
332
|
-
prepare(object, view_name: view_name, local_options: options)
|
333
|
-
end
|
334
|
-
private_class_method :prepare_for_render
|
335
|
-
|
336
|
-
def self.inherited(subclass)
|
337
|
-
subclass.send(:view_collection).inherit(view_collection)
|
338
|
-
end
|
339
|
-
private_class_method :inherited
|
340
|
-
|
341
|
-
def self.object_to_hash(object, view_name:, local_options:)
|
342
|
-
view_collection.fields_for(view_name).each_with_object({}) do |field, hash|
|
343
|
-
next if field.skip?(object, local_options)
|
344
|
-
hash[field.name] = field.extract(object, local_options)
|
345
|
-
end
|
346
|
-
end
|
347
|
-
private_class_method :object_to_hash
|
348
|
-
|
349
|
-
def self.include_associations(object, view_name:)
|
350
|
-
unless defined?(ActiveRecord::Base) &&
|
351
|
-
object.is_a?(ActiveRecord::Base) &&
|
352
|
-
object.respond_to?(:klass)
|
353
|
-
return object
|
354
|
-
end
|
355
|
-
# TODO: Do we need to support more than `eager_load` ?
|
356
|
-
fields_to_include = associations(view).select { |a|
|
357
|
-
a.options[:include] != false
|
358
|
-
}.map(&:method)
|
359
|
-
if !fields_to_include.empty?
|
360
|
-
object.eager_load(*fields_to_include)
|
361
|
-
else
|
362
|
-
object
|
363
|
-
end
|
364
|
-
end
|
365
|
-
private_class_method :include_associations
|
366
|
-
|
367
|
-
def self.jsonify(blob)
|
368
|
-
Blueprinter.configuration.jsonify(blob)
|
369
|
-
end
|
370
|
-
private_class_method :jsonify
|
371
|
-
|
372
|
-
def self.current_view
|
373
|
-
@current_view ||= view_collection[:default]
|
374
|
-
end
|
375
|
-
private_class_method :current_view
|
376
|
-
|
377
|
-
def self.view_collection
|
378
|
-
@view_collection ||= ViewCollection.new
|
379
|
-
end
|
380
|
-
private_class_method :view_collection
|
381
|
-
|
382
|
-
def self.array_like?(object)
|
383
|
-
object.is_a?(Array) || active_record_relation?(object)
|
384
|
-
end
|
385
|
-
private_class_method :array_like?
|
386
|
-
|
387
|
-
def self.associations(view_name = :default)
|
388
|
-
view_collection.fields_for(view_name).select { |f| f.options[:association] }
|
389
|
-
end
|
390
|
-
private_class_method :associations
|
391
333
|
end
|
392
334
|
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
module Blueprinter
|
2
|
+
module BaseHelpers
|
3
|
+
def self.included(base)
|
4
|
+
base.extend(SingletonMethods)
|
5
|
+
end
|
6
|
+
|
7
|
+
module SingletonMethods
|
8
|
+
private
|
9
|
+
def active_record_relation?(object)
|
10
|
+
!!(defined?(ActiveRecord::Relation) &&
|
11
|
+
object.is_a?(ActiveRecord::Relation))
|
12
|
+
end
|
13
|
+
|
14
|
+
def prepare_for_render(object, options)
|
15
|
+
view_name = options.delete(:view) || :default
|
16
|
+
root = options.delete(:root)
|
17
|
+
meta = options.delete(:meta)
|
18
|
+
validate_root_and_meta(root, meta)
|
19
|
+
prepare(object, view_name: view_name, local_options: options, root: root, meta: meta)
|
20
|
+
end
|
21
|
+
|
22
|
+
def prepare_data(object, view_name, local_options)
|
23
|
+
prepared_object = include_associations(object, view_name: view_name)
|
24
|
+
if array_like?(object)
|
25
|
+
prepared_object.map do |obj|
|
26
|
+
object_to_hash(obj,
|
27
|
+
view_name: view_name,
|
28
|
+
local_options: local_options)
|
29
|
+
end
|
30
|
+
else
|
31
|
+
object_to_hash(prepared_object,
|
32
|
+
view_name: view_name,
|
33
|
+
local_options: local_options)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def prepend_root_and_meta(data, root, meta)
|
38
|
+
return data unless root
|
39
|
+
ret = { root => data }
|
40
|
+
meta ? ret.merge!(meta: meta) : ret
|
41
|
+
end
|
42
|
+
|
43
|
+
def inherited(subclass)
|
44
|
+
subclass.send(:view_collection).inherit(view_collection)
|
45
|
+
end
|
46
|
+
|
47
|
+
def object_to_hash(object, view_name:, local_options:)
|
48
|
+
view_collection.fields_for(view_name).each_with_object({}) do |field, hash|
|
49
|
+
next if field.skip?(object, local_options)
|
50
|
+
hash[field.name] = field.extract(object, local_options)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
def validate_root_and_meta(root, meta)
|
55
|
+
case root
|
56
|
+
when String, Symbol
|
57
|
+
# no-op
|
58
|
+
when NilClass
|
59
|
+
raise BlueprinterError, "meta requires a root to be passed" if meta
|
60
|
+
else
|
61
|
+
raise BlueprinterError, "root should be one of String, Symbol, NilClass"
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def include_associations(object, view_name:)
|
66
|
+
unless defined?(ActiveRecord::Base) &&
|
67
|
+
object.is_a?(ActiveRecord::Base) &&
|
68
|
+
object.respond_to?(:klass)
|
69
|
+
return object
|
70
|
+
end
|
71
|
+
# TODO: Do we need to support more than `eager_load` ?
|
72
|
+
fields_to_include = associations(view).select { |a|
|
73
|
+
a.options[:include] != false
|
74
|
+
}.map(&:method)
|
75
|
+
if !fields_to_include.empty?
|
76
|
+
object.eager_load(*fields_to_include)
|
77
|
+
else
|
78
|
+
object
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def jsonify(blob)
|
83
|
+
Blueprinter.configuration.jsonify(blob)
|
84
|
+
end
|
85
|
+
|
86
|
+
def current_view
|
87
|
+
@current_view ||= view_collection[:default]
|
88
|
+
end
|
89
|
+
|
90
|
+
def view_collection
|
91
|
+
@view_collection ||= ViewCollection.new
|
92
|
+
end
|
93
|
+
|
94
|
+
def array_like?(object)
|
95
|
+
object.is_a?(Array) || active_record_relation?(object)
|
96
|
+
end
|
97
|
+
|
98
|
+
def associations(view_name = :default)
|
99
|
+
view_collection.fields_for(view_name).select { |f| f.options[:association] }
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
data/lib/blueprinter/version.rb
CHANGED
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.13.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Hess
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2019-01
|
12
|
+
date: 2019-03-01 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: factory_bot
|
@@ -127,16 +127,16 @@ dependencies:
|
|
127
127
|
name: sqlite3
|
128
128
|
requirement: !ruby/object:Gem::Requirement
|
129
129
|
requirements:
|
130
|
-
- - "
|
130
|
+
- - "~>"
|
131
131
|
- !ruby/object:Gem::Version
|
132
|
-
version:
|
132
|
+
version: 1.3.6
|
133
133
|
type: :development
|
134
134
|
prerelease: false
|
135
135
|
version_requirements: !ruby/object:Gem::Requirement
|
136
136
|
requirements:
|
137
|
-
- - "
|
137
|
+
- - "~>"
|
138
138
|
- !ruby/object:Gem::Version
|
139
|
-
version:
|
139
|
+
version: 1.3.6
|
140
140
|
- !ruby/object:Gem::Dependency
|
141
141
|
name: yard
|
142
142
|
requirement: !ruby/object:Gem::Requirement
|
@@ -176,7 +176,7 @@ files:
|
|
176
176
|
- lib/blueprinter/extractors/hash_extractor.rb
|
177
177
|
- lib/blueprinter/extractors/public_send_extractor.rb
|
178
178
|
- lib/blueprinter/field.rb
|
179
|
-
- lib/blueprinter/helpers/
|
179
|
+
- lib/blueprinter/helpers/base_helpers.rb
|
180
180
|
- lib/blueprinter/version.rb
|
181
181
|
- lib/blueprinter/view.rb
|
182
182
|
- lib/blueprinter/view_collection.rb
|
@@ -200,8 +200,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
200
200
|
- !ruby/object:Gem::Version
|
201
201
|
version: '0'
|
202
202
|
requirements: []
|
203
|
-
|
204
|
-
rubygems_version: 2.5.1
|
203
|
+
rubygems_version: 3.0.1
|
205
204
|
signing_key:
|
206
205
|
specification_version: 4
|
207
206
|
summary: Simple Fast Declarative Serialization Library
|
@@ -1,18 +0,0 @@
|
|
1
|
-
module Blueprinter
|
2
|
-
module ActiveRecordHelpers
|
3
|
-
def self.included(base)
|
4
|
-
base.extend(SingletonMethods)
|
5
|
-
end
|
6
|
-
|
7
|
-
def active_record_relation?(object)
|
8
|
-
self.class.active_record_relation?(object)
|
9
|
-
end
|
10
|
-
|
11
|
-
module SingletonMethods
|
12
|
-
def active_record_relation?(object)
|
13
|
-
!!(defined?(ActiveRecord::Relation) &&
|
14
|
-
object.is_a?(ActiveRecord::Relation))
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|