mirror of
https://github.com/kemko/liquid.git
synced 2026-01-01 15:55:40 +03:00
133 lines
3.2 KiB
Ruby
133 lines
3.2 KiB
Ruby
module Liquid
|
|
# Container for liquid nodes which conveniently wraps decision making logic
|
|
#
|
|
# Example:
|
|
#
|
|
# c = Condition.new(1, '==', 1)
|
|
# c.evaluate #=> true
|
|
#
|
|
class Condition #:nodoc:
|
|
@@operators = {
|
|
'=='.freeze => ->(cond, left, right) { cond.send(:equal_variables, left, right) },
|
|
'!='.freeze => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
|
|
'<>'.freeze => ->(cond, left, right) { !cond.send(:equal_variables, left, right) },
|
|
'<'.freeze => :<,
|
|
'>'.freeze => :>,
|
|
'>='.freeze => :>=,
|
|
'<='.freeze => :<=,
|
|
'contains'.freeze => lambda do |cond, left, right|
|
|
if left && right && left.respond_to?(:include?)
|
|
right = right.to_s if left.is_a?(String)
|
|
left.include?(right)
|
|
else
|
|
false
|
|
end
|
|
end
|
|
}
|
|
|
|
def self.operators
|
|
@@operators
|
|
end
|
|
|
|
attr_reader :attachment
|
|
attr_accessor :left, :operator, :right
|
|
|
|
def initialize(left = nil, operator = nil, right = nil)
|
|
@left = left
|
|
@operator = operator
|
|
@right = right
|
|
@child_relation = nil
|
|
@child_condition = nil
|
|
end
|
|
|
|
def evaluate(context = Context.new)
|
|
result = interpret_condition(left, right, operator, context)
|
|
|
|
case @child_relation
|
|
when :or
|
|
result || @child_condition.evaluate(context)
|
|
when :and
|
|
result && @child_condition.evaluate(context)
|
|
else
|
|
result
|
|
end
|
|
end
|
|
|
|
def or(condition)
|
|
@child_relation = :or
|
|
@child_condition = condition
|
|
end
|
|
|
|
def and(condition)
|
|
@child_relation = :and
|
|
@child_condition = condition
|
|
end
|
|
|
|
def attach(attachment)
|
|
@attachment = attachment
|
|
end
|
|
|
|
def else?
|
|
false
|
|
end
|
|
|
|
def inspect
|
|
"#<Condition #{[@left, @operator, @right].compact.join(' '.freeze)}>"
|
|
end
|
|
|
|
private
|
|
|
|
def equal_variables(left, right)
|
|
if left.is_a?(Liquid::Expression::MethodLiteral)
|
|
if right.respond_to?(left.method_name)
|
|
return right.send(left.method_name)
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
|
|
if right.is_a?(Liquid::Expression::MethodLiteral)
|
|
if left.respond_to?(right.method_name)
|
|
return left.send(right.method_name)
|
|
else
|
|
return nil
|
|
end
|
|
end
|
|
|
|
left == right
|
|
end
|
|
|
|
def interpret_condition(left, right, op, context)
|
|
# If the operator is empty this means that the decision statement is just
|
|
# a single variable. We can just poll this variable from the context and
|
|
# return this as the result.
|
|
return context.evaluate(left) if op.nil?
|
|
|
|
left = context.evaluate(left)
|
|
right = context.evaluate(right)
|
|
|
|
operation = self.class.operators[op] || raise(Liquid::ArgumentError.new("Unknown operator #{op}"))
|
|
|
|
if operation.respond_to?(:call)
|
|
operation.call(self, left, right)
|
|
elsif left.respond_to?(operation) && right.respond_to?(operation)
|
|
begin
|
|
left.send(operation, right)
|
|
rescue ::ArgumentError, TypeError => e
|
|
raise Liquid::ArgumentError.new(e.message)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
class ElseCondition < Condition
|
|
def else?
|
|
true
|
|
end
|
|
|
|
def evaluate(_context)
|
|
true
|
|
end
|
|
end
|
|
end
|