Module: Contracts::Validators

Included in:
Contract
Defined in:
lib/contracts/contract/validators.rb

Constant Summary collapse

DEFAULT_VALIDATOR_STRATEGIES =
{
  # e.g. lambda {true}
  Proc => lambda { |contract| contract },

  # e.g. [Num, String]
  # TODO: account for these errors too
  Array => lambda do |contract|
    lambda do |arg|
      return false unless arg.is_a?(Array) && arg.length == contract.length
      arg.zip(contract).all? do |_arg, _contract|
        Contract.valid?(_arg, _contract)
      end
    end
  end,

  # e.g. { :a => Num, :b => String }
  Hash => lambda do |contract|
    lambda do |arg|
      return false unless arg.is_a?(Hash)
      contract.keys.all? do |k|
        Contract.valid?(arg[k], contract[k])
      end
    end
  end,

  Range => lambda do |contract|
    lambda do |arg|
      contract.include?(arg)
    end
  end,

  Regexp => lambda do |contract|
    lambda do |arg|
      arg =~ contract
    end
  end,

  Contracts::SplatArgs => lambda do |contract|
    lambda do |arg|
      Contract.valid?(arg, contract.contract)
    end
  end,

  Contracts::Func => lambda do |_|
    lambda do |arg|
      arg.is_a?(Method) || arg.is_a?(Proc)
    end
  end,

  :valid => lambda do |contract|
    lambda { |arg| contract.valid?(arg) }
  end,

  :class => lambda do |contract|
    lambda { |arg| arg.is_a?(contract) }
  end,

  :default => lambda do |contract|
    lambda { |arg| contract == arg }
  end
}.freeze

Instance Method Summary collapse

Instance Method Details

#clean_memoized_validatorsObject



130
131
132
# File 'lib/contracts/contract/validators.rb', line 130

def clean_memoized_validators
  @_memoized_validators = {}
end

#make_validator(contract) ⇒ Object



98
99
100
101
102
103
104
105
106
# File 'lib/contracts/contract/validators.rb', line 98

def make_validator(contract)
  contract_id = Support.contract_id(contract)

  if memoized_validators.key?(contract_id)
    return memoized_validators[contract_id]
  end

  memoized_validators[contract_id] = make_validator!(contract)
end

#make_validator!(contract) ⇒ Object

This is a little weird. For each contract we pre-make a proc to validate it so we don’t have to go through this decision tree every time. Seems silly but it saves us a bunch of time (4.3sec vs 5.2sec)



86
87
88
# File 'lib/contracts/contract/validators.rb', line 86

def make_validator!(contract)
  validator_strategies[validator_key(contract)].call(contract)
end

#memoized_validatorsObject



125
126
127
# File 'lib/contracts/contract/validators.rb', line 125

def memoized_validators
  @_memoized_validators ||= clean_memoized_validators
end

#override_validator(name, &block) ⇒ Object

Allows to override validator with custom one. Example:

Contract.override_validator(Array) do |contract|
  lambda do |arg|
    # .. implementation for Array contract ..
  end
end

Contract.override_validator(:class) do |contract|
  lambda do |arg|
    arg.is_a?(contract) || arg.is_a?(RSpec::Mocks::Double)
  end
end


78
79
80
# File 'lib/contracts/contract/validators.rb', line 78

def override_validator(name, &block)
  validator_strategies[name] = block
end

#reset_validatorsObject



109
110
111
112
# File 'lib/contracts/contract/validators.rb', line 109

def reset_validators
  clean_memoized_validators
  restore_validators
end

#restore_validatorsObject



120
121
122
# File 'lib/contracts/contract/validators.rb', line 120

def restore_validators
  @_validator_strategies = DEFAULT_VALIDATOR_STRATEGIES.dup
end

#validator_key(contract) ⇒ Object



90
91
92
93
94
95
96
# File 'lib/contracts/contract/validators.rb', line 90

def validator_key(contract)
  klass = contract.class
  return klass  if validator_strategies.key?(klass)
  return :valid if contract.respond_to? :valid?
  return :class if klass == Class || klass == Module
  :default
end

#validator_strategiesObject



115
116
117
# File 'lib/contracts/contract/validators.rb', line 115

def validator_strategies
  @_validator_strategies ||= restore_validators
end