PostgreSQL’s temporary tables
Let’s say you have a new ActiveRecord concern to add a new feature to your models. This concern extracts a database behavior. For simplicity, we are going to use a database query for tagging:
module TaggableConcern
  extend ActiveSupport::Concern
  included do
    scope :with_tag, ->(tag) {
      where('? = ANY(tags)', tag)
    }
    scope :with_tags, ->(tags) {
      where('tags @> ARRAY[?]::varchar[]', Array(tags))
    }
  end
endHow do you test concerns in isolation without including them in an application model?
PostgreSQL is packed full of wonderful features. One of those features is hidden in the CREATE TABLE synopsis:
CREATE TEMPORARY TABLE table_name (...)
ON COMMIT { PRESERVE ROWS | DELETE ROWS | DROP }Using this in Rails is pretty straightforward because create_table accepts :temporary as boolean argument to mark the table as ephemeral and  options parameter to add extra options for the table definition, like when to remove it:
create_table :my_table, temporary: true, options: 'ON COMMIT DROP' do |t|
  ...
endHere is an example using this:
RSpec.describe TaggableConcern, type: :model do
  before do
    class TempBook < ApplicationRecord
      include TaggableConcern
      connection.create_table :temp_books, temporary: true, options: 'ON COMMIT DROP' do |t|
        t.string :name
        t.string :tags, array: true
        t.timestamps
      end
    end
  end
  let!(:ruby) do
    TempBook.create!(
      name: 'Programming Rails',
      tags: %w[ruby rails web]
    )
  end
  let!(:elixir) do
    TempBook.create!(
      name: 'Programming Phoenix',
      tags: %w[elixir phoenix web]
    )
  end
  let!(:go) do
    TempBook.create!(
      name: 'Programming Go',
      tags: %w[go programming]
    )
  end
  describe '.with_tag' do
    subject { TempBook.with_tag(query) }
    context 'with nil value' do
      let(:query) { nil }
      it { is_expected.to be_empty }
    end
    context 'with one match' do
      let(:query) { 'ruby' }
      it { is_expected.to match_array([ruby]) }
    end
    context 'with multiple matches' do
      let(:query) { 'web' }
      it { is_expected.to match_array([ruby, elixir]) }
    end
  end
  describe '.with_tags' do
    subject { TempBook.with_tags(query) }
    context 'with nil value' do
      let(:query) { nil }
      it { is_expected.to be_empty }
    end
    context 'with empty array' do
      let(:query) { [] }
      it { is_expected.to be_empty }
    end
    context 'with a common tag' do
      let(:query) { 'web' }
      it { is_expected.to match_array([ruby, elixir]) }
    end
    context 'with a common tag as array' do
      let(:query) { ['web'] }
      it { is_expected.to match_array([ruby, elixir]) }
    end
    context 'with mixed tags' do
      let(:query) { ['ruby', 'elixir'] }
      it { is_expected.to be_empty }
    end
    context 'with mixed tags' do
      let(:query) { ['ruby', 'web'] }
      it { is_expected.to match_array([ruby]) }
    end
  end
endFinished in 0.1783 seconds (files took 0.59146 seconds to load)
9 examples, 0 failures