home / skills / el-feo / ai-context / ruby

ruby skill

/plugins/ruby-rails/skills/ruby

npx playbooks add skill el-feo/ai-context --skill ruby

Review the files below or copy the command above to add this skill to your agents.

Files (3)
SKILL.md
31.7 KB
---
name: ruby
description: Comprehensive Ruby development skill covering language fundamentals, object-oriented design patterns, error handling strategies, performance optimization, modern Ruby 3.x features (pattern matching, ractors, typed Ruby), testing patterns, metaprogramming, concurrency, and Rails-specific best practices. Use when writing Ruby code, refactoring, implementing design patterns, handling exceptions, optimizing performance, writing tests, or applying Ruby idioms and conventions.
---

# Ruby Development Skill

## Purpose

This skill provides comprehensive guidance for Ruby development, covering language fundamentals, object-oriented design, error handling, performance optimization, and modern Ruby (3.x+) features. It synthesizes knowledge from Ruby internals, best practices, and official documentation to help Claude write idiomatic, maintainable, and performant Ruby code.

## When to Use This Skill

Use this skill when:

- Writing or reviewing Ruby code
- Debugging Ruby applications
- Optimizing Ruby performance
- Implementing object-oriented designs
- Handling errors and exceptions
- Working with Ruby's standard library
- Using modern Ruby features (pattern matching, types, fibers, ractors)
- Building Rails applications or Ruby gems

## Ruby Philosophy and Core Principles

### Matz's Design Philosophy

Ruby is designed to make programmers happy. It prioritizes:

