#-- # Copyright (c) 2007-2009, 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 # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. # # Made in Japan. #++ require 'openwfe/expressions/condition' module OpenWFE # # Some constants shared by 'cursor', 'loop', 'iterator' and # the CommandExpression. # module CommandConstants protected A_COMMAND_FIELD = 'command-field' F_COMMAND = '__cursor_command__' A_DISALLOW = 'disallow' C_BACK = 'back' C_SKIP = 'skip' C_BREAK = 'break' C_CANCEL = 'cancel' C_OVER = 'over' BREAK_COMMANDS = [ C_BREAK, C_CANCEL, C_OVER ] C_REWIND = 'rewind' C_CONTINUE = 'continue' C_JUMP = 'jump' A_STEP = 'step' end # # A mixin shared by 'iterator' and 'cursor' ('loop'), simply # provides the methods for looking up the "command" (break, skip, # rewind, ...) from the workitem and the process. # module CommandMixin include CommandConstants include ConditionMixin protected def determine_command_and_step (workitem) # # at first, look at the condition attribute %w{ break rewind }.each do |cmd| return [ cmd, 0 ] \ if eval_condition("#{cmd}-if", workitem, "#{cmd}-unless") end # # then look at the command field command_field = lookup_command_field(workitem) command, step = lookup_command(command_field, workitem) disallow_list = lookup_disallow(workitem) command = nil \ if disallow_list and disallow_list.include?(command) workitem.attributes.delete(command_field) [ command, step ] end private # # Looks up the value in the command field. # def lookup_command_field (workitem) lookup_attribute(A_COMMAND_FIELD, workitem, :default => F_COMMAND) end # # Returns the command and the step # def lookup_command (command_field, workitem) command = workitem.attributes[command_field] return [ nil, 1 ] unless command # # this corresponds to the "just one step forward" default command, step = command.strip.split step = step ? step.to_i : 1 step = -step if command == C_BACK [ command, step ] end # # Fetches the value of the 'disallow' cursor attribute. # def lookup_disallow (workitem) lookup_array_attribute(A_DISALLOW, workitem) end end # # This class implements the following expressions : back, break, # cancel, continue, jump, rewind, skip. # # They are generally used inside of a 'cursor' (CursorExpression) or # a 'loop' (LoopExpression), they can be used outside, but their result # (the value of the field '\_\_cursor_command__' will be used as soon as the # flow enters a cursor or a loop). # # In fact, this expression is only a nice wrapper that sets the # value of the field "\_\_cursor_command__" to its name ('back' for example) # plus to the 'step' attribute value. # # For example simply sets the value of the field # '\_\_cursor_command__' to 'skip 3'. # # (The field \_\_cursor_command__ is, by default, read and # obeyed by the 'cursor' expression). # # With Ruby process definitions, you can directly write : # # skip 2 # jump "0" # # instead of # # skip :step => "2" # jump :step => "0" # # Likewise, in an XML process definition, you can write # # 2 # # although that might still look lighter (it's longer though) : # # # # # About the command themselves : # # * back : will go back from the number of given steps, 1 by default # * break : will exit the cursor (or the loop) # * cancel : an alias for 'break' # * continue : will exit the cursor, if in a loop, will get back at step 0 # * jump : will move the cursor (or loop) to an absolute given position (count starts at 0) # * rewind : an alias for continue # * skip : skips the given number of steps # # # All those command support an 'if' attribute to restrict their execution : # # cursor do # go_to_shop # check_prices # _break :if => "${price} > ${f:current_cash}" # buy_stuff # end # # The 'rif' attribute may be used instead of the 'if' attribute. Its value # is some ruby code that, when evaluating to true will let the command # be executed. # # _skip 2, :rif => "workitem.customers.size % 2 == 0" # # # # skips if the nb of customers is pair # # Note that the 'rif' attribute will work only if the # :ruby_eval_allowed parameter is set to true in the engine's # application context. # # engine.application_context[:ruby_eval_allowed] = true # class CursorCommandExpression < FlowExpression include CommandConstants include ConditionMixin names :back, :skip, :continue, :break, :cancel, :rewind, :jump, :over def apply (workitem) conditional = eval_condition(:if, workitem, :unless) # # for example : if conditional == nil or conditional command = @fei.expression_name step = lookup_attribute(A_STEP, workitem) step = fetch_text_content(workitem) unless step step = 1 unless step step = Integer(step) command = "#{command} #{step}" #if step != 1 workitem.attributes[F_COMMAND] = command end reply_to_parent(workitem) end end end