lib/ruote/exp/condition.rb in ruote-2.1.11 vs lib/ruote/exp/condition.rb in ruote-2.2.0
- old
+ new
@@ -1,7 +1,7 @@
#--
-# Copyright (c) 2005-2010, John Mettraux, jmettraux@gmail.com
+# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
@@ -23,93 +23,180 @@
#++
module Ruote::Exp
+ #
+ # A few helper methods for evaluating :if and :unless expression
+ # attributes in process definitions.
+ #
module Condition
- SET_REGEX = /(\S*?)( +is)?( +not)?( +set)$/
- COMPARISON_REGEX = /(.*?) *(==|!=|>=|<=|>|<|=~) *(.*)/
+ #
+ # A runtime error for unusable comparison strings.
+ #
+ class ConditionError < RuntimeError
- def self.apply? (sif, sunless)
+ def initialize(code)
+ super(
+ "couldn't interpret >#{code}<, " +
+ "if it comes from a ${xx} construct, please use ${\"xx} or ${'yy}")
+ end
+ end
+ REGEXES = {
+ 'evl_set' => /^(.+?)( +is)?( +not)?( +set)$/,
+ 'evl_null' => /^(.+?)( +is)?( +not)?( +null)$/,
+ 'evl_empty' => /^(.+[\]}"'])( +is)?( +not)?( +empty)$/,
+ 'evl_in' => /^(.+?)( +is)?( +not)?( +in +)(\[.*\]|\{.*\})$/
+ }
+
+ def self.apply?(sif, sunless)
+
return (true?(sif)) if sif
return ( ! true?(sunless)) if sunless
true
end
- # TODO : rconditional
- # is it really necessary ? there is already ${r:xxx}
+ # Returns true if the given conditional string evaluates to true.
+ #
+ def self.true?(conditional)
- def self.true? (conditional)
+ conditional = unescape(conditional.to_s)
- conditional = unescape(conditional)
-
- if m = SET_REGEX.match(conditional)
- eval_is(m)
- elsif m = COMPARISON_REGEX.match(conditional)
- compare(m)
- else
- to_b(conditional)
+ REGEXES.each do |method, regex|
+ if m = regex.match(conditional)
+ return self.send(method, m)
+ end
end
+
+ evl(conditional) ? true : false
+
+ rescue ArgumentError => ae
+
+ raise ConditionError.new(conditional)
end
+ # Evaluates the given [conditional] code string and returns the
+ # result.
+ #
+ # Note : this is not a full Ruby evaluation !
+ #
+ def self.eval(code)
+
+ evl(code)
+
+ rescue ArgumentError => ae
+
+ raise ConditionError.new(code)
+ end
+
protected
- def self.eval_is (match)
+ def self.parse(conditional)
- match = match[1..-2].select { |e| e != nil }
+ Rufus::TreeChecker.parse(conditional)
- negative = match.find { |m| m == ' not' }
+ rescue NoMethodError => nme
- first = match.first.strip
- is_set = first != '' && first != 'is'
+ raise NoMethodError.new(
+ "/!\\ please upgrade your rufus-treechecker gem /!\\"
+ )
- negative ? (not is_set) : is_set
+ rescue => e
+
+ [ :false ]
end
- def self.unescape (s)
+ def self.unescape(s)
- s ? s.to_s.gsub('&', '&').gsub('>', '>').gsub('<', '<') : nil
+ s.gsub('&', '&').gsub('>', '>').gsub('<', '<')
end
- def self.to_b (o)
+ COMPARATORS = %w[ == > < != >= <= ].collect { |c| c.to_sym }
- o = o.strip if o.is_a?(String)
+ def self.evl(tree)
- not(o == nil || o == false || o == 'false' || o == '')
- end
+ return evl(parse(tree)) if tree.is_a?(String)
- def self.compare (m)
+ return nil if tree == []
- return (m[1].=~(Regexp.new(m[3])) != nil) if m[2] == '=~'
+ return tree.last if tree.first == :str
+ return tree.last if tree.first == :lit
+ return tree.last.to_s if tree.first == :const
+ return nil if tree == [ :nil ]
+ return true if tree == [ :true ]
+ return false if tree == [ :false ]
- a = narrow_to_f(m[1])
- b = narrow_to_f(m[3])
+ return ( ! evl(tree.last)) if tree.first == :not
- if a.class != b.class
- a = m[1]
- b = m[3]
+ return evl(tree[1]) && evl(tree[2]) if tree[0] == :and
+ return evl(tree[1]) || evl(tree[2]) if tree[0] == :or
+
+ return tree[1..-1].collect { |e| evl(e) } if tree[0] == :array
+ return Hash.[](*tree[1..-1].collect { |e| evl(e) }) if tree[0] == :hash
+
+ if tree[0] == :match3
+ return evl(tree[2]) =~ evl(tree[1])
end
+ if tree[0] == :call && tree[2] == :=~
+ return evl(tree[1]) =~ Regexp.new(evl(tree.last.last).to_s)
+ end
- #a.send(m[2], b)
- # ruby 1.8.x doesn't like that one
+ if tree[0] == :call && COMPARATORS.include?(tree[2])
+ return evl(tree[1]).send(tree[2], evl(tree.last.last))
+ end
- a = strip(a)
- b = strip(b)
+ return flatten(tree) if tree[0] == :call
- m[2] == '!=' ? ( ! a.send('==', b)) : a.send(m[2], b)
+ raise ArgumentError
+
+ #require 'ruby2ruby'
+ #Ruby2Ruby.new.process(Sexp.from_array(tree))
+ # returns the raw Ruby as a String
+ # it's nice but "Loan/Grant" becomes "(Loan / Grant)"
end
- def self.narrow_to_f (s)
+ KEYWORDS = %w[ call const arglist ].collect { |w| w.to_sym }
- Float(s) rescue s
+ def self.flatten(tree)
+
+ (tree.flatten - KEYWORDS).collect { |e| e.nil? ? ' ' : e.to_s }.join.strip
end
- def self.strip (s)
+ def self.evl_set(match)
- s.respond_to?(:strip) ? s.strip : s
+ set = evl(match[1])
+ set = set != nil && set != ''
+ set = false if match[1].match(/is$/) && match[2].nil?
+
+ match[3].nil? ? set : ( ! set)
+ end
+
+ def self.evl_empty(match)
+
+ object = evl(match[1])
+
+ empty = if object.respond_to?(:empty?)
+ object.empty?
+ elsif object.nil?
+ true
+ else
+ false
+ end
+
+ ( ! match[3].nil? ^ empty)
+ end
+
+ def self.evl_null(match)
+
+ ( ! match[3].nil? ^ evl(match[1]).nil?)
+ end
+
+ def self.evl_in(match)
+
+ ( ! match[3].nil? ^ evl(match[5]).include?(evl(match[1]))) rescue false
end
end
end