class Jet::Contract::Attribute::Builder

Public Class Methods

new(opts = {}) click to toggle source
# File lib/jet/contract/attribute/builder.rb, line 7
def initialize(opts = {})
  @opts = { required: true }.merge(opts)
  checks(opts[:checks]) if opts[:checks]
  contract(opts[:contract]) if opts[:contract]
  each(*opts[:each]) if opts[:each]
end

Public Instance Methods

call(types = nil, checks = nil) click to toggle source
# File lib/jet/contract/attribute/builder.rb, line 14
def call(types = nil, checks = nil)
  types = types.nil? ? Contract.types : Contract.types!(types)
  checks = checks.nil? ? Contract.checks : Contract.checks!(checks)

  Attribute.new(
    type_with(types),
    check_set_with(checks),
    contract: @opts[:contract]&.(types, checks),
    each: @opts[:each]&.(types, checks),
    required: @opts[:required]
  )
end
checks(checks) click to toggle source
# File lib/jet/contract/attribute/builder.rb, line 27
def checks(checks)
  @opts[:checks] = checks.each_with_object([]) do |check, a|
    case check
    when Hash
      check.each { |(k, v)| a << [k.to_sym, v] }
    when Array
      a << [check.first.to_sym].concat(check[1..-1])
    else
      a << [check.to_sym]
    end
  end
  self
end
contract(contract = nil, &blk) click to toggle source
# File lib/jet/contract/attribute/builder.rb, line 41
def contract(contract = nil, &blk)
  raise ArgumentError, "cannot provide :contract if :each is set" if @opts[:each]
  auto_type!("contract", :hash)
  raise ArgumentError, "must provide either `contract` or a block" unless
    !contract.nil? ^ block_given?

  @opts[:contract] =
    if block_given?
      Contract::Builder.new.tap { |b| b.instance_eval(&blk) }
    else
      Jet.type_check!(":contract", contract, Contract, Contract::Builder)
    end
  self
end
each(*args, &blk) click to toggle source
# File lib/jet/contract/attribute/builder.rb, line 56
def each(*args, &blk)
  raise ArgumentError, "cannot provide :each if :contract is set" if @opts[:contract]
  auto_type!("each", :array)
  raise ArgumentError, "must provide either `args` or a block" unless
    args.any? ^ block_given?

  @opts[:each] =
    if block_given?
      self.class.new.instance_eval(&blk)
    elsif args.size == 1 && args.first.is_a?(self.class)
      args.first
    else
      self.class.new.is(*args)
    end
  self
end
is(type, *checks) click to toggle source
# File lib/jet/contract/attribute/builder.rb, line 73
def is(type, *checks)
  @opts[:maybe] = false
  type(type, *checks)
  self
end
maybe(type, *checks) click to toggle source
# File lib/jet/contract/attribute/builder.rb, line 79
def maybe(type, *checks)
  @opts[:maybe] = true
  type(type, *checks)
  self
end
opts() click to toggle source
# File lib/jet/contract/attribute/builder.rb, line 91
def opts
  @opts.dup
end
type(type, *checks) click to toggle source
# File lib/jet/contract/attribute/builder.rb, line 85
def type(type, *checks)
  @opts[:type] = Jet.type_check!("`type`", type, Symbol, Type).to_sym
  checks(checks)
  self
end

Private Instance Methods

auto_type!(method, type) click to toggle source
# File lib/jet/contract/attribute/builder.rb, line 97
def auto_type!(method, type)
  type(type) unless @opts[:type]
  raise ArgumentError, "##{method} can only be used with type :#{type}" unless
    @opts[:type] == type
  type
end
check_set_with(checks) click to toggle source
# File lib/jet/contract/attribute/builder.rb, line 104
def check_set_with(checks)
  return unless @opts[:checks]&.any?
  Check::Set.new(*@opts[:checks].map { |name, *args| [checks[name]].concat(args) })
end
type_with(types) click to toggle source
# File lib/jet/contract/attribute/builder.rb, line 109
def type_with(types)
  type = types.fetch(@opts[:type].to_sym)
  return type.maybe if @opts[:maybe]
  raise "#{type.inspect} is a maybe? type (hint: use #maybe instead of #is)" if
    type.maybe?
  type
end