internal/skills/content/ruby-guide/SKILL.md
Ruby language guardrails, patterns, and best practices for AI-assisted development. Use when working with Ruby files (.rb), Gemfile, Rakefile, or when the user mentions Ruby. Provides block/proc patterns, metaprogramming guidelines, Bundler conventions, and testing standards specific to this project's coding standards.
npx skillsauth add ar4mirez/samuel ruby-guideInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Applies to: Ruby 3.2+, Gems, APIs, CLIs, Web Applications
# frozen_string_literal: true in every .rb fileGemfile + Gemfile.lock)gem "rails", "~> 7.1"bundle audit before merging to check for vulnerable gemsGemfile.lock for applications; omit for gemsrequired_ruby_version in .gemspec filesrubocop before every commit (no exceptions)snake_case for methods/variables/files, PascalCase for classes/modules, SCREAMING_SNAKE_CASE for constants?, dangerous methods end with !# frozen_string_literal: true
# Bad: deeply nested
def process(user)
if user
if user.active?
do_something(user) if user.verified?
end
end
end
# Good: guard clauses
def process(user)
return unless user
return unless user.active?
return unless user.verified?
do_something(user)
end
{} for single-line blocks, do...end for multi-lineblock_given? + yield over explicit &block parameter# Block for resource management
File.open("data.txt", "r") do |file|
file.each_line { |line| process(line) }
end
# Lambda vs Proc
validator = ->(x) { x.positive? } # strict arity, returns from lambda
transformer = proc { |x| x.to_s } # flexible arity, returns from enclosing
# Point-free style
names = users.map(&:name)
rescueStandardErrorensure for cleanup (not rescue for flow control)#message with actionable information in custom errorsclass PaymentError < StandardError; end
class InsufficientFundsError < PaymentError; end
def charge(account, amount)
raise InsufficientFundsError, "account #{account.id} needs #{amount}" if account.balance < amount
account.debit(amount)
rescue Stripe::CardError => e
Rails.logger.error("Payment failed for account=#{account.id}: #{e.message}")
raise PaymentError, "card declined: #{e.message}"
end
define_method sparingly; prefer explicit method definitionsmethod_missing with respond_to_missing?Module#prepend over alias_method chainseval with string arguments (use block form of class_eval)class DynamicFinder
def method_missing(method_name, *args)
if method_name.to_s.start_with?("find_by_")
attribute = method_name.to_s.delete_prefix("find_by_")
find_by_attribute(attribute, args.first)
else
super
end
end
def respond_to_missing?(method_name, include_private = false)
method_name.to_s.start_with?("find_by_") || super
end
end
mygem/
├── lib/
│ ├── mygem.rb # Entry point, require dependencies
│ └── mygem/
│ ├── version.rb # VERSION constant
│ ├── client.rb # Core logic
│ └── errors.rb # Custom error classes
├── spec/
│ ├── spec_helper.rb
│ └── mygem/
│ └── client_spec.rb
├── Gemfile
├── mygem.gemspec
└── Rakefile
myapp/
├── app/
│ ├── models/ # Domain objects
│ ├── services/ # Business logic (POROs)
│ └── validators/ # Input validation
├── config/
├── db/migrate/ # Database migrations
├── lib/tasks/ # Rake tasks
├── spec/
│ ├── spec_helper.rb
│ ├── models/
│ └── services/
├── Gemfile
├── Gemfile.lock
└── Rakefile
#call)# Chain Enumerable methods over manual loops
users.select(&:active?).map(&:email).sort
# Lazy evaluation for large collections
File.open("huge.log").each_line.lazy
.select { |line| line.include?("ERROR") }
.map { |line| parse_error(line) }
.first(10)
# each_with_object for building hashes
totals = orders.each_with_object(Hash.new(0)) do |order, sums|
sums[order.category] += order.amount
end
case response
in { status: 200, body: { data: Array => items } }
process_items(items)
in { status: 404 }
handle_not_found
in { status: (500..) }
handle_server_error
end
# Pin operator for variable binding
expected_id = 42
case record
in { id: ^expected_id, name: String => name }
puts "Found: #{name}"
end
# frozen_string_literal: true
name = "hello"
name << " world" # => FrozenError
# Use +"" or .dup when mutation is needed
mutable = +"hello"
mutable << " world" # => "hello world"
VALID_STATUSES = %w[pending active suspended].freeze
CONFIG_DEFAULTS = { timeout: 30, retries: 3 }.freeze
workers = 4.times.map do
Ractor.new do
loop do
task = Ractor.receive
Ractor.yield expensive_computation(task)
end
end
end
tasks.each_with_index { |task, i| workers[i % workers.size].send(task) }
results = workers.map(&:take)
RSpec.describe PaymentService do
subject(:service) { described_class.new(gateway: gateway) }
let(:gateway) { instance_double(PaymentGateway) }
let(:account) { build(:account, balance: 100.0) }
describe "#charge" do
context "when account has sufficient funds" do
before { allow(gateway).to receive(:process).and_return(true) }
it "debits the account" do
expect { service.charge(account, 50.0) }
.to change { account.balance }.from(100.0).to(50.0)
end
end
context "when account has insufficient funds" do
it "raises InsufficientFundsError" do
expect { service.charge(account, 200.0) }
.to raise_error(InsufficientFundsError, /needs 200/)
end
end
end
end
describe for class/method, context for scenario (prefix with "when" or "with")let for lazy data, let! only when eager evaluation is neededsubject for the object under test, described_class over hardcoded class namesinstance_double over generic double for type safetyFactoryBot.define do
factory :user do
sequence(:email) { |n| "user#{n}@example.com" }
name { "Test User" }
active { true }
trait(:admin) { role { "admin" } }
trait(:inactive) { active { false } }
end
end
lib/foo/bar.rb -> spec/foo/bar_spec.rbsimplecov)webmock or vcr for HTTP stubbing (no real network in unit tests)bundle install # Install dependencies
bundle exec rspec # Run tests through Bundler
bundle exec rubocop # Lint
bundle exec rubocop -a # Auto-fix safe cops
bundle exec rake # Default task (usually tests)
bundle audit # Check for vulnerable gems
bundle outdated # Show outdated gems
# .rubocop.yml
require:
- rubocop-rspec
- rubocop-performance
AllCops:
NewCops: enable
TargetRubyVersion: 3.2
Style/FrozenStringLiteralComment:
Enabled: true
EnforcedStyle: always
Metrics/MethodLength:
Max: 20
Metrics/CyclomaticComplexity:
Max: 10
# Gemfile
source "https://rubygems.org"
ruby "~> 3.2"
gem "dry-struct", "~> 1.6"
gem "zeitwerk", "~> 2.6"
group :development, :test do
gem "rspec", "~> 3.13"
gem "rubocop", "~> 1.60", require: false
gem "factory_bot", "~> 6.4"
end
group :test do
gem "simplecov", require: false
gem "webmock", "~> 3.19"
end
For detailed patterns and examples, see:
development
Zig language guardrails, patterns, and best practices for AI-assisted development. Use when working with Zig files (.zig), build.zig, or when the user mentions Zig. Provides comptime patterns, allocator conventions, C interop guidelines, and testing standards specific to this project's coding standards.
tools
WordPress framework guardrails, patterns, and best practices for AI-assisted development. Use when working with WordPress projects, or when the user mentions WordPress. Provides theme development, plugin architecture, REST API, blocks, and security guidelines.
tools
Toolkit for interacting with and testing local web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing browser screenshots, and viewing browser logs. Use when testing web apps, automating browser interactions, or debugging frontend issues.
tools
Suite of tools for creating elaborate, multi-component web applications using modern frontend technologies (React, Tailwind CSS, shadcn/ui). Use for complex projects requiring state management, routing, or shadcn/ui components - not for simple single-file HTML/JSX pages.