blueprinter 0.12.1 → 0.13.0

Sign up to get free protection for your applications and to get access to all the features.
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