| name | code:ruby |
| description | Ruby coding practices and idioms. Service objects, Data classes, pattern matching, dry-rb, memoization.
<example>
Context: User is writing Ruby code
user: "implement a service object for user registration"
</example>
<example>
Context: User needs Ruby patterns
user: "what's the best way to handle config in Ruby"
</example>
|
Tools Reference
Built-in Tools
| Tool | Purpose |
|---|
Read | Read .rb files |
Write | Create new Ruby files |
Edit | Modify Ruby code |
Bash | Run bundle, ruby, rspec, standardrb, srb |
Glob | Find Ruby files (*.rb) |
Grep | Search Ruby code |
Related Skills
marauder:code:ruby-cli - Thor CLI development
marauder:code:ruby-test - RSpec testing
marauder:code:ruby-rails - Rails API development
marauder:code:ruby-gem - Gem development
marauder:code:ruby-tooling - Lint/format/typecheck/validate
Ruby Coding Practices
Modern Ruby idioms focused on readability and maintainability.
Module Organization
Flat Namespace Declaration
module Foo::Bar::Baz
def self.call
end
end
module Foo
module Bar
module Baz
end
end
end
Module Functions
module StringUtils
module_function
def truncate(str, length)
str.length > length ? "#{str[0, length - 3]}..." : str
end
def slugify(str)
str.downcase.strip.gsub(/\s+/, '-').gsub(/[^\w-]/, '')
end
end
Class-Level Singleton Methods
module Api::Client
class << self
def get(url)
end
private
def connection
@connection ||= Faraday.new
end
end
end
Service Objects
Encapsulate business logic in callable objects:
class CreateOrder
def self.call(...)
new(...).call
end
def initialize(user:, items:, coupon: nil)
@user = user
@items = items
@coupon = coupon
end
def call
return failure(:no_items) if @items.empty?
return failure(:invalid_coupon) unless valid_coupon?
order = build_order
order.save ? success(order) : failure(order.errors)
end
private
end
Data Structures
Data Classes (Ruby 3.2+)
Order = Data.define(:id, :items, :status) do
def total
items.sum(&:price)
end
end
Struct (When Mutability Needed)
Point = Struct.new(:x, :y, keyword_init: true) do
def distance_from_origin
Math.sqrt(x**2 + y**2)
end
end
Pattern Matching (Ruby 3.0+)
case response
in { status: 200, body: }
process(body)
in { status: 404 }
handle_not_found
in { status: 500, error: message }
log_error(message)
end
Keyword Arguments
fetch_data(url, use_cache: false, timeout: 30)
def wrapper(...)
wrapped_method(...)
end
Error Handling
module MyApp
class Error < StandardError; end
class ValidationError < Error; end
class NotFoundError < Error; end
end
def fetch_user(id)
api.get("/users/#{id}")
rescue Faraday::ResourceNotFound
nil
rescue Faraday::TimeoutError => e
raise MyApp::ApiError.new("Service unavailable", status: 503)
end
Memoization
def current_user
@current_user ||= User.find(session[:user_id])
end
def feature_enabled?
return @feature_enabled if defined?(@feature_enabled)
@feature_enabled = expensive_check
end
dry-rb Stack
Prefer dry-rb gems where applicable:
| Need | Use |
|---|
| Validation | dry-validation / dry-schema |
| Types & structs | dry-types / dry-struct |
| Dependency injection | dry-auto_inject + dry-container |
| Transactions | dry-transaction / dry-monads |
Forbidden
Never commit:
binding.pry or byebug
puts / p for debugging
- Monkey-patching core classes
eval with user input
- Wildcard rescues (
rescue => e)
- Magic numbers (use named constants)