blueprinter 1.1.1 → 1.2.1

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.
@@ -1,448 +1,374 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'blueprinter/association'
4
- require 'blueprinter/extractors/association_extractor'
5
- require 'blueprinter/field'
6
- require 'blueprinter/helpers/base_helpers'
7
- require 'blueprinter/reflection'
3
+ require_relative 'association'
4
+ require_relative 'extractors/association_extractor'
5
+ require_relative 'field'
6
+ require_relative 'reflection'
7
+ require_relative 'rendering'
8
+ require_relative 'view_collection'
8
9
 
9
10
  module Blueprinter
10
11
  class Base
11
- include BaseHelpers
12
12
  extend Reflection
13
+ extend Rendering
13
14
 
14
- # Specify a field or method name used as an identifier. Usually, this is
15
- # something like :id
16
- #
17
- # Note: identifiers are always rendered and considered their own view,
18
- # similar to the :default view.
19
- #
20
- # @param method [Symbol] the method or field used as an identifier that you
21
- # want to set for serialization.
22
- # @param name [Symbol] to rename the identifier key in the JSON
23
- # output. Defaults to method given.
24
- # @param extractor [AssociationExtractor,AutoExtractor,BlockExtractor,HashExtractor,PublicSendExtractor]
25
- # @yield [object, options] The object and the options passed to render are
26
- # also yielded to the block.
27
- #
28
- # Kind of extractor to use.
29
- # Either define your own or use Blueprinter's premade extractors.
30
- # Defaults to AutoExtractor
31
- #
32
- # @example Specifying a uuid as an identifier.
33
- # class UserBlueprint < Blueprinter::Base
34
- # identifier :uuid
35
- # # other code
36
- # end
37
- #
38
- # @example Passing a block to be evaluated as the value.
39
- # class UserBlueprint < Blueprinter::Base
40
- # identifier :uuid do |user, options|
41
- # options[:current_user].anonymize(user.uuid)
42
- # end
43
- # end
44
- #
45
- # @return [Field] A Field object
46
- def self.identifier(method, name: method, extractor: Blueprinter.configuration.extractor_default.new, &block)
47
- view_collection[:identifier] << Field.new(
48
- method,
49
- name,
50
- extractor,
51
- self,
52
- block: block
53
- )
54
- end
55
-
56
- # Specify a field or method name to be included for serialization.
57
- # Takes a required method and an option.
58
- #
59
- # @param method [Symbol] the field or method name you want to include for
60
- # serialization.
61
- # @param options [Hash] options to overide defaults.
62
- # @option options [AssociationExtractor,BlockExtractor,HashExtractor,PublicSendExtractor] :extractor
63
- # Kind of extractor to use.
64
- # Either define your own or use Blueprinter's premade extractors. The
65
- # Default extractor is AutoExtractor
66
- # @option options [Symbol] :name Use this to rename the method. Useful if
67
- # if you want your JSON key named differently in the output than your
68
- # object's field or method name.
69
- # @option options [String,Proc] :datetime_format Format Date or DateTime object
70
- # If the option provided is a String, the object will be formatted with given strftime
71
- # formatting.
72
- # If this option is a Proc, the object will be formatted by calling the provided Proc
73
- # on the Date/DateTime object.
74
- # @option options [Symbol,Proc] :if Specifies a method, proc or string to
75
- # call to determine if the field should be included (e.g.
76
- # `if: :include_first_name?, or if: Proc.new { |_field_name, user, options| options[:current_user] == user }).
77
- # The method, proc or string should return or evaluate to a true or false value.
78
- # @option options [Symbol,Proc] :unless Specifies a method, proc or string
79
- # to call to determine if the field should be included (e.g.
80
- # `unless: :include_first_name?, or unless: Proc.new { |_field_name, user, options| options[:current_user] != user }).
81
- # The method, proc or string should return or evaluate to a true or false value.
82
- # @yield [object, options] The object and the options passed to render are
83
- # also yielded to the block.
84
- #
85
- # @example Specifying a user's first_name to be serialized.
86
- # class UserBlueprint < Blueprinter::Base
87
- # field :first_name
88
- # # other code
89
- # end
90
- #
91
- # @example Passing a block to be evaluated as the value.
92
- # class UserBlueprint < Blueprinter::Base
93
- # field :full_name do |object, options|
94
- # "options[:title_prefix] #{object.first_name} #{object.last_name}"
95
- # end
96
- # # other code
97
- # end
98
- #
99
- # @example Passing an if proc and unless method.
100
- # class UserBlueprint < Blueprinter::Base
101
- # def skip_first_name?(_field_name, user, options)
102
- # user.first_name == options[:first_name]
103
- # end
104
- #
105
- # field :first_name, unless: :skip_first_name?
106
- # field :last_name, if: ->(_field_name, user, options) { user.first_name != options[:first_name] }
107
- # # other code
108
- # end
109
- #
110
- # @return [Field] A Field object
111
- def self.field(method, options = {}, &block)
112
- current_view << Field.new(
113
- method,
114
- options.fetch(:name) { method },
115
- options.fetch(:extractor) { Blueprinter.configuration.extractor_default.new },
116
- self,
117
- options.merge(block: block)
118
- )
119
- end
15
+ class << self
16
+ # Specify a field or method name used as an identifier. Usually, this is
17
+ # something like `:id`.
18
+ #
19
+ # Note: identifiers are always rendered and considered their own view,
20
+ # similar to the :default view.
21
+ #
22
+ # @param method [Symbol] the method or field used as an identifier that you
23
+ # want to set for serialization.
24
+ # @param name [Symbol] to rename the identifier key in the JSON
25
+ # output. Defaults to method given.
26
+ # @param extractor [AssociationExtractor,AutoExtractor,BlockExtractor,HashExtractor,PublicSendExtractor]
27
+ # @yield [object, options] The object and the options passed to render are
28
+ # also yielded to the block.
29
+ #
30
+ # Kind of extractor to use.
31
+ # Either define your own or use Blueprinter's premade extractors.
32
+ # Defaults to AutoExtractor
33
+ #
34
+ # @example Specifying a uuid as an identifier.
35
+ # class UserBlueprint < Blueprinter::Base
36
+ # identifier :uuid
37
+ # # other code
38
+ # end
39
+ #
40
+ # @example Passing a block to be evaluated as the value.
41
+ # class UserBlueprint < Blueprinter::Base
42
+ # identifier :uuid do |user, options|
43
+ # options[:current_user].anonymize(user.uuid)
44
+ # end
45
+ # end
46
+ #
47
+ # @return [Field] A Field object
48
+ def identifier(method, name: method, extractor: Blueprinter.configuration.extractor_default.new, &block)
49
+ view_collection[:identifier] << Field.new(
50
+ method,
51
+ name,
52
+ extractor,
53
+ self,
54
+ block:
55
+ )
56
+ end
120
57
 
