Files
claude-engineering-plugin/plugins/compounding-engineering/skills/andrew-kane-gem-writer/references/database-adapters.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

4.2 KiB

Database Adapter Patterns

Abstract Base Class Pattern

# lib/strong_migrations/adapters/abstract_adapter.rb
module StrongMigrations
  module Adapters
    class AbstractAdapter
      def initialize(checker)
        @checker = checker
      end

      def min_version
        nil
      end

      def set_statement_timeout(timeout)
        # no-op by default
      end

      def check_lock_timeout
        # no-op by default
      end

      private

      def connection
        @checker.send(:connection)
      end

      def quote(value)
        connection.quote(value)
      end
    end
  end
end

PostgreSQL Adapter

# lib/strong_migrations/adapters/postgresql_adapter.rb
module StrongMigrations
  module Adapters
    class PostgreSQLAdapter < AbstractAdapter
      def min_version
        "12"
      end

      def set_statement_timeout(timeout)
        select_all("SET statement_timeout = #{timeout.to_i * 1000}")
      end

      def set_lock_timeout(timeout)
        select_all("SET lock_timeout = #{timeout.to_i * 1000}")
      end

      def check_lock_timeout
        lock_timeout = connection.select_value("SHOW lock_timeout")
        lock_timeout_sec = timeout_to_sec(lock_timeout)
        # validation logic
      end

      private

      def select_all(sql)
        connection.select_all(sql)
      end

      def timeout_to_sec(timeout)
        units = {"us" => 1e-6, "ms" => 1e-3, "s" => 1, "min" => 60}
        timeout.to_f * (units[timeout.gsub(/\d+/, "")] || 1e-3)
      end
    end
  end
end

MySQL Adapter

# lib/strong_migrations/adapters/mysql_adapter.rb
module StrongMigrations
  module Adapters
    class MySQLAdapter < AbstractAdapter
      def min_version
        "8.0"
      end

      def set_statement_timeout(timeout)
        select_all("SET max_execution_time = #{timeout.to_i * 1000}")
      end

      def check_lock_timeout
        lock_timeout = connection.select_value("SELECT @@lock_wait_timeout")
        # validation logic
      end
    end
  end
end

MariaDB Adapter (MySQL variant)

# lib/strong_migrations/adapters/mariadb_adapter.rb
module StrongMigrations
  module Adapters
    class MariaDBAdapter < MySQLAdapter
      def min_version
        "10.5"
      end

      # Override MySQL-specific behavior
      def set_statement_timeout(timeout)
        select_all("SET max_statement_time = #{timeout.to_i}")
      end
    end
  end
end

Adapter Detection Pattern

Use regex matching on adapter name:

def adapter
  @adapter ||= case connection.adapter_name
    when /postg/i
      Adapters::PostgreSQLAdapter.new(self)
    when /mysql|trilogy/i
      if connection.try(:mariadb?)
        Adapters::MariaDBAdapter.new(self)
      else
        Adapters::MySQLAdapter.new(self)
      end
    when /sqlite/i
      Adapters::SQLiteAdapter.new(self)
    else
      Adapters::AbstractAdapter.new(self)
    end
end

Multi-Database Support (PgHero pattern)

module PgHero
  class << self
    attr_accessor :databases
  end

  self.databases = {}

  def self.primary_database
    databases.values.first
  end

  def self.capture_query_stats(database: nil)
    db = database ? databases[database] : primary_database
    db.capture_query_stats
  end

  class Database
    attr_reader :id, :config

    def initialize(id, config)
      @id = id
      @config = config
    end

    def connection_model
      @connection_model ||= begin
        Class.new(ActiveRecord::Base) do
          self.abstract_class = true
        end.tap do |model|
          model.establish_connection(config)
        end
      end
    end

    def connection
      connection_model.connection
    end
  end
end

Connection Switching

def with_connection(database_name)
  db = databases[database_name.to_s]
  raise Error, "Unknown database: #{database_name}" unless db

  yield db.connection
end

# Usage
PgHero.with_connection(:replica) do |conn|
  conn.execute("SELECT * FROM users")
end

SQL Dialect Handling

def quote_column(column)
  case adapter_name
  when /postg/i
    %("#{column}")
  when /mysql/i
    "`#{column}`"
  else
    column
  end
end

def boolean_value(value)
  case adapter_name
  when /postg/i
    value ? "true" : "false"
  when /mysql/i
    value ? "1" : "0"
  else
    value.to_s
  end
end