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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b1a0e758e71974ab97aac018c58a8fb143cedcbb0cbdfc60c62fb06050defecd
4
- data.tar.gz: 455ff66f83def7e8aad7bb35df752580e664499ac3cb872c6354cacc9b881f9e
3
+ metadata.gz: 7305adc0699ba1c10026b7dbfc2de138d85b7e05f19de91d70fd10069bf274b1
4
+ data.tar.gz: a15a1d19a578a472c3cc91228070ff32441137e57b241571578e72ba89a5f892
5
5
  SHA512:
6
- metadata.gz: f1a697280e9f1da3ab3c59053449c965fc4785c077c6b336291451adc6dbdfa13876989c93ace254ae2545ec9cb0ff4cc5c7d9f64211d34d77a6539217b2c178
7
- data.tar.gz: 8bc59b0805ea6bda87dd948677f8a375c321fc7d0a880882d13e4a34d5b6a844dc6c3916bd9b2913b58c3b349844f8dc86357bdf7a879c35764137c8dff65f9d
6
+ metadata.gz: f93a3442dd563f3f929e8d1964a320823819a2ef8bc4aa292751caa35bd33c2dfe74716e49f38bf856d594ebe12251fa4ca0f9ecde01ea7fe2a564f3c9e2a375
7
+ data.tar.gz: 3ffea695ddd219bd1ec1721853c9a519159d883c31259545440511b8700b82dddc1b9e55595cd886c808f2b09aacf9e47f41e6b65efd7f8034b512c3576d0061
@@ -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
@@ -18,7 +18,7 @@ RDoc::Task.new(:rdoc) do |rdoc|
18
18
  end
19
19
 
20
20
  RSpec::Core::RakeTask.new(:spec) do |t|
21
- t.rspec_opts = '--pattern spec/**/*_spec.rb'
21
+ t.rspec_opts = '--pattern spec/**/*_spec.rb --warnings'
22
22
  end
23
23
 
24
24
  Rake::TestTask.new(:benchmarks) do |t|
@@ -1,4 +1,3 @@
1
- require_relative 'blueprinter/configuration'
2
1
  require_relative 'blueprinter/base'
3
2
 
4
3
  module Blueprinter
@@ -1,5 +1,5 @@
1
1
  require_relative 'blueprinter_error'
2
- require_relative 'helpers/active_record_helpers'
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: {method => 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
- name,
131
- AssociationExtractor,
132
- self,
133
- options.merge(association: true))
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
- current_view << Field.new(field_name, field_name, AutoExtractor, self)
239
+ field(field_name)
219
240
  end
220
241
  end
221
242
 
@@ -1,9 +1,6 @@
1
1
  # @api private
2
2
  module Blueprinter
3
3
  class Extractor
4
- def initialize
5
- end
6
-
7
4
  def extract(field_name, object, local_options, options={})
8
5
  fail NotImplementedError, "An Extractor must implement #extract"
9
6
  end
@@ -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 = object.public_send(association_name)
5
- return (value || options[:default]) if value.nil?
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) ? HashExtractor : PublicSendExtractor
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
@@ -1,7 +1,7 @@
1
1
  module Blueprinter
2
2
  class BlockExtractor < Extractor
3
3
  def extract(field_name, object, local_options, options = {})
4
- options[:block][field_name].call(object, local_options)
4
+ options[:block].call(object, local_options)
5
5
  end
6
6
  end
7
7
  end
@@ -21,13 +21,13 @@ class Blueprinter::Field
21
21
  private
22
22
 
23
23
  def if_callable
24
- return @if_callable unless @if_callable.nil?
25
- @if_callable ||= callable_from(:if)
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 unless @unless_callable.nil?
30
- @unless_callable ||= callable_from(:unless)
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)
@@ -1,3 +1,3 @@
1
1
  module Blueprinter
2
- VERSION = '0.6.0'
2
+ VERSION = '0.7.0'
3
3
  end
@@ -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
@@ -9,6 +9,12 @@ module Blueprinter
9
9
  }
10
10
  end
11
11
 
12
+ def inherit(view_collection)
13
+ view_collection.views.each do |view_name, view|
14
+ self[view_name].inherit(view)
15
+ end
16
+ end
17
+
12
18
  def has_view?(view_name)
13
19
  views.has_key? view_name
14
20
  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.6.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-06-05 00:00:00.000000000 Z
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: rails
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.3
190
+ rubygems_version: 2.7.6
163
191
  signing_key:
164
192
  specification_version: 4
165
193
  summary: Simple Fast Declarative Serialization Library