blueprinter 1.1.2 → 1.3.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.
@@ -1,448 +1,392 @@
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
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.default_extractor, &block)
49
+ view_collection[:identifier] << Field.new(
50
+ method,
51
+ name,
52
+ extractor,
53
+ self,
54
+ block:
55
+ )
56
+ end
55
57
 
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
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
120
115
 
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]
116
+ current_view << Field.new(
117
+ method,
118
+ options.fetch(:name) { method },
119
+ options.fetch(:extractor) { Blueprinter.configuration.default_extractor },
120
+ self,
121
+ options.merge(block:)
122
+ )
123
+ end
153
124
 
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
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
+ # @option options [Hash, Proc] :options Additional options to merge into the
137
+ # options hash passed to the nested blueprint. Can be a static Hash or a Proc
138
+ # that receives the parent object and returns a Hash.
139
+ # @yield [object, options] The object and the options passed to render are
140
+ # also yielded to the block.
141
+ #
142
+ # @example Specifying an association
143
+ # class UserBlueprint < Blueprinter::Base
144
+ # # code
145
+ # association :vehicles, view: :extended, blueprint: VehiclesBlueprint
146
+ # # code
147
+ # end
148
+ #
149
+ # @example Passing a block to be evaluated as the value.
150
+ # class UserBlueprint < Blueprinter::Base
151
+ # association :vehicles, blueprint: VehiclesBlueprint do |user, opts|
152
+ # user.vehicles + opts[:additional_vehicles]
153
+ # end
154
+ # end
155
+ #
156
+ # @example Passing static options to the nested blueprint.
157
+ # class UserBlueprint < Blueprinter::Base
158
+ # association :vehicles, blueprint: VehiclesBlueprint, options: { show_owner: true }
159
+ # end
160
+ #
161
+ # @example Passing dynamic options based on the parent object.
162
+ # class UserBlueprint < Blueprinter::Base
163
+ # association :vehicles, blueprint: VehiclesBlueprint, options: ->(user) { { owner_name: user.name } }
164
+ # end
165
+ #
166
+ # @return [Association] An object
167
+ # @raise [Blueprinter::Errors::InvalidBlueprint] if provided blueprint is not valid
168
+ def association(method, options = {}, &block)
169
+ raise ArgumentError, ':blueprint must be provided when defining an association' unless options[:blueprint]
169
170
 
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
171
+ method = method.to_sym
172
+ current_view << Association.new(
173
+ method:,
174
+ name: options.fetch(:name) { method },
175
+ extractor: options.fetch(:extractor) { association_extractor },
176
+ blueprint: options.fetch(:blueprint),
177
+ parent_blueprint: self,
178
+ view: options.fetch(:view, :default),
179
+ options: options.except(:name, :extractor, :blueprint, :view).merge(block:)
180
+ )
181
+ end
194
182
 
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
183
+ # Specify one or more field/method names to be included for serialization.
184
+ # Takes at least one field or method names.
185
+ #
186
+ # @param method [Symbol] the field or method name you want to include for
187
+ # serialization.
188
+ #
189
+ # @example Specifying a user's first_name and last_name to be serialized.
190
+ # class UserBlueprint < Blueprinter::Base
191
+ # fields :first_name, :last_name
192
+ # # other code
193
+ # end
194
+ #
195
+ # @return [Array<Symbol>] an array of field names
196
+ def fields(*field_names)
197
+ field_names.each do |field_name|
198
+ field(field_name)
199
+ end
200
+ end
219
201
 
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
202
+ # Specify one transformer to be included for serialization.
203
+ # Takes a class which extends Blueprinter::Transformer
204
+ #
205
+ # @param class name [Class] which implements the method transform to include for
206
+ # serialization.
207
+ #
208
+ #
209
+ # @example Specifying a DynamicFieldTransformer transformer for including dynamic fields to be serialized.
210
+ # class User
211
+ # def custom_columns
212
+ # dynamic_fields # which is an array of some columns
213
+ # end
214
+ #
215
+ # def custom_fields
216
+ # custom_columns.each_with_object({}) { |col,result| result[col] = send(col) }
217
+ # end
218
+ # end
219
+ #
220
+ # class UserBlueprint < Blueprinter::Base
221
+ # fields :first_name, :last_name
222
+ # transform DynamicFieldTransformer
223
+ # # other code
224
+ # end
225
+ #
226
+ # class DynamicFieldTransformer < Blueprinter::Transformer
227
+ # def transform(hash, object, options)
228
+ # hash.merge!(object.dynamic_fields)
229
+ # end
230
+ # end
231
+ #
232
+ # @return [Array<Class>] an array of transformers
233
+ def transform(transformer)
234
+ current_view.add_transformer(transformer)
235
+ end
244
236
 
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
237
+ # Specify another view that should be mixed into the current view.
238
+ #
239
+ # @param view_name [Symbol] the view to mix into the current view.
240
+ #
241
+ # @example Including a normal view into an extended view.
242
+ # class UserBlueprint < Blueprinter::Base
243
+ # # other code...
244
+ # view :normal do
245
+ # fields :first_name, :last_name
246
+ # end
247
+ # view :extended do
248
+ # include_view :normal # include fields specified from above.
249
+ # field :description
250
+ # end
251
+ # #=> [:first_name, :last_name, :description]
252
+ # end
253
+ #
254
+ # @return [Array<Symbol>] an array of view names.
255
+ def include_view(view_name)
256
+ current_view.include_view(view_name)
257
+ end
254
258
 
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
259
+ # Specify additional views that should be mixed into the current view.
260
+ #
261
+ # @param view_name [Array<Symbol>] the views to mix into the current view.
262
+ #
263
+ # @example Including the normal and special views into an extended view.
264
+ # class UserBlueprint < Blueprinter::Base
265
+ # # other code...
266
+ # view :normal do
267
+ # fields :first_name, :last_name
268
+ # end
269
+ # view :special do
270
+ # fields :birthday, :company
271
+ # end
272
+ # view :extended do
273
+ # include_views :normal, :special # include fields specified from above.
274
+ # field :description
275
+ # end
276
+ # #=> [:first_name, :last_name, :birthday, :company, :description]
277
+ # end
278
+ #
279
+ # @return [Array<Symbol>] an array of view names.
280
+ def include_views(*view_names)
281
+ current_view.include_views(view_names)
282
+ end
259
283
 
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)
284
+ # Exclude a field that was mixed into the current view.
285
+ #
286
+ # @param field_name [Symbol] the field to exclude from the current view.
287
+ #
288
+ # @example Excluding a field from being included into the current view.
289
+ # view :normal do
290
+ # fields :position, :company
291
+ # end
292
+ # view :special do
293
+ # include_view :normal
294
+ # field :birthday
295
+ # exclude :position
296
+ # end
297
+ # #=> [:company, :birthday]
298
+ #
299
+ # @return [Array<Symbol>] an array of field names
300
+ def exclude(field_name)
301
+ current_view.exclude_field(field_name)
276
302
  end
