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 << self 带 attr_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)
参考文件
更深层模式见:
- 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 仓库和文章的链接