121
- # Specify an associated object to be included for serialization.
122
- # Takes a required method and an option.
123
- #
124
- # @param method [Symbol] the association name
125
- # @param options [Hash] options to overide defaults.
126
- # @option options [Symbol] :blueprint Required. Use this to specify the
127
- # blueprint to use for the associated object.
128
- # @option options [Symbol] :name Use this to rename the association in the
129
- # JSON output.
130
- # @option options [Symbol] :view Specify the view to use or fall back to
131
- # to the :default view.
132
- # @yield [object, options] The object and the options passed to render are
133
- # also yielded to the block.
134
- #
135
- # @example Specifying an association
136
- # class UserBlueprint < Blueprinter::Base
137
- # # code
138
- # association :vehicles, view: :extended, blueprint: VehiclesBlueprint
139
- # # code
140
- # end
141
- #
142
- # @example Passing a block to be evaluated as the value.
143
- # class UserBlueprint < Blueprinter::Base
144
- # association :vehicles, blueprint: VehiclesBlueprint do |user, opts|
145
- # user.vehicles + opts[:additional_vehicles]
146
- # end
147
- # end
148
- #
149
- # @return [Association] An object
150
- # @raise [Blueprinter::Errors::InvalidBlueprint] if provided blueprint is not valid
151
- def self.association(method, options = {}, &block)
152
- raise ArgumentError, ':blueprint must be provided when defining an association' unless options[:blueprint]
58
+ # Specify a field or method name to be included for serialization.
59
+ # Takes a required method and an option.
60
+ #
61
+ # @param method [Symbol] the field or method name you want to include for
62
+ # serialization.
63
+ # @param options [Hash] options to overide defaults.
64
+ # @option options [AssociationExtractor,BlockExtractor,HashExtractor,PublicSendExtractor] :extractor
65
+ # Kind of extractor to use.
66
+ # Either define your own or use Blueprinter's premade extractors. The
67
+ # Default extractor is AutoExtractor
68
+ # @option options [Symbol] :name Use this to rename the method. Useful if
69
+ # if you want your JSON key named differently in the output than your
70
+ # object's field or method name.
71
+ # @option options [String,Proc] :datetime_format Format Date or DateTime object
72
+ # If the option provided is a String, the object will be formatted with given strftime
73
+ # formatting.
74
+ # If this option is a Proc, the object will be formatted by calling the provided Proc
75
+ # on the Date/DateTime object.
76
+ # @option options [Symbol,Proc] :if Specifies a method, proc or string to
77
+ # call to determine if the field should be included (e.g.
78
+ # `if: :include_name?, or if: Proc.new { |_field_name, user, options| options[:current_user] == user }).
79
+ # The method, proc or string should return or evaluate to a true or false value.
80
+ # @option options [Symbol,Proc] :unless Specifies a method, proc or string
81
+ # to call to determine if the field should be included (e.g.
82
+ # `unless: :include_name?, or unless: Proc.new { |_field_name, user, options| options[:current_user] != user }).
83
+ # The method, proc or string should return or evaluate to a true or false value.
84
+ # @yield [object, options] The object and the options passed to render are
85
+ # also yielded to the block.
86
+ #
87
+ # @example Specifying a user's first_name to be serialized.
88
+ # class UserBlueprint < Blueprinter::Base
89
+ # field :first_name
90
+ # # other code
91
+ # end
92
+ #
93
+ # @example Passing a block to be evaluated as the value.
94
+ # class UserBlueprint < Blueprinter::Base
95
+ # field :full_name do |object, options|
96
+ # "options[:title_prefix] #{object.first_name} #{object.last_name}"
97
+ # end
98
+ # # other code
99
+ # end
100
+ #
101
+ # @example Passing an if proc and unless method.
102
+ # class UserBlueprint < Blueprinter::Base
103
+ # def skip_first_name?(_field_name, user, options)
104
+ # user.first_name == options[:first_name]
105
+ # end
106
+ #
107
+ # field :first_name, unless: :skip_first_name?
108
+ # field :last_name, if: ->(_field_name, user, options) { user.first_name != options[:first_name] }
109
+ # # other code
110
+ # end
111
+ #
112
+ # @return [Field] A Field object
113
+ def field(method, options = {}, &block)
114
+ method = method.to_sym
153
115
 
