名称: 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 << self与attr_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代码)
参考文件
更深入的模式,请参阅:
- references/module-organization.md - 目录布局、方法分解
- references/rails-integration.md - Railtie、Engine、on_load模式
- references/database-adapters.md - 多数据库支持模式
- references/testing-patterns.md - 多版本测试、CI设置
- references/resources.md - 链接到Kane的仓库和文章