blueprinter 0.7.0 → 0.8.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
- SHA256:
3
- metadata.gz: 7305adc0699ba1c10026b7dbfc2de138d85b7e05f19de91d70fd10069bf274b1
4
- data.tar.gz: a15a1d19a578a472c3cc91228070ff32441137e57b241571578e72ba89a5f892
2
+ SHA1:
3
+ metadata.gz: 4f911f54c6fe83cbebf48847eee6b7b596931689
4
+ data.tar.gz: afe0b665b414c0b6fa9d86e853a2c94030318bed
5
5
  SHA512:
6
- metadata.gz: f93a3442dd563f3f929e8d1964a320823819a2ef8bc4aa292751caa35bd33c2dfe74716e49f38bf856d594ebe12251fa4ca0f9ecde01ea7fe2a564f3c9e2a375
7
- data.tar.gz: 3ffea695ddd219bd1ec1721853c9a519159d883c31259545440511b8700b82dddc1b9e55595cd886c808f2b09aacf9e47f41e6b65efd7f8034b512c3576d0061
6
+ metadata.gz: 8e41f7b0dccca6fd0640b2468edff7606d3afe23a08de4968f88e90ec6315eb8d99a89d756da5228330d2dc9f44e6c840adebf825d853fa94828e21e0521b345
7
+ data.tar.gz: 60e2e16868012f1e8956baf46ae7d9f5aba743c94fcde224d6d3d985f6ae355fca4a5512ebedf48e2226724a8ef0ee7338cfeb43c5715772b801e2b371c78a99
data/CHANGELOG.md CHANGED
@@ -1,24 +1,34 @@
1
- ## 0.7.0 - 2018/10/17
1
+ ## 0.8.0 - 2018/11/19
2
+
3
+ * [FEATURE] Extend Support for other JSON encoders like yajl-ruby. Please see pr [#118](https://github.com/procore/blueprinter/pull/118). Thanks to [@ritikesh](https://github.com/ritikesh).
4
+ * [BUGFIX] Do not raise error on null date with `date_format` option. Please see pr [#117](https://github.com/procore/blueprinter/pull/117). Thanks to [@tpltn](https://github.com/tpltn).
5
+ * [FEATURE] Add `default` option to `field`s which will be used as the serialized value instead of `null` when the field evaluates to null. Please see pr [#115](https://github.com/procore/blueprinter/pull/115). Thanks to [@mcclayton](https://github.com/mcclayton).
6
+ * [BUGFIX] Made Base.associations completely private since they are not used outside of the Blueprinter base. Please see pr [#112](https://github.com/procore/blueprinter/pull/112). Thanks to [@philipqnguyen](https://github.com/philipqnguyen).
7
+ * [BUGFIX] Fix issue where entire Blueprinter module was marked api private. Please see pr [#111](https://github.com/procore/blueprinter/pull/111). Thanks to [@philipqnguyen](https://github.com/philipqnguyen).
8
+ * [FEATURE] Allow identifiers to be defined with a block. Please see pr [#110](https://github.com/procore/blueprinter/pull/110). Thanks to [@hugopeixoto](https://github.com/hugopeixoto).
9
+ * [ENHANCEMENT] Update docs regarding the args yielded to blocks. Please see pr [#108](https://github.com/procore/blueprinter/pull/108). Thanks to [@philipqnguyen](https://github.com/philipqnguyen).
10
+ * [ENHANCEMENT] Use `field` method in fields. Please see pr [#107](https://github.com/procore/blueprinter/pull/107). Thanks to [@hugopeixoto](https://github.com/hugopeixoto).
2
11
 
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.
12
+ ## 0.7.0 - 2018/10/17
5
13
 
14
+ * [FEATURE] Allow associations to be defined with a block. Please see pr [#106](https://github.com/procore/blueprinter/pull/106). Thanks to [@hugopeixoto](https://github.com/hugopeixoto).
15
+ * [FEATURE] Inherit view definition when using inheritance. Please see pr [#105](https://github.com/procore/blueprinter/pull/105). Thanks to [@hugopeixoto](https://github.com/hugopeixoto).
6
16
 
7
17
  ## 0.6.0 - 2018/06/05
8
18
 
9
- * [FEATURE] Add `date_time` format as an option to `field`. Please see pr #68. Thanks to @njbbaer.
10
- * [FEATURE] Add conditional field support `:unless` and `:if` as an option to `field`. Please see pr #86. Thanks to @ojab.
11
- * [BUGFIX] Fix case where miscellaneous options were not being passed through the `AutoExtractor`. See pr #83.
19
+ * [FEATURE] Add `date_time` format as an option to `field`. Please see pr #68. Thanks to [@njbbaer](https://github.com/njbbaer).
20
+ * [FEATURE] Add conditional field support `:unless` and `:if` as an option to `field`. Please see pr [#86](https://github.com/procore/blueprinter/pull/86). Thanks to [@ojab](https://github.com/ojab).
21
+ * [BUGFIX] Fix case where miscellaneous options were not being passed through the `AutoExtractor`. See pr [#83](https://github.com/procore/blueprinter/pull/83).
12
22
 
13
23
  ## 0.5.0 - 2018/05/15
14
24
 
15
25
  * [FEATURE] Add `default` option to `association` which will be used as the serialized value instead of `null` when the association evaluates to null.
16
- See PR #78 by @vinaya-procore.
26
+ See PR [#78](https://github.com/procore/blueprinter/pull/78) by [@vinaya-procore](https://github.com/vinaya-procore).
17
27
 
18
28
  ## 0.4.0 - 2018/05/02
19
29
 
20
30
  * [FEATURE] Add `render_as_hash` which will output a hash instead of
21
- a JSON String. See PR #76 by @amayer171 and Issue #73.
31
+ a JSON String. See PR [#76](https://github.com/procore/blueprinter/pull/76) by [@amayer171](https://github.com/amayer171) and Issue [#73](https://github.com/procore/blueprinter/issues/73).
22
32
 
23
33
  ## 0.3.0 - 2018/04/05
24
34
 
@@ -35,7 +45,7 @@ field(:first_name, extractor: CustomExtractor)
35
45
  ```
36
46
 
37
47
  * [ENHANCEMENT] Renamed Serializer classes to Extractor. See #72.
38
- * [ENHANCEMENT] Updated README. See #66, #65
48
+ * [ENHANCEMENT] Updated README. See pr [#66](https://github.com/procore/blueprinter/pull/66), [#65](https://github.com/procore/blueprinter/pull/65)
39
49
 
40
50
  ## 0.2.0 - 2018/01/22
41
51
 
@@ -44,8 +54,8 @@ Breaking Changes. To upgrade, ensure that any associated objects have a blueprin
44
54
  association :comments, blueprint: CommentsBlueprint
45
55
  ```
46
56
 
47
- * [BUGFIX] Remove Optimizer class. See #61.
48
- * [BUGFIX] Require associated objects to have a Blueprint, so that objects will always serialize properly. See #60.
57
+ * [BUGFIX] Remove Optimizer class. See [#61](https://github.com/procore/blueprinter/pull/61).
58
+ * [BUGFIX] Require associated objects to have a Blueprint, so that objects will always serialize properly. See [#60](https://github.com/procore/blueprinter/pull/60).
49
59
 
50
60
  ## 0.1.0 - 2018/01/17
51
61
 
data/README.md CHANGED
@@ -144,15 +144,15 @@ Output:
144
144
  }
145
145
  ```
146
146
 
147
- #### Default option
148
- By default, an association that evaluates to `nil` is serialized as `nil`. A default serialized value can be
149
- specified as option on the association for cases when the association could potentially evaluate to `nil`.
147
+ ### Default Association/Field Option
148
+ By default, an association or field that evaluates to `nil` is serialized as `nil`. A default serialized value can be
149
+ specified as option on the association or field for cases when the association/field could potentially evaluate to `nil`.
150
150
  ```ruby
151
151
  class UserBlueprint < Blueprinter::Base
152
152
  identifier :uuid
153
153
 
154
154
  view :normal do
155
- fields :first_name, :last_name
155
+ field :first_name, default: "N/A"
156
156
  association :company, blueprint: CompanyBlueprint, default: {}
157
157
  end
158
158
  end
@@ -160,13 +160,13 @@ end
160
160
 
161
161
  ### Defining a field directly in the Blueprint
162
162
 
163
- You can define a field directly in the Blueprint by passing it a block. This is especially useful if the object does not already have such an attribute or method defined, and you want to define it specifically for use with the Blueprint. For example:
163
+ You can define a field directly in the Blueprint by passing it a block. This is especially useful if the object does not already have such an attribute or method defined, and you want to define it specifically for use with the Blueprint. This is done by passing `field` a block. The block also yields the object and any options that were passed from `render`. For example:
164
164
 
165
165
  ```ruby
166
166
  class UserBlueprint < Blueprinter::Base
167
167
  identifier :uuid
168
- field :full_name do |user|
169
- "#{user.first_name} #{user.last_name}"
168
+ field :full_name do |user, options|
169
+ "#{options[:title_prefix]} #{user.first_name} #{user.last_name}"
170
170
  end
171
171
  end
172
172
  ```
@@ -174,7 +174,34 @@ end
174
174
  Usage:
175
175
 
176
176
  ```ruby
177
- puts UserBlueprint.render(user)
177
+ puts UserBlueprint.render(user, title_prefix: "Mr")
178
+ ```
179
+
180
+ Output:
181
+
182
+ ```json
183
+ {
184
+ "uuid": "733f0758-8f21-4719-875f-262c3ec743af",
185
+ "full_name": "Mr John Doe"
186
+ }
187
+ ```
188
+
189
+ #### Defining an identifier directly in the Blueprint
190
+
191
+ You can also pass a block to an identifier:
192
+
193
+ ```ruby
194
+ class UserBlueprint < Blueprinter::Base
195
+ identifier :uuid do |user, options|
196
+ options[:current_user].anonymize(user.uuid)
197
+ end
198
+ end
199
+ ```
200
+
201
+ Usage:
202
+
203
+ ```ruby
204
+ puts UserBlueprint.render(user, current_user: current_user)
178
205
  ```
179
206
 
180
207
  Output:
@@ -182,7 +209,6 @@ Output:
182
209
  ```json
183
210
  {
184
211
  "uuid": "733f0758-8f21-4719-875f-262c3ec743af",
185
- "full_name": "John Doe"
186
212
  }
187
213
  ```
188
214
 
@@ -199,8 +225,8 @@ end
199
225
  class UserBlueprint < Blueprinter::Base
200
226
  identifier :uuid
201
227
 
202
- association :projects, blueprint: ProjectBlueprint do |user|
203
- user.projects + user.company.projects
228
+ association :projects, blueprint: ProjectBlueprint do |user, options|
229
+ user.projects + options[:draft_projects]
204
230
  end
205
231
  end
206
232
  ```
@@ -208,7 +234,7 @@ end
208
234
  Usage:
209
235
 
210
236
  ```ruby
211
- puts UserBlueprint.render(user)
237
+ puts UserBlueprint.render(user, draft_projects: Project.where(draft: true))
212
238
  ```
213
239
 
214
240
  Output:
@@ -321,6 +347,21 @@ Ensure that you have the `Oj` gem installed in your Gemfile if you haven't alrea
321
347
  gem 'oj'
322
348
  ```
323
349
 
350
+ ## Yajl-ruby
351
+
352
+ [yajl-ruby](https://github.com/brianmario/yajl-ruby) is a fast and powerful JSON generator/parser. To use `yajl-ruby` in place of `JSON / OJ`, use:
353
+
354
+ ```ruby
355
+ require 'yajl' # you can skip this if OJ has already been required.
356
+
357
+ Blueprinter.configure do |config|
358
+ config.generator = Yajl::Encoder # default is JSON
359
+ config.method = :encode # default is generate
360
+ end
361
+ ```
362
+
363
+ ##### Note: You should be doing this only if you aren't using `yajl-ruby` through the JSON API by requiring `yajl/json_gem`. More details [here](https://github.com/brianmario/yajl-ruby#json-gem-compatibility-api). In this case, `JSON.generate` is patched to use `Yajl::Encoder.encode` internally.
364
+
324
365
  ## How to Document
325
366
 
326
367
  We use [Yard](https://yardoc.org/) for documentation. Here are the following
@@ -26,6 +26,9 @@ module Blueprinter
26
26
  # @param name [Symbol] to rename the identifier key in the JSON
27
27
  # output. Defaults to method given.
28
28
  # @param extractor [AssociationExtractor,AutoExtractor,BlockExtractor,HashExtractor,PublicSendExtractor]
29
+ # @yield [object, options] The object and the options passed to render are
30
+ # also yielded to the block.
31
+ #
29
32
  # Kind of extractor to use.
30
33
  # Either define your own or use Blueprinter's premade extractors.
31
34
  # Defaults to AutoExtractor
@@ -36,13 +39,22 @@ module Blueprinter
36
39
  # # other code
37
40
  # end
38
41
  #
42
+ # @example Passing a block to be evaluated as the value.
43
+ # class UserBlueprint < Blueprinter::Base
44
+ # identifier :uuid do |user, options|
45
+ # options[:current_user].anonymize(user.uuid)
46
+ # end
47
+ # end
48
+ #
39
49
  # @return [Field] A Field object
40
- def self.identifier(method, name: method, extractor: AutoExtractor.new)
41
- view_collection[:identifier] << Field.new(method, name, extractor, self)
42
- end
43
-
44
- def self.inherited(subclass)
45
- subclass.send(:view_collection).inherit(view_collection)
50
+ def self.identifier(method, name: method, extractor: AutoExtractor.new, &block)
51
+ view_collection[:identifier] << Field.new(
52
+ method,
53
+ name,
54
+ extractor,
55
+ self,
56
+ block: block,
57
+ )
46
58
  end
47
59
 
48
60
  # Specify a field or method name to be included for serialization.
@@ -68,8 +80,8 @@ module Blueprinter
68
80
  # to call to determine if the field should be included (e.g.
69
81
  # `unless: :include_first_name?, or unless: Proc.new { |user, options| options[:current_user] != user }).
70
82
  # The method, proc or string should return or evaluate to a true or false value.
71
- # @yield [Object] The object passed to `render` is also passed to the
72
- # block.
83
+ # @yield [object, options] The object and the options passed to render are
84
+ # also yielded to the block.
73
85
  #
74
86
  # @example Specifying a user's first_name to be serialized.
75
87
  # class UserBlueprint < Blueprinter::Base
@@ -79,7 +91,9 @@ module Blueprinter
79
91
  #
80
92
  # @example Passing a block to be evaluated as the value.
81
93
  # class UserBlueprint < Blueprinter::Base
82
- # field :full_name {|obj| "#{obj.first_name} #{obj.last_name}"}
94
+ # field :full_name do |object, options|
95
+ # "options[:title_prefix] #{object.first_name} #{object.last_name}"
96
+ # end
83
97
  # # other code
84
98
  # end
85
99
  #
@@ -96,16 +110,13 @@ module Blueprinter
96
110
  #
97
111
  # @return [Field] A Field object
98
112
  def self.field(method, options = {}, &block)
99
- options = if block_given?
100
- {name: method, extractor: BlockExtractor.new, block: block}
101
- else
102
- {name: method, extractor: AutoExtractor.new}
103
- end.merge(options)
104
- current_view << Field.new(method,
105
- options[:name],
106
- options[:extractor],
107
- self,
108
- options)
113
+ current_view << Field.new(
114
+ method,
115
+ options.fetch(:name) { method },
116
+ options.fetch(:extractor) { AutoExtractor.new },
117
+ self,
118
+ options.merge(block: block),
119
+ )
109
120
  end
110
121
 
111
122
  # Specify an associated object to be included for serialization.
@@ -119,8 +130,8 @@ module Blueprinter
119
130
  # JSON output.
120
131
  # @option options [Symbol] :view Specify the view to use or fall back to
121
132
  # to the :default view.
122
- # @yield [Object] The object passed to `render` is also passed to the
123
- # block.
133
+ # @yield [object, options] The object and the options passed to render are
134
+ # also yielded to the block.
124
135
  #
125
136
  # @example Specifying an association
126
137
  # class UserBlueprint < Blueprinter::Base
@@ -131,27 +142,20 @@ module Blueprinter
131
142
  #
132
143
  # @example Passing a block to be evaluated as the value.
133
144
  # class UserBlueprint < Blueprinter::Base
134
- # association :vehicles, blueprint: VehiclesBlueprint do |user|
135
- # user.vehicles + user.company.vehicles
145
+ # association :vehicles, blueprint: VehiclesBlueprint do |user, opts|
146
+ # user.vehicles + opts[:additional_vehicles]
136
147
  # end
137
148
  # end
138
149
  #
139
150
  # @return [Field] A Field object
140
151
  def self.association(method, options = {}, &block)
141
152
  raise BlueprinterError, 'blueprint required' unless options[:blueprint]
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
153
 
150
- current_view << Field.new(method,
151
- name,
152
- AssociationExtractor.new,
153
- self,
154
- options.merge(association: true))
154
+ field(
155
+ method,
156
+ options.merge(association: true, extractor: AssociationExtractor.new),
157
+ &block
158
+ )
155
159
  end
156
160
 
157
161
  # Generates a JSON formatted String.
@@ -240,11 +244,6 @@ module Blueprinter
240
244
  end
241
245
  end
242
246
 
243
- # @api private
244
- def self.associations(view_name = :default)
245
- view_collection.fields_for(view_name).select { |f| f.options[:association] }
246
- end
247
-
248
247
  # Specify another view that should be mixed into the current view.
249
248
  #
250
249
  # @param view_name [Symbol] the view to mix into the current view.
@@ -311,6 +310,11 @@ module Blueprinter
311
310
 
312
311
  private
313
312
 
313
+ def self.inherited(subclass)
314
+ subclass.send(:view_collection).inherit(view_collection)
315
+ end
316
+ private_class_method :inherited
317
+
314
318
  def self.object_to_hash(object, view_name:, local_options:)
315
319
  view_collection.fields_for(view_name).each_with_object({}) do |field, hash|
316
320
  next if field.skip?(object, local_options)
@@ -338,7 +342,7 @@ module Blueprinter
338
342
  private_class_method :include_associations
339
343
 
340
344
  def self.jsonify(blob)
341
- Blueprinter.configuration.generator.generate(blob)
345
+ Blueprinter.configuration.jsonify(blob)
342
346
  end
343
347
  private_class_method :jsonify
344
348
 
@@ -356,5 +360,10 @@ module Blueprinter
356
360
  object.is_a?(Array) || active_record_relation?(object)
357
361
  end
358
362
  private_class_method :array_like?
363
+
364
+ def self.associations(view_name = :default)
365
+ view_collection.fields_for(view_name).select { |f| f.options[:association] }
366
+ end
367
+ private_class_method :associations
359
368
  end
360
369
  end
@@ -1,9 +1,14 @@
1
1
  module Blueprinter
2
2
  class Configuration
3
- attr_accessor :generator
3
+ attr_accessor :generator, :method
4
4
 
5
5
  def initialize
6
6
  @generator = JSON
7
+ @method = :generate
8
+ end
9
+
10
+ def jsonify(blob)
11
+ generator.public_send(method, blob)
7
12
  end
8
13
  end
9
14
 
@@ -1,5 +1,5 @@
1
- # @api private
2
1
  module Blueprinter
2
+ # @api private
3
3
  class Extractor
4
4
  def extract(field_name, object, local_options, options={})
5
5
  fail NotImplementedError, "An Extractor must implement #extract"
@@ -1,7 +1,11 @@
1
1
  module Blueprinter
2
2
  class AssociationExtractor < Extractor
3
+ def initialize
4
+ @extractor = AutoExtractor.new
5
+ end
6
+
3
7
  def extract(association_name, object, local_options, options={})
4
- value = options[:extractor].extract(association_name, object, local_options, options)
8
+ value = @extractor.extract(association_name, object, local_options, options.except(:default))
5
9
  return options[:default] if value.nil?
6
10
  view = options[:view] || :default
7
11
  options[:blueprint].prepare(value, view_name: view, local_options: local_options)
@@ -3,17 +3,29 @@ module Blueprinter
3
3
  def initialize
4
4
  @hash_extractor = HashExtractor.new
5
5
  @public_send_extractor = PublicSendExtractor.new
6
+ @block_extractor = BlockExtractor.new
6
7
  end
7
8
 
8
9
  def extract(field_name, object, local_options, options = {})
9
- extractor = object.is_a?(Hash) ? @hash_extractor : @public_send_extractor
10
- extraction = extractor.extract(field_name, object, local_options, options)
11
- options.key?(:datetime_format) ? format_datetime(extraction, options[:datetime_format]) : extraction
10
+ extraction = extractor(object, options).extract(field_name, object, local_options, options)
11
+ value = options.key?(:datetime_format) ? format_datetime(extraction, options[:datetime_format]) : extraction
12
+ value.nil? ? options[:default] : value
12
13
  end
13
14
 
14
15
  private
15
16
 
17
+ def extractor(object, options)
18
+ if options[:block]
19
+ @block_extractor
20
+ elsif object.is_a?(Hash)
21
+ @hash_extractor
22
+ else
23
+ @public_send_extractor
24
+ end
25
+ end
26
+
16
27
  def format_datetime(datetime, format)
28
+ return nil if datetime.nil?
17
29
  datetime.strftime(format)
18
30
  rescue NoMethodError
19
31
  raise BlueprinterError, 'Cannot format invalid DateTime object'
@@ -1,3 +1,3 @@
1
1
  module Blueprinter
2
- VERSION = '0.7.0'
2
+ VERSION = '0.8.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.7.0
4
+ version: 0.8.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: 2018-10-17 00:00:00.000000000 Z
12
+ date: 2018-11-20 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: factory_bot
@@ -53,6 +53,20 @@ dependencies:
53
53
  - - "~>"
54
54
  - !ruby/object:Gem::Version
55
55
  version: '3.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: yajl-ruby
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: 1.4.1
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: 1.4.1
56
70
  - !ruby/object:Gem::Dependency
57
71
  name: pry
58
72
  requirement: !ruby/object:Gem::Requirement
@@ -187,7 +201,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
187
201
  version: '0'
188
202
  requirements: []
189
203
  rubyforge_project:
190
- rubygems_version: 2.7.6
204
+ rubygems_version: 2.5.1
191
205
  signing_key:
192
206
  specification_version: 4
193
207
  summary: Simple Fast Declarative Serialization Library