davidteren
2/27/2019 - 8:01 PM

Handy Snippets

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