Making ActiveRecord associations readonly based on state

My use-case:

* Trip has many destinations and travelers
* Trip states are: 
draft -> checked out -> paid
draft -> checked out -> failed (which should allow them to edit again)

* Trip workflow actually is: 
being edited -> for confirmation -> go to payment gateway (set to checked out) -> go back to success/fail
payment gateway sends payment details to system whether payment was success (paid) or failed (failed)

To disable editing on trip:

class Trip < ApplicationRecord
  def readonly?
    !draft?
  end

  def draft?
    !checked_out?
  end

  def checked_out?
    return false if checked_out_on_changed?
    # this is required, otherwise saving the relations will always fail
    # as draft will always return false even during the saving process
    
    !checked_out_on.blank?
  end
end

To disable changes on related records (destination/traveler):

class Destination < ApplicationRecord
  validate :trip_is_in_draft

protected
  def trip_is_in_draft
    errors.add(:trip, "should be in draft") if !trip.draft?
  end
end

Testing Email Deliveries using Cucumber/Capybara/Minitest


# features/support/env.rb
Around('@email') do |scenario, block|
  ActionMailer::Base.delivery_method = :test
  ActionMailer::Base.perform_deliveries = true
  ActionMailer::Base.deliveries.clear

  old_adapter = ActiveJob::Base.queue_adapter
  ActiveJob::Base.queue_adapter = :inline
  # ActiveJob::Base.queue_adapter.perform_enqueued_jobs = true
  block.call

  ActiveJob::Base.queue_adapter = old_adapter
end


# features/steps/*_step.rb

Then(/^I should receive an email$/) do
  # assuming mailcatcher
  assert_difference -> { ActionMailer::Base.deliveries.length } do
    # do stuff that is supposed to send an email
  end
end


# features/*.feature

@email
Scenario: Checkout page
  When I press the checkout button
  Then I should receive an email