| 名称 | andrew-kane-gem-writer |
| 描述 | 此技能应用于遵循 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 仓库和文章的链接