# originally at http://www.opendbms.com/sequenced.rb# author Jeffrey Moss (zardinuk on IRC)module Sequenced module SequencedClass # USAGE: in your class definitions, you can manipulate the sequence array # # class Product < ActionController::Base # include Sequenced # # sequence.push {'customer_information' => proc { @order.legal_entity.nil? }}, # {'billing_information' => proc { @order.billing_address.nil? }} # # def customer_information # ... # end # # def billing_information # ... # end # end # # class PhoneProduct < Product # sequence.insert 0, {'product_selection' => TRUE } # ask this first! # sequence.push {'phone_lines' => proc { @order.phone_lines.empty? } # # def product_selection # if @request.post? # @product = @params['product'] # redirect_to next_action # next_action is defined by Sequenced # end # end # end # # The purpose of the mixin is to allow you to easily determin the next # action in a complex string of events, and to be able to embed that anywhere # a url_for, redirect_to, link_to, form_tag any of that, and eliminate the # maintenance of a giant linked list, to make adding a new action somewhere # much easier. # You can also manually set a next_action in your flash['next_action'] which # functions as a return page, for instance, if you want to return to the same # page they were at before authentication was required. Also pass next_action # in as a parameter, in a hidden form element for example, which is more # robust than putting it in flash attr_accessor :sequence def inherited(subclass) super subclass.sequence = sequence.clone # each subclass has a new copy of the sequence end end class << self def append_features(incoming) super incoming.extend SequencedClass incoming.sequence = [] end end # instance methods def next_action action = action_name return @next_action ||= @params['next_action'] || flash['next_action'] || begin action = process_sequence_action( self.class.sequence[ (self.class.sequence.index(action) || -1) + 1 ] ) end while (action.nil?) end def previous_action action = action_name return @previous_action ||= begin action = process_sequence_action( self.class.sequence[ (self.class.sequence.index(action) || -2) - 1 ] ) end while (action.nil?) end private def process_sequence_action(action) action.each do |action_name, conditions| # TODO: should be able to support different operations by returning # symbols other than true/false, like :action, :controller, :skip, stuff like that case conditions when Proc return {:action => action_name} if instance_eval &conditions #evaluates in current context when String return {:action => action_name} if instance_eval conditions else return {:action => action_name} if conditions end break # loop only runs once end endend