Module: Brainstem::Concerns::ControllerDSL::ClassMethods

Defined in:
lib/brainstem/concerns/controller_dsl.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#brainstem_params_contextObject

In order to correctly scope the DSL, we must have a context under which keys are stored. The default context is _default (to avoid any potential collisions with methods named ‘default’), and this context is used as the parent context for all other contexts.

The context will change, for example, when we are adding keys to the configuration for actions. In those cases, the context becomes the action_name.

Any methods that change the context should change it back upon conclusion so that the assumption of consistent scope inside a block is possible.



44
45
46
# File 'lib/brainstem/concerns/controller_dsl.rb', line 44

def brainstem_params_context
  @brainstem_params_context
end

Instance Method Details

#actions(*axns, &block) ⇒ Object

Invokes action for each symbol in the argument list. Used to specify shared configuration.



187
188
189
# File 'lib/brainstem/concerns/controller_dsl.rb', line 187

def actions(*axns, &block)
  axns.flatten.each { |name| action_context name, &block }
end

#brainstem_params(&block) ⇒ Object

Container method that sets up base scoping for the configuration.



49
50
51
52
53
# File 'lib/brainstem/concerns/controller_dsl.rb', line 49

def brainstem_params(&block)
  self.brainstem_params_context = DEFAULT_BRAINSTEM_PARAMS_CONTEXT
  class_eval(&block)
  self.brainstem_params_context = nil
end

#consumes(*mime_types) ⇒ Object

Used only for Open API Specification generation. #

A list of MIME types the endpoints can consume. This overrides the default consumes definition on the Info object in the Open API Specification.

Parameters:

  • mime_types (Array<String>)

    Array of mime types



377
378
379
# File 'lib/brainstem/concerns/controller_dsl.rb', line 377

def consumes(*mime_types)
  configuration[brainstem_params_context][:consumes] = mime_types.flatten
end

#convert_to_proc(field_name_or_proc) ⇒ Proc Also known as: format_root_name

Converts the field name into a Proc.

Parameters:

  • text (String, Symbol, Proc)

    The title to set

Returns:

  • (Proc)


507
508
509
# File 'lib/brainstem/concerns/controller_dsl.rb', line 507

def convert_to_proc(field_name_or_proc)
  field_name_or_proc.respond_to?(:call) ? field_name_or_proc : Proc.new { field_name_or_proc.to_s }
end

#deprecated(deprecated) ⇒ Object

Used only for Open API Specification generation. #

Declares this operation to be deprecated. Usage of the declared operation should be refrained.

Parameters:

  • schemes (Hash)

    Array of schemes applicable to the endpoint



445
446
447
# File 'lib/brainstem/concerns/controller_dsl.rb', line 445

def deprecated(deprecated)
  configuration[brainstem_params_context][:deprecated] = deprecated
end

#description(text, options = { nodoc: false, internal: false }) ⇒ Object

Specifies a low-level description of a particular context, usually (but not exclusively) reserved for methods.

Setting the :nodoc option marks this description as ‘internal use only’, and causes formatters not to display a description.

Setting the :internal option marks this description as ‘internal use only’, and causes formatters not to display a description unless documentation is generated with ‘–include-internal` flag.

Parameters:

  • text (String)

    The description to set

  • options (Hash) (defaults to: { nodoc: false, internal: false })

    options to record with the description

Options Hash (options):

  • :nodoc (Boolean)

    whether this description should not be output in the documentation.



147
148
149
# File 'lib/brainstem/concerns/controller_dsl.rb', line 147

def description(text, options = { nodoc: false, internal: false })
  configuration[brainstem_params_context][:description] = options.merge(info: text)
end

#documented!Object

Temporary implementation to track controllers that have been documented.



58
59
60
# File 'lib/brainstem/concerns/controller_dsl.rb', line 58

def documented!
  configuration[brainstem_params_context][:documented] = true
end

#dynamic_key_field(type, options = {}) ⇒ Object

Allows defining a field with a dynamic key either under a field block or the custom response block.

Parameters:

  • type (Symbol)

    the data type of the response.

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :info (String)

    the documentation for the param

  • :item_type (String, Symbol)

    The data type of the items contained in a field. Only used when the data type of the response is an ‘array`.



