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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: c7119c095ce28afa7d7645ed67c2f6c18e9d930f
4
- data.tar.gz: 576c31713e9e5e8993c0986ed49886d2f54a0d11
2
+ SHA256:
3
+ metadata.gz: 00bf7f1cfbdc877cc258ddc862d695f7ed202a956c1088e3d3f762e84556fa49
4
+ data.tar.gz: 761ccae943b7f909584d9db53a4025fff1e23a2602ce1b564fa69bd2821e6388
5
5
  SHA512:
6
- metadata.gz: 4aeb3a9af16e89e066fd23c4dd8c5829914d30cb5996da89f1d9ec93bb0703155d89c8f84f773103ebf944e8c0b6c350095b5f7ef60e179f6482ed90a255d807
7
- data.tar.gz: 25df4ff051176e5f558743d221e68a71ecec6128e914af5d0d0d1c6479fdae88af9bb0d1d30cb1c834a6cf821ff91e8370ff2daaba3cd248d7aa7a2299b424a7
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
@@ -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/active_record_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 ActiveRecordHelpers
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
- prepared_object = include_associations(object, view_name: view_name)
233
- if array_like?(object)
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
@@ -1,3 +1,3 @@
1
1
  module Blueprinter
2
- VERSION = '0.12.1'
2
+ VERSION = '0.13.0'
3
3
  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.12.1
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-25 00:00:00.000000000 Z
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: '0'
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: '0'
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/active_record_helpers.rb
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
- rubyforge_project:
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