#
#--
# Copyright (c) 2007-2008, John Mettraux, OpenWFE.org
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are met:
#
# . Redistributions of source code must retain the above copyright notice, this
# list of conditions and the following disclaimer.
#
# . Redistributions in binary form must reproduce the above copyright notice,
# this list of conditions and the following disclaimer in the documentation
# and/or other materials provided with the distribution.
#
# . Neither the name of the "OpenWFE" nor the names of its contributors may be
# used to endorse or promote products derived from this software without
# specific prior written permission.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
# INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
# CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
# POSSIBILITY OF SUCH DAMAGE.
#++
#
#
# "made in Japan"
#
# John Mettraux at openwfe.org
#
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_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
protected
def determine_command_and_step (workitem)
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 = if step
step.to_i
else
1
end
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
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