Incorrect super call in state-bound methods #196

Closed
the8472 opened this Issue Jun 29, 2012 · 3 comments

Comments

Projects
None yet
3 participants

the8472 commented Jun 29, 2012

class MyClass
  state_machine do
    state :my_state do
      def my_method
      end
    end
  end
end

obj = MyClass.new
obj.state = :some_other_state
obj.my_method if obj.respond_to? :my_method

This results in an exception that super is not found

The cause is in state.rb#L199 where you do

self.class.state_machine(#{machine_name.inspect}).states.fetch(#{name.inspect}).call(self, #{method.inspect}, lambda {super(*args, &block)}, *args, &block)

Which means the method itself is still defined (causing the respond_to? to succeed) which then calls super, which fails.

jturkel commented Feb 11, 2013

This bug also means that you can't define the behavior of methods in different states e.g.

class MyClass
  extend StateMachine::MacroMethods

  state_machine :state, :initial => :my_state do
    state :my_state do
      def my_method
        puts "Hello from my_state"
      end
    end

    state :my_other_state do
      def my_method
        puts "Hello from my_other_state"
      end
    end
  end

end

obj = MyClass.new
obj.state = :my_other_state
obj.my_method

This results in the following exception:

NoMethodError: super: no superclass method `my_method' for #<MyClass:0x007fd293ade928 @state=:my_other_state>
    from /Users/jturkel/.rvm/gems/ruby-1.9.3-p362@dandelion/gems/state_machine-1.1.2/lib/state_machine/state.rb:193:in `block in my_method'
    from /Users/jturkel/.rvm/gems/ruby-1.9.3-p362@dandelion/gems/state_machine-1.1.2/lib/state_machine/state.rb:217:in `call'
    from /Users/jturkel/.rvm/gems/ruby-1.9.3-p362@dandelion/gems/state_machine-1.1.2/lib/state_machine/state.rb:217:in `call'
    from /Users/jturkel/.rvm/gems/ruby-1.9.3-p362@dandelion/gems/state_machine-1.1.2/lib/state_machine/state.rb:193:in `my_method'
    from /Users/jturkel/.rvm/gems/ruby-1.9.3-p362@dandelion/gems/state_machine-1.1.2/lib/state_machine/state.rb:193:in `block in my_method'
    from /Users/jturkel/.rvm/gems/ruby-1.9.3-p362@dandelion/gems/state_machine-1.1.2/lib/state_machine/state.rb:217:in `call'
    from /Users/jturkel/.rvm/gems/ruby-1.9.3-p362@dandelion/gems/state_machine-1.1.2/lib/state_machine/state.rb:217:in `call'
    from /Users/jturkel/.rvm/gems/ruby-1.9.3-p362@dandelion/gems/state_machine-1.1.2/lib/state_machine/state.rb:193:in `my_method'

jturkel commented Feb 11, 2013

Doh. Ignore my comment above. Looks like it works if you use a string not a symbol for the state name i.e. the following works:

obj.state = 'my_other_state'

obrie closed this in feff2a5 Mar 31, 2013

Owner

obrie commented Mar 31, 2013

@the8472 I went down multiple paths with this and the only reasonable improvement was to change any calls to state-bound methods to be explicit with an InvalidContext error instead of what would appear to be a NoMethodError. Because of the way the methods get defined and how they could be defined in superclasses (whether explicitly or via method_missing) it's not possible to change the behavior of respond_to?).

It could be possible to define something like state_respond_to? that only checks for state-bound methods, but I don't have any plans to implement that for the time being. I can consider it was part of the 2.0 release.

obrie was assigned Mar 31, 2013

@shuhaowu shuhaowu added a commit to Shopify/state_machine that referenced this issue Dec 18, 2014

@obrie @shuhaowu obrie + shuhaowu Raise InvalidContext error when the current state does not define a s…
…tate-driven behavior. Closes #196
2dbb0c5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment