Ruby Idioms and Patterns
Ruby rewards expressiveness, convention, and developer happiness. Modern Ruby (3.x) favors pattern matching, Ractor for concurrency, and strict typing via Sorbet/RBS. Idiomatic Ruby = readable, tested, convention-following.
Scope: Ruby coding idioms. Test naming: GEMINI.md § Testing Strategy. Logging: @.gemini/skills/logging-and-observability-principles/SKILL.md.
Modern Ruby Features (3.x)
Pattern matching:
case result
in { status: :success, data: Task => task }
render json: task
in { status: :not_found, id: String => id }
render json: { error: "Task #{id} not found" }, status: :not_found
end
Endless methods for simple accessors:
def full_name = "#{first_name} #{last_name}"
Data class (3.2+) for immutable value objects:
TaskResult = Data.define(:task, :status)
result = TaskResult.new(task: task, status: :created)
Error Handling
Domain exception hierarchies:
class DomainError < StandardError; end
class NotFoundError < DomainError
attr_reader :resource, :resource_id
def initialize(resource, resource_id)
@resource = resource
@resource_id = resource_id
super("#{resource} '#{resource_id}' not found")
end
end
rescue specific exceptions — never bare rescue.
Use ensure for cleanup — equivalent to finally.
Naming
- snake_case for methods, variables, file names.
- PascalCase for classes, modules.
- UPPER_SNAKE_CASE for constants.
? suffix for boolean queries: active?, valid?.
! suffix for destructive or dangerous methods: save!, delete!.
Testing
RSpec (preferred) or Minitest:
RSpec.describe TaskService do
describe '#create' do
it 'creates a task with valid attributes' do
task = service.create(title: 'Test', priority: :high)
expect(task.title).to eq('Test')
end
it 'raises ValidationError for blank title' do
expect { service.create(title: '', priority: :high) }
.to raise_error(ValidationError)
end
end
end
let for lazy setup, before for eager setup.
Factory Bot for test data — never fixtures for complex models.
Formatting and Static Analysis
| Tool |
Purpose |
Command |
| RuboCop |
Formatting + linting |
rubocop --autocorrect |
| Sorbet |
Type checking |
srb tc |
| Brakeman |
Security scanning |
brakeman --no-pager |
bundle audit |
CVE scanning |
bundle audit check --update |
Related
- Code Idioms and Conventions GEMINI.md § Code Idioms and Conventions
- Testing Strategy GEMINI.md § Testing Strategy
- Error Handling Principles GEMINI.md § Error Handling Principles
1---2name: ruby-idioms3description: Ruby Idioms and Patterns4---56## Ruby Idioms and Patterns78Ruby rewards expressiveness, convention, and developer happiness. Modern Ruby (3.x) favors pattern matching, Ractor for concurrency, and strict typing via Sorbet/RBS. Idiomatic Ruby = readable, tested, convention-following.910> Scope: Ruby coding idioms. Test naming: GEMINI.md § Testing Strategy. Logging: `@.gemini/skills/logging-and-observability-principles/SKILL.md`.1112### Modern Ruby Features (3.x)13141. **Pattern matching:**15 ```ruby16 case result17 in { status: :success, data: Task => task }18 render json: task19 in { status: :not_found, id: String => id }20 render json: { error: "Task #{id} not found" }, status: :not_found21 end22 ```23242. **Endless methods for simple accessors:**25 ```ruby26 def full_name = "#{first_name} #{last_name}"27 ```28293. **Data class (3.2+) for immutable value objects:**30 ```ruby31 TaskResult = Data.define(:task, :status)32 result = TaskResult.new(task: task, status: :created)33 ```3435### Error Handling36371. **Domain exception hierarchies:**38 ```ruby39 class DomainError < StandardError; end4041 class NotFoundError < DomainError42 attr_reader :resource, :resource_id43 def initialize(resource, resource_id)44 @resource = resource45 @resource_id = resource_id46 super("#{resource} '#{resource_id}' not found")47 end48 end49 ```50512. **`rescue` specific exceptions — never bare `rescue`.**52533. **Use `ensure` for cleanup** — equivalent to `finally`.5455### Naming56571. **snake_case** for methods, variables, file names.582. **PascalCase** for classes, modules.593. **UPPER_SNAKE_CASE** for constants.604. **`?` suffix** for boolean queries: `active?`, `valid?`.615. **`!` suffix** for destructive or dangerous methods: `save!`, `delete!`.6263### Testing64651. **RSpec (preferred) or Minitest:**66 ```ruby67 RSpec.describe TaskService do68 describe '#create' do69 it 'creates a task with valid attributes' do70 task = service.create(title: 'Test', priority: :high)71 expect(task.title).to eq('Test')72 end7374 it 'raises ValidationError for blank title' do75 expect { service.create(title: '', priority: :high) }76 .to raise_error(ValidationError)77 end78 end79 end80 ```81822. **`let` for lazy setup, `before` for eager setup.**83843. **Factory Bot for test data** — never fixtures for complex models.8586### Formatting and Static Analysis8788| Tool | Purpose | Command |89|---|---|---|90| RuboCop | Formatting + linting | `rubocop --autocorrect` |91| Sorbet | Type checking | `srb tc` |92| Brakeman | Security scanning | `brakeman --no-pager` |93| `bundle audit` | CVE scanning | `bundle audit check --update` |9495### Related96- Code Idioms and Conventions GEMINI.md § Code Idioms and Conventions97- Testing Strategy GEMINI.md § Testing Strategy98- Error Handling Principles GEMINI.md § Error Handling Principles