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].freeze
NESTED_OPTS =
%i[virtual sensitive].freeze
ARRAY_OPTS =
%i[of length default validate virtual sensitive required transform].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeContractBuilder

Returns a new instance of ContractBuilder.



313
314
315
316
# File 'lib/permittable.rb', line 313

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

Instance Attribute Details

#finalizerObject (readonly)

Returns the value of attribute finalizer.



311
312
313
# File 'lib/permittable.rb', line 311

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.



345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
# File 'lib/permittable.rb', line 345

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)
  @fields << field
end

#build(&block) ⇒ Object



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

def build(&block)
  instance_eval(&block)
  @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)


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

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, &block) ⇒ Object



338
339
340
# File 'lib/permittable.rb', line 338

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

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

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



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

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