Claude
Skills
Sign in
Back

ruby-patterns

Included with Lifetime
$97 forever

Modern Ruby idioms, design patterns, metaprogramming techniques, and best practices. Use when writing Ruby code or refactoring for clarity.

Design

What this skill does


# Ruby Patterns Skill

## Tier 1: Quick Reference - Common Idioms

### Conditional Assignment

```ruby
# Set if nil
value ||= default_value

# Set if falsy (nil or false)
value = value || default_value

# Safe navigation
user&.profile&.avatar&.url
```

### Array and Hash Shortcuts

```ruby
# Array creation
%w[apple banana orange]  # ["apple", "banana", "orange"]
%i[name email age]        # [:name, :email, :age]

# Hash creation
{ name: 'John', age: 30 }  # Symbol keys
{ 'name' => 'John' }       # String keys

# Hash access with default
hash.fetch(:key, default)
hash[:key] || default
```

### Enumerable Shortcuts

```ruby
# Transformation
array.map(&:upcase)
array.select(&:active?)
array.reject(&:empty?)

# Aggregation
array.sum
array.max
array.min
numbers.reduce(:+)

# Finding
array.find(&:valid?)
array.any?(&:present?)
array.all?(&:valid?)
```

### String Operations

```ruby
# Interpolation
"Hello #{name}!"

# Safe interpolation
"Result: %{value}" % { value: result }

# Multiline
<<~TEXT
  Heredoc with indentation
  removed automatically
TEXT
```

### Block Syntax

```ruby
# Single line - use braces
array.map { |x| x * 2 }

# Multi-line - use do/end
array.each do |item|
  process(item)
  log(item)
end

# Symbol to_proc
array.map(&:to_s)
array.select(&:even?)
```

### Guard Clauses

```ruby
def process(user)
  return unless user
  return unless user.active?

  # Main logic here
end
```

### Case Statements

```ruby
# Traditional
case status
when 'active'
  activate
when 'inactive'
  deactivate
end

# With ranges
case age
when 0..17
  'minor'
when 18..64
  'adult'
else
  'senior'
end
```

---

## Tier 2: Detailed Instructions - Design Patterns

### Creational Patterns

**Factory Pattern:**
```ruby
class UserFactory
  def self.create(type, attributes)
    case type
    when :admin
      AdminUser.new(attributes)
    when :member
      MemberUser.new(attributes)
    when :guest
      GuestUser.new(attributes)
    else
      raise ArgumentError, "Unknown user type: #{type}"
    end
  end
end

# Usage
user = UserFactory.create(:admin, name: 'John', email: '[email protected]')
```

**Builder Pattern:**
```ruby
class QueryBuilder
  def initialize
    @conditions = []
    @order = nil
    @limit = nil
  end

  def where(condition)
    @conditions << condition
    self
  end

  def order(column)
    @order = column
    self
  end

  def limit(count)
    @limit = count
    self
  end

  def build
    query = "SELECT * FROM users"
    query += " WHERE #{@conditions.join(' AND ')}" if @conditions.any?
    query += " ORDER BY #{@order}" if @order
    query += " LIMIT #{@limit}" if @limit
    query
  end
end

# Usage
query = QueryBuilder.new
  .where("active = true")
  .where("age > 18")
  .order("created_at DESC")
  .limit(10)
  .build
```

**Singleton Pattern:**
```ruby
require 'singleton'

class Configuration
  include Singleton

  attr_accessor :api_key, :timeout

  def initialize
    @api_key = ENV['API_KEY']
    @timeout = 30
  end
end

# Usage
config = Configuration.instance
config.api_key = 'new_key'
```

### Structural Patterns

**Decorator Pattern:**
```ruby
# Simple decorator
class User
  attr_accessor :name, :email

  def initialize(name, email)
    @name = name
    @email = email
  end
end

class AdminUser < SimpleDelegator
  def permissions
    [:read, :write, :delete, :admin]
  end

  def admin?
    true
  end
end

# Usage
user = User.new('John', '[email protected]')
admin = AdminUser.new(user)
admin.name  # Delegates to user
admin.admin?  # From decorator

# Using Ruby's Forwardable
require 'forwardable'

class UserDecorator
  extend Forwardable
  def_delegators :@user, :name, :email

  def initialize(user)
    @user = user
  end

  def display_name
    "#{@user.name} (#{@user.email})"
  end
end
```

