Class: CanHasState::Definition

Inherits:
Object
  • Object
show all
Defined in:
lib/can_has_state/definition.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(column_name, parent_context, &block) ⇒ Definition



6
7
8
9
10
11
12
13
# File 'lib/can_has_state/definition.rb', line 6

def initialize(column_name, parent_context, &block)
  @parent_context = parent_context
  @column = column_name.to_sym
  @states = {}
  @triggers = []
  instance_eval(&block)
  @initial_state ||= @states.keys.first
end

Instance Attribute Details

#columnObject (readonly)

Returns the value of attribute column.



4
5
6
# File 'lib/can_has_state/definition.rb', line 4

def column
  @column
end

#initial_stateObject (readonly)

Returns the value of attribute initial_state.



4
5
6
# File 'lib/can_has_state/definition.rb', line 4

def initial_state
  @initial_state
end

#statesObject (readonly)

Returns the value of attribute states.



4
5
6
# File 'lib/can_has_state/definition.rb', line 4

def states
  @states
end

#triggersObject (readonly)

Returns the value of attribute triggers.



4
5
6
# File 'lib/can_has_state/definition.rb', line 4

def triggers
  @triggers
end

Instance Method Details

#allow?(record, to) ⇒ Boolean



84
85
86
87
88
89
90
91
92
93
94
95
96
97
# File 'lib/can_has_state/definition.rb', line 84

def allow?(record, to)
  to &&= to.to_s
  return false unless known?(to)
  states[to][:guards].all? do |g|
    case g
    when Proc
      g.call record
    when Symbol, String
      record.send g
    else
      raise ArgumentError, "Expecing Symbol or Proc for :guard, got #{g.class} : #{g}"
    end
  end
end

#extend_machine(&block) ⇒ Object



16
17
18
# File 'lib/can_has_state/definition.rb', line 16

def extend_machine(&block)
  instance_eval(&block)
end

#known?(to) ⇒ Boolean



79
80
81
82
# File 'lib/can_has_state/definition.rb', line 79

def known?(to)
  to &&= to.to_s
  @states.keys.include? to
end

#message(to) ⇒ Object



99
100
101
102
# File 'lib/can_has_state/definition.rb', line 99

def message(to)
  to &&= to.to_s
  states[to][:message]
end

#on(pairs) ⇒ Object

Raises:

  • (ArgumentError)


67
68
69
70
71
72
73
74
75
# File 'lib/can_has_state/definition.rb', line 67

def on(pairs)
  trigger  = pairs.delete :trigger
  deferred = pairs.delete :deferred
  raise(ArgumentError, "use of deferred triggers requires support for #after_save callbacks") if deferred && !@parent_context.respond_to?(:after_save)
  pairs.each do |from, to|
    @triggers << {:from=>Array(from).map(&:to_s), :to=>Array(to).map(&:to_s), 
                  :trigger=>Array(trigger), :type=>:trigger, :deferred=>!!deferred}
  end
end

#state(state_name, *args) ⇒ Object



21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# File 'lib/can_has_state/definition.rb', line 21

def state(state_name, *args)
  options = args.extract_options!
  state_name = state_name.to_s

  if args.include? :initial
    @initial_state = state_name
  end

  # TODO: turn even guards into types of triggers ... then support :guard as a trigger param
  guards = []
  message = :invalid_transition
  # TODO: differentiate messages for :from errors vs. :guard errors

  options.each do |key, val|
    case key
    when :from
      from_vals = Array(val).map(&:to_s)
      from_vals << nil # for new records
      guards << Proc.new do |r|
        val_was = r.send("#{column}_was")
        val_was &&= val_was.to_s
        from_vals.include? val_was
      end
    when :guard, :require
      guards += Array(val)
    when :message
      message = val
    when :timestamp
      @triggers << {:from=>["*"], :to=>[state_name], :trigger=>[Proc.new{|r| r.send("#{val}=", Time.now.utc)}]}
    when :on_enter
      @triggers << {:from=>["*"], :to=>[state_name], :trigger=>Array(val), :type=>:on_enter}
    when :on_enter_deferred
      raise(ArgumentError, "use of deferred triggers requires support for #after_save callbacks") unless @parent_context.respond_to?(:after_save)
      @triggers << {:from=>["*"], :to=>[state_name], :trigger=>Array(val), :type=>:on_enter, :deferred=>true}
    when :on_exit
      @triggers << {:from=>[state_name], :to=>["*"], :trigger=>Array(val), :type=>:on_exit}
    when :on_exit_deferred
      raise(ArgumentError, "use of deferred triggers requires support for #after_save callbacks") unless @parent_context.respond_to?(:after_save)
      @triggers << {:from=>[state_name], :to=>["*"], :trigger=>Array(val), :type=>:on_exit, :deferred=>true}
    end
  end

  @states[state_name] = {:guards=>guards, :message=>message}
end

#trigger(record, from, to, deferred = false) ⇒ Object



105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
# File 'lib/can_has_state/definition.rb', line 105

def trigger(record, from, to, deferred=false)
  from &&= from.to_s
  to &&= to.to_s
  # Rails.logger.debug "Checking triggers for transition #{from} to #{to} (deferred:#{deferred.inspect})"
  @triggers.select do |trigger|
    deferred ? trigger[:deferred] : !trigger[:deferred]
  end.select do |trigger|
    (trigger[:from].include?("*") || trigger[:from].include?(from)) &&
        (trigger[:to].include?("*") || trigger[:to].include?(to))
  # end.each do |trigger|
  #   Rails.logger.debug "  Matched trigger: #{trigger[:from].inspect} -- #{trigger[:to].inspect}"
  end.each do |trigger|
    call_triggers record, trigger
  end
end