accessing object without transition.object

7 views
Skip to first unread message

Marshall

unread,
Oct 11, 2009, 5:13:28 PM10/11/09
to state-fu
Hi there,

First of all, great job on this plugin.

I implemented it exactly as described for my Job model, with this code
at the end of the machine:

states do
accepted { object.save! }
end

However, upon trying to transition between states, I receive this
error:

NoMethodError: undefined method_name `object' for "#<Job:
0x392d74c>":Job

I think, "OK, it's saying object doesn't exist for Job, then that code
must be executing within the context of Job already." So i try
self.save! instead of object.save! and I get "SystemStackError: stack
level too deep"

I decide I want to figure out what self *is* at that point, and have
it output to my log, and discover that its an instance of the State Fu
Executioner. I check out executioner.rb and see that object is
accessed by calling "transition.object". So I change the end of my
machine to look like this:

states do
accepted { transition.object.save! }
end

...and it works perfectly. I have to do the same thing for any of the
hooks in my states that access object.

Can you tell me why I had to do transition.object when all of the
examples simply use object?

Thanks so much!

Warmly,
Marshall

Marshall

unread,
Oct 11, 2009, 6:34:26 PM10/11/09
to state-fu
OK, I take that back, I think. I can't access object at all, no matter
what. I have no idea what I did before. :)

Why can't I access object?

David Lee

unread,
Oct 12, 2009, 3:03:02 AM10/12/09
to stat...@googlegroups.com
Hi Marshall,

I somewhat recently rewrote the code that handles the execution context - it's now in executioner.rb, which you bravely waded into :)

You should be able to get the desired behaviour with simply

states do
  accepted :save!
end

or if you'd rather,

states do
  accepted { save! }
end

... as the executioner uses some voodoo to give a resonable impression it *is* the "object". Sorry about the confusion. Looks like the README needs to be updated :/ thanks for pointing this out.

Please let me know how you go, or if you have any other questions. I'm still not entirely convinced the amount of black magic surrounding block execution is worthwhile ...
--
cheers,
David Lee

Marshall

unread,
Oct 13, 2009, 4:33:54 AM10/13/09
to state-fu
Hi David,

Thanks for your detailed response.

I tried both of your suggestions and I'm still getting Stack Level Too
Deep.

Also note that I have to use transition.object in each state to access
object:

state :purchased do
on_entry { JobNotifier.send_later(:deliver_pending_approval,
transition.object, transition.object.company) }
event :approve, :to => :approved
end

I can do this with the states block too, but that still produces the
stack error. It does not like being saved!

I tested in both Rails 2.3.2 and 2.3.4.

I see that it uses method_missing to catch the save! action. This is
at the top of the stack trace:

from /opt/local/lib/ruby/gems/1.8/gems/davidlee-state-fu-0.12.1/lib/
support/core_ext.rb:66:in `method_missing'

and then below:

from /opt/local/lib/ruby/gems/1.8/gems/davidlee-state-fu-0.12.1/lib/
executioner.rb:75:in `evaluate_with_arguments'
from /opt/local/lib/ruby/gems/1.8/gems/davidlee-state-fu-0.12.1/lib/
support/core_ext.rb:74:in `with_methods_on'
from /opt/local/lib/ruby/gems/1.8/gems/davidlee-state-fu-0.12.1/lib/
executioner.rb:74:in `evaluate_with_arguments'
from /opt/local/lib/ruby/gems/1.8/gems/davidlee-state-fu-0.12.1/lib/
executioner.rb:81:in `evaluate'
from /opt/local/lib/ruby/gems/1.8/gems/davidlee-state-fu-0.12.1/lib/
transition.rb:305:in `evaluate'
from /opt/local/lib/ruby/gems/1.8/gems/davidlee-state-fu-0.12.1/lib/
transition.rb:158:in `run_hook'
from /opt/local/lib/ruby/gems/1.8/gems/davidlee-state-fu-0.12.1/lib/
transition.rb:193:in `fire!'
from /opt/local/lib/ruby/gems/1.8/gems/davidlee-state-fu-0.12.1/lib/
transition.rb:189:in `each'
from /opt/local/lib/ruby/gems/1.8/gems/davidlee-state-fu-0.12.1/lib/
transition.rb:189:in `fire!'
from /opt/local/lib/ruby/gems/1.8/gems/davidlee-state-fu-0.12.1/lib/
transition.rb:184:in `each'
from /opt/local/lib/ruby/gems/1.8/gems/davidlee-state-fu-0.12.1/lib/
transition.rb:184:in `fire!'
from /opt/local/lib/ruby/gems/1.8/gems/davidlee-state-fu-0.12.1/lib/
binding.rb:92:in `fire_transition!'
from /opt/local/lib/ruby/gems/1.8/gems/davidlee-state-fu-0.12.1/lib/
method_factory.rb:34:in `purchase!'
from /opt/local/lib/ruby/gems/1.8/gems/davidlee-state-fu-0.12.1/lib/
method_factory.rb:168:in `__send__'
from /opt/local/lib/ruby/gems/1.8/gems/davidlee-state-fu-0.12.1/lib/
method_factory.rb:168:in `method_missing'

Thanks for your help.

Marshall

unread,
Oct 13, 2009, 4:35:00 AM10/13/09
to state-fu
Here's a more readable stack trace:

/lib/executioner.rb:75:in `evaluate_with_arguments'
/lib/support/core_ext.rb:74:in `with_methods_on'
/lib/executioner.rb:74:in `evaluate_with_arguments'
/lib/executioner.rb:81:in `evaluate'
/lib/transition.rb:305:in `evaluate'
/lib/transition.rb:158:in `run_hook'
/lib/transition.rb:193:in `fire!'
/lib/transition.rb:189:in `each'
/lib/transition.rb:189:in `fire!'
/lib/transition.rb:184:in `each'
/lib/transition.rb:184:in `fire!'
/lib/binding.rb:92:in `fire_transition!'
/lib/method_factory.rb:34:in `purchase!'
/lib/method_factory.rb:168:in `__send__'
/lib/method_factory.rb:168:in `method_missing'

David Lee

unread,
Oct 13, 2009, 7:06:31 PM10/13/09
to stat...@googlegroups.com
Well, you should be able to use #target in place of #transition.object ... now ...

Ahh - you're using the bundled AS::Lite aren't you?

I have a sneaking suspicion I've made a glaring omission from AS::Lite on ruby1.8, and that the issue is that instance_exec is undefined for your #object.

Are you able to tell me how it responds to respond_to?(:instance_exec) ?

If this is the case, I should be able to patch it very easily (though I probably owe you a beer for the trouble). I'll take a look at this in a bit more depth as soon as I get the chance.

cheers,
David
--
cheers,
David Lee

Marshall Sontag

unread,
Oct 13, 2009, 7:19:49 PM10/13/09
to stat...@googlegroups.com
I'm not trying to use AS::Lite, at least not intentionally.

>> j = Job.last
>> j.respond_to?(:instance_exec)
=> true
>> j.purchase!
  SystemStackError: stack level too deep

I commented out this block: 

# if ActiveSupport is absent, install a very small subset of it for
# some convenience methods
#unless Object.const_defined?('ActiveSupport')  #:nodoc
#  Dir[File.join(File.dirname( __FILE__), 'active_support_lite','**' )].sort.each do |lib|
#    lib = File.expand_path lib
#    next unless File.file?( lib )
#    require lib
#  end
#
#  class Hash #:nodoc
#    include ActiveSupport::CoreExtensions::Hash::Keys
#  end
#end

But I still get Stack Level Too Deep, so it doesn't appear that AS:Lite is the issue.