277
- end
278
303
 
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
304
+ # When mixing multiple views under a single view, some fields may required to be excluded from
305
+ # current view
306
+ #
307
+ # @param [Array<Symbol>] the fields to exclude from the current view.
308
+ #
309
+ # @example Excluding mutiple fields from being included into the current view.
310
+ # view :normal do
311
+ # fields :name,:address,:position,
312
+ # :company, :contact
313
+ # end
314
+ # view :special do
315
+ # include_view :normal
316
+ # fields :birthday,:joining_anniversary
317
+ # excludes :position,:address
318
+ # end
319
+ # => [:name, :company, :contact, :birthday, :joining_anniversary]
320
+ #
321
+ # @return [Array<Symbol>] an array of field names
322
+ def excludes(*field_names)
323
+ current_view.exclude_fields(field_names)
324
+ end
313
325
 
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
326
+ # Specify a view and the fields it should have.
327
+ # It accepts a view name and a block. The block should specify the fields.
328
+ #
329
+ # @param view_name [Symbol] the view name
330
+ # @yieldreturn [#fields,#field,#include_view,#exclude] Use this block to
331
+ # specify fields, include fields from other views, or exclude fields.
332
+ #
333
+ # @example Using views
334
+ # view :extended do
335
+ # fields :position, :company
336
+ # include_view :normal
337
+ # exclude :first_name
338
+ # end
339
+ #
340
+ # @return [View] a Blueprinter::View object
341
+ def view(view_name)
342
+ self.view_scope = view_collection[view_name]
343
+ view_collection[:default].track_definition_order(view_name)
344
+ yield
345
+ view_collection.invalidate_cache!
346
+ self.view_scope = view_collection[:default]
347
+ end
335
348
 
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.
349
+ # Check whether or not a Blueprint supports the supplied view.
350
+ # It accepts a view name.
351
+ #
352
+ # @param view_name [Symbol] the view name
353
+ #
354
+ # @example With the following Blueprint
355
+ #
356
+ # class ExampleBlueprint < Blueprinter::Base
357
+ # view :custom do
358
+ # end
359
+ # end
360
+ #
361
+ # ExampleBlueprint.view?(:custom) => true
362
+ # ExampleBlueprint.view?(:doesnt_exist) => false
363
+ #
364
+ # @return [Boolean] a boolean value indicating if the view is
365
+ # supported by this Blueprint.
366
+ def view?(view_name)
367
+ view_collection.view?(view_name)
368
+ end
357
369
 
358
- def self.include_views(*view_names)
359
- current_view.include_views(view_names)
360
- end
370
+ def view_collection
371
+ @_view_collection ||= ViewCollection.new
372
+ end
361
373
 
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
374
+ private
381
375
 
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
376
+ attr_accessor :view_scope
400
377
 
401
- def self.excludes(*field_names)
402
- current_view.exclude_fields(field_names)
403
- end
378
+ # Returns the current view during Blueprint definition based on the view_scope.
379
+ def current_view
380
+ view_scope || view_collection[:default]
381
+ end
404
382
 
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
383
+ def inherited(subclass)
384
+ subclass.send(:view_collection).inherit(view_collection)
385
+ end
426
386
 
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
387
+ def association_extractor
388
+ @_association_extractor ||= AssociationExtractor.new
389
+ end
446
390
  end
447
391
  end
448
392
  end