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.
/plugin marketplace add bastos/ruby-plugin-marketplace/plugin install ruby@ruby-plugin-marketplaceThis skill inherits all available tools. When active, it can use any tool Claude has access to.
Comprehensive guide to Ruby 3.x core language features, idioms, and best practices.
Blocks are anonymous functions passed to methods:
# Block with do...end (multi-line)
[1, 2, 3].each do |n|
puts n * 2
end
# Block with braces (single-line, returns value)
doubled = [1, 2, 3].map { |n| n * 2 }
# Yielding to blocks
def with_timing
start = Time.now
result = yield
puts "Took #{Time.now - start}s"
result
end
with_timing { expensive_operation }
# Block with arguments
def transform(value)
yield(value) if block_given?
end
transform(5) { |n| n * 2 } # => 10
# Capturing blocks as Proc
def save_block(&block)
@callback = block
end
# Proc - flexible argument handling, returns from enclosing method
my_proc = Proc.new { |a, b| a + b }
my_proc = proc { |a, b| a + b }
my_proc.call(1, 2) # => 3
my_proc.call(1, 2, 3) # => 3 (extra args ignored)
my_proc.call(1) # => TypeError (nil + 1)
# Lambda - strict argument checking, returns to caller
my_lambda = ->(a, b) { a + b }
my_lambda = lambda { |a, b| a + b }
my_lambda.call(1, 2) # => 3
my_lambda.call(1, 2, 3) # ArgumentError
my_lambda.call(1) # ArgumentError
# Key difference: return behavior
def proc_return
p = Proc.new { return "from proc" }
p.call
"after proc" # Never reached
end
def lambda_return
l = -> { return "from lambda" }
l.call
"after lambda" # This executes
end
# Convert method to proc
def double(n) = n * 2
[1, 2, 3].map(&method(:double)) # => [2, 4, 6]
# Unbound methods
unbound = String.instance_method(:upcase)
bound = unbound.bind("hello")
bound.call # => "HELLO"
numbers = [1, 2, 3, 4, 5]
# Transformation
numbers.map { |n| n * 2 } # => [2, 4, 6, 8, 10]
numbers.flat_map { |n| [n, n * 2] } # => [1, 2, 2, 4, 3, 6, 4, 8, 5, 10]
# Filtering
numbers.select { |n| n.even? } # => [2, 4]
numbers.reject { |n| n.even? } # => [1, 3, 5]
numbers.filter_map { |n| n * 2 if n.even? } # => [4, 8]
# Reduction
numbers.reduce(0) { |sum, n| sum + n } # => 15
numbers.reduce(:+) # => 15
numbers.sum # => 15
# Searching
numbers.find { |n| n > 3 } # => 4
numbers.find_index { |n| n > 3 } # => 3
numbers.any? { |n| n > 3 } # => true
numbers.all? { |n| n > 0 } # => true
numbers.none? { |n| n > 10 } # => true
numbers.one? { |n| n == 3 } # => true
# Grouping
numbers.group_by { |n| n % 2 } # => {1=>[1,3,5], 0=>[2,4]}
numbers.partition { |n| n.even? } # => [[2,4], [1,3,5]]
numbers.tally # Count occurrences
# Ordering
numbers.sort_by { |n| -n } # => [5, 4, 3, 2, 1]
numbers.max_by { |n| n % 3 } # => 2
numbers.min_by { |n| n % 3 } # => 3
# Chaining
numbers.lazy.select { |n| n.even? }.map { |n| n * 2 }.first(2)
# Process large/infinite sequences efficiently
(1..Float::INFINITY)
.lazy
.select { |n| n % 3 == 0 }
.map { |n| n * 2 }
.first(5)
# => [6, 12, 18, 24, 30]
# Memory-efficient file processing
File.foreach("large_file.txt")
.lazy
.map(&:chomp)
.select { |line| line.include?("ERROR") }
.first(10)
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
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
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
case number
in n if n.negative?
"negative"
in n if n.zero?
"zero"
in n if n.positive?
"positive"
end
expected = 42
case value
in ^expected
"matched expected value"
in other
"got #{other} instead of #{expected}"
end
begin
risky_operation
rescue SpecificError => e
handle_specific_error(e)
rescue StandardError => e
handle_general_error(e)
else
# Runs if no exception
success_callback
ensure
# Always runs
cleanup
end
# Inline rescue (use sparingly)
value = risky_operation rescue default_value
# Retry pattern
attempts = 0
begin
attempts += 1
unreliable_api_call
rescue NetworkError
retry if attempts < 3
raise
end
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
├── NoMemoryError
├── ScriptError
├── SignalException
├── SystemExit
└── StandardError (catch this, not Exception)
├── ArgumentError
├── IOError
├── NameError
│ └── NoMethodError
├── RangeError
├── RuntimeError (default for `raise`)
├── TypeError
└── ZeroDivisionError
# Immutable value objects
Point = Data.define(:x, :y)
p = Point.new(1, 2)
p.x # => 1
p.with(x: 10) # => Point(x: 10, y: 2)
# With methods
Point = Data.define(:x, :y) do
def distance_from_origin
Math.sqrt(x**2 + y**2)
end
end
# True parallelism without GVL
ractors = 4.times.map do |i|
Ractor.new(i) do |n|
# Runs in parallel
heavy_computation(n)
end
end
results = ractors.map(&:take)
# Single-expression method shorthand
def double(n) = n * 2
def greet(name) = "Hello, #{name}!"
def square(n) = n ** 2
module StringExtensions
refine String do
def shout
upcase + "!"
end
end
end
class Greeter
using StringExtensions
def greet(name)
"hello #{name}".shout # => "HELLO NAME!"
end
end
# Outside the class, shout is not available
"test".shout # NoMethodError
# Use _1, _2, etc. for simple blocks
[1, 2, 3].map { _1 * 2 } # => [2, 4, 6]
[[1, 2], [3, 4]].map { _1 + _2 } # => [3, 7]
# Prefer
def process(value)
return if value.nil?
return "empty" if value.empty?
# main logic
end
# Over
def process(value)
if value
if value.empty?
"empty"
else
# main logic
end
end
end
# Safe navigation operator
user&.profile&.avatar&.url
# With default
user&.profile&.avatar&.url || "default.png"
# frozen_string_literal: true
# All string literals are frozen, improving performance
name = "Alice" # Frozen, cannot be mutated
name << " Bob" # FrozenError
# Use +@ to get mutable copy
mutable = +name
mutable << " Bob" # OK
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.