ugnius-s

best-practices-ruby-style

0
0
# Install this skill:
npx skills add ugnius-s/skills --skill "best-practices-ruby-style"

Install specific skill from multi-skill repository

# Description

Ruby code style and formatting guidelines from the official Ruby Style Guide. Use PROACTIVELY when writing, reviewing, or generating any Ruby code to ensure consistent formatting, proper naming conventions, idiomatic patterns, and clean code structure.

# SKILL.md


name: best-practices-ruby-style
description: Ruby code style and formatting guidelines from the official Ruby Style Guide. Use PROACTIVELY when writing, reviewing, or generating any Ruby code to ensure consistent formatting, proper naming conventions, idiomatic patterns, and clean code structure.


Core Principles

Write code for humans first. Optimize for readability and maintainability over cleverness.


Formatting

Indentation & Spacing

  • 2 spaces per indentation level (no tabs)
  • 80 characters max line length (120 absolute max)
  • Spaces around operators: sum = 1 + 2
  • Spaces after commas: method(a, b, c)
  • No spaces inside parentheses/brackets: method(arg), array[0]
  • Spaces around { and before } in blocks: items.each { |x| puts x }
  • No spaces in string interpolation: "Hello #{name}"

Empty Lines

  • One empty line between method definitions
  • Empty line after include/extend/prepend block
  • Empty line around access modifiers (private, protected)
  • No empty lines at start/end of class/method bodies

Multi-line

# Align arguments or use single indent
Mailer.deliver(
  to: '[email protected]',
  from: '[email protected]',
  subject: 'Important'
)

# Chain with leading dot
query
  .where(active: true)
  .order(:name)

Detailed examples: See references/formatting.md


Naming

Type Convention Example
Variables, methods, symbols snake_case user_name, calculate_total
Classes, modules CamelCase UserAccount, HTTPClient
Constants SCREAMING_SNAKE_CASE MAX_RETRIES, DEFAULT_TIMEOUT
Files, directories snake_case user_account.rb
Predicates End with ? valid?, empty?
Dangerous methods End with ! (if safe version exists) save!, destroy!

Naming Anti-Patterns

# ❌ Bad predicate names
def is_valid?; end
def has_items?; end

# βœ… Good predicate names  
def valid?; end
def items?; end

# ❌ Unused variables without prefix
result = hash.map { |k, v| v * 2 }

# βœ… Prefix unused with underscore
result = hash.map { |_k, v| v * 2 }

Detailed conventions: See references/naming.md


Control Flow

Guard Clauses

# ❌ Nested conditionals
def process(data)
  if data
    if data[:valid]
      do_work(data)
    end
  end
end

# βœ… Guard clauses
def process(data)
  return unless data
  return unless data[:valid]

  do_work(data)
end

Conditionals

# Use unless for negative conditions (never with else)
return unless valid?

# Modifier form for single-line
do_something if condition

# Ternary for simple expressions only
status = active? ? 'on' : 'off'

# case over if-elsif when comparing same value
case status
when :active then perform_action
when :pending then wait
else fail
end

Boolean Logic

# βœ… Use &&/|| for boolean expressions
if valid? && authorized?

# βœ… Use and/or only for control flow
user = find_user or raise 'Not found'

More patterns: See references/idioms.md


Methods

Definition

# Parentheses with parameters, omit without
def greet(name)
  "Hello, #{name}"
end

def default_greeting
  'Hello'
end

# Keyword arguments for booleans/optionals
def create_user(name:, admin: false, notify: true)
end

# Required keyword args before optional
def fetch(url:, timeout: 30, retries: 3)
end

Guidelines

  • Keep methods < 10 LOC (prefer < 5)
  • Max 3-4 parameters (use keyword args or objects)
  • Avoid explicit return at end of method
  • Avoid explicit self except for assignment

Detailed patterns: See references/methods.md


Classes & Modules

Structure Order

class User
  # 1. Extend/include/prepend
  include Comparable
  extend ClassMethods

  # 2. Constants
  ROLES = %w[admin member guest].freeze

  # 3. Attribute macros
  attr_reader :name, :email

  # 4. Other macros (validations, etc.)
  validates :email, presence: true

  # 5. Class methods
  def self.find_by_email(email)
  end

  # 6. Initialize
  def initialize(name:, email:)
    @name = name
    @email = email
  end

  # 7. Public instance methods
  def full_info
    "#{name} <#{email}>"
  end

  # 8. Protected/private at the end
  private

  def validate_email
  end
end

Guidelines

  • One class per file
  • Use explicit nesting for namespaces (not class Foo::Bar)
  • Prefer module_function over extend self
  • No class variables (@@) - use class instance variables
  • Use def self.method for class methods

Detailed patterns: See references/classes.md


Collections & Strings

Arrays & Hashes

# Literal syntax
arr = []
hash = {}

# Word arrays
STATES = %w[draft open closed]
ROLES = %i[admin member guest]

# Symbol keys with modern syntax
user = { name: 'Alice', age: 30 }

# Hash#fetch for required keys
email = params.fetch(:email)
timeout = config.fetch(:timeout, 30)

Strings

# Interpolation over concatenation
"Hello, #{name}!"

# Single quotes when no interpolation needed
path = '/api/users'

# Use << for building strings in loops
html = ''
items.each { |item| html << "<li>#{item}</li>" }

Enumerable

# Prefer map/select/reduce over each with mutation
names = users.map(&:name)
active = users.select(&:active?)
total = prices.reduce(0, :+)

# flat_map over map + flatten
all_tags = posts.flat_map(&:tags)

# Use transform_keys/transform_values
data.transform_keys(&:to_sym)
data.transform_values(&:upcase)

More idioms: See references/idioms.md


Exceptions

# Use raise (not fail)
raise ArgumentError, 'Invalid input'

# Implicit begin in methods
def fetch_data
  api.get(url)
rescue Timeout::Error => e
  handle_timeout(e)
end

# Specific exceptions first
rescue IOError => e
  # handle IO
rescue StandardError => e
  # handle general

# Never rescue Exception (catches signals)
# Always rescue StandardError or more specific

Blocks & Lambdas

# Braces for single-line, do/end for multi-line
items.map { |x| x * 2 }

items.each do |item|
  process(item)
  log(item)
end

# Proc shorthand when method is only operation
names.map(&:upcase)

# Stabby lambda syntax
validator = ->(x) { x > 0 }

# Multi-line lambda
processor = lambda do |data|
  validate(data)
  transform(data)
end

Quick Reference

Do Don't
unless condition if !condition
arr.first / arr.last arr[0] / arr[-1]
hash.key?(:foo) hash.has_key?(:foo)
hash.fetch(:key) hash[:key] (when required)
%w[a b c] ['a', 'b', 'c']
each_key / each_value keys.each / values.each
map / select / reduce collect / find_all / inject
size length / count

# Supported AI Coding Agents

This skill is compatible with the SKILL.md standard and works with all major AI coding agents:

Learn more about the SKILL.md standard and how to use these skills with your preferred AI coding agent.