154
- current_view << Association.new(
155
- method: method,
156
- name: options.fetch(:name) { method },
157
- extractor: options.fetch(:extractor) { AssociationExtractor.new },
158
- blueprint: options.fetch(:blueprint),
159
- parent_blueprint: self,
160
- view: options.fetch(:view, :default),
161
- options: options.except(
162
- :name,
163
- :extractor,
164
- :blueprint,
165
- :view
166
- ).merge(block: block)
167
- )
168
- end
116
+ current_view << Field.new(
117
+ method,
118
+ options.fetch(:name) { method },
119
+ options.fetch(:extractor) { Blueprinter.configuration.extractor_default.new },
120
+ self,
121
+ options.merge(block:)
122
+ )
123
+ end
169
124
 
170
- # Generates a JSON formatted String.
171
- # Takes a required object and an optional view.
172
- #
173
- # @param object [Object] the Object to serialize upon.
174
- # @param options [Hash] the options hash which requires a :view. Any
175
- # additional key value pairs will be exposed during serialization.
176
- # @option options [Symbol] :view Defaults to :default.
177
- # The view name that corresponds to the group of
178
- # fields to be serialized.
179
- # @option options [Symbol|String] :root Defaults to nil.
180
- # Render the json/hash with a root key if provided.
181
- # @option options [Any] :meta Defaults to nil.
182
- # Render the json/hash with a meta attribute with provided value
183
- # if both root and meta keys are provided in the options hash.
184
- #
185
- # @example Generating JSON with an extended view
186
- # post = Post.all
187
- # Blueprinter::Base.render post, view: :extended
188
- # # => "[{\"id\":1,\"title\":\"Hello\"},{\"id\":2,\"title\":\"My Day\"}]"
189
- #
190
- # @return [String] JSON formatted String
191
- def self.render(object, options = {})
192
- jsonify(prepare_for_render(object, options))
193
- end
125
+ # Specify an associated object to be included for serialization.
126
+ # Takes a required method and an option.
127
+ #
128
+ # @param method [Symbol] the association name
129
+ # @param options [Hash] options to overide defaults.
130
+ # @option options [Symbol] :blueprint Required. Use this to specify the
131
+ # blueprint to use for the associated object.
132
+ # @option options [Symbol] :name Use this to rename the association in the
133
+ # JSON output.
134
+ # @option options [Symbol] :view Specify the view to use or fall back to
135
+ # to the :default view.
136
+ # @yield [object, options] The object and the options passed to render are
137
+ # also yielded to the block.
138
+ #
139
+ # @example Specifying an association
140
+ # class UserBlueprint < Blueprinter::Base
141
+ # # code
142
+ # association :vehicles, view: :extended, blueprint: VehiclesBlueprint
143
+ # # code
144
+ # end
145
+ #
146
+ # @example Passing a block to be evaluated as the value.
147
+ # class UserBlueprint < Blueprinter::Base
148
+ # association :vehicles, blueprint: VehiclesBlueprint do |user, opts|
149
+ # user.vehicles + opts[:additional_vehicles]
150
+ # end
151
+ # end
152
+ #
153
+ # @return [Association] An object
154
+ # @raise [Blueprinter::Errors::InvalidBlueprint] if provided blueprint is not valid
155
+ def association(method, options = {}, &block)
156
+ raise ArgumentError, ':blueprint must be provided when defining an association' unless options[:blueprint]
194
157
 
