# Ruby Patterns

> When to activate: idiomatic Ruby, blocks procs lambdas, modules mixins, include extend prepend, duck typing, keyword arguments, Struct, Data.define, method_missing, Ruby enumerable methods

- Skill: `mattakushi432/ruby-patterns` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mattakushi432/ruby-patterns`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mattakushi432/ruby-patterns/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Mattakushi432 (https://skillmd.com/u/mattakushi432)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/mattakushi432/ruby-patterns

---


# Ruby Patterns

## When to Use

Writing or reviewing idiomatic Ruby: choosing between blocks and lambdas, composing behavior with modules, or modeling small value types.

## Core Patterns

### Blocks, Procs, and Lambdas

```ruby
# Block — the common case, implicit, no object until captured
[1, 2, 3].each { |n| puts n }

# Proc — lenient arity, `return` exits the enclosing method
adder = Proc.new { |a, b| a + b }
adder.call(1, 2, 3) # extra arg silently ignored

# Lambda — strict arity, `return` exits only the lambda itself
adder = ->(a, b) { a + b }
adder.call(1, 2) # => 3
adder.call(1)    # ArgumentError — lambdas enforce arity
```

Default to lambdas for anything stored and called later — the strict arity and local `return` behavior avoid a class of subtle bugs procs introduce.

### Modules: include, extend, prepend

```ruby
module Trackable
  def track(event)
    Analytics.record(self.class.name, event)
  end
end

class Order
  include Trackable   # adds as instance methods
end

class Report
  extend Trackable     # adds as class/singleton methods
end

module Loud
  def greet
    super.upcase
  end
end

class Greeter
  prepend Loud          # inserts Loud ABOVE Greeter in the ancestor chain
  def greet = "hello"
end
Greeter.new.greet # => "HELLO" — Loud#greet runs first, calls super
```

### Duck Typing Over Type Checks

```ruby
# BAD: rigid, breaks polymorphism
def process(payment)
  raise TypeError unless payment.is_a?(CreditCardPayment)
  payment.charge
end

# GOOD: trust the interface, not the class
def process(payment)
  payment.charge # works for CreditCardPayment, PayPalPayment, anything with #charge
end
```

### Enumerable Over Manual Loops

```ruby
# BAD
total = 0
items.each { |item| total += item.price if item.in_stock? }

# GOOD — declarative, composes
total = items.select(&:in_stock?).sum(&:price)

active_emails = users.filter_map { |u| u.email if u.active? }
```

### Keyword Arguments for Multi-Parameter Methods

```ruby
# BAD: positional args, easy to swap by mistake
def create_order(customer, total, currency, priority)
end

# GOOD: self-documenting call sites, order-independent
def create_order(customer:, total:, currency: "USD", priority: :normal)
end

create_order(customer: user, total: 4200, currency: "EUR")
```

### Value Objects with Struct / Data.define

```ruby
# Data.define (Ruby 3.2+) — immutable value object, minimal boilerplate
Money = Data.define(:cents, :currency) do
  def add(other)
    raise ArgumentError, "currency mismatch" unless currency == other.currency
    Money.new(cents: cents + other.cents, currency:)
  end
end

price = Money.new(cents: 1500, currency: "USD")
```

## Checklist

- [ ] Lambdas used for stored/passed-around callables; blocks for immediate iteration
- [ ] `Data.define`/`Struct` used for small immutable value objects instead of ad-hoc hashes
- [ ] Keyword arguments used once a method takes 3+ parameters
- [ ] Enumerable methods (`map`, `select`, `sum`, `filter_map`) preferred over manual `each` + accumulator
- [ ] `method_missing` avoided unless building a genuine proxy/DSL — and paired with `respond_to_missing?`

## Anti-Patterns

```ruby
# BAD: method_missing as a lazy alternative to defining real methods
class Config
  def method_missing(name, *args)
    @data[name]
  end
end

# GOOD: explicit, discoverable, works with respond_to?
class Config
  def initialize(data) = @data = data
  def [](key) = @data[key]
end
```

```ruby
# BAD: mutating an argument passed by reference
def add_tax!(order)
  order.total += order.total * 0.1
end

# GOOD: return a new value, keep call sites predictable
def with_tax(order)
  order.with(total: order.total * 1.1)
end
```

## Quick Reference

| Need | Use |
|---|---|
| Store a callable for later | `lambda` / `->() {}` |
| Immediate iteration | block (`each`, `map`) |
| Add behavior to instances | `include` |
| Add behavior to the class itself | `extend` |
| Override with access to original via `super` | `prepend` |
| Small immutable value object | `Data.define` |

## See Also

- `skills/ruby-ecosystem/rails-patterns.md`
- `skills/ruby-ecosystem/ruby-testing.md`

