rubyruby-on-rails-7hotwire-railsaasmturbo

Why is my AASM state machine not triggered with Rails 7 Turbo patch links?


I just updated my Rails 6 app to Rails 7 and have problems updating my :patch and :delete links to Turbo.

For example, in one of my views I have this link...

 link_to("Mark as sent", status_url(quote), :data => {:'turbo_method' => :patch})

... which is handled by this controller:

class StatusController < ApplicationController

  def update
    @quote = Quote.find(params[:id])
    @quote.send_it! # Should trigger AASM
    flash[:notice] = "Quote marked as sent."
    redirect_to edit_quote_path(@quote)
  end

end

In the model I am using AASM as a state machine:

class Quote < ApplicationRecord

  include AASM
     
  aasm :column => "status" do
    state :draft, :initial => true
    state :inquired
    state :sent
    state :downloaded
    state :accepted
    state :rejected

    event :send_it do
      transitions :from => [:draft, :inquired], :to => :sent
    end

    ...

    event :reset_it do
      transitions :from => [:inquired, :sent, :downloaded, :accepted, :rejected], :to => :draft
    end

  end

end

The problem is that the state machine does not get triggered when I hit the link. The flash message and the redirect work but the state is not changed in the database. When I replace @quote.send_it! with @quote.update_column(:status, "sent")it works, however.

Can anybody tell me what I'm missing here?


Solution

  • I don't quite see how turbo is related. Except that I think your redirect isn't actually working:

    Redirected to http://127.0.0.1:3000/quotes/1/edit
    Completed 302 Found in 18ms (ActiveRecord: 4.3ms | Allocations: 7265)
    
    Started PATCH "/quotes/1/edit" for 127.0.0.1 at 2022-08-12 
     
    ActionController::RoutingError (No route matches [PATCH] "/quotes/1/edit"):
    # NOTE: ^ not quite a redirect
    
    #       v but it doesn't show on a page, it just refreshes the current one.
    Started GET "/quotes" for 127.0.0.1 at 2022-08-12 17:51:28 -0400
    
    #       and if the current page were /quotes/1/edit then it would look like
    #       redirect worked, but I was submitting from /quotes.
    

    Update your controller to actually show any errors:

    def update
      @quote = Quote.find(params[:id])
    
      # NOTE: if transition fails, `send_it!` returns `false` 
      #       (or raises an error for invalid transitions)
      #       when you run `@quote.update_column(:status, "sent")`
      #       validations and state machine are not triggered and it works.
      if @quote.send_it!
        flash.notice = "Quote marked as sent."
      else
        flash.notice = @quote.errors.full_messages.join(", ")
      end
    
      respond_to do |format|
        # in case you want add a stream response later
        # format.turbo_stream { # TODO }
        format.html { redirect_to edit_quote_path(@quote), status: :see_other }
        # NOTE: Redirect as a GET request instead of PATCH ^
      end
    end
    

    Or just add whiny_persistence flag and check the logs, this will raise validation errors:

    aasm column: :status, whiny_persistence: true do