195
- # Generates a hash.
196
- # Takes a required object and an optional view.
197
- #
198
- # @param object [Object] the Object to serialize upon.
199
- # @param options [Hash] the options hash which requires a :view. Any
200
- # additional key value pairs will be exposed during serialization.
201
- # @option options [Symbol] :view Defaults to :default.
202
- # The view name that corresponds to the group of
203
- # fields to be serialized.
204
- # @option options [Symbol|String] :root Defaults to nil.
205
- # Render the json/hash with a root key if provided.
206
- # @option options [Any] :meta Defaults to nil.
207
- # Render the json/hash with a meta attribute with provided value
208
- # if both root and meta keys are provided in the options hash.
209
- #
210
- # @example Generating a hash with an extended view
211
- # post = Post.all
212
- # Blueprinter::Base.render_as_hash post, view: :extended
213
- # # => [{id:1, title: Hello},{id:2, title: My Day}]
214
- #
215
- # @return [Hash]
216
- def self.render_as_hash(object, options = {})
217
- prepare_for_render(object, options)
218
- end
158
+ method = method.to_sym
159
+ current_view << Association.new(
160
+ method:,
161
+ name: options.fetch(:name) { method },
162
+ extractor: options.fetch(:extractor) { AssociationExtractor.new },
163
+ blueprint: options.fetch(:blueprint),
164
+ parent_blueprint: self,
165
+ view: options.fetch(:view, :default),
166
+ options: options.except(:name, :extractor, :blueprint, :view).merge(block:)
167
+ )
168
+ end
219
169
 