1. **Developer Productivity** - Write less code to accomplish more
2. **Readability** - Code should read like natural language
3. **Flexibility** - Multiple ways to accomplish tasks (TMTOWTDI - There's More Than One Way To Do It)
4. **Object-Oriented Everything** - Everything is an object, including primitives
5. **Duck Typing** - "If it walks like a duck and quacks like a duck, it's a duck"

### Ruby's Core Characteristics

```ruby
# Everything is an object
5.times { puts "Hello" }          # Integer is an object
"hello".upcase                    # String is an object
nil.class                         # => NilClass

# Blocks are first-class citizens
[1, 2, 3].map { |n| n * 2 }      # => [2, 4, 6]

# Open classes - can modify any class
class String
  def shout
    "#{upcase}!"
  end
end

"hello".shout                     # => "HELLO!"

# Duck typing - focus on behavior, not type
def process(thing)
  thing.call if thing.respond_to?(:call)
end
```

## Object-Oriented Design in Ruby

### The Ruby Object Model

Understanding Ruby's object model is crucial for effective programming:

```ruby
# Class hierarchy
class Animal
  def speak
    "Some sound"
  end
end

class Dog < Animal
  def speak
    "Woof!"
  end
end

# Every class is an instance of Class
Dog.class                         # => Class
Dog.superclass                    # => Animal
Animal.superclass                 # => Object
Object.superclass                 # => BasicObject

# Singleton methods (eigenclass/metaclass)
dog = Dog.new
def dog.name
  "Buddy"
end

dog.name                          # => "Buddy"
Dog.new.name                      # NoMethodError
```

### Composition Over Inheritance

Prefer composition and modules over deep inheritance hierarchies:

```ruby
# ❌ Bad: Deep inheritance
class Vehicle
end

class LandVehicle < Vehicle
end

class Car < LandVehicle
end

class SportsCar < Car
end

# ✅ Good: Composition with modules
module Drivable
  def drive
    "Driving..."
  end
end

module Flyable
  def fly
    "Flying..."
  end
end

class Car
  include Drivable
end

class Plane
  include Flyable
  include Drivable  # Can taxi on ground
end
```

### Single Responsibility Principle

Each class should have one reason to change:

```ruby
# ❌ Bad: Multiple responsibilities
class User
  def save
    # Database logic
  end

  def send_email
    # Email logic
  end

  def generate_report
    # Report logic
  end
end

# ✅ Good: Separate concerns
class User
  def save
    UserRepository.new.save(self)
  end
end

class UserMailer
  def send_welcome_email(user)
    # Email logic
  end
end

class UserReportGenerator
  def generate(user)
    # Report logic
  end
end
```

### Dependency Injection

Inject dependencies rather than hardcoding them:

```ruby
# ❌ Bad: Hard dependency
class OrderProcessor
  def process(order)
    PaymentGateway.new.charge(order.amount)
    EmailService.new.send_confirmation(order)
  end
end

# ✅ Good: Dependency injection
class OrderProcessor
  def initialize(payment_gateway: PaymentGateway.new,
                 email_service: EmailService.new)
    @payment_gateway = payment_gateway
    @email_service = email_service
  end

  def process(order)
    @payment_gateway.charge(order.amount)
    @email_service.send_confirmation(order)
  end
end
```

### Law of Demeter (Principle of Least Knowledge)

Avoid reaching through multiple objects:

```ruby
# ❌ Bad: Train wreck
customer.orders.last.line_items.first.price

# ✅ Good: Delegate or encapsulate
class Customer
  def last_order_first_item_price
    orders.last&.first_item_price
  end
end

class Order
  def first_item_price
    line_items.first&.price
  end
end

customer.last_order_first_item_price
```

## Error Handling and Exceptions

### The Exception Hierarchy

```
Exception
├── NoMemoryError
├── ScriptError
│   ├── LoadError
│   ├── NotImplementedError
│   └── SyntaxError
├── SignalException
│   └── Interrupt
├── StandardError (Default rescue catches this)
│   ├── ArgumentError
│   ├── IOError
│   │   └── EOFError
│   ├── IndexError
│   ├── LocalJumpError
│   ├── NameError
│   │   └── NoMethodError
│   ├── RangeError
│   ├── RegexpError
│   ├── RuntimeError (Default raise creates this)
│   ├── SecurityError
│   ├── SystemCallError
│   ├── ThreadError
│   ├── TypeError
│   └── ZeroDivisionError
├── SystemExit
└── SystemStackError
```

### Exception Handling Best Practices

#### 1. Exceptions Should Be Exceptional

Use exceptions for exceptional cases, not control flow:

```ruby
# ❌ Bad: Using exceptions for control flow
def find_user(id)
  user = User.find(id)
rescue ActiveRecord::RecordNotFound
  nil
end

# ✅ Good: Use explicit checks
def find_user(id)
  User.find_by(id: id)
end
```

#### 2. Rescue Specific Exceptions

Always rescue specific exceptions, never bare rescue:

```ruby
# ❌ Bad: Catches everything, including SystemExit
begin
  dangerous_operation
rescue
  # Too broad!
end

# ✅ Good: Rescue specific exceptions
begin
  dangerous_operation
rescue NetworkError, TimeoutError => e
  logger.error("Network issue: #{e.message}")
  retry_operation
end
```

#### 3. Fail Fast, Fail Loudly

Let errors propagate unless you can handle them meaningfully:

```ruby
# ❌ Bad: Swallowing exceptions
def process_data(data)
  result = parse(data)
rescue => e
  nil  # Silent failure!
end

# ✅ Good: Let it fail or handle meaningfully
def process_data(data)
  parse(data)
rescue ParseError => e
  logger.error("Failed to parse data: #{e.message}")
  raise  # Re-raise to propagate
end
```

#### 4. Use ensure for Cleanup

Always use `ensure` for cleanup code:

```ruby
# ✅ Proper resource management
def process_file(filename)
  file = File.open(filename)
  process(file)
ensure
  file&.close
end

# Better: Use blocks that auto-close
def process_file(filename)
  File.open(filename) do |file|
    process(file)
  end  # Automatically closed
end
```

#### 5. Custom Exceptions for Domain Logic

Create custom exceptions for your domain:

```ruby
# Define custom exceptions
class PaymentError < StandardError; end
class InsufficientFundsError < PaymentError; end
class InvalidCardError < PaymentError; end

# Use them meaningfully
def charge_card(card, amount)
  raise InvalidCardError, "Card expired" if card.expired?
  raise InsufficientFundsError if balance < amount

  process_charge(card, amount)
end

# Caller can handle appropriately
begin
  charge_card(card, 100)
rescue InsufficientFundsError => e
  notify_user("Insufficient funds")
rescue InvalidCardError => e
  notify_user("Please update your card")
rescue PaymentError => e
  # Catch all payment errors
  logger.error("Payment failed: #{e.message}")
end
```

#### 6. The Weirich raise/fail Convention

Use `fail` for exceptions you expect to be rescued, `raise` for re-raising:

```ruby
def process_order(order)
  fail ArgumentError, "Order cannot be nil" if order.nil?

  begin
    payment_gateway.charge(order)
  rescue PaymentError => e
    logger.error("Payment failed: #{e.message}")
    raise  # Re-raise with raise
  end
end
```

#### 7. Provide Context in Exceptions

Include helpful information in exception messages:

```ruby
# ❌ Bad: Vague message
raise "Invalid input"

# ✅ Good: Descriptive message with context
raise ArgumentError, "Expected positive integer for age, got: #{age.inspect}"
```

### Alternative Error Handling Patterns

#### Result Objects

Return result objects instead of raising exceptions:

```ruby
class Result
  attr_reader :value, :error

  def initialize(value: nil, error: nil)
    @value = value
    @error = error
  end

  def success?
    error.nil?
  end

  def failure?
    !success?
  end
end

def divide(a, b)
  return Result.new(error: "Division by zero") if b.zero?
  Result.new(value: a / b)
end

result = divide(10, 2)
if result.success?
  puts result.value
else
  puts "Error: #{result.error}"
end
```

#### Caller-Supplied Fallback Strategy

Let callers define error handling:

```ruby
def fetch_user(id, &fallback)
  User.find(id)
rescue ActiveRecord::RecordNotFound => e
  fallback ? fallback.call(e) : raise
end

# Usage
user = fetch_user(999) { |e| User.new(name: "Guest") }
```

## Ruby Performance and Optimization

### Understanding Ruby's VM (YARV)

Ruby 3.x uses YARV (Yet Another Ruby VM) with JIT compilation:

```ruby
# Enable JIT (YJIT in Ruby 3.1+)
# Run with: ruby --yjit your_script.rb

# Check JIT status
puts "JIT enabled: #{defined?(RubyVM::YJIT)}"

# Profile JIT compilation
RubyVM::YJIT.runtime_stats if defined?(RubyVM::YJIT)
```

### Memory Management and Garbage Collection

Ruby uses generational garbage collection:

```ruby
# Check GC stats
GC.stat
# => {:count=>23, :heap_allocated_pages=>145, ...}

# Manual GC control (rarely needed)
GC.disable  # Disable GC temporarily
# ... do intensive work
GC.enable
GC.start    # Force GC

# Monitor object allocations
before = GC.stat(:total_allocated_objects)
# ... your code
after = GC.stat(:total_allocated_objects)
puts "Allocated: #{after - before} objects"
```

### Performance Best Practices

#### 1. Avoid Creating Unnecessary Objects

```ruby
# ❌ Bad: Creates many string objects
1000.times do |i|
  "User #{i}"  # New string each time
end

# ✅ Good: Reuse strings with interpolation
template = "User %d"
1000.times do |i|
  template % i
end

# ✅ Even better: Use frozen strings
MESSAGE = "Processing".freeze
```

#### 2. Use Symbols for Repeated Strings

```ruby
# ❌ Bad: Creates new string objects
hash = { "name" => "John", "age" => 30 }

# ✅ Good: Symbols are immutable and reused
hash = { name: "John", age: 30 }
```

#### 3. Prefer Enumerable Methods Over Loops

```ruby
# ❌ Bad: Manual loop
result = []
array.each do |item|
  result << item * 2 if item > 0
end

# ✅ Good: Chained enumerable methods
result = array.select { |item| item > 0 }
              .map { |item| item * 2 }

# ✅ Even better: Single pass with each_with_object
result = array.each_with_object([]) do |item, acc|
  acc << item * 2 if item > 0
end
```

#### 4. Use Lazy Enumerables for Large Collections

```ruby
# ❌ Bad: Creates intermediate arrays
(1..1_000_000).select { |n| n.even? }
              .map { |n| n * 2 }
              .first(10)

# ✅ Good: Lazy evaluation
(1..1_000_000).lazy
              .select { |n| n.even? }
              .map { |n| n * 2 }
              .first(10)
```

#### 5. Cache Expensive Computations

```ruby
# ❌ Bad: Recomputes every time
class User
  def full_name
    "#{first_name} #{last_name}".strip
  end
end

# ✅ Good: Memoization
class User
  def full_name
    @full_name ||= "#{first_name} #{last_name}".strip
  end
end

# ⚠️ Careful with nil/false values
def expensive_check
  return @result if defined?(@result)
  @result = compute_result
end
```

## Modern Ruby Features (3.x+)

### Pattern Matching (Ruby 2.7+)

```ruby
# Basic pattern matching
case [1, 2, 3]
in [a, b, c]
  puts "#{a}, #{b}, #{c}"
end

# Hash patterns
case { name: "John", age: 30 }
in { name: "John", age: age }
  puts "John is #{age}"
in { name:, age: }  # Variable punning
  puts "#{name} is #{age}"
end

# Array patterns with rest
case [1, 2, 3, 4, 5]
in [first, *rest, last]
  puts "First: #{first}, Last: #{last}, Rest: #{rest}"
end

# Rightward assignment (Ruby 3.0+)
{ name: "John", age: 30 } => { name:, age: }
puts name  # => "John"

# Guard clauses
case value
in String => s if s.length > 10
  puts "Long string: #{s}"
in String => s
  puts "Short string: #{s}"
end
```

### Endless Method Definition (Ruby 3.0+)

```ruby
# Traditional
def square(x)
  x * x
end

# Endless method (for simple one-liners)
def square(x) = x * x
def full_name = "#{first_name} #{last_name}"
def admin? = role == "admin"
```

### Numbered Parameters (Ruby 2.7+)

```ruby
# Traditional block parameters
[1, 2, 3].map { |n| n * 2 }

# Numbered parameters
[1, 2, 3].map { _1 * 2 }

# Multiple numbered parameters
hash.map { [_1, _2 * 2] }
```

### Rightward Assignment (Ruby 3.0+)

```ruby
# Traditional assignment
result = compute_value()
puts result

# Rightward assignment (useful in method chains)
compute_value() => result
puts result

# Useful for debugging
calculate_price.tap { p _1 } => price
```

### Ractors (Ruby 3.0+) - True Parallelism

```ruby
# Create parallel-safe ractor
r = Ractor.new do
  received = Ractor.receive
  received * 2
end

r.send(21)
r.take  # => 42

# Multiple ractors
results = 4.times.map do |i|
  Ractor.new(i) do |n|
    # Heavy computation
    (1..1000000).reduce(:+) + n
  end
end

results.map(&:take)  # Runs in parallel
```

### Typed Ruby with RBS (Ruby 3.0+)

```ruby
# Define types in .rbs files
# user.rbs
class User
  attr_reader name: String
  attr_reader age: Integer

  def initialize: (name: String, age: Integer) -> void
  def adult?: () -> bool
end

# Use TypeProf to generate signatures
# $ typeprof user.rb

# Validate with Steep or RBS
# $ steep check
```

### Fiber Scheduler (Ruby 3.0+) - Non-blocking I/O

```ruby
require 'async'

# Async execution with fibers
Async do
  Async do
    puts "Task 1 start"
    sleep 2
    puts "Task 1 end"
  end

  Async do
    puts "Task 2 start"
    sleep 1
    puts "Task 2 end"
  end
end
# Both tasks run concurrently
```

## Ruby Standard Library Essentials

### Working with Collections

```ruby
# Array operations
arr = [1, 2, 3, 4, 5]

arr.first(2)                # => [1, 2]
arr.last(2)                 # => [4, 5]
arr.sample                  # Random element
arr.shuffle                 # Randomize order
arr.rotate(2)               # => [3, 4, 5, 1, 2]
arr.combination(2).to_a     # All 2-element combinations
arr.permutation(2).to_a     # All 2-element permutations

# Hash operations
hash = { a: 1, b: 2, c: 3 }

hash.fetch(:d, 0)           # => 0 (default value)
hash.dig(:nested, :key)     # Safe nested access
hash.transform_values(&:to_s)  # => { a: "1", b: "2", c: "3" }
hash.slice(:a, :b)          # => { a: 1, b: 2 }
hash.merge(d: 4)            # Non-destructive merge

# Set operations
require 'set'
s1 = Set[1, 2, 3]
s2 = Set[2, 3, 4]

s1 | s2                     # Union => #<Set: {1, 2, 3, 4}>
s1 & s2                     # Intersection => #<Set: {2, 3}>
s1 - s2                     # Difference => #<Set: {1}>
```

### String Manipulation

```ruby
# String methods
str = "  Hello, World!  "

str.strip                   # => "Hello, World!"
str.split(", ")             # => ["Hello", "World!"]
str.gsub("World", "Ruby")   # => "  Hello, Ruby!  "
str.scan(/\w+/)             # => ["Hello", "World"]
str.start_with?("Hello")    # => false (has spaces)
str.include?("World")       # => true

# String interpolation
name = "John"
age = 30
"#{name} is #{age}"         # => "John is 30"
"2 + 2 = #{2 + 2}"          # => "2 + 2 = 4"

# Heredocs
text = <<~TEXT
  This is a heredoc.
  Indentation is removed.
  Very useful for multi-line strings.
TEXT

# Frozen strings (immutable)
CONSTANT = "immutable".freeze
# Or with magic comment:
# frozen_string_literal: true
```

### File I/O

```ruby
# Reading files
content = File.read("file.txt")
lines = File.readlines("file.txt")

# Block-based reading (auto-closes)
File.open("file.txt") do |file|
  file.each_line do |line|
    puts line
  end
end

# Writing files
File.write("output.txt", "Hello, World!")

File.open("output.txt", "w") do |file|
  file.puts "Line 1"
  file.puts "Line 2"
end

# File operations
File.exist?("file.txt")
File.directory?("path")
File.size("file.txt")
File.mtime("file.txt")      # Modification time

# Directory operations
Dir.glob("**/*.rb")         # Find all Ruby files recursively
Dir.foreach("path") { |file| puts file }
Dir.mkdir("new_dir")
```

### Regular Expressions

```ruby
# Pattern matching
text = "Hello, my email is [email protected]"

# Match operator
text =~ /\w+@\w+\.\w+/      # => 18 (match position)

# Match method
match = text.match(/(\w+)@(\w+)\.(\w+)/)
match[0]                     # => "[email protected]"
match[1]                     # => "john"
match[2]                     # => "example"

# Named captures
match = text.match(/(?<user>\w+)@(?<domain>\w+)\.(?<tld>\w+)/)
match[:user]                 # => "john"
match[:domain]               # => "example"

# Scan for all matches
emails = text.scan(/\w+@\w+\.\w+/)

# Replace with regex
text.gsub(/\b\w{4}\b/, "****")  # Mask 4-letter words
```

## Testing Ruby Code

### Minitest (Standard Library)

```ruby
require 'minitest/autorun'

class UserTest < Minitest::Test
  def setup
    @user = User.new(name: "John", age: 30)
  end

  def test_adult_with_age_over_18
    assert @user.adult?
  end

  def test_name_is_capitalized
    assert_equal "John", @user.name
  end

  def test_invalid_age_raises_error
    assert_raises(ArgumentError) do
      User.new(name: "John", age: -5)
    end
  end

  def teardown
    # Cleanup if needed
  end
end
```

### RSpec (Popular Testing Framework)

```ruby
require 'rspec'

RSpec.describe User do
  let(:user) { User.new(name: "John", age: 30) }

  describe '#adult?' do
    context 'when age is over 18' do
      it 'returns true' do
        expect(user.adult?).to be true
      end
    end

    context 'when age is under 18' do
      let(:user) { User.new(name: "Jane", age: 15) }

      it 'returns false' do
        expect(user.adult?).to be false
      end
    end
  end

  describe '#initialize' do
    it 'raises error for negative age' do
      expect { User.new(name: "John", age: -5) }
        .to raise_error(ArgumentError, /negative age/)
    end
  end

  describe '#name' do
    it 'returns capitalized name' do
      expect(user.name).to eq("John")
    end
  end
end
```

### Testing Best Practices

```ruby
# 1. Use descriptive test names
def test_user_is_adult_when_age_is_over_18
  # Clear what is being tested
end

# 2. Arrange-Act-Assert pattern
def test_order_total
  # Arrange
  order = Order.new
  order.add_item(item: "Book", price: 10)
  order.add_item(item: "Pen", price: 2)

  # Act
  total = order.total

  # Assert
  assert_equal 12, total
end

# 3. Test one thing per test
# ❌ Bad: Tests multiple things
def test_user
  assert user.valid?
  assert_equal "John", user.name
  assert_equal 30, user.age
end

# ✅ Good: Separate tests
def test_user_is_valid
  assert user.valid?
end

def test_user_name
  assert_equal "John", user.name
end

# 4. Use fixtures/factories for test data
# factories.rb
FactoryBot.define do
  factory :user do
    name { "John" }
    age { 30 }
    email { "[email protected]" }
  end
end

# In tests
user = create(:user)
user_attrs = attributes_for(:user)
```

## Common Ruby Patterns and Idioms

### Method Chaining (Fluent Interface)

```ruby
class QueryBuilder
  def initialize
    @conditions = []
    @order = nil
  end

  def where(condition)
    @conditions << condition
    self  # Return self for chaining
  end

  def order(field)
    @order = field
    self
  end

  def to_sql
    sql = "SELECT * FROM users"
    sql += " WHERE #{@conditions.join(' AND ')}" unless @conditions.empty?
    sql += " ORDER BY #{@order}" if @order
    sql
  end
end

# Usage
query = QueryBuilder.new
         .where("age > 18")
         .where("active = true")
         .order("name")
         .to_sql
```

### Builder Pattern

```ruby
class UserBuilder
  def initialize
    @user = User.new
  end

  def with_name(name)
    @user.name = name
    self
  end

  def with_email(email)
    @user.email = email
    self
  end

  def build
    @user
  end
end

# Usage
user = UserBuilder.new
        .with_name("John")
        .with_email("[email protected]")
        .build
```

### Null Object Pattern

```ruby
class NullUser
  def name
    "Guest"
  end

  def admin?
    false
  end

  def logged_in?
    false
  end
end

class UserSession
  def current_user
    @current_user || NullUser.new
  end
end

# Usage - no nil checks needed
session = UserSession.new
puts session.current_user.name  # "Guest" instead of error
```

### Strategy Pattern

```ruby
# Define strategies
class CreditCardPayment
  def process(amount)
    # Credit card logic
  end
end

class PayPalPayment
  def process(amount)
    # PayPal logic
  end
end

# Use strategy
class Order
  def initialize(payment_strategy)
    @payment_strategy = payment_strategy
  end

  def checkout(amount)
    @payment_strategy.process(amount)
  end
end

# Usage
order = Order.new(CreditCardPayment.new)
order.checkout(100)
```

### Observer Pattern

```ruby
require 'observer'

class Order
  include Observable

  attr_reader :status

  def status=(new_status)
    @status = new_status
    changed
    notify_observers(self)
  end
end

class Logger
  def update(order)
    puts "Order status changed to: #{order.status}"
  end
end

class Emailer
  def update(order)
    puts "Sending email about: #{order.status}"
  end
end

# Usage
order = Order.new
order.add_observer(Logger.new)
order.add_observer(Emailer.new)
order.status = "shipped"
```

## Ruby Code Style and Conventions

### Naming Conventions

```ruby
# Classes and Modules: PascalCase
class UserAccount
end

module PaymentProcessing
end

# Methods and Variables: snake_case
def calculate_total_price
  total_amount = 0
end

# Constants: SCREAMING_SNAKE_CASE
MAX_RETRIES = 3
DEFAULT_TIMEOUT = 30

# Predicate methods: end with ?
def valid?
  errors.empty?
end

def admin?
  role == 'admin'
end

# Dangerous methods: end with !
def save!  # Raises exception on failure
  raise "Invalid" unless valid?
  persist
end

def downcase!  # Mutates the object
  @value = @value.downcase
end
```

### Code Organization

```ruby
# Class organization
class User
  # 1. Extend and include statements
  extend SomeModule
  include AnotherModule

  # 2. Constants
  MAX_NAME_LENGTH = 100

  # 3. Attribute macros
  attr_reader :id
  attr_accessor :name

  # 4. Class methods
  def self.find(id)
    # ...
  end

  # 5. Initialization
  def initialize(name)
    @name = name
  end

  # 6. Public instance methods
  def full_name
    "#{first_name} #{last_name}"
  end

  # 7. Protected methods
  protected

  def internal_helper
    # ...
  end

  # 8. Private methods
  private

  def calculate_something
    # ...
  end
end
```

### Ruby Style Guidelines

```ruby
# Use 2 spaces for indentation
def method_name
  if condition
    do_something
  end
end

# Avoid ternary operators for multi-line
# ❌ Bad
result = some_long_condition ?
         long_true_value :
         long_false_value

# ✅ Good
result = if some_long_condition
          long_true_value
        else
          long_false_value
        end

# Use %w for word arrays
# ❌ Bad
STATES = ['draft', 'published', 'archived']

# ✅ Good
STATES = %w[draft published archived]

# Use symbols for hash keys
# ❌ Bad (when strings aren't needed)
{ 'name' => 'John', 'age' => 30 }

# ✅ Good
{ name: 'John', age: 30 }

# Use guard clauses
# ❌ Bad
def process(value)
  if value
    if value.valid?
      # ... main logic
    end
  end
end

# ✅ Good
def process(value)
  return unless value
  return unless value.valid?

  # ... main logic
end

# Avoid returning from ensure
# ❌ Bad - return value is ignored
def bad_example
  return 42
ensure
  return 0  # This overrides!
end

# ✅ Good
def good_example
  result = 42
ensure
  cleanup
end
```

## Debugging Ruby Code

### Using pry for Debugging

```ruby
require 'pry'

def complex_method(data)
  result = transform(data)
  binding.pry  # Execution pauses here
  result * 2
end

# In pry session:
# - ls: List available methods
# - show-method method_name: Show method source
# - cd object: Enter object context
# - whereami: Show context
# - continue: Resume execution
```

### Using ruby/debug (Ruby 3.1+)

```ruby
require 'debug'

def calculate(x, y)
  debugger  # Execution pauses here
  result = x + y
  result
end

# Commands:
# - step: Step into
# - next: Step over
# - continue: Resume
# - info: Show information
# - break: Set breakpoint
```

### Logging Best Practices

```ruby
require 'logger'

logger = Logger.new(STDOUT)
logger.level = Logger::INFO

# Different log levels
logger.debug("Detailed debug information")
logger.info("Informational messages")
logger.warn("Warning messages")
logger.error("Error messages")
logger.fatal("Fatal errors")

# Structured logging
logger.info("User logged in") do
  { user_id: 123, ip: "192.168.1.1" }
end
```

## Concurrency and Threading

### Thread Basics

```ruby
# Create threads
threads = 3.times.map do |i|
  Thread.new(i) do |thread_num|
    puts "Thread #{thread_num} starting"
    sleep 1
    puts "Thread #{thread_num} done"
  end
end

# Wait for all threads
threads.each(&:join)

# Thread-local variables
Thread.current[:user_id] = 123
Thread.current[:user_id]  # => 123
```

### Thread Safety

```ruby
# ❌ Bad: Race condition
class Counter
  def initialize
    @count = 0
  end

  def increment
    @count += 1  # Not atomic!
  end
end

# ✅ Good: Thread-safe with mutex
class Counter
  def initialize
    @count = 0
    @mutex = Mutex.new
  end

  def increment
    @mutex.synchronize do
      @count += 1
    end
  end
end

# ✅ Better: Use Concurrent::AtomicFixnum
require 'concurrent'

counter = Concurrent::AtomicFixnum.new(0)
counter.increment
```

### Ractors for Parallelism (Ruby 3.0+)

```ruby
# True parallel execution
def parallel_map(array, &block)
  ractors = array.map do |item|
    Ractor.new(item, block) do |value, transform|
      transform.call(value)
    end
  end

  ractors.map(&:take)
end

# Usage
results = parallel_map([1, 2, 3, 4]) { |n| n * 2 }
# => [2, 4, 6, 8]
```

## Metaprogramming

### method_missing

```ruby
class DynamicAccessor
  def initialize(data)
    @data = data
  end

  def method_missing(method, *args)
    if @data.key?(method)
      @data[method]
    else
      super
    end
  end

  def respond_to_missing?(method, include_private = false)
    @data.key?(method) || super
  end
end

# Usage
obj = DynamicAccessor.new(name: "John", age: 30)
obj.name  # => "John"
obj.age   # => 30
```

### define_method

```ruby
class Model
  %w[name email age].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

# Creates name, name=, email, email=, age, age= methods
```

### class_eval and instance_eval

```ruby
# class_eval: Evaluates in class context
String.class_eval do
  def shout
    upcase + "!"
  end
end

"hello".shout  # => "HELLO!"

# instance_eval: Evaluates in instance context
str = "hello"
str.instance_eval do
  def custom_method
    "Custom: #{self}"
  end
end

str.custom_method  # => "Custom: hello"
```

## Memory and Performance Profiling

### Benchmark Module

```ruby
require 'benchmark'

n = 1_000_000
Benchmark.bm(20) do |x|
  x.report("Array#each:") do
    arr = []
    n.times { |i| arr << i }
  end

  x.report("Array#map:") do
    (0...n).map { |i| i }
  end

  x.report("Array.new:") do
    Array.new(n) { |i| i }
  end
end
```

### Memory Profiler

```ruby
require 'memory_profiler'

report = MemoryProfiler.report do
  # Code to profile
  1000.times { "string" + "concatenation" }
end

report.pretty_print
```

### Ruby Profiler

```ruby
require 'ruby-prof'

result = RubyProf.profile do
  # Code to profile
  10_000.times { expensive_operation }
end

printer = RubyProf::FlatPrinter.new(result)
printer.print(STDOUT)
```

## Common Pitfalls and How to Avoid Them

### 1. Modifying Collections During Iteration

```ruby
# ❌ Bad: Modifies while iterating
array = [1, 2, 3, 4, 5]
array.each do |item|
  array.delete(item) if item.even?  # Unpredictable!
end

# ✅ Good: Use reject or delete_if
array.reject! { |item| item.even? }
# Or
array.delete_if { |item| item.even? }
```

### 2. Unintended Global Variable Modification

```ruby
# ❌ Bad: Global variable
$user_count = 0

# ✅ Good: Class or instance variable
class UserCounter
  @count = 0

  class << self
    attr_accessor :count
  end
end
```

### 3. String Concatenation in Loops

```ruby
# ❌ Bad: Creates many string objects
result = ""
1000.times { |i| result += "#{i} " }

# ✅ Good: Use array join
result = 1000.times.map { |i| "#{i} " }.join

# ✅ Better: Use string builder
result = String.new
1000.times { |i| result << "#{i} " }
```

### 4. Forgetting to Return Values

```ruby
# ❌ Bad: No explicit return
def calculate
  total = items.sum
  # Implicitly returns total, but unclear
end

# ✅ Good: Explicit return for clarity
def calculate
  total = items.sum
  return total
end

# ✅ Best: Last expression is return value
def calculate
  items.sum
end
```

## Framework-Specific Guidance

### Rails-Specific Best Practices

```ruby
# Use scopes for reusable queries
class User < ApplicationRecord
  scope :active, -> { where(active: true) }
  scope :recent, -> { where('created_at > ?', 1.week.ago) }
end

# Use concerns for shared behavior
module Timestampable
  extend ActiveSupport::Concern

  included do
    before_save :update_timestamp
  end

  def update_timestamp
    self.updated_at = Time.current
  end
end

# Use strong parameters
class UsersController < ApplicationController
  def create
    @user = User.new(user_params)
    # ...
  end

  private

  def user_params
    params.require(:user).permit(:name, :email, :age)
  end
end

# Eager loading to avoid N+1 queries
# ❌ Bad: N+1 query
users = User.all
users.each { |user| puts user.posts.count }

# ✅ Good: Eager load
users = User.includes(:posts).all
users.each { |user| puts user.posts.count }
```

## Quick Reference Commands

```bash
# Ruby version
ruby -v

# Run Ruby file
ruby script.rb

# Interactive Ruby (IRB)
irb

# Execute inline Ruby
ruby -e "puts 'Hello, World!'"

# Check syntax without executing
ruby -c script.rb

# Run with warnings
ruby -w script.rb

# Install gem
gem install gem_name

# List installed gems
gem list

# Update gems
gem update

# Bundle install (Rails)
bundle install

# Run tests
ruby test/my_test.rb
rake test
rspec spec/

# Ruby documentation
ri String#upcase
ri Array

# Generate documentation
rdoc
yard doc
```

## Resources and Further Learning

- **Official Ruby Documentation**: <https://docs.ruby-lang.org>
- **Ruby Style Guide**: <https://rubystyle.guide>
- **Ruby Weekly Newsletter**: <https://rubyweekly.com>
- **The Ruby Toolbox**: <https://www.ruby-toolbox.com>
- **RubyGems**: <https://rubygems.org>

## Summary

Ruby is designed for developer happiness and productivity. When writing Ruby code:

1. **Write readable code** - Code is read more than it's written
2. **Follow conventions** - Consistency helps teams collaborate
3. **Test thoroughly** - Tests give confidence in refactoring
4. **Handle errors explicitly** - Fail fast and provide context
5. **Optimize when necessary** - Profile before optimizing
6. **Embrace Ruby's features** - Use blocks, modules, and metaprogramming appropriately
7. **Stay current** - Ruby 3.x brings significant improvements

Remember: Ruby rewards simple, expressive code that clearly communicates intent.