lib/openwfe/expressions/fe_iterator.rb in openwferu-0.9.12 vs lib/openwfe/expressions/fe_iterator.rb in openwferu-0.9.12.863

- old
+ new

@@ -28,21 +28,20 @@ # 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. #++ # -# $Id: definitions.rb 2725 2006-06-02 13:26:32Z jmettraux $ -# # # "made in Japan" # # John Mettraux at openwfe.org # require 'openwfe/expressions/wtemplate' require 'openwfe/expressions/flowexpression' +require 'openwfe/expressions/fe_command' module OpenWFE # @@ -60,44 +59,80 @@ # # Within the iteration, the workitem field "\_\_ic__" contains the number # of elements in the iteration and the field "\_\_ip__" the index of the # current iteration. # + # The 'iterator' expression understands the same cursor commands as the + # CursorExpression. One can thus exit an iterator or skip steps in it. + # + # iterator :on_value => "alice, bob, charles, doug", to_variable => "v" do + # sequence do + # participant :variable_ref => "v" + # skip 1, :if => "${f:reply} == 'skip next'" + # end + # end + # class IteratorExpression < WithTemplateExpression + include CommandMixin names :iterator + # + # an Iterator instance that holds the list of values being iterated + # upon. + # attr_accessor :iterator def apply (workitem) if @children.length < 1 - reply_to_parent(workitem) + reply_to_parent workitem return end @iterator = Iterator.new(self, workitem) - if not @iterator.has_next? + if @iterator.size < 1 reply_to_parent workitem return end reply workitem end def reply (workitem) - unless @iterator.has_next? - reply_to_parent(workitem) + command, step = determine_command_and_step workitem + + vars = if not command + + @iterator.next workitem + + elsif command == C_BREAK or command == C_CANCEL + + nil + + elsif command == C_REWIND or command == C_CONTINUE + + @iterator.rewind workitem + + elsif command.match "^#{C_JUMP}" + + @iterator.jump workitem, step + + else # C_SKIP or C_BACK + + @iterator.skip workitem, step + end + + unless vars + reply_to_parent workitem return end - vars = @iterator.next(self, workitem) + store_itself - store_itself() - get_expression_pool.launch_template( self, @iterator.index, @children[0], workitem, vars) end end @@ -109,17 +144,21 @@ class Iterator ITERATOR_COUNT = "__ic__" ITERATOR_POSITION = "__ip__" - attr_accessor \ - :iteration_list, - :to_field, - :to_variable, - :value_separator, - :counter + attr_accessor :iteration_index + attr_accessor :iteration_list + attr_accessor :to_field + attr_accessor :to_variable + attr_accessor :value_separator + # + # Builds a new iterator, serving a given iterator_expression. + # The second parameter is the workitem (as applied to the iterator + # expression). + # def initialize (iterator_expression, workitem) @to_field = iterator_expression\ .lookup_attribute(:to_field, workitem) @to_variable = iterator_expression\ @@ -128,11 +167,11 @@ @value_separator = iterator_expression\ .lookup_attribute(:value_separator, workitem) @value_separator = /,\s*/ unless @value_separator - @counter = 0 + @iteration_index = 0 raw_list = iterator_expression.lookup_vf_attribute( workitem, :value, :on) @iteration_list = extract_iteration_list(raw_list) @@ -142,61 +181,128 @@ # # Has the iteration a next element ? # def has_next? - @iteration_list.length > 0 + + @iteration_index < @iteration_list.size end # + # Returns the size of this iterator, or rather, the size of the + # underlying iteration list. + # + def size + + @iteration_list.size + end + + # # Prepares the iterator expression and the workitem for the next # iteration. # - def next (iterator_expression, workitem) + def next (workitem) - result = {} + position_at workitem, @iteration_index + end - value = @iteration_list.pop + # + # Positions the iterator back at position 0. + # + def rewind (workitem) - if @to_field - workitem.attributes[@to_field] = value + position_at workitem, 0 + end + + # + # Jumps to a given position in the iterator + # + def jump (workitem, index) + + index = if index < 0 + 0 + elsif index >= @iteration_list.size + @iteration_list.size else - #iterator_expression.set_variable(@to_variable, value) - result[@to_variable] = value + index end - workitem.attributes[ITERATOR_POSITION] = @counter - result[ITERATOR_POSITION] = @counter + position_at workitem, index + end - @counter = @counter + 1 + # + # Jumps a certain number of positions in the iterator. + # + def skip (workitem, offset) - result + jump workitem, @iteration_index + offset end + # + # The current index (whereas @iteration_index already points to + # the next element). + # def index - return @counter - 1 + + @iteration_index - 1 end protected + # + # Positions the iterator absolutely. + # + def position_at (workitem, position) + + result = {} + + value = @iteration_list[position] + + return nil unless value + + if @to_field + workitem.attributes[@to_field] = value + else + result[@to_variable] = value + end + + workitem.attributes[ITERATOR_POSITION] = position + result[ITERATOR_POSITION] = position + + @iteration_index = position + 1 + + result + end + + # + # Extracts the iteration list from any value. + # def extract_iteration_list (raw_list) - l = if is_suitable_list(raw_list) + if is_suitable_list?(raw_list) raw_list else extract_list_from_string(raw_list.to_s) end - - return l.reverse end - def is_suitable_list (instance) - instance.respond_to? :pop and \ - instance.respond_to? :reverse and \ + # + # Returns true if the given instance can be directly + # used as a list. + # + def is_suitable_list? (instance) + + (not instance.is_a?(String)) and \ + instance.respond_to? :[] and \ instance.respond_to? :length end + # + # Extracts the list from the string (comma separated list + # usually). + # def extract_list_from_string (s) + s.split(@value_separator) end end end