Starter Step

Passing parameters to acts_as_state_machine events

Posted on: January 19, 2010

This morning I ran into a small roadblock working with the acts_as_state_machine gem. My code started like this:

class Communication < ActiveRecord::Base
  include AASM
  aasm_column :status

  aasm_state :draft
  aasm_state :pending
  aasm_state :approved
  aasm_state :rejected

  aasm_event :submit do
    transitions :to => :pending, :from => :draft
  end

  aasm_event :approve do
    transitions :to => :approved, :from => [:pending, :rejected]
  end

  aasm_event :reject do
    transitions :to => :rejected, :from => [:pending, :approved], :guard => :validate_rejection_reason
  end

  aasm_initial_state :draft

  def validate_rejection_reason
    if self.rejection_reason
      true
    else
      raise AASM::InvalidTransition.new('A rejection reason is required')
    end
  end
end

This is a basic example using AASM to provide an approval process for my Communication model. I wanted the ability to just call instance.reject(“some reason”) or instance.reject!(“some reason”). I was really thinking to hard about it. The assm_event just creates some methods for me….so why not just decorate those methods using ruby aliases. This is what I ended up with:

  include AASM
  aasm_column :status

  aasm_state :draft
  aasm_state :pending
  aasm_state :approved
  aasm_state :rejected

  aasm_event :submit do
    transitions :to => :pending, :from => :draft
  end

  aasm_event :approve do
    transitions :to => :approved, :from => [:pending, :rejected]
  end

  aasm_event :reject do
    transitions :to => :rejected, :from => [:pending, :approved], :guard => :validate_rejection_reason
  end
  alias old_reject reject
  alias old_reject! reject!

  aasm_initial_state :draft

  def reject(reason)
    self.rejection_reason = reason
    self.old_reject
  end

  def reject!(reason)
    self.rejection_reason = reason
    self.old_reject!
  end

  def validate_rejection_reason
    if self.rejection_reason
      true
    else
      raise AASM::InvalidTransition.new('A rejection reason is required')
    end
  end

Notice the use of alias for reject and reject!. Now you have to call reject with a rejection reason. There may be another way to get parameters in to the event call….but this is how I rolled mine 🙂

Advertisements

5 Responses to "Passing parameters to acts_as_state_machine events"

Very useful, thank you!

Good to know… you could alternatively use alias_method_chain for better readability outside of the model:

# play nice
def reject_with_reason(reason)
if reason.present?
self.rejection_reason = reason
reject_without_reason
else
errors[:rejection_reason] < e
::Logger.warn e.message
end

When you don’t:

@thing = Thing.find(1)
@thing.reject!

Wow, it stripped half my comment 😦 Not nice WordPress.

Maybe this will work…


# play nice
def reject_with_reason(reason)
if reason.present?
self.rejection_reason = reason
reject_without_reason
else
errors[:rejection_reason] << "is required."
false
end
end

# be nasty
def reject_with_reason!(reason)
if reason.present?
self.rejection_reason = reason
reject_without_reason!
else
raise AASM::InvalidTransition.new('A rejection reason is required')
end
end

alias_method_chain :reject, :reason
alias_method_chain :reject!, :reason

Then, when rejecting, you’re able to call reject_with_reason(!) and you still have the ability to bypass a reason if desired. Obviously in your scenario you’re forcing validation, but I’m just pointing out what you could do if passing a reason were optional.

When you want a reason:


@thing = Thing.find(1)
if @thing.reject_with_reason("It was really bad.")
puts "Yay, you did it!"
else
puts @thing.errors
end

@thing = Thing.find(1)
begin
@thing.reject_with_reason!("Fee fi fo fum.")
rescue => e
::Logger.warn e.message
end

When you don’t:


@thing = Thing.find(1)
@thing.reject!

Makes sense Jen. For this case I always wanted a rejection reason, so my solution to alias seemed pretty light.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s


  • Dave: I can tell you're a ruby guy because you forgot the 'return' keyword. Thanks for the tip though!
  • Chandrashekhar H M: Hi, Thanks its working fine in iOS 6 but not in iOS 7.0. Any Suggestion on this.
  • Coeur: To change a rootViewController, without all this TVNavigationController : myNewRoot = [[UIViewController alloc] init]; myNavigationController.view
%d bloggers like this: