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

此技能用于编写Ruby gems,遵循Andrew Kane的经验证模式和最佳实践,适合创建、重构、设计gem API,或开发干净、最小化、生产就绪的Ruby库代码。关键词:Ruby gem, Andrew Kane, 代码模式, 生产就绪, 库开发, 架构设计, 后端开发

架构设计 0 次安装 0 次浏览 更新于 3/9/2026

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

Andrew Kane Gem Writer 技能

编写Ruby gems,遵循Andrew Kane从100+ gems和374M+下载量(如Searchkick、PgHero、Chartkick、Strong Migrations、Lockbox、Ahoy、Blazer、Groupdate、Neighbor、Blind Index)中积累的实战模式。

核心理念

简洁优于巧妙。 零或最少依赖。明确代码优于元编程。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 keywords: #{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,而非配置对象:

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
  • 配置对象(使用类访问器)
  • @@class_variables(使用class << self
  • 直接require Rails gems
  • 许多运行时依赖
  • 在gems中提交Gemfile.lock
  • RSpec(使用Minitest)
  • 重型DSL(偏好明确的Ruby代码)

参考文件

更深入的模式,请参阅: