class Sqreen::ConditionEvaluator

Evaluate a condition, resolving literals using BindingAccessor.

{ "%and" => ["true", "true"] } -> true
{ "%or"  => ["true", "false"] } -> true
{ "%and" => ["false", "true"] } -> false

{ "%equal" => ["coucou", "#.args[0]"] } -> "coucou" == args[0]
{ "%hash_val_include" => ["toto is a small guy", "#.request_params"] } ->
     true if one value of request params in included
     in the sentence 'toto is a small guy'.

Combine expressions:

{ "%or" =>
  [
    { "%hash_val_include" => ["AAA", "#.request_params"] },
    { "%hash_val_include" => ["BBB", "#.request_params"] },
  ]
}

will return true if one of the request_params include either AAA or BBB.

Constants

AND_OPERATOR
EQ_OPERATOR
GTE_OPERATOR
GT_OPERATOR
HASH_INC_OPERATOR
HASH_KEY_OPERATOR
INC_OPERATOR
LTE_OPERATOR
LT_OPERATOR
NEQ_OPERATOR
OPERATORS_ARITY
OR_OPERATOR

Public Class Methods

hash_key_include?(values, hash, min_value_size, rem = 10) click to toggle source

Predicate: Is one of values deeply present in keys of hash @params value [Array] Array of objects to find @params hash [Hash] Hash to search into @params min_value_size [Fixnum] to compare against

# File lib/sqreen/condition_evaluator.rb, line 66
def self.hash_key_include?(values, hash, min_value_size, rem = 10)
  return true if rem <= 0
  if hash.is_a?(Array)
    return hash.any? do |v|
      hash_key_include?(values, v, min_value_size, rem - 1)
    end
  end

  return false unless hash.is_a?(Hash)

  hash.any? do |hkey, hval|
    case hkey
    when NilClass
      false
    else
      if hkey.respond_to?(:empty?) && hkey.empty?
        false
      else
        key_incl = if values.is_a?(String)
                     str_include?(values, hkey.to_s)
                   else
                     values.include?(hkey.to_s)
                   end

        key_incl || hash_key_include?(values, hval, min_value_size, rem - 1)
      end
    end
  end
end
hash_val_include?(value, hash, min_value_size, rem = 20) click to toggle source

Predicate: Is value deeply included in hash @params value [Object] object to find @params hash [Hash] Hash to search into @params min_value_size [Fixnum] to compare against

# File lib/sqreen/condition_evaluator.rb, line 35
def self.hash_val_include?(value, hash, min_value_size, rem = 20)
  return true if rem <= 0
  vals = hash
  vals = hash.values if hash.is_a?(Hash)

  vals.any? do |hval|
    case hval
    when Hash, Array
      ConditionEvaluator.hash_val_include?(value, hval,
                                           min_value_size, rem - 1)
    when NilClass
      false
    else
      if hval.respond_to?(:empty?) && hval.empty?
        false
      else
        v = hval.to_s
        if v.size < min_value_size
          false
        else
          ConditionEvaluator.str_include?(value.to_s, v)
        end
      end
    end
  end
end
new(cond) click to toggle source

Initialize evaluator @param cond [Hash] condition Hash

# File lib/sqreen/condition_evaluator.rb, line 115
def initialize(cond)
  unless cond == true || cond == false
    unless cond.respond_to? :each
      raise(Sqreen::Exception, "cond should be a Hash (was #{cond.class})")
    end
  end
  @raw = cond
  @compiled = compile_expr(cond, 10)
end
str_include?(str, what) click to toggle source

Test is a str contains what. Rencode if necessary

# File lib/sqreen/condition_evaluator.rb, line 97
def self.str_include?(str, what)
  str1 = if str.encoding != Encoding::UTF_8
           str.encode(Encoding::UTF_8, :invalid => :replace,
                                       :undef => :replace)
         else
           str
         end
  str2 = if what.encoding != Encoding::UTF_8
           what.encode(Encoding::UTF_8, :invalid => :replace,
                                        :undef => :replace)
         else
           what
         end
  str1.include?(str2)
end

Public Instance Methods

evaluate(*args) click to toggle source

Evaluate the condition @params *args: BindingAccessor evaluate arguments

# File lib/sqreen/condition_evaluator.rb, line 127
def evaluate(*args)
  evaluate_expr(@compiled, 10, *args)
end

Protected Instance Methods

compile_expr(exp, rem) click to toggle source
# File lib/sqreen/condition_evaluator.rb, line 133
def compile_expr(exp, rem)
  return exp if exp == true || exp == false
  return true if exp.empty?
  raise(Sqreen::Exception, 'too deep call detected') if rem <= 0
  h = {}
  exp.each do |op, values|
    unless op.is_a? String
      raise Sqreen::Exception, "op should be a String (was #{op.class})"
    end
    unless values.is_a?(Array)
      raise Sqreen::Exception, "values should be an Array (was #{values.class})"
    end
    h[op] = values.map do |v|
      case v
      when Hash
        compile_expr(v, rem - 1)
      when 'true'
        true
      when 'false'
        false
      else
        BindingAccessor.new(v.to_s)
      end
    end
  end
  h
end
evaluate_expr(exp, rem, *args) click to toggle source
# File lib/sqreen/condition_evaluator.rb, line 185
def evaluate_expr(exp, rem, *args)
  return exp if exp == true || exp == false
  return true if exp.empty?
  raise(Sqreen::Exception, 'too deep call detected') if rem <= 0
  exp.all? do |op, values|
    val_to_res = lambda do |v|
      case v
      when Hash
        evaluate_expr(v, rem - 1, *args)
      when true, false
        v
      else
        v.resolve(*args)
      end
    end

    arity = OPERATORS_ARITY[op]
    if !arity.nil? && values.size != arity
      raise(Sqreen::Exception, "bad arg count #{values.inspect} (op #{op} wanted #{arity})")
    end
    bool = case op
           when OR_OPERATOR
             values.reduce(false) do |_, v|
               r = val_to_res.call(v)
               break r if r
               r
             end
           when AND_OPERATOR
             values.reduce(true) do |_, v|
               r = val_to_res.call(v)
               break r unless r
               r
             end
           when EQ_OPERATOR
             res = values.map(&val_to_res)
             res[0] == res[1]
           when NEQ_OPERATOR
             res = values.map(&val_to_res)
             res[0] != res[1]
           when GT_OPERATOR
             res = values.map(&val_to_res)
             res[0] > res[1]
           when GTE_OPERATOR
             res = values.map(&val_to_res)
             res[0] >= res[1]
           when LT_OPERATOR
             res = values.map(&val_to_res)
             res[0] < res[1]
           when LTE_OPERATOR
             res = values.map(&val_to_res)
             res[0] <= res[1]
           when INC_OPERATOR
             res = values.map(&val_to_res)
             unless res[0].respond_to?(:include?)
               raise(Sqreen::Exception, "no include on res #{res[0].inspect}")
             end
             if res[0].is_a?(String)
               ConditionEvaluator.str_include?(res[0], res[1])
             else
               res[0].include?(res[1])
             end
           when HASH_INC_OPERATOR
             res = values.map(&val_to_res)
             ConditionEvaluator.hash_val_include?(res[0], res[1], res[2])
           when HASH_KEY_OPERATOR
             res = values.map(&val_to_res)
             ConditionEvaluator.hash_key_include?(res[0], res[1], res[2])
           else
             # FIXME: this should be check in compile
             raise(Sqreen::Exception, "unknown op #{op})")
           end
    bool
  end
end