346
347
348
# File 'lib/brainstem/concerns/controller_dsl.rb', line 346

def dynamic_key_field(type, options = {})
  field(DYNAMIC_KEY, type, options)
end

#dynamic_key_fields(type, options = {}, &block) ⇒ Object

Allows defining a field block with a dynamic key for a custom response

Parameters:

  • type (Symbol)

    the data type of the response.

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :info (String)

    the documentation for the param

  • :item_type (String, Symbol)

    The data type of the items contained in a field. Only used when the data type of the response is an ‘array`.



315
316
317
# File 'lib/brainstem/concerns/controller_dsl.rb', line 315

def dynamic_key_fields(type, options = {}, &block)
  fields(DYNAMIC_KEY, type, options, &block)
end

#external_doc(doc_config) ⇒ Object

Used only for Open API Specification generation. #

Additional external documentation for this operation. e.g {

  "description": "Find more info here",
  "url": "https://swagger.io"
}

Parameters:

  • doc_config (Hash)

    Hash with the ‘description` & `url` properties of the external documentation.



420
421
422
# File 'lib/brainstem/concerns/controller_dsl.rb', line 420

def external_doc(doc_config)
  configuration[brainstem_params_context][:external_doc] = doc_config
end

#field(name, type, options = {}) ⇒ Object

Allows defining a field either under a field block or the custom response block.

Parameters:

  • name (Symbol)

    the name of the field of the response.

  • type (Symbol)

    the data type of the response.

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :info (String)

    the documentation for the param

  • :item_type (String, Symbol)

    The data type of the items contained in a field. Only used when the data type of the response is an ‘array`.



329
330
331
332
333
334
335
# File 'lib/brainstem/concerns/controller_dsl.rb', line 329

def field(name, type, options = {})
  custom_response = configuration[brainstem_params_context][:custom_response]
  raise "`fields` must be nested under a response block" if custom_response.nil?

  formatted_name = convert_to_proc(name)
  custom_response[formatted_name] = format_field_configuration(custom_response, name, type, options)
end

#fields(name, type, options = {}, &block) ⇒ Object

Allows defining a field block for a custom response

Parameters:

  • name (Symbol)

    the name of the field block of the response.

  • type (Symbol)

    the data type of the response.

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :info (String)

    the documentation for the param

  • :item_type (String, Symbol)

    The data type of the items contained in a field. Only used when the data type of the response is an ‘array`.



295
296
297
298
299
300
301
302
303
304
# File 'lib/brainstem/concerns/controller_dsl.rb', line 295

def fields(name, type, options = {}, &block)
  custom_response = configuration[brainstem_params_context][:custom_response]
  raise "`fields` must be nested under a response block" if custom_response.nil?

  formatted_name = convert_to_proc(name)
  field_block_config = format_field_configuration(custom_response, name, type, options, &block)

  custom_response[formatted_name] = field_block_config
  with_options(format_ancestry_options(formatted_name, field_block_config), &block)
end

#format_ancestry_options(field_name_proc, options = {}) ⇒ Object

Formats the ancestry options of the field. Returns a hash with ancestors.



525
526
527
528
529
530
# File 'lib/brainstem/concerns/controller_dsl.rb', line 525

def format_ancestry_options(field_name_proc, options = {})
  ancestors = options[:ancestors].try(:dup) || []
  ancestors << field_name_proc

  { ancestors: ancestors }.with_indifferent_access.reject { |_, v| v.blank? }
end

#format_field_configuration(configuration_map, name, type, options = {}, &block) ⇒ Object

Formats the configuration of the param and returns the default configuration if not specified.



535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
# File 'lib/brainstem/concerns/controller_dsl.rb', line 535

