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.



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

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.



161
162
163
# File 'lib/brainstem/concerns/controller_dsl.rb', line 161

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.



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

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



304
305
306
# File 'lib/brainstem/concerns/controller_dsl.rb', line 304

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)


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

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



372
373
374
# File 'lib/brainstem/concerns/controller_dsl.rb', line 372

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

#description(text, options = { nodoc: 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.

Parameters:

  • text (String)

    The description to set

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

    options to record with the description

Options Hash (options):

  • :nodoc (Boolean)

    whether this description should not be output in the documentation.



121
122
123
# File 'lib/brainstem/concerns/controller_dsl.rb', line 121

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

#documented!Object

Temporary implementation to track controllers that have been documented.



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

def documented!
  configuration[brainstem_params_context][:documented] = true
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.



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

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. Ideally used when the data type of the response is an ‘array`.



269
270
271
272
273
274
275
# File 'lib/brainstem/concerns/controller_dsl.rb', line 269

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, 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. Ideally used when the data type of the response is an ‘array`.



248
249
250
251
252
253
254
255
256
257
# File 'lib/brainstem/concerns/controller_dsl.rb', line 248

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, 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.



452
453
454
455
456
457
# File 'lib/brainstem/concerns/controller_dsl.rb', line 452

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, type, options = {}, &block) ⇒ Object

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



462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
# File 'lib/brainstem/concerns/controller_dsl.rb', line 462

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

  field_config[:type] = type.to_s
  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

  # Rollup `required` attribute to ancestors if true
  if field_config[:required]
    (field_config[:ancestors] || []).reverse.each do |ancestor_key|
      configuration_map[ancestor_key][:required] = true if configuration_map.has_key?(ancestor_key)
    end
  end

  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.



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

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

#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.



184
185
186
# File 'lib/brainstem/concerns/controller_dsl.rb', line 184

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

#nodoc!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.



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

def nodoc!
  configuration[brainstem_params_context][:nodoc] = true
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)


287
288
289
290
291
292
293
# File 'lib/brainstem/concerns/controller_dsl.rb', line 287

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 }) ⇒ 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.

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 })

    options to record with the presenter

Options Hash (options):

  • :nodoc (Boolean)

    whether this presenter should not be output in the documentation.



84
85
86
87
88
89
90
# File 'lib/brainstem/concerns/controller_dsl.rb', line 84

def presents(target_class = :default, options = { nodoc: 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



317
318
319
# File 'lib/brainstem/concerns/controller_dsl.rb', line 317

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

#reset_configuration!Object



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

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. Ideally used when the data type of the response is an ‘array`.



225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/brainstem/concerns/controller_dsl.rb', line 225

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,
    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



360
361
362
# File 'lib/brainstem/concerns/controller_dsl.rb', line 360

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



331
332
333
# File 'lib/brainstem/concerns/controller_dsl.rb', line 331

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.



133
134
135
136
137
138
139
# File 'lib/brainstem/concerns/controller_dsl.rb', line 133

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



149
150
151
152
153
154
155
# File 'lib/brainstem/concerns/controller_dsl.rb', line 149

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 }) ⇒ 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.

Parameters:

  • text (String)

    The title to set

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

    options to record with the title

Options Hash (options):

  • :nodoc (Boolean)

    whether this title should not be output in the documentation.



106
107
108
# File 'lib/brainstem/concerns/controller_dsl.rb', line 106

def title(text, options = { nodoc: 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.



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

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:

  • field_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. Ideally used when the data type of the field is an ‘array`, `object` or `hash`.



205
206
207
208
209
210
211
212
213
# File 'lib/brainstem/concerns/controller_dsl.rb', line 205

def valid(name, type = nil, options = {}, &block)
  valid_params = configuration[brainstem_params_context][:valid_params]
  param_config = format_field_configuration(valid_params, 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