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
Written by Javier Toledo
Related protips
2 Responses
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!
@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