If you can help me solve this, I'll buy you a 6 pack of beer. :)  Otherwise, I'll have to jump to another state machine. :(


Marshall

unread,
Oct 14, 2009, 5:20:48 PM10/14/09
to state-fu
Well if you can't advise a solution at the moment, would you be able
to tell me which revision to pull that was prior to these latest
changes?

Thanks...
> On Tue, Oct 13, 2009 at 4:06 PM, David Lee <deathtoallfanat...@gmail.com>wrote:
>
> > Well, you should be able to use #target in place of #transition.object ...
> > now ...
>
> > Ahh - you're using the bundled AS::Lite aren't you?
>
> > I have a sneaking suspicion I've made a glaring omission from AS::Lite on
> > ruby1.8, and that the issue is that instance_exec is undefined for your
> > #object.
>
> > Are you able to tell me how it responds to respond_to?(:instance_exec) ?
>
> > If this is the case, I should be able to patch it very easily (though I
> > probably owe you a beer for the trouble). I'll take a look at this in a bit
> > more depth as soon as I get the chance.
>
> > cheers,
> > David
>

David Lee

unread,
Oct 14, 2009, 11:46:33 PM10/14/09
to stat...@googlegroups.com
It's a bit tricky without seeing your code (hence I sent you a direct message) but it looks like you may be triggering an infinite cascade of transitions.

Try changing #execute to #after (which will fire after the transition is accepted), which might alleviate the issue. The other cause of a stack overflow i've seen is caused by calling a method which doesn't exist, in certain circumstances where method_missing is being patched into by something other than StateFu (eg, rspec) - so double check that you're not calling a method which doesn't exist.

Hope this helps.

cheers,
David
--
cheers,
David Lee

David Lee

unread,
Oct 17, 2009, 6:31:46 AM10/17/09
to stat...@googlegroups.com
Ok, I just pushed v0.13.0 to master, which is a big update focused on removing all the voodoo, magic and cleverness I possibly could surrounding execution context and method definition.

The new rule is: methods always get executed in the context of your object. If they take an argument, it's called with the current transition; if they take a second argument, it holds the arguments passed to the transition (if any).

There's no longer any use of method_missing, and when methods are defined on your #object, they're now standard instance methods, defined when the machine is bound to the class (ie, when you define a machine do ... end block).

This is a "possibly disruptive" update (i.e., it may require changes to your code if you're already using StateFu), but I'm convinced it's worthwhile as it should make developing and debugging state machines (and StateFu) *much* easier, and reduce the scope for possible conflict with other libraries.

Please let me know how it pans out for you.

cheers,
David
--
cheers,
David Lee

Marshall

unread,
Oct 18, 2009, 2:03:51 AM10/18/09
to state-fu
I immediately ripped out the egotistical "aasm_states" and replaced
them with humble state-fu "states" and it worked flawlessly! Thanks
for all your hard (and very prompt) work.

I appreciate the simplification of the code, too. I owe you a six
pack. :)

On Oct 17, 3:31 am, David Lee <deathtoallfanat...@gmail.com> wrote:
> Ok, I just pushed v0.13.0 to master, which is a big update focused on
> removing all the voodoo, magic and cleverness I possibly could surrounding
> execution context and method definition.
>
> The new rule is: methods always get executed in the context of your object.
> If they take an argument, it's called with the current transition; if they
> take a second argument, it holds the arguments passed to the transition (if
> any).
>
> There's no longer any use of method_missing, and when methods are defined on
> your #object, they're now standard instance methods, defined when the
> machine is bound to the class (ie, when you define a machine do ... end
> block).
>
> This is a "possibly disruptive" update (i.e., it may require changes to your
> code if you're already using StateFu), but I'm convinced it's worthwhile as
> it should make developing and debugging state machines (and StateFu) *much*
> easier, and reduce the scope for possible conflict with other libraries.
>
> Please let me know how it pans out for you.
>
> cheers,
> David
>
> On Thu, Oct 15, 2009 at 2:46 PM, David Lee <deathtoallfanat...@gmail.com>wrote:
>
> > It's a bit tricky without seeing your code (hence I sent you a direct
> > message) but it looks like you may be triggering an infinite cascade of
> > transitions.
>
> > Try changing #execute to #after (which will fire after the transition is
> > accepted), which might alleviate the issue. The other cause of a stack
> > overflow i've seen is caused by calling a method which doesn't exist, in
> > certain circumstances where method_missing is being patched into by
> > something other than StateFu (eg, rspec) - so double check that you're not
> > calling a method which doesn't exist.
>
> > Hope this helps.
>
> > cheers,
> > David
>
> ...
>
> read more »

David Lee

unread,
Oct 18, 2009, 5:59:04 PM10/18/09
to stat...@googlegroups.com
Nah, I owe you for kicking me in the pants for allowing so much complexity to creep into the edges of the library ;)

Glad to hear it worked out.

best regards,
David
--
cheers,
David Lee
Reply all
Reply to author
Forward
0 new messages