aasmの作るイベントメソッドがちょっと変
aasm_eventで定義されるイベントメソッドは、引数を取ることができる。これは、lib/aasm/aasm.rbに定義されているとおり。
def aasm_event(name, options = {}, &block) sm = AASM::StateMachine[self] unless sm.events.has_key?(name) sm.events[name] = AASM::SupportingClasses::Event.new(name, options, &block) end define_method("#{name.to_s}!") do |*args| aasm_fire_event(name, true, *args) end define_method("#{name.to_s}") do |*args| aasm_fire_event(name, false, *args) end end
というわけで、こんなのを作ってみた。
class Hoge < ActiveRecord::Base include AASM has_one :hoges_fuga has_one :fuga, :through => :hoges_fuga attr_accessor :target_activity aasm_column :state aasm_initial_state :inactive aasm_state :inactive aasm_state :active aasm_event :activate do transitions :from => :inactive, :to => :active, :on_transition => :on_activation end def on_activation(arg) puts arg.inspect true end end
で、こんなのを実行。
h = Hoge.create(:name => 'a name') h.activate('activated!!') => false
なん…だと…
そもそも、on_activationも実行されていないというのか…
というわけで、aasmの内部を解析。
まずは、aasm#aasm_fire_eventを調査。
def aasm_fire_event(name, persist, *args) old_state = aasm_state_object_for_state(aasm_current_state) event = self.class.aasm_events[name] old_state.call_action(:exit, self) # new event before callback event.call_action(:before, self) new_state_name = event.fire(self, *args) ...
というわけで、event(lib/aasm/event.rb)の定義を参照。
def fire(obj, to_state=nil, *args) transitions = @transitions.select { |t| t.from == obj.aasm_current_state } raise AASM::InvalidTransition, "Event '#{name}' cannot transition from '#{obj.aasm_current_state}'" if transitions.size == 0 next_state = nil transitions.each do |transition| next if to_state and !Array(transition.to).include?(to_state) if transition.perform(obj) next_state = to_state || Array(transition.to).first transition.execute(obj, *args) break end end next_state end
あれ?
ってことは…
fireには、第2引数はto_stateで渡っているから*1、そのままnext_stateがnilとしてaasm_fire_eventに返る、よってイベントメソッドの結果はreturn false、となるのね。了解。
ということは、イベントハンドラには引数を指定していても、実際にイベントを起動するときには、上記の例で言えば
hoge.activate!(nil, 'activated!!!')
と渡さないといけない、というわけだ。
しかし、ステートマシンのそもそものあり方から言えば、イベントの引数で遷移先状態を渡す、というのは邪道であって、aasm_fire_eventのevent.fireを呼び出す場合、to_stateはnilで渡すべきなのでは、と考えると、バグのような気もするが、はてさて。
というか、aasmでイベントに引数を渡したいと考えるのは少数なのか…
*1:transitions.each doの直後のnext if条件に抵触するため