Class: Permittable::ContractBuilder

Inherits:
Object
  • Object
show all
Defined in:
lib/permittable.rb

Overview

Builds the frozen field list from the permit_params block. Every declaration is validated eagerly: a bad contract is a programmer error and should fail at class load, not at request time.

Constant Summary collapse

SCALAR_OPTS =
%i[in format length default normalize validate virtual sensitive transform message].freeze
NESTED_OPTS =
%i[virtual sensitive message].freeze
ARRAY_OPTS =
%i[of length default validate virtual sensitive required transform message].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeContractBuilder

Returns a new instance of ContractBuilder.



322
323
324
325
# File 'lib/permittable.rb', line 322

def initialize
  @fields = []
  @finalizer = nil
end

Instance Attribute Details

#finalizerObject (readonly)

Returns the value of attribute finalizer.



320
321
322
# File 'lib/permittable.rb', line 320

def finalizer
  @finalizer
end

Instance Method Details

#array(name, **opts, &block) ⇒ Object

Array of scalars (of:, default :string) or, with a block, an array of nested hashes. Optional unless required: true; length: constrains the element COUNT.



354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
# File 'lib/permittable.rb', line 354

def array(name, **opts, &block)
  name = field_name!(name)
  assert_opts!(name, opts, ARRAY_OPTS)
  required = opts.delete(:required) ? true : false

  field = { name: name, kind: :array, required: required, **opts }
  if block
    raise ArgumentError, "#{LABEL}: array :#{name} takes of: OR a block, not both" if opts.key?(:of)

    field[:fields] = nested_fields!(name, &block)
    field.delete(:of)
  else
    field[:of] = scalar_type!(name, opts[:of] || :string)
  end
  validate_length!(name, field[:length]) if field.key?(:length)
  validate_callable!(name, :validate, field[:validate]) if field.key?(:validate)
  validate_callable!(name, :transform, field[:transform]) if field.key?(:transform)
  validate_array_default!(field) if field.key?(:default)
  validate_message!(field)
  @fields << field
end

#buildObject



327
328
329
330
# File 'lib/permittable.rb', line 327

def build(&)
  instance_eval(&)
  @fields.map(&:freeze).freeze
end

#finalize(&block) ⇒ Object

Post-validation reshaping of the whole contract — see the module comment. Once per contract, top level only.

Raises:

  • (ArgumentError)


334
335
336
337
338
339
# File 'lib/permittable.rb', line 334

def finalize(&block)
  raise ArgumentError, "#{LABEL}: finalize requires a block" unless block
  raise ArgumentError, "#{LABEL}: finalize may only be declared once per contract" if @finalizer

  @finalizer = block
end

#optional(name, type = nil, **opts) ⇒ Object



347
348
349
# File 'lib/permittable.rb', line 347

def optional(name, type = nil, **opts, &)
  add_field(name, type, required: false, opts: opts, &)
end

#required(name, type = nil, **opts) ⇒ Object

required :name defaults the type to :string. A block instead of a type declares a nested hash of sub-fields.



343
344
345
# File 'lib/permittable.rb', line 343

def required(name, type = nil, **opts, &)
  add_field(name, type, required: true, opts: opts, &)
end