Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,5 @@ gem "activestorage"
gem "rake"
gem "rspec"
gem "rubocop-katalyst"
gem "solid_cable"
gem "solid_queue"
22 changes: 22 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ PATH
GEM
remote: https://rubygems.org/
specs:
actioncable (8.1.3)
actionpack (= 8.1.3)
activesupport (= 8.1.3)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
zeitwerk (~> 2.6)
actionpack (8.1.3)
actionview (= 8.1.3)
activesupport (= 8.1.3)
Expand Down Expand Up @@ -90,6 +96,7 @@ GEM
minitest (6.0.6)
drb (~> 2.0)
prism (~> 1.5)
nio4r (2.7.5)
nokogiri (1.19.4-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.19.4-aarch64-linux-musl)
Expand Down Expand Up @@ -218,6 +225,11 @@ GEM
rubocop-rspec (~> 3.5)
ruby-progressbar (1.13.0)
securerandom (0.4.1)
solid_cable (4.0.0)
actioncable (>= 7.2)
activejob (>= 7.2)
activerecord (>= 7.2)
railties (>= 7.2)
solid_queue (1.4.0)
activejob (>= 7.1)
activerecord (>= 7.1)
Expand All @@ -236,6 +248,10 @@ GEM
unicode-emoji (4.2.0)
uri (1.1.1)
useragent (0.16.11)
websocket-driver (0.8.1)
base64
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
zeitwerk (2.8.2)

PLATFORMS
Expand All @@ -254,9 +270,11 @@ DEPENDENCIES
rake
rspec
rubocop-katalyst
solid_cable
solid_queue

CHECKSUMS
actioncable (8.1.3) sha256=e5bc7f75e44e6a22de29c4f43176927c3a9ce4824464b74ed18d8226e75a80f0
actionpack (8.1.3) sha256=af998cae4d47c5d581a2cc363b5c77eb718b7c4b45748d81b1887b25621c29a3
actionview (8.1.3) sha256=1347c88c7f3edb38100c5ce0e9fb5e62d7755f3edc1b61cce2eb0b2c6ea2fd5d
activejob (8.1.3) sha256=a149b1766aa8204c3c3da7309e4becd40fcd5529c348cffbf6c9b16b565fe8d3
Expand Down Expand Up @@ -291,6 +309,7 @@ CHECKSUMS
loofah (2.25.1) sha256=d436c73dbd0c1147b16c4a41db097942d217303e1f7728704b37e4df9f6d2e04
marcel (1.2.1) sha256=1678e9360e32f9eafa917c80029e2f6d10b2715c66a4b87b6d0da9b9cd1f859f
minitest (6.0.6) sha256=153ea36d1d987a62942382b61075745042a2b3123b1cd48f4c3675af9cc7d6f1
nio4r (2.7.5) sha256=6c90168e48fb5f8e768419c93abb94ba2b892a1d0602cb06eef16d8b7df1dca1
nokogiri (1.19.4-aarch64-linux-gnu) sha256=1269fb644a6de405057a53dd5c762b1209b43ca7424f839454d3dbc677c31a8f
nokogiri (1.19.4-aarch64-linux-musl) sha256=35c65b9ce72b3bb03207bdbe7067915019dc18c1b9b59139684bd6690fdd01af
nokogiri (1.19.4-arm-linux-gnu) sha256=a301313e38bb065d68239e79734bcd6f56fb6efaacebde29e9abf2a4735340ca
Expand Down Expand Up @@ -336,6 +355,7 @@ CHECKSUMS
rubocop-rspec_rails (2.32.0) sha256=4a0d641c72f6ebb957534f539d9d0a62c47abd8ce0d0aeee1ef4701e892a9100
ruby-progressbar (1.13.0) sha256=80fc9c47a9b640d6834e0dc7b3c94c9df37f08cb072b7761e4a71e22cff29b33
securerandom (0.4.1) sha256=cc5193d414a4341b6e225f0cb4446aceca8e50d5e1888743fac16987638ea0b1
solid_cable (4.0.0) sha256=8379680ef6bf36e195eb876a6306ea290f87d5fa10bc4a757bc2a918f83229b5
solid_queue (1.4.0) sha256=e6a18d196f0b27cb6e3c77c5b31258b05fb634f8ed64fb1866ed164047216c2a
stringio (3.2.0) sha256=c37cb2e58b4ffbd33fe5cd948c05934af997b36e0b6ca6fdf43afa234cf222e1
thor (1.5.0) sha256=e3a9e55fe857e44859ce104a84675ab6e8cd59c650a49106a05f55f136425e73
Expand All @@ -346,6 +366,8 @@ CHECKSUMS
unicode-emoji (4.2.0) sha256=519e69150f75652e40bf736106cfbc8f0f73aa3fb6a65afe62fefa7f80b0f80f
uri (1.1.1) sha256=379fa58d27ffb1387eaada68c749d1426738bd0f654d812fcc07e7568f5c57c6
useragent (0.16.11) sha256=700e6413ad4bb954bb63547fa098dddf7b0ebe75b40cc6f93b8d54255b173844
websocket-driver (0.8.1) sha256=5ab238238ce230e5d4b262d2be39624c867914eab99171dc4952b58b577c2d96
websocket-extensions (0.1.5) sha256=1c6ba63092cda343eb53fc657110c71c754c56484aad42578495227d717a8241
zeitwerk (2.8.2) sha256=7212a61311083c604184b1ea2574b9aa05cd14f855a0841c06985cabe9181d12

