Help us improve
Share bugs, ideas, or general feedback.
From ruby-rails
Provides Ruby conventions, idioms, error handling patterns, performance optimization, and modern 3.x+ features like pattern matching and Data classes for idiomatic pure Ruby code.
npx claudepluginhub el-feo/ai-context --plugin ruby-railsHow this skill is triggered — by the user, by Claude, or both
Slash command
/ruby-rails:rubyThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Use `fail` for first-time exceptions, `raise` only for re-raising:
Guides writing new Ruby code with modern 3.x syntax, Sandi Metz's 4 rules for developers, and idiomatic best practices. Use for creating files, methods, or refactoring to ensure clarity, simplicity, and maintainability.
Provides Ruby coding conventions and best practices for naming, service objects, RSpec testing, error handling, AWS SDK usage, logging, and security patterns.
Writes idiomatic Ruby code with metaprogramming, Rails patterns, and performance optimization. Specializes in Ruby on Rails, gem development, and testing with RSpec/Minitest.
Share bugs, ideas, or general feedback.
Use fail for first-time exceptions, raise only for re-raising:
def process(order)
fail ArgumentError, "Order cannot be nil" if order.nil?
begin
gateway.charge(order)
rescue PaymentError => e
logger.error("Payment failed: #{e.message}")
raise # re-raise with raise
end
end
Group domain exceptions under a base error:
module MyApp
class Error < StandardError; end
class PaymentError < Error; end
class InsufficientFundsError < PaymentError; end
end
# Rescue at any granularity:
rescue MyApp::InsufficientFundsError # specific
rescue MyApp::PaymentError # category
rescue MyApp::Error # all app errors
Use result objects instead of exceptions for expected failure paths:
class Result
attr_reader :value, :error
def self.success(value) = new(value: value)
def self.failure(error) = new(error: error)
def initialize(value: nil, error: nil) = (@value, @error = value, error)
def success? = error.nil?
def failure? = !success?
end
Let callers define error handling via blocks:
def fetch_user(id, &fallback)
User.find(id)
rescue ActiveRecord::RecordNotFound => e
fallback ? fallback.call(e) : raise
end
user = fetch_user(999) { |_| User.new(name: "Guest") }
See references/error_handling.md for full patterns and retry strategies.
case response
in { status: 200, body: { users: [{ name: }, *] } }
"First user: #{name}"
in { status: (400..), error: message }
"Error: #{message}"
end
# Find pattern
case array
in [*, String => str, *]
"Found string: #{str}"
end
# Pin operator
expected = 200
case response
in { status: ^expected, body: }
process(body)
end
# Endless methods (3.0+)
def square(x) = x * x
def admin? = role == "admin"
# Numbered block parameters (2.7+)
[1, 2, 3].map { _1 * 2 }
# Data class - immutable value objects (3.2+)
Point = Data.define(:x, :y)
p = Point.new(x: 1, y: 2)
p.with(x: 3) # => Point(x: 3, y: 2)
# Hash#except (3.0+)
params.except(:password, :admin)
# filter_map (2.7+) - select + map in one pass
users.filter_map { |u| u.email if u.active? }
# tally (2.7+)
%w[a b a c b a].tally # => {"a"=>3, "b"=>2, "c"=>1}
See references/modern_ruby.md for ractors, fiber scheduler, RBS types, and advanced pattern matching.
# frozen_string_literal: true
# Add to top of every file. Prevents mutation, reduces allocations.
# When you need mutable: String.new("hello") or +"hello"
# each_with_object for building results (avoids intermediate arrays)
totals = items.each_with_object(Hash.new(0)) do |item, hash|
hash[item.category] += item.amount
end
# Lazy enumerables for large/infinite sequences
(1..Float::INFINITY).lazy.select(&:odd?).map { _1 ** 2 }.first(10)
# Simple (only works if result is truthy)
def users = @users ||= User.all.to_a
# Safe (handles nil/false results)
def feature_enabled?
return @feature_enabled if defined?(@feature_enabled)
@feature_enabled = expensive_check
end
# Bad: O(n^2) with +=
result = ""; items.each { |i| result += i.to_s }
# Good: O(n) with <<
result = String.new; items.each { |i| result << i.to_s }
# Best: join
items.map(&:to_s).join
See references/performance.md for YJIT, GC tuning, benchmarking, and profiling tools.
def process(value)
return unless value
return unless value.valid?
# main logic here
end
STATES = %w[draft published archived] # word array
FIELDS = %i[name email created_at] # symbol array
config.fetch(:api_key) # raises KeyError if missing
config.fetch(:timeout, 30) # default value
config.fetch(:handler) { build_handler } # lazy default
user&.profile&.avatar_url # returns nil if any link is nil
? suffix: returns boolean (empty?, valid?, admin?)! suffix: dangerous version - mutates receiver or raises on failure (save!, sort!)references/modern_ruby.md - Pattern matching, ractors, fiber scheduler, RBS typesreferences/error_handling.md - Exception hierarchies, result objects, retry patternsreferences/performance.md - YJIT, GC tuning, benchmarking, profiling