lib/ruote/exp/fe_inc.rb in ruote-2.2.0 vs lib/ruote/exp/fe_inc.rb in ruote-2.3.0

- old
+ new

@@ -1,7 +1,7 @@ #-- -# Copyright (c) 2005-2011, John Mettraux, jmettraux@gmail.com +# Copyright (c) 2005-2012, 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 @@ -21,13 +21,10 @@ # # Made in Japan. #++ -require 'ruote/exp/fe_set' - - module Ruote::Exp # # Increments or decrements the value found in a process variable or # a workitem field. @@ -73,12 +70,12 @@ # # sequence do # set 'v:x' => %w[ a b c d ] # repeat do # dec 'v:x', :pos => 'head' - # _break :unless => '${v:d}' - # participant '${v:d}' + # _break :unless => '${__result__}' + # participant '${__result__}' # end # end # # is equivalent to # @@ -106,100 +103,170 @@ # # set 'v:x' => [ 'alfred', 'bryan', 'carl' ] # dec 'v:x', :val => 'bryan' # # the variable 'x' now holds [ 'alfred', 'carl' ] # - # 'dec' places the removed value in the local variable named 'd'. This trick + # 'dec' places the removed value in workitem field '__result__'. This trick # was used in the above iterator example. # # A specific variable or field can be specified via the :to_var / :to_field # attributes : # # dec 'v:x', :to_v => 'a' # participant :ref => '${v:a}' # - class IncExpression < SetExpression + # + # == nested value + # + # (Since ruote 2.3.0) + # + # If nested expressions are provided the __result__ workitem field is + # used for inc. + # + # inc 'v:x' do + # set '__result__' => 3 + # end + # + # will increase the value of the variable x by 3. + # + # + # == push and pop + # + # push and pop are aliases for inc and dec respectively. There is a major + # difference though: they'll force the target value into an array. + # + # sequence do + # set 'v:x' => 2 + # push 'v:x' => 3 + # end + # + # will result in a variable x holding [ 2, 3 ] as value. + # + # Likewise, + # + # pop 'v:x' + # + # will force a value of [] into the variable x if it wasn't previously set + # or its value was not an array with more than one element. + # + class IncExpression < SequenceExpression - names :inc, :dec, :increment, :decrement + names :inc, :dec, :increment, :decrement, :push, :pop def apply - if var_key = has_attribute(:v, :var, :variable) + h.variables ||= {} # ensures a local scope + reply(h.applied_workitem) + end + + def reply_to_parent(workitem) + + h.applied_workitem['fields'] = workitem['fields'] + + key, value = if var_key = has_attribute(:v, :var, :variable) + var = attribute(var_key) - set_v(var, new_value(:var, var)) + [ "v:#{var}", new_value(:var, var, nil) ] + elsif field_key = has_attribute(:f, :fld, :field) field = attribute(field_key) - set_f(field, new_value(:field, field)) - else + [ field, new_value(:field, field, nil) ] - k = attribute_text + elsif k = att_text - raise( - ArgumentError.new('no variable or field to increment/decrement') - ) if k.length < 1 + [ k, new_value(nil, k, nil) ] - set_vf(k, new_value(nil, k)) + elsif kv = find_kv - #else - # raise ArgumentError.new( - # "missing a variable or field target in #{tree.inspect}") + k, v = kv + + [ k, new_value(nil, k, v) ] + + else + + raise(ArgumentError.new('no variable or field to increment/decrement')) end - reply_to_parent(h.applied_workitem) - end + h.variables = nil + # the local scope is over, + # variables set here will be set in the parent scope - def reply(workitem) + if dec? && value.is_a?(Array) + k, car, value = value + set_vf(k || '__result__', car) + end - # never called + set_vf(key, value) + + super(h.applied_workitem) end protected - def new_value(type, key) + def find_kv - dec = name.match(/^dec/) + compile_atts.reject { |k, v| + COMMON_ATT_KEYS.include?(k) || + k.match(/^to_/) + }.first + end + def dec? + + @dec ||= !!(name.match(/^dec/) or name == 'pop') + end + + def new_value(type, key, increment) + if type == nil && m = PREFIX_REGEX.match(key) type = (m[1][0, 1] == 'f' ? :field : :var) key = m[2] end - delta = lookup_val + delta = increment.nil? ? lookup_val : increment + if delta.nil? && @msg && @msg['action'] == 'reply' + delta = h.applied_workitem['fields']['__result__'] + end + ndelta = Ruote.narrow_to_number(delta || 1) - ndelta = -ndelta if dec && ndelta + ndelta = -ndelta if dec? && ndelta value = type == :var ? lookup_variable(key) : Ruote.lookup(h.applied_workitem['fields'], key) + value = case value + when NilClass then [] + when Array then value + else [ value ] + end if (name == 'push' || name == 'pop') + pos = attribute(:position) || attribute(:pos) return ((value || 0) + ndelta) if ndelta && (not value.is_a?(Array)) pos ||= 'tail' value ||= [] - return (pos == 'tail' ? value + [ delta ] : [ delta ] + value) unless dec + return (pos == 'tail' ? value + [ delta ] : [ delta ] + value) unless dec? - to_v, to_f = determine_tos - to_v = 'd' if to_v.nil? && to_f.nil? - car, cdr = if delta != nil (value.delete(delta) != nil ) ? [ delta, value ] : [ nil, value ] elsif pos == 'tail' [ value[-1], value[0..-2] ] else [ value[0], value[1..-1] ] end - to_v ? set_v(to_v, car) : set_f(to_f, car) + to_v, to_f = determine_tos + key = to_v ? "v:#{to_v}" : to_f - cdr + [ key, car, cdr ] end end end