BUNDLED WITH
Expand Down
16 changes: 16 additions & 0 deletions lib/generators/thermite/install/solid_cable/USAGE
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
Description:
Installs Solid Cable using a single, shared database (Katalyst's preferred setup).

Unlike `solid_cable:install`, this does not provision a separate cable
database. The solid_cable tables are added to the primary database via a
normal migration in db/migrate, and config/cable.yml uses the solid_cable
adapter without a `connects_to` configuration.

Requires the solid_cable gem to be present in the host application.

Example:
bin/rails thermite:install:solid_cable

This will:
Create config/cable.yml
Create a db/migrate migration for the solid_cable tables
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# frozen_string_literal: true

require "rails"
require "rails/generators"
require "rails/generators/active_record/migration"

require "active_record"
require "yaml"

# For a single-database install, solid cable's documented setup is to copy the contents
# of db/cable_schema.rb into a normal migration, delete db/cable_schema.rb,
# remove connects_to from config/cable.yml, then run db:migrate.
# Thermite follows that path by generating the migration directly.
#
# solid_cable is an optional dependency: it is required lazily in #verify_solid_cable!
# so the generator still loads (and lists) when solid_cable is absent.
module Thermite
module Install
class SolidCableGenerator < Rails::Generators::Base
# Provides migration_template, next_migration_number (timestamped) and the
# CreateMigration action that makes re-runs safe: identical content is
# skipped, a same-named migration with different content raises a conflict.
include ActiveRecord::Generators::Migration

# Reuse solid_cable's own templates. Resolved lazily so the class loads
# without solid_cable present; populated once #verify_solid_cable! requires it.
def self.source_root
::SolidCable::InstallGenerator.source_root if defined?(::SolidCable::InstallGenerator)
end

# Search our own templates (the migration) first, then solid_cable's
# (cable.yml).
def self.source_paths
[File.expand_path("templates", __dir__), source_root].compact
end

# Our source_root points into solid_cable, so the inherited USAGE lookup
# would resolve to solid_cable's USAGE. Point it at ours instead.
def self.usage_path
File.expand_path("USAGE", __dir__)
end

# Fail early with a helpful message when solid_cable isn't available.
def verify_solid_cable!
require "solid_cable/version"
require "generators/solid_cable/install/install_generator"
rescue LoadError
raise Thor::Error, <<~MSG.strip
thermite:install:solid_cable requires the solid_cable gem, which is not available.
Add it to your Gemfile and run `bundle install`:

gem "solid_cable"
MSG
end

# Based on SolidCable::InstallGenerator#copy_files but writing schema to a migration instead
# @see https://github.com/rails/solid_cable#single-database-configuration
def copy_files
template "config/cable.yml"
migration_template "add_solid_cable.rb", "db/migrate/add_solid_cable.rb", skip: true
end

private

# The application's extra environments, derived from config/environments/*.rb so
# we don't hard-code names like "staging" / "uat".
def extra_environments
environments
.reject { |pathname| local?(pathname) || production?(pathname) }
.map { |path| path.basename(".rb").to_s }
end

def environments
Pathname(destination_root).join("config/environments").glob("*.rb")
end

def local?(pathname)
%w[development test].include?(pathname.basename(".rb").to_s)
end

def production?(pathname)
pathname.basename(".rb").to_s == "production"
end

def solid_cable_schema_body
schema = File.read(File.join(self.class.source_root, "db/cable_schema.rb"))

schema
# Keep only the schema block body.
.sub(%r{\A.*ActiveRecord::Schema[^\n]+do\n}, "")
.sub(/\nend\n?\z/, "")
.lines
.map { |line| line == "\n" ? line : " #{line}" }
.join
end
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# frozen_string_literal: true

class <%= migration_class_name %> < ActiveRecord::Migration[<%= ActiveRecord::Migration.current_version %>]
def change
<%= solid_cable_schema_body.chomp %>
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
development:
adapter: async

test:
adapter: test

production: &production
adapter: solid_cable
polling_interval: 0.1.seconds
message_retention: 1.day

<% extra_environments.each do |environment| -%>

<%= environment %>:
<<: *production
<% end -%>
5 changes: 5 additions & 0 deletions lib/thermite/tasks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,10 @@
task :active_storage do
Rails::Command.invoke :generate, ["thermite:install:active_storage"]
end

desc "Install Solid Cable"
task :solid_cable do
Rails::Command.invoke :generate, ["thermite:install:solid_cable"]
end
end
end
31 changes: 31 additions & 0 deletions spec/generators/thermite/install/solid_cable_generator_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# frozen_string_literal: true

require "rails_helper"

require "generators/thermite/install/solid_cable/solid_cable_generator"

RSpec.describe Thermite::Install::SolidCableGenerator do
it "completes successfully" do
expect { run_generator }.not_to raise_error
end

it "writes cable config and migration" do
run_generator

assert_file "config/cable.yml"
assert_migration "db/migrate/add_solid_cable.rb"
end

it "includes non-standard environments like staging in the cable config" do
run_generator

assert_file "config/cable.yml", /^staging:/
end

it "raises a helpful error when solid_cable is unavailable" do
allow(generator).to receive(:require).and_raise(LoadError)

expect { generator.verify_solid_cable! }
.to raise_error(Thor::Error, /requires the solid_cable gem/)
end
end
1 change: 1 addition & 0 deletions spec/support/generator_helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

require "rails/generators/testing/behavior"
require "rails/generators/testing/assertions"
require "minitest"
require "minitest/assertions"
require "fileutils"

Expand Down