acts_as_state_machine with auto named_scope powers May 21st, 2008
Rails 2.1 (Soon to be released?) brings us integrated named_scope‘ing. I’ve already mentioned named_scope here and there, and you can find a complete writeup on Ryan Daigle’s Blog
While working on projects on Rails Edge, we used to have the same pattern over and over.
class Mail < ActiveRecord::Base include AASM aasm_state :created aasm_state :sending aasm_state :sent aasm_state :failed aasm_state :delayed named_scope :created, :conditions => {:status => 'created'} named_scope :sending, :conditions => {:status => 'sending'} named_scope :sent, :conditions => {:status => 'sent'} named_scope :failed, :conditions => {:status => 'failed'} named_scope :delayed, :conditions => {:status => 'delayed'} end
For each state we had, we created a named_scope, because it is so great and easy to be able to say Mail.failed.each .... However, this isn’t really DRY, and we can see a clear pattern here. When you think about it, you really should have a scope for every state you define. So I forked from AASM on github and started hacking away.
The result:
class Mail < ActiveRecord::Base include AASM aasm_state :created aasm_state :sending aasm_state :sent aasm_state :failed aasm_state :delayed end ... >> Mail.failed => [] >> Mail.sent => [#<Mail id: 1, email: "jan@domain.com", content: "hallo", :aasm_state: "sent", created_at: "2008-05-21 15:39:38", updated_at: "2008-05-21 15:39:38">, #<Mail id: 2, email: "jan@domain.com", status: "sent", created_at: "2008-05-21 15:39:39", updated_at: "2008-05-21 15:39:39">]
You can find my
This has all been implemented in the AASM Core now.named_scope enabled AASM on github. At the moment only the master branch has been patched with my functionality, I’ll do the no_aasm_prefix branch somewhere this evening / tomorrow. Let me know if you’re using it, and what could be better
For those interested in the code, this is the bit that does the magic.
module AASM::NamedScopeMethods def self.add_named_scope base # Don't add unless it's a class which understands `named_scope` return unless base.respond_to? :named_scope base.extend AASM::NamedScopeMethods::ClassMethods base.class_eval do class << self alias_method :aasm_state_without_named_scope, :aasm_state alias_method :aasm_state, :aasm_state_with_named_scope end end end module ClassMethods def aasm_state_with_named_scope name, options = {} aasm_state_without_named_scope name, options self.named_scope name, :conditions => {self.aasm_column => name.to_s} unless self.scopes.include?(name) end end end
Gotcha
While testing the plugin we found out 1 gotcha to this: do not define a aasm_state :new, because this will override the constructor of the class, and you’ll lose all creating-functionality.
Update
This is now default AASM behaviour as of this commit
l
You seem to know very well AASM so I give it a try: Is-it possible to add args to events methods and add additional code to process them ?
I don’t know if this is a good purpose but I needed this point on a project…
That is not possible in the current implementation. The bla(one) will be run in the Event class when the event is defined. Patches to add this behaviour are always welcome, ofcourse ;-)
Thanks for your reply, I am interested for this behaviour but the AASM complexity is too high for me actually :) (I don’t know very well block/yield coding concepts)