Search code examples
ruby-on-railsrubyruby-on-rails-4ruby-on-rails-5state-machine

Rails using state machine in polymorphic


I have this polymorphic model.

class Note < ApplicationRecord

  belongs_to :notable, polymorphic: true, optional: false


  state_machine :is_status, initial: :pending do
    transition pending: :approved, on: %i[approve]
    transition pending: :cancelled, on: %i[cancel]
  end
end

and other two models

class Invoice < ApplicationRecord

  has_many :notes, as: :notable, dependent: :destroy
end

class Person < ApplicationRecord

  has_many :notes, as: :notable, dependent: :destroy
end

As you can see I'm attaching the note in two models its either person or invoice. and also using state machine. The scenario is I want to use the state machine in invoice only? is this possible. so. if my notable_type is "Invoice". I got my status is "pending" else if is Person I got status: nil


Solution

  • I recommend you create a new model that encapsulates the behavior of the state machine and then attach that model to the invoice.

    If the state machine is intended to represent the state of an Invoice then make it so InvoiceState belongs_to Invoice and Invoice has_one InvoiceState.

    On the other hand, if you want to use this state machine to represent the more general concept of completeness then name it something appropriately generic (TransactionState, etc.) and attach it via a polymorphic relationship, like Note.

    What I describe might look like this:

    class Note < ApplicationRecord
      belongs_to :notable, polymorphic: true, optional: false
    end
    
    class Person < ApplicationRecord    
      has_many :notes, as: :notable, dependent: :destroy
    end
    
    class Invoice < ApplicationRecord    
      has_many :notes, as: :notable, dependent: :destroy
      has_one :invoice_state, dependent: :destroy
    end
    
    class InvoiceState < ApplicationRecord
      belongs_to :invoice, optional: false
    
      state_machine :status, initial: :pending do
        transition pending: :approved, on: %i[approve]
        transition pending: :cancelled, on: %i[cancel]
      end
    end