EzDevInfo.com

aasm

AASM - State machines for Ruby classes (plain Ruby, ActiveRecord, Mongoid, MongoMapper)

Validating a finite state machine (using AASM) on Rails

I'm using AASM by Rubyist to build a 4-step wizard for an AR object. According to the state of the object, there are different validations that need to be done. What is the smartest way to validate an object according to it's state on a certain transition?


Source: (StackOverflow)

Ruby add dynamic events using AASM

I've got a class in a program which is handling game states. I'm actually handling it with AASM so to create an event I have to use something like aasm_event :name ... inside the class.

I need to be able to load other files who dynamically have to add events and states to the class.

How is it possible ?

Thank you in advance.


Source: (StackOverflow)

Advertisements

AASM is not working with my rails 3 and ruby 1.8.7 ( undefined method `name' for nil:NilClass )

I'm using Rails 3.2.2, with aasm gem, I have Document model this way:

class Document < ActiveRecord::Base
  include AASM

  aasm do  
    state :unread, :initial => true
    state :read
    state :closed

    event :view do
      transitions :to => :read, :from => [:unread]
    end

    event :close do
      transitions :to => :closed, :from => [:read, :unread]
    end
  end

now on my console:

➜  ✗ bundle exec rails c
Loading development environment (Rails 3.2.2)
irb(main):006:0> Document.create!(:title => 'test')
   (0.2ms)  BEGIN
  SQL (0.3ms)  INSERT INTO `documents` (`aasm_state`, `checklist_id`, `created_at`, `description`, `dir`, `planned_date`, `procedure_id`, `section`, `subsection`, `title`, `updated_at`) VALUES (0, NULL, '2012-06-16 20:03:18', NULL, NULL, NULL, NULL, NULL, NULL, 'test', '2012-06-16 20:03:18')
   (0.4ms)  COMMIT
=> #<Document id: 28, title: "test", description: nil, dir: nil, section: nil, subsection: nil, planned_date: nil, procedure_id: nil, checklist_id: nil, created_at: "2012-06-16 20:03:18", updated_at: "2012-06-16 20:03:18", aasm_state: 0>
irb(main):007:0> doc = Document.last
  Document Load (0.3ms)  SELECT `documents`.* FROM `documents` ORDER BY `documents`.`id` DESC LIMIT 1
=> #<Document id: 28, title: "test", description: nil, dir: nil, section: nil, subsection: nil, planned_date: nil, procedure_id: nil, checklist_id: nil, created_at: "2012-06-16 20:03:18", updated_at: "2012-06-16 20:03:18", aasm_state: 0>
irb(main):008:0> doc.view!
NoMethodError: undefined method `name' for nil:NilClass
    from /Library/Ruby/Gems/1.8/gems/aasm-3.0.6/lib/aasm/supporting_classes/state.rb:15:in `=='
    from /Library/Ruby/Gems/1.8/gems/aasm-3.0.6/lib/aasm/aasm.rb:143:in `aasm_state_object_for_state'
    from (irb):8:in `find'
    from /Library/Ruby/Gems/1.8/gems/aasm-3.0.6/lib/aasm/aasm.rb:143:in `each'
    from /Library/Ruby/Gems/1.8/gems/aasm-3.0.6/lib/aasm/aasm.rb:143:in `find'
    from /Library/Ruby/Gems/1.8/gems/aasm-3.0.6/lib/aasm/aasm.rb:143:in `aasm_state_object_for_state'
    from /Library/Ruby/Gems/1.8/gems/aasm-3.0.6/lib/aasm/aasm.rb:158:in `aasm_fire_event'
    from /Library/Ruby/Gems/1.8/gems/aasm-3.0.6/lib/aasm/base.rb:48:in `view!'
    from (irb):8    

As you can see I keep getting

undefined method `name' for nil:NilClass

I'm using Ruby 1.8.7.


Source: (StackOverflow)

Rails error handling with AASM state machine

I'm using the rubyist-aasm state machine for handling the different states in my Event object (event initialized, event discussed, event published, etc.). I added guards to prevent state changes when certain conditions aren't met.

This all works fine but it doesn't show any errors when a state change was rejected by the guard. Any idea how I can see the state didn't change? I could check the states manually but it sounds like an ugly solution.

aasm_state :firststate 
aasm_state :secondstate  

aasm_event :approve do
  transitions :to => :secondstate, :from => [:firststate], :guard => :has_a_price? 
end

def has_a_price?   
  self.price.present?
end

Source: (StackOverflow)

How to set after callback for specific transition only in aasm?

I have 2 events:

event :event1, after: :event2! do
  transitions to: :state2, from: :state1, guard: proc {some func}
  transitions to: :state3, from: :state1
end    

event :event2 do
  transitions to: :state3, from: state2, guard: proc {some func}
end

How can I set after callback for first transition only in event1? (I cann't replace second transition to other event)

I tried

event :event1 do
  transitions to: :state2, from: :state1, after: :event2!, guard: proc {some func}
  transitions to: :state3, from: :state1
end 

But it not working


Source: (StackOverflow)

What is the best way to halt a transition with AASM

When a method being called in the success or enter phases of a state transition throw errors, what is the best way to catch this and ensure that the state reverts back to the previous state.

I'm using the AASM gem.


Source: (StackOverflow)

How to add a default AASM state to existing model

I have an existing model in rails and I want to add AASM states to it.

From my understanding, I should add a state column to my database through migrations first and then add some states to my rails model. How do I set a default state value according to a value in another column?

Am I on the right track at all?


Source: (StackOverflow)

Background Video Processing with Rails

I am trying to get uploaded videos to be converted in the background, running windows. Some of what I am using:

gem 'paperclip'
gem 'delayed_job_active_record'
gem 'ffmpeg'

I have edited the registry to allow the ffmpeg command to be ran from anywhere, I get a popup that I assume is ffmpeg because it goes away too quickly, guess the command is wrong so if anyone knows what's wrong with it please let me know. But the real problem is that it just hangs there, it says:

[2012-12-09 22:47:03] ERROR invalid body size.
[2012-12-09 22:47:03] ERROR Errno::ECONNABORTED: An established connection was a
borted by the software in your host machine.
        C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/webrick/httpresponse.rb:396:i
n `write'
        C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/webrick/httpresponse.rb:396:i
n `<<'
        C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/webrick/httpresponse.rb:396:i
n `_write_data'
        C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/webrick/httpresponse.rb:368:i
n `send_body_string'
        C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/webrick/httpresponse.rb:249:i
n `send_body'
        C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/webrick/httpresponse.rb:152:i
n `send_response'
        C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/webrick/httpserver.rb:110:in
`run'
        C:/RailsInstaller/Ruby1.9.3/lib/ruby/1.9.1/webrick/server.rb:191:in `blo
ck in start_thread'

Does anyone know how to properly get this working? I've went through a few tutorials that have bits and pieces of what I need but I can't get them working together. Here's what I have so far, lemme know if you need more:

Model:

class Video < ActiveRecord::Base

  belongs_to :user
  has_many :comments, dependent: :destroy
  attr_accessible :video, :user_id, :video_file_name, :title, :public, :description, :views

  has_attached_file :video, url: "/users/:user_id/videos/:id/:basename_:style.:extension"

  #process_in_background :video #causes death

  validates :video, presence: true
  validates :description, presence: true, length: { minimum: 5, maximum: 100}
  validates :title, presence: true, length: { minimum: 1, maximum: 15 }

  validates_attachment_size :video, less_than: 1.gigabytes
  validates_attachment :video, presence: true

  default_scope order: 'created_at DESC'

  Paperclip.interpolates :user_id do |attachment, style|attachment.instance.user_id
  end

  #before_post_process do |video|
   # false if video.status == "converting"
  #end

  def perform
    command = <<-end_command
      start ffmpeg -i #{ '/public/users/:user_id/videos/:id/:basename_:style.:extension' }  -ar 22050 -ab 32 -s 1280x720 -vcodec webm -r 25 -qscale 8 -f webm -y #{ '/public/users/:user_id/videos/:id/:basename_.webm' }

    end_command
    success = system(command)
    logger.debug 'Converting File: ' + success.to_s
    if success && $?.exitstatus.to_i == 0
      #self.converted!
      self.status = "converted"
    else
      #self.failure!
      self.status = "failed"
    end
  end

  handle_asynchronously :perform

  def self.search(search)
    if search
      find(:all, conditions: ["public = 't' AND title LIKE ?", "%#{search}%"], order: "created_at DESC")
    else
      find(:all, conditions: ["public = 't'"], order: "created_at DESC")
    end
  end

  def self.admin_search(search)
    if search
      find(:all, conditions: ['title LIKE ?', "%#{search}%"], order: "created_at DESC")
    else
      find(:all, order: "created_at DESC")
    end
  end

  private

    # This updates the stored filename with the new flash video file
    def set_new_filename
      #update_attribute(:filename, "#{filename}.#{id}.webm")
      update_attribute(:content_type, "video/x-webm")
    end

end

Controller:

class VideosController < ApplicationController
    before_filter :signed_in_user, only: [:upload, :update, :destroy]
    before_filter :admin_user, only: :admin_index

    def upload
        @video = Video.new
        # generate a unique id for the upload
        @uuid = (0..29).to_a.map {|x| rand(10)}
    end

    def create
        @video = Video.new(params[:video])
        @video.user_id = current_user.id

        if @video.save
            @video.delay.perform
            flash[:success] = "Uploaded Succefully!"
            redirect_to @video.user
            Delayed::Worker.new.start
        else
            render 'upload'
        end
    end

    def show
        @video = Video.find(params[:id])
        @comments = @video.comments.paginate(page: params[:page], per_page: 6)
        if !@video.public
            if !signed_in? || current_user.id != @video.user_id  && !current_user.admin && !current_user.approved?(@video.user)
            flash[:notice] = "Video is private"
            redirect_to root_path
        end
    end
    end

    def update
        @video = Video.find(params[:id])
        if @video.update_attributes(params[:video])
      flash[:success] = "Video preferences saved"
    else
        flash[:fail] = "Failed to update video preferences"
    end
    redirect_to :back
  end

    def destroy
        @video = Video.find(params[:id])
        @video.destroy
        flash[:deleted] = "Deleted Succefully!"
        redirect_to :back
    end

    def index
        @videos = Video.paginate(page: params[:page], per_page: 6).search(params[:search])
    end

    def admin_index
        @videos = Video.paginate(page: params[:page], per_page: 6).admin_search(params[:search])
    end

    def ajax_video_comments
        @video = Video.find(params[:id])
        @comments = @video.comments.paginate(page: params[:page], per_page: 6)

        respond_to do |format|
        format.js   { render partial: 'shared/comments', content_type: 'text/html' }
    end
    end

    def ajax_video_watched
        @video = Video.find(params[:id])
        @video.views += 1
        @video.save
    end

    private

    def signed_in_user
        redirect_to root_path, notice: "Please Login." unless signed_in?
    end

    def admin_user
        redirect_to(root_path) unless current_user.admin?
    end

end

Source: (StackOverflow)

How do I avoid duplication of code using AASM?

So I have in my project multiple models where many of them have an activate and deactivate function, which I manage using AASM

aasm column: 'status' do
  state :active, :initial => true
  state :inactive

  event :deactivate do
    transitions :from => :active, :to => :inactive
  end

  event :activate do
    transitions :from => :inactive, :to => :active
  end
end

I would like to avoid having duplication of this code, it is in 4 different models and I will probably not add any more states to them.

Thanks in advance


Source: (StackOverflow)

Getting list of states/events from a model that AASM

I successfully integrated the most recent AASM gem into an application, using it for the creation of a wizard. In my case I have a model order

class Order < ActiveRecord::Base

  belongs_to :user
  has_one :billing_plan, :dependent => :destroy
  named_scope :with_user, ..... <snip>

  include AASM

  aasm_column :aasm_state
  aasm_initial_state :unauthenticated_user

  aasm_state :unauthenticated_user, :after_exit => [:set_state_completed]
  aasm_state : <snip>

  <and following the event definitions>

end

Now I would like to give an administrator the possibility to create his own graphs through the AASM states. Therefore I created two additional models called OrderFlow and Transition where there order_flow has many transitions and order belongs_to order_flow.

No problem so far. Now I would like to give my admin the possibility to dynamically add existing transitions / events to an order_flow graph.

The problem now is, that I do not find any possibility to get a list of all events / transitions out of my order model. aasm_states_for_select seems to be the correct candidate, but I cannot call it on my order model.

Can anyone help?

Thx in advance. J.


Source: (StackOverflow)

How do I implement aasm in Rails 3 for what I want it to do?

I am a Rails n00b and have been advised that in order for me to keep track of the status of my user's accounts (i.e. paid, unpaid (and therefore disabled), free trial, etc.) I should use an 'AASM' gem.

So I found one that seems to be the most popular: https://github.com/rubyist/aasm But the instructions are pretty vague.

I have a Users model and a Plan model. User's model manages everything you might expect (username, password, first name, etc.). Plan model manages the subscription plan that users should be assigned to (with the restrictions).

So I am trying to figure out how to use the AASM gem to do what I want to do, but no clue where to start.

Do I create a new model ? Then do I setup a relationship between my User model and the model for AASM ? How do I setup a relationship? As in, a user 'has_many' states ? That doesn't seem to make much sense to me.

Any guidance would be really appreciated.

Thanks.

Edit: If anyone else is confused by AASMs like myself, here is a nice explanation of their function in Rails by the fine folks at Envy Labs: http://blog.envylabs.com/2009/08/the-rails-state-machine/

Edit2: How does this look:

include AASM


  aasm_column :current_state

  aasm_state :paid
  aasm_state :free_trial
  aasm_state :disabled #this is for accounts that have exceed free trial and have not paid
  #aasm_state :free_acct

  aasm_event :pay do
    transitions :to => :paid, :from => [:free_trial, :disabled]
    transitions :to => :disabled, :from => [:free_trial, :paid]
  end

Source: (StackOverflow)

Register callback for all transitions in AASM?

There are 2 methods I want to call after every state transition. Right now I'm doing:

  aasm_event :nominate_for_publishing, :before => [:set_state_last_updated_by, :set_state_updated_at] do
    transitions :to => :under_review, :from => [:work_in_progress]
  end

  aasm_event :publish, :before => [:set_state_last_updated_by, :set_state_updated_at] do
    transitions :to => :published, :from => [:work_in_progress, :under_review], :guard => :is_publishable?
  end

  aasm_event :unpublish, :before => [:set_state_last_updated_by, :set_state_updated_at] do
    transitions :to => :work_in_progress, :from => [:published, :under_review]
  end

Obviously this isn't the best approach. I'm duplicating code, and more fundamentally, I'm associating callbacks with specific transitions when they really apply to the state machine as a whole. What's a better way to handle this?


Source: (StackOverflow)

Ruby on rails AASM Change transition on button click

My question is very simple: how can I change aasm transitions on button click? What should I put in my view?

I have two buttons: Approve and Reject. My states look like this:

  aasm :column => 'state' do
    state :pending, :initial => true
    state :approved
    state :rejected

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

    event :reject do
      transitions :to => :rejected, :from => [:pending]
    end
  end

UPDATE: My params are these:

{"utf8"=>"✓", "_method"=>"put", "authenticity_token"...", "commit"=>"APP", "id"=>"65"}.

And this is how I access action from the view:

= form_for([:admin, shop], method: :put, data: { confirm: "You sure?" }) do |f|
  = f.submit "Approve", :controller => :shops, :action => :approve

My controller code:

def approve
  @shop = Shop.find(params[:id])
  @shop.approve!
end

Routes:

namespace :admin do
  get "shops/:id/approve" => "shops#approve"

Source: (StackOverflow)

How to call ActionMailer method in aasm callback?

Im learning ruby on rails and have a trouble with aasm callbacks and actionmailer. I have a hotels model. Heres a code:

class Hotel < ActiveRecord::Base
  include AASM

  scope :approved_hotels, -> { where(aasm_state: "approved") }

  has_many :comments
    belongs_to :user, :counter_cache => true
    has_many :ratings
  belongs_to :address

  aasm do
    state :pending, initial: true
    state :approved
    state :rejected

    event :approve, :after => :send_email do
      transitions from: :pending, to: :approved 
    end
    event :reject, :after => :send_email do
      transitions from: :pending, to: :rejected
    end
  end

  def send_email

  end
end

As you see user has to get email when state of the hotel he added was changed. Heres what i wrote but its not THE solution cos user gets emails every time admin updates hotel with "pending" state.

class HotelsController < ApplicationController
  before_filter :authenticate_user!, except: [:index, :show, :top5hotels]

  def update
    @hotel = Hotel.find(params[:id])

    if @hotel.aasm_state == "pending"
      @hotel.aasm_state = params[:state]
      UserMailer.changed_state_email(current_user, @hotel.name, 
      @hotel.aasm_state).deliver
    end

    if @hotel.update_attributes!(params[:hotel])
      redirect_to admin_hotel_path(@hotel), notice: "Hotel was successfully updated."
    else
      render "edit"
    end
  end
end

So i think i need to use callback but i dont know how to call

UserMailer.changed_state_email(current_user, @hotel.name, 
        @hotel.aasm_state).deliver

from the model. I tried

UserMailer.changed_state_email(User.find(:id), Hotel.find(:name), 
        Hotel.find(aasm_state)).deliver

but that doesnt work. Im really out of options and looking for any help. Thanks!


Source: (StackOverflow)

Reuse scope in query with has_many relationship to same STI table

Child1 and Child2 have an STI relationship with Entity, and Child2 has_many Child1. Child1 has a status column managed by AASM.

class Entity < ActiveRecord::Base
end

class Child1 < Entity
  include AASM
  aasm_column 'status' do
    state :owned #also creates scope Child1.owned
    state :sold
  end

  belongs_to :child2
end

class Child2 < Entity
  has_many :child1s
end

I would like to create a scope on Child2 for each state on Child1. It should return all Child2 records that have one or more Child1 records that are in that state. Ideally it would reuse the scopes that AASM creates automatically e.g.

scope :owned, -> {joins(:child1s).merge(Child1.owned)} #in Child2

...which is nice and clean and DRY. Unfortunately the SQL this generates is confused by the join on the same table:

irb(main):001:0> Child2.owned
  Child2 Load (35.5ms)  SELECT "entities".* FROM "entities"
INNER JOIN "entities" "child1_entities" ON "child1_entities"."child2_id" = "entities"."id" AND "child1_entities"."type" IN ('Child1')
WHERE "entities"."type" IN ('Child2') AND "entities"."status" = 'owned'

The last part of the where clause should be child1_entities.status = 'owned'.

I could write the entire query in SQL or Arel but I'm hoping to find something that even if I have to go there to specify an alias for the child1 join, I can still reuse the scopes I already have in Child1.


Source: (StackOverflow)