class SomeRuleSet
def self.criteria
constraints.map(&:description)
end
def self.constraints
[
Constraints::Max.new('something', 12, 'days', &:value),
Constraints::Max.new('another', 42, 'months', &:value),
Constraints::Match.new('example', nil, &:value),
Constraints::Range.new('range example', 5, 10, &:value)
]
end
def self.run(some_object_array)
some_object_array
.map { |thing| Result.new(thing) }
.each { |result| result.apply_constraints(constraints) }
end
end
# result object
class Result
attr_reader :thing
def initialize(thing)
@thing = thing
@passed = true
@fail_reasons = []
end
def apply_constraints(constraints)
constraints.each do |constraint|
result = constraint.evaluate(thing)
@passed &&= result.passed
@fail_reasons << result.detail unless result.passed
end
end
def fail_reasons
@fail_reasons.join(' ')
end
def passed?
@passed
end
end
# contraint result
module Constraints
class Result
attr_reader :passed, :skipped, :detail
def initialize(passed, detail, skipped = false)
@passed = passed
@skipped = skipped
@detail = detail
end
end
end
# example constraints
module Constraints
class Max
def initialize(item_name, amount, unit, &block)
@amount = amount
@item_name = item_name
@block = block
@unit = unit
end
def description
"Max #{@item_name} of #{@amount} #{@unit}"
end
def evaluate(thing)
val = @block.call(thing)
if val.nil?
Result.new(false, "There is no value for #{@item_name}.")
elsif val <= @amount
Result.new(true, "#{@item_name} is less than #{@amount} #{@unit}.")
else
Result.new(false, "max range reached for #{@item_name}.")
end
end
end
end