11

I use state_machine with ActiveRecord on one of my Rails 3.1 application. I found the syntax to access records with different states to be cumbersome. Is it possible to define each state to be the scope at the same time without writing scope definitions by hand?

Consider following example:

class User < ActiveRecord:Base
  state_machine :status, :initial => :foo do
    state :foo
    state :bar

    # ...
  end
end

# state_machine syntax:
User.with_status :foo
User.with_status :bar

# desired syntax:
User.foo
User.bar
Andrew
  • 8,330
  • 11
  • 45
  • 78

4 Answers4

18

I'm adding the following to my models:

state_machine.states.map do |state|
  scope state.name, :conditions => { :state => state.name.to_s }
end

Not sure if you count this as "writing scope definitions by hand?"

Lars Haugseth
  • 14,721
  • 2
  • 45
  • 49
8

Just in case, if somebody is still looking for this, there are following methods added while defining state_machine:

class Vehicle < ActiveRecord::Base
  named_scope :with_states, lambda {|*states| {:conditions => {:state => states}}}
  # with_states also aliased to with_state

  named_scope :without_states, lambda {|*states| {:conditions => ['state NOT IN (?)', states]}}
  # without_states also aliased to without_state
end

# to use this:
Vehicle.with_state(:parked)

I like to use this because there will never be conflict with state name. You can find more information on state_machine's ActiveRecord integration page.

Bonus is that it allows to pass array so I often do something like:

scope :cancelled, lambda { with_state([:cancelled_by_user, :cancelled_by_staff]) }
Lucas
  • 2,587
  • 1
  • 18
  • 17
3

I also needed this functionality, but state_machine has nothing similar. Although I've found this gist, but aasm seems like a better state machine alternative in this case.

Zsolt
  • 1,464
  • 13
  • 22
  • Thanks, this is useful gist indeed. I found that `state_machine` gem is better in my case, except this problem with scopes. – Andrew Mar 31 '12 at 15:47
0

I will show you a way which can be used if the model has multiple state_machines too.

It works even in the case when your states are integers.

def Yourmodel.generate_scopes_for_state_machines   state_machines.each_pair do |machine_name, that_machine|
    that_machine.states.map do |state|
      # puts "will create these scopes: #{machine_name}_#{state.name} state: #{state.value} "
      # puts "will create these scopes: #{machine_name}_#{state.name} state: #{state.name.to_s} "
      # Price.scope "#{machine_name}_#{state.name}", :conditions => { machine_name => state.name.to_s }
      Price.scope "#{machine_name}_#{state.name}", :conditions => { machine_name => state.value }
    end   end end

Yourmodel.generate_scopes_for_state_machines
Boti
  • 3,275
  • 1
  • 29
  • 54