def format_field_configuration(configuration_map, name, type, options = {}, &block)
  field_config = options.with_indifferent_access

  field_config[:type] = type.to_s
  field_config[:dynamic_key] = true if name.present? && name.to_sym == DYNAMIC_KEY

  if options.has_key?(:item_type)
    field_config[:item_type] = field_config[:item_type].to_s
  elsif field_config[:type] == 'array'
    field_config[:item_type] = block_given? ? 'hash' : 'string'
  end

  # Inherit `nodoc` attribute from parent
  parent_key = (field_config[:ancestors] || []).reverse.first
  if parent_key && (parent_field_config = configuration_map[parent_key])
    field_config[:nodoc] ||= !!parent_field_config[:nodoc]
  end

  field_config.delete(:nested_levels) if field_config[:nested_levels].to_i < 2

  DEFAULT_FIELD_CONFIG.merge(field_config).with_indifferent_access
end

#format_root_ancestry_options(root_name) ⇒ Object

Formats the ancestry options of the field. Returns a hash with ancestors & root.



515
516
517
518
519
520
# File 'lib/brainstem/concerns/controller_dsl.rb', line 515

def format_root_ancestry_options(root_name)
  root_proc = format_root_name(root_name)
  ancestors = [root_proc]

  { root: root_proc, ancestors: ancestors }.with_indifferent_access.reject { |_, v| v.blank? }
end

#internal!(description = true) ⇒ Object

Specifies that the scope should not be documented unless the ‘–include-internal` flag is passed in the CLI. Setting this on the default context will force the controller to be undocumented, whereas setting it within an action context will force that action to be undocumented.



78
79
80
# File 'lib/brainstem/concerns/controller_dsl.rb', line 78

def internal!(description = true)
  configuration[brainstem_params_context][:internal] = description
end

#model_params(root = Proc.new { |klass| klass.brainstem_model_name }, &block) ⇒ Object

Allows the bulk specification of :root options. Useful for denoting parameters which are nested under a resource.

root may be specified as a string or symbol, which will represent the final root key.

However, root can also be specified as a Proc / callable object, in which case it is evaluated at format time, passed the controller constant. By default, if no argument is passed, it will return the controller’s brainstem_model_name dynamically.

We provide this functionality as a way to handle parameter inheritance in subclasses where the brainstem_model_name may not be the same as the parent class.



210
211
212
# File 'lib/brainstem/concerns/controller_dsl.rb', line 210

def model_params(root = Proc.new { |klass| klass.brainstem_model_name }, &block)
  with_options(format_root_ancestry_options(root), &block)
end

#nodoc!(description = true) ⇒ Object

Specifies that the scope should not be documented. Setting this on the default context will force the controller to be undocumented, whereas setting it within an action context will force that action to be undocumented.



68
69
70
# File 'lib/brainstem/concerns/controller_dsl.rb', line 68

def nodoc!(description = true)
  configuration[brainstem_params_context][:nodoc] = description
end

#operation_id(unique_id) ⇒ Object

Used only for Open API Specification generation. #

Unique string used to identify the operation. The id MUST be unique among all operations described in the API. Tools and libraries MAY use the operation_id to uniquely identify an operation, therefore, it is recommended to follow common programming naming conventions.

Parameters:

  • unique_id (String)


360
361
362
363
364
365
366
# File 'lib/brainstem/concerns/controller_dsl.rb', line 360

def operation_id(unique_id)
  if brainstem_params_context == DEFAULT_BRAINSTEM_PARAMS_CONTEXT
    raise "`operation_id` is endpoint specific and cannot be defined on the controller"
  end

  configuration[brainstem_params_context][:operation_id] = unique_id
end

#presents(target_class = :default, options = { nodoc: false, internal: false }) ⇒ Object

Specifies which presenter is used for the controller / action. By default, expects presentation on all methods, and falls back to the class derived from brainstem_model_name if a name is not given.

Setting the :nodoc option marks this presenter as ‘internal use only’, and causes formatters to display this as not indicated.

Setting the :internal option marks this presenter as ‘internal use only’, and causes formatters to not display this unless documentation is generated with ‘–include-internal` flag.

Parameters:

  • target_class (Class) (defaults to: :default)

    the target class of the presenter (i.e the model it presents)

  • options (Hash) (defaults to: { nodoc: false, internal: false })

    options to record with the presenter

Options Hash (options):

  • :nodoc (Boolean)

    whether this presenter should not be output in the documentation.



