blueprinter 0.5.0 → 0.6.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
2
  SHA256:
3
- metadata.gz: 72efd8dd2ec9f959aa0a4ab79db6aca9a8654ea7a66474524fdfb49277fbd315
4
- data.tar.gz: b04bc7182d997999f0bf30babe1abb5ad9bd8f35206cc0648f00cae4026d6900
3
+ metadata.gz: b1a0e758e71974ab97aac018c58a8fb143cedcbb0cbdfc60c62fb06050defecd
4
+ data.tar.gz: 455ff66f83def7e8aad7bb35df752580e664499ac3cb872c6354cacc9b881f9e
5
5
  SHA512:
6
- metadata.gz: af5cd971e59e741c3edf95a5778f2462c679769bf4e95ba37f725fd8508f765bc296d5fd0412c8cbb1cbee41b213bd5f7a0c2b46f3c3b9ec63fb02c82acfac7b
7
- data.tar.gz: 418d5b106f804d9e11dede74cc89cf03c9734a0b15ef31a5cf8554b2ba0fd9e7e3515db3e3b15f2fee2e21c484c962cae03967bcd55990e9a5690fc04f50bdc2
6
+ metadata.gz: f1a697280e9f1da3ab3c59053449c965fc4785c077c6b336291451adc6dbdfa13876989c93ace254ae2545ec9cb0ff4cc5c7d9f64211d34d77a6539217b2c178
7
+ data.tar.gz: 8bc59b0805ea6bda87dd948677f8a375c321fc7d0a880882d13e4a34d5b6a844dc6c3916bd9b2913b58c3b349844f8dc86357bdf7a879c35764137c8dff65f9d
@@ -1,3 +1,9 @@
1
+ ## 0.6.0 - 2018/06/05
2
+
3
+ * [FEATURE] Add `date_time` format as an option to `field`. Please see pr #68. Thanks to @njbbaer.
4
+ * [FEATURE] Add conditional field support `:unless` and `:if` as an option to `field`. Please see pr #86. Thanks to @ojab.
5
+ * [BUGFIX] Fix case where miscellaneous options were not being passed through the `AutoExtractor`. See pr #83.
6
+
1
7
  ## 0.5.0 - 2018/05/15
2
8
 
3
9
  * [FEATURE] Add `default` option to `association` which will be used as the serialized value instead of `null` when the association evaluates to null.
data/README.md CHANGED
@@ -42,7 +42,7 @@ And the output would look like:
42
42
  ```
43
43
 
44
44
  ### Views
45
- You may define different ouputs by utilizing views:
45
+ You may define different outputs by utilizing views:
46
46
  ```ruby
47
47
  class UserBlueprint < Blueprinter::Base
48
48
  identifier :uuid
@@ -190,6 +190,25 @@ Output:
190
190
  }
191
191
  ```
192
192
 
193
+ ### Custom formatting for dates and times
194
+ To define a custom format for a Date or DateTime field, include the option `datetime_format` with the associated `strptime` format.
195
+
196
+ Usage:
197
+ ```ruby
198
+ class UserBlueprint < Blueprinter::Base
199
+ identifier :name
200
+ field :birthday, datetime_format: "%m/%d/%Y"
201
+ end
202
+ ```
203
+
204
+ Output:
205
+ ```json
206
+ {
207
+ "name": "John Doe",
208
+ "birthday": "03/04/1994"
209
+ }
210
+ ```
211
+
193
212
  ## Installation
194
213
  Add this line to your application's Gemfile:
195
214
 
@@ -37,7 +37,7 @@ module Blueprinter
37
37
  #
38
38
  # @return [Field] A Field object
39
39
  def self.identifier(method, name: method, extractor: AutoExtractor)
40
- view_collection[:identifier] << Field.new(method, name, extractor)
40
+ view_collection[:identifier] << Field.new(method, name, extractor, self)
41
41
  end
42
42
 
43
43
  # Specify a field or method name to be included for serialization.
@@ -53,6 +53,16 @@ module Blueprinter
53
53
  # @option options [Symbol] :name Use this to rename the method. Useful if
54
54
  # if you want your JSON key named differently in the output than your
55
55
  # object's field or method name.
56
+ # @option options [String] :datetime_format Format Date or DateTime object
57
+ # with given strftime formatting
58
+ # @option options [Symbol,Proc] :if Specifies a method, proc or string to
59
+ # call to determine if the field should be included (e.g.
60
+ # `if: :include_first_name?, or if: Proc.new { |user, options| options[:current_user] == user }).
61
+ # The method, proc or string should return or evaluate to a true or false value.
62
+ # @option options [Symbol,Proc] :unless Specifies a method, proc or string
63
+ # to call to determine if the field should be included (e.g.
64
+ # `unless: :include_first_name?, or unless: Proc.new { |user, options| options[:current_user] != user }).
65
+ # The method, proc or string should return or evaluate to a true or false value.
56
66
  # @yield [Object] The object passed to `render` is also passed to the
57
67
  # block.
58
68
  #