**Adapter Pattern:**
```ruby
# Adapting third-party API
class LegacyPaymentGateway
  def make_payment(amount, card)
    # Legacy implementation
  end
end

class PaymentAdapter
  def initialize(gateway)
    @gateway = gateway
  end

  def process(amount:, card_number:)
    card = { number: card_number }
    @gateway.make_payment(amount, card)
  end
end

# Usage
legacy = LegacyPaymentGateway.new
adapter = PaymentAdapter.new(legacy)
adapter.process(amount: 100, card_number: '1234')
```

**Composite Pattern:**
```ruby
class File
  attr_reader :name, :size

  def initialize(name, size)
    @name = name
    @size = size
  end

  def total_size
    size
  end
end

class Directory
  attr_reader :name

  def initialize(name)
    @name = name
    @contents = []
  end

  def add(item)
    @contents << item
  end

  def total_size
    @contents.sum(&:total_size)
  end
end

# Usage
root = Directory.new('root')
root.add(File.new('file1.txt', 100))
subdir = Directory.new('subdir')
subdir.add(File.new('file2.txt', 200))
root.add(subdir)
root.total_size  # 300
```

### Behavioral Patterns

**Strategy Pattern:**
```ruby
class PaymentProcessor
  def initialize(strategy)
    @strategy = strategy
  end

  def process(amount)
    @strategy.process(amount)
  end
end

class CreditCardStrategy
  def process(amount)
    puts "Processing #{amount} via credit card"
  end
end

class PayPalStrategy
  def process(amount)
    puts "Processing #{amount} via PayPal"
  end
end

# Usage
processor = PaymentProcessor.new(CreditCardStrategy.new)
processor.process(100)

processor = PaymentProcessor.new(PayPalStrategy.new)
processor.process(100)
```

**Observer Pattern:**
```ruby
require 'observer'

class Order
  include Observable

  attr_reader :status

  def initialize
    @status = :pending
  end

  def complete!
    @status = :completed
    changed
    notify_observers(self)
  end
end

class EmailNotifier
  def update(order)
    puts "Sending email: Order #{order.object_id} is #{order.status}"
  end
end

class SMSNotifier
  def update(order)
    puts "Sending SMS: Order #{order.object_id} is #{order.status}"
  end
end

# Usage
order = Order.new
order.add_observer(EmailNotifier.new)
order.add_observer(SMSNotifier.new)
order.complete!  # Both notifiers triggered
```

**Command Pattern:**
```ruby
class Command
  def execute
    raise NotImplementedError
  end

  def undo
    raise NotImplementedError
  end
end

class CreateUserCommand < Command
  def initialize(user_service, params)
    @user_service = user_service
    @params = params
    @user = nil
  end

  def execute
    @user = @user_service.create(@params)
  end

  def undo
    @user_service.delete(@user.id) if @user
  end
end

class CommandInvoker
  def initialize
    @history = []
  end

  def execute(command)
    command.execute
    @history << command
  end

  def undo
    command = @history.pop
    command&.undo
  end
end

# Usage
invoker = CommandInvoker.new
command = CreateUserCommand.new(user_service, { name: 'John' })
invoker.execute(command)
invoker.undo  # Rolls back
```

### Metaprogramming Techniques

**Dynamic Method Definition:**
```ruby
class Model
  ATTRIBUTES = [:name, :email, :age]

  ATTRIBUTES.each do |attr|
    define_method(attr) do
      instance_variable_get("@#{attr}")
    end

    define_method("#{attr}=") do |value|
      instance_variable_set("@#{attr}", value)
    end
  end
end

# Usage
model = Model.new
model.name = 'John'
model.name  # 'John'
```

**Method Missing:**
```ruby
class DynamicFinder
  def initialize(data)
    @data = data
  end

  def method_missing(method_name, *args)
    if method_name.to_s.start_with?('find_by_')
      attribute = method_name.to_s.sub('find_by_', '')
      @data.find { |item| item[attribute.to_sym] == 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

# Usage
data = [
  { name: 'John', email: '[email protected]' },
  { name: 'Jane', email: '[email protected]' }
]
finder = DynamicFinder.new(data)
finder.find_by_name('John')  # { name: 'John', ... }
finder.find_by_email('[email protected]')  # { name: 'Jane', ... }
```

**Class Macros (DSL):**
```rub

Related in Design