220
- # Generates a JSONified hash.
221
- # Takes a required object and an optional view.
222
- #
223
- # @param object [Object] the Object to serialize upon.
224
- # @param options [Hash] the options hash which requires a :view. Any
225
- # additional key value pairs will be exposed during serialization.
226
- # @option options [Symbol] :view Defaults to :default.
227
- # The view name that corresponds to the group of
228
- # fields to be serialized.
229
- # @option options [Symbol|String] :root Defaults to nil.
230
- # Render the json/hash with a root key if provided.
231
- # @option options [Any] :meta Defaults to nil.
232
- # Render the json/hash with a meta attribute with provided value
233
- # if both root and meta keys are provided in the options hash.
234
- #
235
- # @example Generating a hash with an extended view
236
- # post = Post.all
237
- # Blueprinter::Base.render_as_json post, view: :extended
238
- # # => [{"id" => "1", "title" => "Hello"},{"id" => "2", "title" => "My Day"}]
239
- #
240
- # @return [Hash]
241
- def self.render_as_json(object, options = {})
242
- prepare_for_render(object, options).as_json
243
- end
170
+ # Specify one or more field/method names to be included for serialization.
171
+ # Takes at least one field or method names.
172
+ #
173
+ # @param method [Symbol] the field or method name you want to include for
174
+ # serialization.
175
+ #
176
+ # @example Specifying a user's first_name and last_name to be serialized.
177
+ # class UserBlueprint < Blueprinter::Base
178
+ # fields :first_name, :last_name
179
+ # # other code
180
+ # end
181
+ #
182
+ # @return [Array<Symbol>] an array of field names
183
+ def fields(*field_names)
184
+ field_names.each do |field_name|
185
+ field(field_name)
186
+ end
187
+ end
244
188
 
245
- # This is the magic method that converts complex objects into a simple hash
246
- # ready for JSON conversion.
247
- #
248
- # Note: we accept view (public interface) that is in reality a view_name,
249
- # so we rename it for clarity
250
- #
251
- # @api private
252
- def self.prepare(object, view_name:, local_options:, root: nil, meta: nil)
253
- raise BlueprinterError, "View '#{view_name}' is not defined" unless view_collection.view? view_name
189
+ # Specify one transformer to be included for serialization.
190
+ # Takes a class which extends Blueprinter::Transformer
191
+ #
192
+ # @param class name [Class] which implements the method transform to include for
193
+ # serialization.
194
+ #
195
+ #
196
+ # @example Specifying a DynamicFieldTransformer transformer for including dynamic fields to be serialized.
197
+ # class User
198
+ # def custom_columns
199
+ # dynamic_fields # which is an array of some columns
200
+ # end
201
+ #
202
+ # def custom_fields
203
+ # custom_columns.each_with_object({}) { |col,result| result[col] = send(col) }
204
+ # end
205
+ # end
206
+ #
207
+ # class UserBlueprint < Blueprinter::Base
208
+ # fields :first_name, :last_name
209
+ # transform DynamicFieldTransformer
210
+ # # other code
211
+ # end
212
+ #
213
+ # class DynamicFieldTransformer < Blueprinter::Transformer
214
+ # def transform(hash, object, options)
215
+ # hash.merge!(object.dynamic_fields)
216
+ # end
217
+ # end
218
+ #
219
+ # @return [Array<Class>] an array of transformers
220
+ def transform(transformer)
221
+ current_view.add_transformer(transformer)
222
+ end
254
223
 