99
100
101
102
103
104
105
# File 'lib/brainstem/concerns/controller_dsl.rb', line 99

def presents(target_class = :default, options = { nodoc: false, internal: false })
  raise "`presents` must be a class (in #{self.to_s})" \
    unless target_class.is_a?(Class) || target_class == :default || target_class.nil?

  target_class = brainstem_model_class if target_class == :default
  configuration[brainstem_params_context][:presents] = options.merge(target_class: target_class)
end

#produces(*mime_types) ⇒ Object

Used only for Open API Specification generation. #

A list of MIME types the endpoints can produce. This overrides the default produces definition on the Info object in the Open API Specification.

Parameters:

  • mime_types (Array<String>)

    Array of mime types



390
391
392
# File 'lib/brainstem/concerns/controller_dsl.rb', line 390

def produces(*mime_types)
  configuration[brainstem_params_context][:produces] = mime_types.flatten
end

#reset_configuration!Object



18
19
20
21
22
23
24
25
26
27
28
# File 'lib/brainstem/concerns/controller_dsl.rb', line 18

def reset_configuration!
  configuration.nest! :_default
  configuration[:_default].tap do |default|
    default.nest! :valid_params
    default.nest! :transforms
    default.nonheritable! :title
    default.nonheritable! :description
    default.nonheritable! :tag
    default.nonheritable! :tag_groups
  end
end

#response(type, options = {}, &block) ⇒ Object

Allows defining a custom response structure for an action.

Parameters:

  • type (Symbol)

    the data type of the response.

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :info (String)

    the documentation for the param

  • :nodoc (Boolean)

    should this block appear in the documentation?

  • :item_type (String, Symbol)

    The data type of the items contained in a field. Only used when the data type of the response is an ‘array`.



271
272
273
274
275
276
277
278
279
280
281
282
283
# File 'lib/brainstem/concerns/controller_dsl.rb', line 271

def response(type, options = {}, &block)
  configuration[brainstem_params_context].nest! :custom_response
  custom_response = configuration[brainstem_params_context][:custom_response]

  custom_response[:_config] = format_field_configuration(
    custom_response,
    nil,
    type,
    options,
    &block
  )
  class_eval(&block) if block_given?
end

#schemes(*schemes) ⇒ Object

Used only for Open API Specification generation. #

The transfer protocol for the operation. Values MUST be from the list: “http”, “https”, “ws”, “wss”. The value overrides the default schemes definition in the Info Object.

Parameters:

  • schemes (Hash)

    Array of schemes applicable to the endpoint



433
434
435
# File 'lib/brainstem/concerns/controller_dsl.rb', line 433

def schemes(*schemes)
  configuration[brainstem_params_context][:schemes] = schemes.flatten
end

#security(*schemes) ⇒ Object

Used only for Open API Specification generation. #

A declaration of which security schemes are applied for this operation. The list of values describes alternative security schemes that can be used. This definition overrides any declared top-level security. To remove a top-level security declaration, an empty array can be used.

Parameters:

  • schemes (Array<Hash>)

    Array of security schemes applicable to the endpoint



404
405
406
# File 'lib/brainstem/concerns/controller_dsl.rb', line 404

def security(*schemes)
  configuration[brainstem_params_context][:security] = schemes.flatten
end

#tag(tag_name) ⇒ Object

Used only for Open API Specification generation. #

Specifies the tag name to be used in tagging a class.

Parameters:

  • tag_name (String)

    The name of the tag.



159
160
161
162
163
164
165
# File 'lib/brainstem/concerns/controller_dsl.rb', line 159

def tag(tag_name)
  unless brainstem_params_context == DEFAULT_BRAINSTEM_PARAMS_CONTEXT
    raise "`tag` is not endpoint specific and is defined on the controller"
  end

  configuration[brainstem_params_context][:tag] = tag_name
end

#tag_groups(*tag_group_names) ⇒ Object

Used only for Open API Specification generation. #

Specifies an array of tag names to group the class under. Used for the x-tags OAS vendor extension.

Parameters:

  • tag_group_names (Array<String>)

    Array of tag group names



175
176
177
178
179
180
181
# File 'lib/brainstem/concerns/controller_dsl.rb', line 175

def tag_groups(*tag_group_names)
  unless brainstem_params_context == DEFAULT_BRAINSTEM_PARAMS_CONTEXT
    raise "`tag_groups` is not endpoint specific and is defined on the controller"
  end

  configuration[brainstem_params_context][:tag_groups] = tag_group_names.flatten
end

#title(text, options = { nodoc: false, internal: false }) ⇒ Object

Specifies a title to be used in the description of a class. Can also be used for method section titles.

Setting the :nodoc option marks this title as ‘internal use only’, and causes formatters to fall back to the controller constant or to the action name as appropriate. If you are trying to set the entire controller or action as nondocumentable, instead, use the .nodoc! method in the desired context without a block.

Setting the :internal option marks this title as ‘internal use only’, and causes formatters to fall back to the controller constant or to the action name as appropriate unless documentation is generated with ‘–include-internal` flag. If you are trying to set the entire controller or action as nondocumentable unless internal, instead, use the .internal! method in the desired context without a block.

