andrew-kane-gem-writer by everyinc/compound-engineering-plugin
npx skills add https://github.com/everyinc/compound-engineering-plugin --skill andrew-kane-gem-writer遵循 Andrew Kane 经过实战检验的模式编写 Ruby gem,这些模式源自下载量超过 3.74 亿次的 100 多个 gem(Searchkick、PgHero、Chartkick、Strong Migrations、Lockbox、Ahoy、Blazer、Groupdate、Neighbor、Blind Index)。
简洁胜于巧妙。 零依赖或最小依赖。显式代码优于元编程。与 Rails 集成但不耦合。每种模式都服务于生产用例。
每个 gem 在 lib/gemname.rb 中都遵循以下确切模式:
# 1. 依赖项(优先使用标准库)
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
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
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
始终使用 ActiveSupport.on_load——切勿直接 require Rails gem:
# 错误做法
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
使用带有 attr_accessor 的 class << self,而非 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
# 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
尽可能实现零运行时依赖:
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)如需更深入的模式,请参阅:
每周安装量
210
仓库
GitHub 星标数
11.0K
首次出现
2026年1月21日
安全审计
安装于
opencode184
gemini-cli183
codex181
claude-code178
cursor177
github-copilot169
Write Ruby gems following Andrew Kane's battle-tested patterns from 100+ gems with 374M+ downloads (Searchkick, PgHero, Chartkick, Strong Migrations, Lockbox, Ahoy, Blazer, Groupdate, Neighbor, Blind Index).
Simplicity over cleverness. Zero or minimal dependencies. Explicit code over metaprogramming. Rails integration without Rails coupling. Every pattern serves production use cases.
Every gem follows this exact pattern in lib/gemname.rb:
# 1. Dependencies (stdlib preferred)
require "forwardable"
# 2. Internal modules
require_relative "gemname/model"
require_relative "gemname/version"
# 3. Conditional Rails (CRITICAL - never require Rails directly)
require_relative "gemname/railtie" if defined?(Rails)
# 4. Module with config and errors
module GemName
class Error < StandardError; end
class InvalidConfigError < Error; end
class << self
attr_accessor :timeout, :logger
attr_writer :client
end
self.timeout = 10 # Defaults set immediately
end
The signature Kane pattern—single method call configures everything:
# Usage
class Product < ApplicationRecord
searchkick word_start: [:name]
end
# Implementation
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
# implementation
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
Always useActiveSupport.on_load—never require Rails gems directly:
# WRONG
require "active_record"
ActiveRecord::Base.include(MyGem::Model)
# CORRECT
ActiveSupport.on_load(:active_record) do
extend GemName::Model
end
# Use prepend for behavior modification
ActiveSupport.on_load(:active_record) do
ActiveRecord::Migration.prepend(GemName::Migration)
end
Use class << self with attr_accessor, not Configuration objects:
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
Simple hierarchy with informative messages:
module GemName
class Error < StandardError; end
class ConfigError < Error; end
class ValidationError < Error; end
end
# Validate early with ArgumentError
def initialize(key:)
raise ArgumentError, "Key must be 32 bytes" unless key&.bytesize == 32
end
# 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
Zero runtime dependencies when possible:
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"
# NO add_dependency lines - dev deps go in Gemfile
end
method_missing (use define_method instead)@@class_variables (use class << self)For deeper patterns, see:
Weekly Installs
210
Repository
GitHub Stars
11.0K
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode184
gemini-cli183
codex181
claude-code178
cursor177
github-copilot169
Perl 5.36+ 现代开发模式与最佳实践 | 构建健壮可维护应用程序指南
1,100 周安装
AI图像生成与编辑Skill:使用FAL.AI API生成和编辑高质量图像
81 周安装
测试质量分析指南:检测异味、提升覆盖率与测试最佳实践
81 周安装
规范驱动开发 (Spec-Driven Development) | 自适应项目规划与执行 | Tech Lead's Club
81 周安装
PPTX文档自动化技能:使用Python和Node.js编程创建编辑PowerPoint演示文稿
81 周安装
project-discover:AI辅助项目逆向工程与知识沉淀工具,一键建立项目SSOT
81 周安装
Angular 17+ 现代开发规范:独立组件、Signal 状态管理与原生控制流最佳实践
81 周安装