255
- object = Blueprinter.configuration.extensions.pre_render(object, self, view_name, local_options)
256
- data = prepare_data(object, view_name, local_options)
257
- prepend_root_and_meta(data, root, meta)
258
- end
224
+ # Specify another view that should be mixed into the current view.
225
+ #
226
+ # @param view_name [Symbol] the view to mix into the current view.
227
+ #
228
+ # @example Including a normal view into an extended view.
229
+ # class UserBlueprint < Blueprinter::Base
230
+ # # other code...
231
+ # view :normal do
232
+ # fields :first_name, :last_name
233
+ # end
234
+ # view :extended do
235
+ # include_view :normal # include fields specified from above.
236
+ # field :description
237
+ # end
238
+ # #=> [:first_name, :last_name, :description]
239
+ # end
240
+ #
241
+ # @return [Array<Symbol>] an array of view names.
242
+ def include_view(view_name)
243
+ current_view.include_view(view_name)
244
+ end
259
245
 
260
- # Specify one or more field/method names to be included for serialization.
261
- # Takes at least one field or method names.
262
- #
263
- # @param method [Symbol] the field or method name you want to include for
264
- # serialization.
265
- #
266
- # @example Specifying a user's first_name and last_name to be serialized.
267
- # class UserBlueprint < Blueprinter::Base
268
- # fields :first_name, :last_name
269
- # # other code
270
- # end
271
- #
272
- # @return [Array<Symbol>] an array of field names
273
- def self.fields(*field_names)
274
- field_names.each do |field_name|
275
- field(field_name)
246
+ # Specify additional views that should be mixed into the current view.
247
+ #
248
+ # @param view_name [Array<Symbol>] the views to mix into the current view.
249
+ #
250
+ # @example Including the normal and special views into an extended view.
251
+ # class UserBlueprint < Blueprinter::Base
252
+ # # other code...
253
+ # view :normal do
254
+ # fields :first_name, :last_name
255
+ # end
256
+ # view :special do
257
+ # fields :birthday, :company
258
+ # end
259
+ # view :extended do
260
+ # include_views :normal, :special # include fields specified from above.
261
+ # field :description
262
+ # end
263
+ # #=> [:first_name, :last_name, :birthday, :company, :description]
264
+ # end
265
+ #
266
+ # @return [Array<Symbol>] an array of view names.
267
+ def include_views(*view_names)
268
+ current_view.include_views(view_names)
276
269
  end
277
- end
278
270
 
279
- # Specify one transformer to be included for serialization.
280
- # Takes a class which extends Blueprinter::Transformer
281
- #
282
- # @param class name [Class] which implements the method transform to include for
283
- # serialization.
284
- #
285
- #
286
- # @example Specifying a DynamicFieldTransformer transformer for including dynamic fields to be serialized.
287
- # class User
288
- # def custom_columns
289
- # self.dynamic_fields # which is an array of some columns
290
- # end
291
- #
292
- # def custom_fields
293
- # custom_columns.each_with_object({}) { |col,result| result[col] = self.send(col) }
294
- # end
295
- # end
296
- #
297
- # class UserBlueprint < Blueprinter::Base
298
- # fields :first_name, :last_name
299
- # transform DynamicFieldTransformer
300
- # # other code
301
- # end
302
- #
303
- # class DynamicFieldTransformer < Blueprinter::Transformer
304
- # def transform(hash, object, options)
305
- # hash.merge!(object.dynamic_fields)
306
- # end
307
- # end
308
- #
309
- # @return [Array<Class>] an array of transformers
310
- def self.transform(transformer)
311
- current_view.add_transformer(transformer)
312
- end
271
+ # Exclude a field that was mixed into the current view.
272
+ #
273
+ # @param field_name [Symbol] the field to exclude from the current view.
274
+ #
275
+ # @example Excluding a field from being included into the current view.
276
+ # view :normal do
277
+ # fields :position, :company
278
+ # end
279
+ # view :special do
280
+ # include_view :normal
281
+ # field :birthday
282
+ # exclude :position
283
+ # end
284
+ # #=> [:company, :birthday]
285
+ #
286
+ # @return [Array<Symbol>] an array of field names
287
+ def exclude(field_name)
288
+ current_view.exclude_field(field_name)
289
+ end
313
290
 