Parameters:

  • text (String)

    The title to set

  • options (Hash) (defaults to: { nodoc: false, internal: false })

    options to record with the title

Options Hash (options):

  • :nodoc (Boolean)

    whether this title should not be output in the documentation.



128
129
130
# File 'lib/brainstem/concerns/controller_dsl.rb', line 128

def title(text, options = { nodoc: false, internal: false })
  configuration[brainstem_params_context][:title] = options.merge(info: text)
end

#transform(transformations) ⇒ Object Also known as: transforms

Adds a transform to the list of transforms. Used to rename incoming params to their internal names for usage.

Examples:


brainstem_params do
  transform :param_from_frontend => :param_for_backend
end

Parameters:

  • transformations (Hash)

    An old_param => new_param mapping.



461
462
463
464
465
466
# File 'lib/brainstem/concerns/controller_dsl.rb', line 461

def transform(transformations)
  transformations.each_pair do |k, v|
    transforms = configuration[brainstem_params_context][:transforms]
    transforms[k.to_sym] = v.to_sym
  end
end

#valid(name, type = nil, options = {}, &block) ⇒ Object

Adds a param to the list of valid params, storing the info sent with it.

Parameters:

  • name (Symbol)

    the name of the param

  • type (String, Symbol) (defaults to: nil)

    the data type of the field. If not specified, will default to ‘string`.

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :info (String)

    the documentation for the param

  • :root (String, Symbol)

    if this is a nested param, under which param should it be nested?

  • :nodoc (Boolean)

    should this param appear in the documentation?

  • :required (Boolean)

    if the param is required for the endpoint

  • :item_type (String, Symbol)

    The data type of the items contained in a field. Only used when the data type of the field is an ‘array`.



231
232
233
234
235
236
237
238
239
# File 'lib/brainstem/concerns/controller_dsl.rb', line 231

def valid(name, type = nil, options = {}, &block)
  valid_params = configuration[brainstem_params_context][:valid_params]
  param_config = format_field_configuration(valid_params, name, type, options, &block)

  formatted_name = convert_to_proc(name)
  valid_params[formatted_name] = param_config

  with_options(format_ancestry_options(formatted_name, param_config), &block) if block_given?
end

#valid_dynamic_param(type, options = {}, &block) ⇒ Object

Adds a param that has a dynamic key to the list of valid params, storing the info sent with it.

Parameters:

  • type (String, Symbol)

    the data type of the field. If not specified, will default to ‘string`.

  • options (Hash) (defaults to: {})

Options Hash (options):

  • :info (String)

    the documentation for the param

  • :root (String, Symbol)

    if this is a nested param, under which param should it be nested?

  • :nodoc (Boolean)

    should this param appear in the documentation?

  • :required (Boolean)

    if the param is required for the endpoint

  • :item_type (String, Symbol)

    The data type of the items contained in a field. Only used when the data type of the field is an ‘array`.



257
258
259
# File 'lib/brainstem/concerns/controller_dsl.rb', line 257

def valid_dynamic_param(type, options = {}, &block)
  valid(DYNAMIC_KEY, type, options, &block)
end