Last Updated: February 25, 2016
·
2.081K
· javier_toledo

Avoid repeating yourself with state_machine

UPDATE: @errinlarsen noticed the previous code stopped working in recent versions of state_machine/rails. I've updated the post to use Rails Concerns.

Sometimes when you are using state_machine you find that there are models that share the same workflow, for instance, when many models need to be accepted by an administrator before being published, and to avoid repeating code you can do the following:

Add a concern in app/models/concerns/shared/acceptable.rb:

module Shared::Acceptable
  extend ActiveSupport::Concern

  included do
    state_machine :initial => :new do
      event :accept do
        transition :new => :published
      end

      event :reject do
        transition :new => :rejected
      end
    end
  end

end

Include the module in your models:

class Comment << ActiveRecord::Base
    include Shared::Acceptable

    ...
end

class Image << ActiveRecord::Base
    include Shared::Acceptable

    ...
end

You can use Concerns to easily share functionality, scopes, configuration, validators or relationship declarations among different models http://api.rubyonrails.org/classes/ActiveSupport/Concern.html

2 Responses
Add your response

I realize this post is old, but I found my way here with a Google search, so I imagine others might, too, and in that case, I thought I should share a fix to the code listed here.

Following the example, I was receiving a "NoMethodError: undefined method state_machine for Acceptable" exception. I modified the code as follows to fix the issue:

module Acceptable
  def self.included(base)
    base.state_machine :initial => :new do
      # ... code removed for brevity
    end
  end
end

I'm not sure if the original code worked when this was first posted, but StateMachine now defines the #state_machine method on Class, and so the listed Module (above; i.e. Acceptable) can't find it. However, you can call the #state_machine method on the base Class, which is passed as an argument to .included.

I hope this helps somebody!

over 1 year ago ·

@errinlarsen thanks for the fix! I'll update the post and mention your contribution.

The listed code worked when it was posted, but since then both state_machine gem and Rails have evolved a lot. Actually, if I had to do this again I'd rather use Rails Concerns instead of a bare module http://api.rubyonrails.org/classes/ActiveSupport/Concern.html

over 1 year ago ·