@@ -68,6 +78,17 @@ module Blueprinter
68
78
  # # other code
69
79
  # end
70
80
  #
81
+ # @example Passing an if proc and unless method..
82
+ # class UserBlueprint < Blueprinter::Base
83
+ # def skip_first_name?(user, options)
84
+ # user.first_name == options[:first_name]
85
+ # end
86
+ #
87
+ # field :first_name, unless: :skip_first_name?
88
+ # field :last_name, if: ->(user, options) { user.first_name != options[:first_name] }
89
+ # # other code
90
+ # end
91
+ #
71
92
  # @return [Field] A Field object
72
93
  def self.field(method, options = {}, &block)
73
94
  options = if block_given?
@@ -78,6 +99,7 @@ module Blueprinter
78
99
  current_view << Field.new(method,
79
100
  options[:name],
80
101
  options[:extractor],
102
+ self,
81
103
  options)
82
104
  end
83
105
 
@@ -107,6 +129,7 @@ module Blueprinter
107
129
  current_view << Field.new(method,
108
130
  name,
109
131
  AssociationExtractor,
132
+ self,
110
133
  options.merge(association: true))
111
134
  end
112
135
 
@@ -163,7 +186,6 @@ module Blueprinter
163
186
  unless view_collection.has_view? view_name
164
187
  raise BlueprinterError, "View '#{view_name}' is not defined"
165
188
  end
166
- fields = view_collection.fields_for(view_name)
167
189
  prepared_object = include_associations(object, view_name: view_name)
168
190
  if array_like?(object)
169
191
  prepared_object.map do |obj|
@@ -193,7 +215,7 @@ module Blueprinter
193
215
  # @return [Array<Symbol>] an array of field names
194
216
  def self.fields(*field_names)
195
217
  field_names.each do |field_name|
196
- current_view << Field.new(field_name, field_name, AutoExtractor)
218
+ current_view << Field.new(field_name, field_name, AutoExtractor, self)
197
219
  end
198
220
  end
199
221
 
@@ -270,6 +292,7 @@ module Blueprinter
270
292
 
271
293
  def self.object_to_hash(object, view_name:, local_options:)
272
294
  view_collection.fields_for(view_name).each_with_object({}) do |field, hash|
295
+ next if field.skip?(object, local_options)
273
296
  hash[field.name] = field.extract(object, local_options)
274
297
  end
275
298
  end
@@ -2,7 +2,16 @@ module Blueprinter
2
2
  class AutoExtractor < Extractor
3
3
  def extract(field_name, object, local_options, options = {})
4
4
  extractor = object.is_a?(Hash) ? HashExtractor : PublicSendExtractor
5
- extractor.extract(field_name, object, local_options, options = {})
5
+ extraction = extractor.extract(field_name, object, local_options, options)
6
+ options.key?(:datetime_format) ? format_datetime(extraction, options[:datetime_format]) : extraction
7
+ end
8
+
9
+ private
10
+
11
+ def format_datetime(datetime, format)
12
+ datetime.strftime(format)
13
+ rescue NoMethodError
14
+ raise BlueprinterError, 'Cannot format invalid DateTime object'
6
15
  end
7
16
  end
8
17
  end
@@ -1,14 +1,44 @@
1
1
  # @api private
2
2
  class Blueprinter::Field
3
- attr_reader :method, :name, :extractor, :options
4
- def initialize(method, name, extractor, options = {})
3
+ attr_reader :method, :name, :extractor, :options, :blueprint
4
+ def initialize(method, name, extractor, blueprint, options = {})
5
5
  @method = method
6
6
  @name = name
7
7
  @extractor = extractor
8
+ @blueprint = blueprint
8
9
  @options = options
9
10
  end
10
11
 
11
12
  def extract(object, local_options)
12
13
  extractor.extract(method, object, local_options, options)
13
14
  end
15
+
16
+ def skip?(object, local_options)
17
+ return true if if_callable && !if_callable.call(object, local_options)
18
+ unless_callable && unless_callable.call(object, local_options)
19
+ end
20
+
21
+ private
22
+
23
+ def if_callable
24
+ return @if_callable unless @if_callable.nil?
25
+ @if_callable ||= callable_from(:if)
26
+ end
27
+
28
+ def unless_callable
29
+ return @unless_callable unless @unless_callable.nil?
30
+ @unless_callable ||= callable_from(:unless)
31
+ end
32
+
33
+ def callable_from(option_name)
34
+ return false unless options.key?(option_name)
35
+
36
+ tmp = options.fetch(option_name)
37
+ case tmp
38
+ when Proc then tmp
39
+ when Symbol then blueprint.method(tmp)
40
+ else
41
+ raise ArgumentError, "#{tmp.class} is passed to :#{option_name}"
42
+ end
43
+ end
14
44
  end
@@ -1,3 +1,3 @@
1
1
  module Blueprinter
2
- VERSION = '0.5.0'
2
+ VERSION = '0.6.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.5.0
4
+ version: 0.6.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-05-15 00:00:00.000000000 Z
12
+ date: 2018-06-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: nokogiri