| name | ruby-modern-idioms |
|---|---|
| description | Review Ruby code for semantic idioms that linters cannot catch. Use when reviewing any Ruby file for method naming violations (call), pattern matching on Result objects instead of if/else, Struct used instead of Data.define, or discouraged idioms (tap, &method). |
Review Ruby code for semantic idioms that linters cannot catch. RuboCop
(rubocop-rails-omakase) already enforces frozen string literals, hash
shorthand, endless methods, and it blocks — do not flag those.
Methods must be named for what they do. call is never acceptable as a method name.
# BAD
class DateParser
def call(input) = ...
end
LocationIqClient.call(lat, lng)
# GOOD
class DateParser
def self.parse(input, hemisphere:) = ...
end
LocationIqClient.reverse_geocode(lat, lng)Flag any method definition named call in any class or module.
When branching on a Result object, use case/in pattern matching. Never use
if result.success? or if result.failure?.
# BAD
result = MagicLink.create_for(email:, ip_address:, user_agent:)
if result.success?
redirect_to root_path
else
render :new, locals: { error: result.errors.first }
end
# GOOD
case MagicLink.create_for(email:, ip_address:, user_agent:)
in { success?: true }
redirect_to root_path
in { success?: false, errors: [error, *] }
render :new, locals: { error: }
endFlag any if result.success?, unless result.failure?, or result.success? &&
in controller or domain code.
Immutable value objects must use Data.define, not Struct or attr_reader classes.
# BAD
ParseResult = Struct.new(:visit_date, :precision, :success?)
class ParseResult
attr_reader :visit_date, :precision
def initialize(visit_date:, precision:) ...
end
# GOOD
ParseResult = Data.define(:visit_date, :precision, :success?) do
def self.success(visit_date, precision) = new(...)
def self.failure = new(...)
endFlag any Struct.new(...) used for immutable result or value objects.
Flag any class that acts as an immutable value object but doesn't use Data.define.
Flag any use of:
&method(:foo)— use{ foo(it) }instead.tap { |x| ... }except in debugging sessions