2

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"
Al17
  • 431
  • 7
  • 20

5 Answers5

6

As mentioned by Srikanth, you'll want to send your request to your controller, however, instead of using a form outright, I'd recommend using a button_to as follows:

<%= button_to "Approve", admin_shops_approve_path(shop.id), method: :put %>
<%= button_to "Disapprove", admin_shops_disapprove_path(shop.id), method: :put %>

This will send a request to your controller. You already seem to be doing this, but to clarify, you'll want to use the following:

#config/routes.rb
namespace :admin do
   resources :shops do
      put :approve #-> domain.com/admin/shops/:id/approve
      put :disapprove #-> domain.com/admin/shops/:id/disapprove
   end
end

#app/controllers/admin/shops_controller.rb
class Admin::ShopsController < ApplicationController
   before_action, :set_shop

   def approve
      @shop.approve!
   end

   def disapprove
      @shop.disapprove!
   end

   private

   def set_shop
      @shop = Shop.find params[:id]
   end
end

This will give you an efficient way to send the required data to your form, triggering the AASM process as a result.

Richard Peck
  • 76,116
  • 9
  • 93
  • 147
2

Aasm works by adding states, transitions and events to the model.

You cannot access the model directly from the view. Instead, you can create respective actions in your controller that you can bind your buttons to.

Note that aasm events have logic, I.e. Which event can you transition to etc. You can do all those validations and provide an appropriate response message from your controller action.

Edit:

Here is an idea of how I would do it:

FooController.rb:

class FooController < ApplicationController
  def approve
    foo = Foo.find(params[:id])
    foo.approve!
  end

  def reject
    foo = Foo.find(params[:id])
    foo.reject!
  end
end

routes.rb:

get "foo/:id/approve" => "foo#approve"
get "foo/:id/reject" => "foo#reject"

Replace Foo with your model!

Srikanth Venugopalan
  • 9,011
  • 3
  • 36
  • 76
  • Thanks, Srikanth! I added action to the controller, and access it from the view. But now I have to solve this error: `param is missing or the value is empty: hotel`. Can you help me please? I've made an update. Thanks again! – Al17 Sep 17 '14 at 14:51
  • Could you post the controller code? I have put a sample here, you could refer that too. – Srikanth Venugopalan Sep 17 '14 at 15:13
  • Thanks! But sorry, same error. I updated.. And also, on button click the url is like http://localhost:3000/admin/shops/65 (no `approve` at the end). – Al17 Sep 17 '14 at 15:39
  • Please check Rich Peck's answer, it is more comprehensive than me typing on the phone :) – Srikanth Venugopalan Sep 17 '14 at 16:17
1

I had a same problem. I've resolved it this way:

Model

class Call < ActiveRecord::Base

  include AASM

  aasm_column :status

  aasm do
    state :start, :initial => true
    state :in_progress
    state :finished
    state :archived

    event :run do
     transitions :from => [:start, :finished, :archived], :to => :in_progress
    end

    event :stop do
      transitions :from => :in_progress, :to => :finished
    end

    event :resolve do
      transitions :from => [:in_progress, :finished], :to => :archived
    end
  end
end

Controller

class CallsController < ApplicationController
  before_action :set_call, only: [:run, :stop, :resolve]

  def run
    redirect_to calls_path if @call.run!
  end

  def stop
    redirect_to calls_path if @call.stop!
  end

  def resolve
    redirect_to calls_path if @call.resolve!
  end

  private

    def set_call
      @call = Call.find(params[:id])
    end
end

Routes routes.rb

resources :calls do
  member do
    put :run
    put :stop
    put :resolve
  end
end

Views calls/index.html.erb

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Status</th>
      <th>Start call</th>
      <th>Stop call</th>
      <th>Resolve call</th>
    </tr>
  </thead>

  <tbody>
    <% @calls.each do |call| %>
      <tr>
        <td><%= call.name %></td>
        <td><%= call.status %></td>
        <td><%= link_to "Start", run_call_path(call) , method: :put %></td>
        <td><%= link_to "Stop", stop_call_path(call), method: :put %></td>
        <td><%= link_to "Resolve", resolve_call_path(call), method: :put %></td>
      </tr>
    <% end %>
  </tbody>
</table>
Sanychcz
  • 11
  • 1
  • 2
0

My current project was required to create AASM selector depends on permission of CanCan and current events.

If somebody looking for this functionality you are welcome to use this gist: https://gist.github.com/alexey/446c6c4ce2a5af0f4fb6851e6d6d514e

Its created fast so please pardon for some ugly methods(will rewrite it soon to much better format)

You are also to welcome read this for complex integration

alexey_the_cat
  • 1,812
  • 19
  • 33
0

You can also add a callback in your model like

before_update { |order| order.log_status_change if order.aasm_state_changed? }

*filed_name_<_changed?>

Amit Agarwal
  • 396
  • 3
  • 12