Files
claude-engineering-plugin/plugins/compounding-engineering/skills/andrew-kane-gem-writer/references/rails-integration.md
Kieran Klaassen 8cc99ab483 feat(plugin): reorganize compounding-engineering v2.0.0
Major restructure of the compounding-engineering plugin:

## Agents (24 total, now categorized)
- review/ (10): architecture-strategist, code-simplicity-reviewer,
  data-integrity-guardian, dhh-rails-reviewer, kieran-rails-reviewer,
  kieran-python-reviewer, kieran-typescript-reviewer,
  pattern-recognition-specialist, performance-oracle, security-sentinel
- research/ (4): best-practices-researcher, framework-docs-researcher,
  git-history-analyzer, repo-research-analyst
- design/ (3): design-implementation-reviewer, design-iterator,
  figma-design-sync
- workflow/ (6): bug-reproduction-validator, every-style-editor,
  feedback-codifier, lint, pr-comment-resolver, spec-flow-analyzer
- docs/ (1): ankane-readme-writer

## Commands (15 total)
- Moved workflow commands to commands/workflows/ subdirectory
- Added: changelog, create-agent-skill, heal-skill, plan_review,
  prime, reproduce-bug, resolve_parallel, resolve_pr_parallel

## Skills (11 total)
- Added: andrew-kane-gem-writer, codify-docs, create-agent-skills,
  dhh-ruby-style, dspy-ruby, every-style-editor, file-todos,
  frontend-design, git-worktree, skill-creator
- Kept: gemini-imagegen

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-24 11:42:18 -08:00

3.7 KiB

Rails Integration Patterns

The Golden Rule

Never require Rails gems directly. This causes loading order issues.

# WRONG - causes premature loading
require "active_record"
ActiveRecord::Base.include(MyGem::Model)

# CORRECT - lazy loading
ActiveSupport.on_load(:active_record) do
  extend MyGem::Model
end

ActiveSupport.on_load Hooks

Common hooks and their uses:

# Models
ActiveSupport.on_load(:active_record) do
  extend GemName::Model        # Add class methods (searchkick, has_encrypted)
  include GemName::Callbacks   # Add instance methods
end

# Controllers
ActiveSupport.on_load(:action_controller) do
  include Ahoy::Controller
end

# Jobs
ActiveSupport.on_load(:active_job) do
  include GemName::JobExtensions
end

# Mailers
ActiveSupport.on_load(:action_mailer) do
  include GemName::MailerExtensions
end

Prepend for Behavior Modification

When overriding existing Rails methods:

ActiveSupport.on_load(:active_record) do
  ActiveRecord::Migration.prepend(StrongMigrations::Migration)
  ActiveRecord::Migrator.prepend(StrongMigrations::Migrator)
end

Railtie Pattern

Minimal Railtie for non-mountable gems:

# lib/gemname/railtie.rb
module GemName
  class Railtie < Rails::Railtie
    initializer "gemname.configure" do
      ActiveSupport.on_load(:active_record) do
        extend GemName::Model
      end
    end

    # Optional: Add to controller runtime logging
    initializer "gemname.log_runtime" do
      require_relative "controller_runtime"
      ActiveSupport.on_load(:action_controller) do
        include GemName::ControllerRuntime
      end
    end

    # Optional: Rake tasks
    rake_tasks do
      load "tasks/gemname.rake"
    end
  end
end

Engine Pattern (Mountable Gems)

For gems with web interfaces (PgHero, Blazer, Ahoy):

# lib/pghero/engine.rb
module PgHero
  class Engine < ::Rails::Engine
    isolate_namespace PgHero

    initializer "pghero.assets", group: :all do |app|
      if app.config.respond_to?(:assets) && defined?(Sprockets)
        app.config.assets.precompile << "pghero/application.js"
        app.config.assets.precompile << "pghero/application.css"
      end
    end

    initializer "pghero.config" do
      PgHero.config = Rails.application.config_for(:pghero) rescue {}
    end
  end
end

Routes for Engines

# config/routes.rb (in engine)
PgHero::Engine.routes.draw do
  root to: "home#index"
  resources :databases, only: [:show]
end

Mount in app:

# config/routes.rb (in app)
mount PgHero::Engine, at: "pghero"

YAML Configuration with ERB

For complex gems needing config files:

def self.settings
  @settings ||= begin
    path = Rails.root.join("config", "blazer.yml")
    if path.exist?
      YAML.safe_load(ERB.new(File.read(path)).result, aliases: true)
    else
      {}
    end
  end
end

Generator Pattern

# lib/generators/gemname/install_generator.rb
module GemName
  module Generators
    class InstallGenerator < Rails::Generators::Base
      source_root File.expand_path("templates", __dir__)

      def copy_initializer
        template "initializer.rb", "config/initializers/gemname.rb"
      end

      def copy_migration
        migration_template "migration.rb", "db/migrate/create_gemname_tables.rb"
      end
    end
  end
end

Conditional Feature Detection

# Check for specific Rails versions
if ActiveRecord.version >= Gem::Version.new("7.0")
  # Rails 7+ specific code
end

# Check for optional dependencies
def self.client
  @client ||= if defined?(OpenSearch::Client)
    OpenSearch::Client.new
  elsif defined?(Elasticsearch::Client)
    Elasticsearch::Client.new
  else
    raise Error, "Install elasticsearch or opensearch-ruby"
  end
end