314
- # Specify another view that should be mixed into the current view.
315
- #
316
- # @param view_name [Symbol] the view to mix into the current view.
317
- #
318
- # @example Including a normal view into an extended view.
319
- # class UserBlueprint < Blueprinter::Base
320
- # # other code...
321
- # view :normal do
322
- # fields :first_name, :last_name
323
- # end
324
- # view :extended do
325
- # include_view :normal # include fields specified from above.
326
- # field :description
327
- # end
328
- # #=> [:first_name, :last_name, :description]
329
- # end
330
- #
331
- # @return [Array<Symbol>] an array of view names.
332
- def self.include_view(view_name)
333
- current_view.include_view(view_name)
334
- end
291
+ # When mixing multiple views under a single view, some fields may required to be excluded from
292
+ # current view
293
+ #
294
+ # @param [Array<Symbol>] the fields to exclude from the current view.
295
+ #
296
+ # @example Excluding mutiple fields from being included into the current view.
297
+ # view :normal do
298
+ # fields :name,:address,:position,
299
+ # :company, :contact
300
+ # end
301
+ # view :special do
302
+ # include_view :normal
303
+ # fields :birthday,:joining_anniversary
304
+ # excludes :position,:address
305
+ # end
306
+ # => [:name, :company, :contact, :birthday, :joining_anniversary]
307
+ #
308
+ # @return [Array<Symbol>] an array of field names
309
+ def excludes(*field_names)
310
+ current_view.exclude_fields(field_names)
311
+ end
335
312
 
336
- # Specify additional views that should be mixed into the current view.
337
- #
338
- # @param view_name [Array<Symbol>] the views to mix into the current view.
339
- #
340
- # @example Including the normal and special views into an extended view.
341
- # class UserBlueprint < Blueprinter::Base
342
- # # other code...
343
- # view :normal do
344
- # fields :first_name, :last_name
345
- # end
346
- # view :special do
347
- # fields :birthday, :company
348
- # end
349
- # view :extended do
350
- # include_views :normal, :special # include fields specified from above.
351
- # field :description
352
- # end
353
- # #=> [:first_name, :last_name, :birthday, :company, :description]
354
- # end
355
- #
356
- # @return [Array<Symbol>] an array of view names.
313
+ # Specify a view and the fields it should have.
314
+ # It accepts a view name and a block. The block should specify the fields.
315
+ #
316
+ # @param view_name [Symbol] the view name
317
+ # @yieldreturn [#fields,#field,#include_view,#exclude] Use this block to
318
+ # specify fields, include fields from other views, or exclude fields.
319
+ #
320
+ # @example Using views
321
+ # view :extended do
322
+ # fields :position, :company
323
+ # include_view :normal
324
+ # exclude :first_name
325
+ # end
326
+ #
327
+ # @return [View] a Blueprinter::View object
328
+ def view(view_name)
329
+ self.view_scope = view_collection[view_name]
330
+ view_collection[:default].track_definition_order(view_name)
331
+ yield
332
+ self.view_scope = view_collection[:default]
333
+ end
357
334
 
