Use when adding new error messages to React, or seeing "unknown error code" warnings.
npx skills add ugnius-s/skills --skill "best-practices-ruby-style"
Install specific skill from multi-skill repository
# Description
Ruby code style and formatting guidelines from the official Ruby Style Guide. Use PROACTIVELY when writing, reviewing, or generating any Ruby code to ensure consistent formatting, proper naming conventions, idiomatic patterns, and clean code structure.
# SKILL.md
name: best-practices-ruby-style
description: Ruby code style and formatting guidelines from the official Ruby Style Guide. Use PROACTIVELY when writing, reviewing, or generating any Ruby code to ensure consistent formatting, proper naming conventions, idiomatic patterns, and clean code structure.
Core Principles
Write code for humans first. Optimize for readability and maintainability over cleverness.
Formatting
Indentation & Spacing
- 2 spaces per indentation level (no tabs)
- 80 characters max line length (120 absolute max)
- Spaces around operators:
sum = 1 + 2 - Spaces after commas:
method(a, b, c) - No spaces inside parentheses/brackets:
method(arg),array[0] - Spaces around
{and before}in blocks:items.each { |x| puts x } - No spaces in string interpolation:
"Hello #{name}"
Empty Lines
- One empty line between method definitions
- Empty line after
include/extend/prependblock - Empty line around access modifiers (
private,protected) - No empty lines at start/end of class/method bodies
Multi-line
# Align arguments or use single indent
Mailer.deliver(
to: '[email protected]',
from: '[email protected]',
subject: 'Important'
)
# Chain with leading dot
query
.where(active: true)
.order(:name)
Detailed examples: See references/formatting.md
Naming
| Type | Convention | Example |
|---|---|---|
| Variables, methods, symbols | snake_case |
user_name, calculate_total |
| Classes, modules | CamelCase |
UserAccount, HTTPClient |
| Constants | SCREAMING_SNAKE_CASE |
MAX_RETRIES, DEFAULT_TIMEOUT |
| Files, directories | snake_case |
user_account.rb |
| Predicates | End with ? |
valid?, empty? |
| Dangerous methods | End with ! (if safe version exists) |
save!, destroy! |
Naming Anti-Patterns
# ❌ Bad predicate names
def is_valid?; end
def has_items?; end
# ✅ Good predicate names
def valid?; end
def items?; end
# ❌ Unused variables without prefix
result = hash.map { |k, v| v * 2 }
# ✅ Prefix unused with underscore
result = hash.map { |_k, v| v * 2 }
Detailed conventions: See references/naming.md
Control Flow
Guard Clauses
# ❌ Nested conditionals
def process(data)
if data
if data[:valid]
do_work(data)
end
end
end
# ✅ Guard clauses
def process(data)
return unless data
return unless data[:valid]
do_work(data)
end
Conditionals
# Use unless for negative conditions (never with else)
return unless valid?
# Modifier form for single-line
do_something if condition
# Ternary for simple expressions only
status = active? ? 'on' : 'off'
# case over if-elsif when comparing same value
case status
when :active then perform_action
when :pending then wait
else fail
end
Boolean Logic
# ✅ Use &&/|| for boolean expressions
if valid? && authorized?
# ✅ Use and/or only for control flow
user = find_user or raise 'Not found'
More patterns: See references/idioms.md
Methods
Definition
# Parentheses with parameters, omit without
def greet(name)
"Hello, #{name}"
end
def default_greeting
'Hello'
end
# Keyword arguments for booleans/optionals
def create_user(name:, admin: false, notify: true)
end
# Required keyword args before optional
def fetch(url:, timeout: 30, retries: 3)
end
Guidelines
- Keep methods < 10 LOC (prefer < 5)
- Max 3-4 parameters (use keyword args or objects)
- Avoid explicit
returnat end of method - Avoid explicit
selfexcept for assignment
Detailed patterns: See references/methods.md
Classes & Modules
Structure Order
class User
# 1. Extend/include/prepend
include Comparable
extend ClassMethods
# 2. Constants
ROLES = %w[admin member guest].freeze
# 3. Attribute macros
attr_reader :name, :email
# 4. Other macros (validations, etc.)
validates :email, presence: true
# 5. Class methods
def self.find_by_email(email)
end
# 6. Initialize
def initialize(name:, email:)
@name = name
@email = email
end
# 7. Public instance methods
def full_info
"#{name} <#{email}>"
end
# 8. Protected/private at the end
private
def validate_email
end
end
Guidelines
- One class per file
- Use explicit nesting for namespaces (not
class Foo::Bar) - Prefer
module_functionoverextend self - No class variables (
@@) - use class instance variables - Use
def self.methodfor class methods
Detailed patterns: See references/classes.md
Collections & Strings
Arrays & Hashes
# Literal syntax
arr = []
hash = {}
# Word arrays
STATES = %w[draft open closed]
ROLES = %i[admin member guest]
# Symbol keys with modern syntax
user = { name: 'Alice', age: 30 }
# Hash#fetch for required keys
email = params.fetch(:email)
timeout = config.fetch(:timeout, 30)
Strings
# Interpolation over concatenation
"Hello, #{name}!"
# Single quotes when no interpolation needed
path = '/api/users'
# Use << for building strings in loops
html = ''
items.each { |item| html << "<li>#{item}</li>" }
Enumerable
# Prefer map/select/reduce over each with mutation
names = users.map(&:name)
active = users.select(&:active?)
total = prices.reduce(0, :+)
# flat_map over map + flatten
all_tags = posts.flat_map(&:tags)
# Use transform_keys/transform_values
data.transform_keys(&:to_sym)
data.transform_values(&:upcase)
More idioms: See references/idioms.md
Exceptions
# Use raise (not fail)
raise ArgumentError, 'Invalid input'
# Implicit begin in methods
def fetch_data
api.get(url)
rescue Timeout::Error => e
handle_timeout(e)
end
# Specific exceptions first
rescue IOError => e
# handle IO
rescue StandardError => e
# handle general
# Never rescue Exception (catches signals)
# Always rescue StandardError or more specific
Blocks & Lambdas
# Braces for single-line, do/end for multi-line
items.map { |x| x * 2 }
items.each do |item|
process(item)
log(item)
end
# Proc shorthand when method is only operation
names.map(&:upcase)
# Stabby lambda syntax
validator = ->(x) { x > 0 }
# Multi-line lambda
processor = lambda do |data|
validate(data)
transform(data)
end
Quick Reference
| Do | Don't |
|---|---|
unless condition |
if !condition |
arr.first / arr.last |
arr[0] / arr[-1] |
hash.key?(:foo) |
hash.has_key?(:foo) |
hash.fetch(:key) |
hash[:key] (when required) |
%w[a b c] |
['a', 'b', 'c'] |
each_key / each_value |
keys.each / values.each |
map / select / reduce |
collect / find_all / inject |
size |
length / count |
# Supported AI Coding Agents
This skill is compatible with the SKILL.md standard and works with all major AI coding agents:
Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.