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('&amp;', '&').gsub('&gt;', '>').gsub('&lt;', '<') : nil + s.gsub('&amp;', '&').gsub('&gt;', '>').gsub('&lt;', '<') 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