358
- def self.include_views(*view_names)
359
- current_view.include_views(view_names)
360
- end
335
+ # Check whether or not a Blueprint supports the supplied view.
336
+ # It accepts a view name.
337
+ #
338
+ # @param view_name [Symbol] the view name
339
+ #
340
+ # @example With the following Blueprint
341
+ #
342
+ # class ExampleBlueprint < Blueprinter::Base
343
+ # view :custom do
344
+ # end
345
+ # end
346
+ #
347
+ # ExampleBlueprint.view?(:custom) => true
348
+ # ExampleBlueprint.view?(:doesnt_exist) => false
349
+ #
350
+ # @return [Boolean] a boolean value indicating if the view is
351
+ # supported by this Blueprint.
352
+ def view?(view_name)
353
+ view_collection.view?(view_name)
354
+ end
361
355
 
362
- # Exclude a field that was mixed into the current view.
363
- #
364
- # @param field_name [Symbol] the field to exclude from the current view.
365
- #
366
- # @example Excluding a field from being included into the current view.
367
- # view :normal do
368
- # fields :position, :company
369
- # end
370
- # view :special do
371
- # include_view :normal
372
- # field :birthday
373
- # exclude :position
374
- # end
375
- # #=> [:company, :birthday]
376
- #
377
- # @return [Array<Symbol>] an array of field names
378
- def self.exclude(field_name)
379
- current_view.exclude_field(field_name)
380
- end
356
+ def view_collection
357
+ @_view_collection ||= ViewCollection.new
358
+ end
381
359
 
382
- # When mixing multiple views under a single view, some fields may required to be excluded from
383
- # current view
384
- #
385
- # @param [Array<Symbol>] the fields to exclude from the current view.
386
- #
387
- # @example Excluding mutiple fields from being included into the current view.
388
- # view :normal do
389
- # fields :name,:address,:position,
390
- # :company, :contact
391
- # end
392
- # view :special do
393
- # include_view :normal
394
- # fields :birthday,:joining_anniversary
395
- # excludes :position,:address
396
- # end
397
- # => [:name, :company, :contact, :birthday, :joining_anniversary]
398
- #
399
- # @return [Array<Symbol>] an array of field names
360
+ private
400
361
 
401
- def self.excludes(*field_names)
402
- current_view.exclude_fields(field_names)
403
- end
362
+ attr_accessor :view_scope
404
363
 
405
- # Specify a view and the fields it should have.
406
- # It accepts a view name and a block. The block should specify the fields.
407
- #
408
- # @param view_name [Symbol] the view name
409
- # @yieldreturn [#fields,#field,#include_view,#exclude] Use this block to
410
- # specify fields, include fields from other views, or exclude fields.
411
- #
412
- # @example Using views
413
- # view :extended do
414
- # fields :position, :company
415
- # include_view :normal
416
- # exclude :first_name
417
- # end
418
- #
419
- # @return [View] a Blueprinter::View object
420
- def self.view(view_name)
421
- @current_view = view_collection[view_name]
422
- view_collection[:default].track_definition_order(view_name)
423
- yield
424
- @current_view = view_collection[:default]
425
- end
364
+ # Returns the current view during Blueprint definition based on the view_scope.
365
+ def current_view
366
+ view_scope || view_collection[:default]
367
+ end
426
368
 
427
- # Check whether or not a Blueprint supports the supplied view.
428
- # It accepts a view name.
429
- #
430
- # @param view_name [Symbol] the view name
431
- #
432
- # @example With the following Blueprint
433
- #
434
- # class ExampleBlueprint < Blueprinter::Base
435
- # view :custom do
436
- # end
437
- # end
438
- #
439
- # ExampleBlueprint.view?(:custom) => true
440
- # ExampleBlueprint.view?(:doesnt_exist) => false
441
- #
442
- # @return [Boolean] a boolean value indicating if the view is
443
- # supported by this Blueprint.
444
- def self.view?(view_name)
445
- view_collection.view? view_name
369
+ def inherited(subclass)
370
+ subclass.send(:view_collection).inherit(view_collection)
371
+ end
446
372
  end
447
373
  end
448
374
  end