AndrewKaneGem编写技能Skill andrew-kane-gem-writer

此技能专门用于按照 Andrew Kane 的最佳实践编写 Ruby gems,包括创建新 gem、重构现有 gem、设计 API 等,强调简洁、无依赖、Rails 集成但不耦合。关键词:Ruby gem 开发, Andrew Kane 模式, 库代码编写, 生产就绪, 软件库。

后端开发 0 次安装 0 次浏览 更新于 3/9/2026

name: andrew-kane-gem-writer description: 此技能应用于遵循 Andrew Kane 的经过验证的模式和哲学编写 Ruby gems。适用于创建新 Ruby gems、重构现有 gems、设计 gem API,或当需要干净、最小化、生产就绪的 Ruby 库代码时。触发于诸如“创建 gem”、“编写 Ruby 库”、“设计 gem API”或提及 Andrew Kane 风格的请求。

Andrew Kane Gem 编写器

基于 Andrew Kane 的经过 100 多个 gems 和 3.74 亿次下载(如 Searchkick、PgHero、Chartkick、Strong Migrations、Lockbox、Ahoy、Blazer、Groupdate、Neighbor、Blind Index)验证的模式编写 Ruby gems。

核心哲学

简单优于聪明。 零或最小依赖。显式代码优于元编程。Rails 集成但不耦合 Rails。每个模式服务于生产用例。

入口点结构

每个 gem 遵循 lib/gemname.rb 中的精确模式:

# 1. 依赖(优先 stdlib)
require "forwardable"

# 2. 内部模块
require_relative "gemname/model"
require_relative "gemname/version"

# 3. 条件性 Rails(关键 - 从不直接 require Rails)
require_relative "gemname/railtie" if defined?(Rails)

# 4. 模块带配置和错误
module GemName
  class Error < StandardError; end
  class InvalidConfigError < Error; end

  class << self
    attr_accessor :timeout, :logger
    attr_writer :client
  end

  self.timeout = 10  # 默认值立即设置
end

类宏 DSL 模式

Kane 的标志性模式——单方法调用配置一切:

# 用法
class Product < ApplicationRecord
  searchkick word_start: [:name]
end

# 实现
module GemName
  module Model
    def gemname(**options)
      unknown = options.keys - KNOWN_KEYWORDS
      raise ArgumentError, "未知关键字: #{unknown.join(", ")}" if unknown.any?

      mod = Module.new
      mod.module_eval do
        define_method :some_method do
          # 实现
        end unless method_defined?(:some_method)
      end
      include mod

      class_eval do
        cattr_reader :gemname_options, instance_reader: false
        class_variable_set :@@gemname_options, options.dup
      end
    end
  end
end

Rails 集成

总是使用 ActiveSupport.on_load——从不直接 require Rails gems:

# 错误
require "active_record"
ActiveRecord::Base.include(MyGem::Model)

# 正确
ActiveSupport.on_load(:active_record) do
  extend GemName::Model
end

# 使用 prepend 修改行为
ActiveSupport.on_load(:active_record) do
  ActiveRecord::Migration.prepend(GemName::Migration)
end

配置模式

使用 class << selfattr_accessor,而非 Configuration 对象:

module GemName
  class << self
    attr_accessor :timeout, :logger
    attr_writer :master_key
  end

  def self.master_key
    @master_key ||= ENV["GEMNAME_MASTER_KEY"]
  end

  self.timeout = 10
  self.logger = nil
end

错误处理

简单层次结构带信息性消息:

module GemName
  class Error < StandardError; end
  class ConfigError < Error; end
  class ValidationError < Error; end
end

# 早期验证带 ArgumentError
def initialize(key:)
  raise ArgumentError, "Key must be 32 bytes" unless key&.bytesize == 32
end

测试(仅 Minitest)

# test/test_helper.rb
require "bundler/setup"
Bundler.require(:default)
require "minitest/autorun"
require "minitest/pride"

# test/model_test.rb
class ModelTest < Minitest::Test
  def test_basic_functionality
    assert_equal expected, actual
  end
end

Gemspec 模式

尽可能零运行时依赖:

Gem::Specification.new do |spec|
  spec.name = "gemname"
  spec.version = GemName::VERSION
  spec.required_ruby_version = ">= 3.1"
  spec.files = Dir["*.{md,txt}", "{lib}/**/*"]
  spec.require_path = "lib"
  # 无 add_dependency 行 - 开发依赖放在 Gemfile 中
end

避免的反模式

  • method_missing(使用 define_method 替代)
  • Configuration 对象(使用类访问器)
  • @@class_variables(使用 class << self
  • 直接 require Rails gems
  • 许多运行时依赖
  • 在 gems 中提交 Gemfile.lock
  • RSpec(使用 Minitest)
  • 重度 DSL(偏好显式 Ruby)

参考文件

更深层模式见: