pluginaweek / state_machine

Adds support for creating state machines for attributes on any Ruby class
http://www.pluginaweek.org
MIT License
3.74k stars 510 forks source link

Required arguments for event transition #239

Closed myitcv closed 11 years ago

myitcv commented 11 years ago

With reference to the 'Overriding the event' section of the docs, is it possible to require an argument (or indeed any number of arguments) for an event?

Something like

class User < ActiveRecord::Base

  state_machine :state, :initial => :new do

    event :set_email do
      transition :new => :pending
    end

    def set_email(email_to_set, *args)
      unconfirmed_email = email_to_set
      super(*args) 
    end

  end
end

Or am I abusing the pattern with this sort of approach?

Thanks in advance.

the8472 commented 11 years ago

you should use a before_ or after_transition handler.

state_machine do
  event :foo do
    transition :one => :two
  end

  before_transition :on => :foo do |obj, trans|
    args = trans.args.dup
    # do whatever you want with the transition arguments here
  end
end

Edit: if you need some required arguments just throw an exception if they're not present

myitcv commented 11 years ago

Thanks. Can you give some insight as to why the arguments are consumed as *args as opposed to a Rails-style hash? The latter would seem more intuitive to me

the8472 commented 11 years ago

you can pass positional parameters and a hash. like a regular method call. if you're using rails you can simply obtain the hash with .extract_options!.

As for why they are not passed to the handlers directly... that i don't know.

obrie commented 11 years ago

@myitcv I actually don't think there's much wrong with what you proposed. I think it's okay to define the method like so:

class User < ActiveRecord::Base
  state_machine :state, :initial => :new do
    event :set_email do
      transition :new => :pending
    end
  end

  def set_email(email_to_set, *args)
    super(email_to_set, *args) 
  end
end

...in order to define required arguments.

@the8472's solution is also perfectly valid.

As to the reason why arguments are accessed via the transition object instead of passed directly into the block... the primary reason was because you will often define before_/after_transition callbacks that can apply to multiple events (and therefore may take different argument lists). I also had chosen that way to try to remain somewhat consistent with most ORMs with the event in question (e.g. save does not pass its arguments -- only the object being saved).

I'll admit I don't know if that was the correct design decision. I'm leaving #193 open to reconsider this when looking at a state_machine 2.0.0 or beyond release. This won't make it into the 1.x series.