| name | ruby-core |
| description | This skill should be used when the user asks about "Ruby basics", "blocks", "procs", "lambdas", "enumerables", "iterators", "pattern matching", "error handling", "exceptions", "Ruby 3 features", "Ractors", "Data class", "endless methods", "refinements", or needs guidance on Ruby language features and idioms. |
| version | 1.0.0 |
Ruby Core Language Features
Comprehensive guide to Ruby 3.x core language features, idioms, and best practices.
Blocks, Procs, and Lambdas
Blocks
Blocks are anonymous functions passed to methods:
[1, 2, 3].each do |n|
puts n * 2
end
doubled = [1, 2, 3].map { |n| n * 2 }
def with_timing
start = Time.now
result = yield
puts "Took #{Time.now - start}s"
result
end
with_timing { expensive_operation }
def transform(value)
yield(value) if block_given?
end
transform(5) { |n| n * 2 }
def save_block(&block)
@callback = block
end
Procs vs Lambdas
my_proc = Proc.new { |a, b| a + b }
my_proc = proc { |a, b| a + b }
my_proc.call(1, 2)
my_proc.call(1, 2, 3)
my_proc.call(1)
my_lambda = ->(a, b) { a + b }
my_lambda = lambda { |a, b| a + b }
my_lambda.call(1, 2)
my_lambda.call(1, 2, 3)
my_lambda.call(1)
def proc_return
p = Proc.new { return "from proc" }
p.call
"after proc"
end
def lambda_return
l = -> { return "from lambda" }
l.call
"after lambda"
end
Method Objects
def double(n) = n * 2
[1, 2, 3].map(&method(:double))
unbound = String.instance_method(:upcase)
bound = unbound.bind("hello")
bound.call
Enumerables
Core Enumerable Methods
numbers = [1, 2, 3, 4, 5]
numbers.map { |n| n * 2 }
numbers.flat_map { |n| [n, n * 2] }
numbers.select { |n| n.even? }
numbers.reject { |n| n.even? }
numbers.filter_map { |n| n * 2 if n.even? }
numbers.reduce(0) { |sum, n| sum + n }
numbers.reduce(:+)
numbers.sum
numbers.find { |n| n > 3 }
numbers.find_index { |n| n > 3 }
numbers.any? { |n| n > 3 }
numbers.all? { |n| n > 0 }
numbers.none? { |n| n > 10 }
numbers.one? { |n| n == 3 }
numbers.group_by { |n| n % 2 }
numbers.partition { |n| n.even? }
numbers.tally
numbers.sort_by { |n| -n }
numbers.max_by { |n| n % 3 }
numbers.min_by { |n| n % 3 }
numbers.lazy.select { |n| n.even? }.map { |n| n * 2 }.first(2)
Lazy Enumerables
(1..Float::INFINITY)
.lazy
.select { |n| n % 3 == 0 }
.map { |n| n * 2 }
.first(5)
File.foreach("large_file.txt")
.lazy
.map(&:chomp)
.select { |line| line.include?("ERROR") }
.first(10)
Pattern Matching (Ruby 3.x)
Basic Patterns
case value
in Integer => n if n > 0
"positive integer: #{n}"
in Integer
"non-positive integer"
in String => s
"string: #{s}"
in [first, *rest]
"array starting with #{first}"
in { name:, age: }
"person: #{name}, #{age}"
in nil
"nothing"
else
"unknown"
end
Array Patterns
case [1, 2, 3]
in [a, b, c]
"three elements: #{a}, #{b}, #{c}"
in [head, *tail]
"head: #{head}, tail: #{tail}"
in [*, last]
"last element: #{last}"
in [first, *, last]
"first: #{first}, last: #{last}"
end
Hash Patterns
case { name: "Alice", age: 30, role: "admin" }
in { name: "Alice", role: }
"Alice is #{role}"
in { name:, age: 18.. }
"#{name} is an adult"
in { **rest }
"other: #{rest}"
end
Guard Clauses
case number
in n if n.negative?
"negative"
in n if n.zero?
"zero"
in n if n.positive?
"positive"
end
Pinning
expected = 42
case value
in ^expected
"matched expected value"
in other
"got #{other} instead of #{expected}"
end
Error Handling
Exception Basics
begin
risky_operation
rescue SpecificError => e
handle_specific_error(e)
rescue StandardError => e
handle_general_error(e)
else
success_callback
ensure
cleanup
end
value = risky_operation rescue default_value
attempts = 0
begin
attempts += 1
unreliable_api_call
rescue NetworkError
retry if attempts < 3
raise
end
Custom Exceptions
class ApplicationError < StandardError; end
class ValidationError < ApplicationError
attr_reader :field, :value
def initialize(message, field:, value:)
@field = field
@value = value
super(message)
end
end
raise ValidationError.new("Invalid email", field: :email, value: input)
Exception Hierarchy
Exception
├── NoMemoryError
├── ScriptError
├── SignalException
├── SystemExit
└── StandardError (catch this, not Exception)
├── ArgumentError
├── IOError
├── NameError
│ └── NoMethodError
├── RangeError
├── RuntimeError (default for `raise`)
├── TypeError
└── ZeroDivisionError
Ruby 3.x Features
Data Classes (Ruby 3.2+)
Point = Data.define(:x, :y)
p = Point.new(1, 2)
p.x
p.with(x: 10)
Point = Data.define(:x, :y) do
def distance_from_origin
Math.sqrt(x**2 + y**2)
end
end
Ractors (Parallel Execution)
ractors = 4.times.map do |i|
Ractor.new(i) do |n|
heavy_computation(n)
end
end
results = ractors.map(&:take)
Endless Methods
def double(n) = n * 2
def greet(name) = "Hello, #{name}!"
def square(n) = n ** 2
Refinements
module StringExtensions
refine String do
def shout
upcase + "!"
end
end
end
class Greeter
using StringExtensions
def greet(name)
"hello #{name}".shout
end
end
"test".shout
Numbered Block Parameters
[1, 2, 3].map { _1 * 2 }
[[1, 2], [3, 4]].map { _1 + _2 }
Best Practices
Guard Clauses
def process(value)
return if value.nil?
return "empty" if value.empty?
end
def process(value)
if value
if value.empty?
"empty"
else
end
end
end
Safe Navigation
user&.profile&.avatar&.url
user&.profile&.avatar&.url || "default.png"
Frozen String Literals
name = "Alice"
name << " Bob"
mutable = +name
mutable << " Bob"