smithery/bastos

ruby-core

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.

Installation

$ npx skills add smithery/bastos --skill ruby-core

Summary

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.

Similar popular skills

Related neighbors and high-traction skills in the same topics — useful to compare before installing.

Also in this package

Other skills from smithery/bastos.

npx skills add smithery/bastos

Browse all from smithery/bastos

More details

Agent compatibility

Declared targets from SKILL.md / docs. Unmarked agents are not listed — the skill may still install via the CLI.

Claude Code Not declared
Cursor Not declared
Codex Not declared
GitHub Copilot Not declared
Windsurf Not declared
Gemini CLI Not declared
Cline Not declared
OpenCode Not declared

Skill metadata

Parsed from SKILL.md frontmatter.

Version1.0.0

Package contents

Files included with this skill beyond the listing page.

  • skill md SKILL.md 7,915 B
  • docs SUMMARY.md 326 B

History

  1. First recorded snapshot · 0 installs

SKILL.md

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:

# 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

Procs vs Lambdas

# 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

Method Objects

# 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"

Enumerables

Core Enumerable Methods

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)

Lazy Enumerables

# 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)

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
  # 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

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+)

# 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

Ractors (Parallel Execution)

# 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)

Endless Methods

# Single-expression method shorthand
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  # => "HELLO NAME!"
  end
end

# Outside the class, shout is not available
"test".shout  # NoMethodError

Numbered Block Parameters

# 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]

Best Practices

Guard Clauses

# 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

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

# With default
user&.profile&.avatar&.url || "default.png"

Frozen String Literals

# 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