CrackedRuby logo

CrackedRuby

Indentation

Ruby code formatting conventions and indentation patterns for consistent, readable code structure.

Ruby Language Fundamentals Basic Syntax and Structure
1.1.8

Overview

Ruby uses indentation primarily for visual organization and readability rather than syntactic significance. Unlike Python, Ruby relies on keywords like end, }, and ) to define code blocks, making indentation optional from a parsing perspective. However, proper indentation remains essential for maintainable code.

The Ruby community has established strong conventions around indentation depth, typically using two spaces per indentation level. This standard appears throughout Ruby's standard library, major frameworks like Rails, and open-source projects. The choice of two spaces balances readability with horizontal space efficiency, preventing excessive rightward drift in deeply nested code.

Ruby's parser ignores whitespace-only indentation differences, treating these two methods identically:

def formatted_method
  if condition
    perform_action
  end
end

def unformatted_method
if condition
perform_action
end
end

Most Ruby developers and automated tools enforce consistent indentation to maintain code quality across teams and projects.

Basic Usage

Standard Ruby indentation follows a two-space increment for each nesting level. This applies to method definitions, class bodies, conditional statements, loops, and block structures.

Method definitions and class structures follow consistent indentation patterns:

class UserProcessor
  def initialize(users)
    @users = users
    @processed_count = 0
  end

  def process_all
    @users.each do |user|
      if user.valid?
        process_user(user)
        @processed_count += 1
      else
        log_invalid_user(user)
      end
    end
  end

  private

  def process_user(user)
    user.activate
    send_welcome_email(user)
  end
end

Control flow statements maintain consistent indentation within their blocks:

def calculate_discount(order)
  case order.total
  when 0..50
    0
  when 51..100
    order.total * 0.05
  when 101..500
    order.total * 0.10
  else
    order.total * 0.15
  end
end

Hash and array literals spanning multiple lines use consistent indentation for readability:

user_config = {
  name: "John Doe",
  preferences: {
    email_notifications: true,
    theme: "dark"
  },
  permissions: [
    "read",
    "write",
    "admin"
  ]
}

Method calls with multiple arguments benefit from aligned indentation:

create_user(
  name: "Jane Smith",
  email: "jane@example.com",
  role: "administrator",
  department: "engineering"
)

Advanced Usage

Complex Ruby structures require careful indentation management to maintain readability. Metaprogramming constructs, module definitions, and deeply nested blocks present indentation challenges that experienced developers handle with specific patterns.

Module and mixin structures follow hierarchical indentation rules:

module Auditable
  extend ActiveSupport::Concern

  included do
    after_save :log_changes
    before_destroy :log_deletion
  end

  class_methods do
    def audit_trail_for(field)
      define_method "#{field}_changed?" do
        previous_changes.key?(field.to_s)
      end

      define_method "previous_#{field}" do
        previous_changes[field.to_s]&.first
      end
    end
  end

  private

  def log_changes
    return unless changed?
    
    AuditLog.create(
      record_type: self.class.name,
      record_id: id,
      changes: changes.except('updated_at', 'created_at')
    )
  end
end

Dynamic method definition requires careful indentation to distinguish between the defining context and the defined method body:

class ReportGenerator
  REPORT_TYPES = %w[daily weekly monthly quarterly].freeze

  REPORT_TYPES.each do |period|
    define_method "generate_#{period}_report" do |date_range = nil|
      date_range ||= default_range_for(period)
      
      data = fetch_data_for_period(period, date_range)
      template = load_template(period)
      
      compile_report(
        data: data,
        template: template,
        metadata: {
          period: period,
          generated_at: Time.current,
          range: date_range
        }
      )
    end
  end

  private

  def default_range_for(period)
    case period
    when 'daily'
      Date.current..Date.current
    when 'weekly'
      1.week.ago.beginning_of_week..Date.current.end_of_week
    when 'monthly'
      Date.current.beginning_of_month..Date.current.end_of_month
    when 'quarterly'
      Date.current.beginning_of_quarter..Date.current.end_of_quarter
    end
  end
end

Complex conditional logic with multiple predicates requires strategic indentation to maintain readability:

def eligible_for_promotion?(employee)
  return false unless employee.active?
  return false if employee.disciplinary_actions.recent.any?
  
  meets_time_requirements = (
    employee.tenure >= 12.months &&
    employee.current_position_tenure >= 6.months
  )
  
  meets_performance_requirements = (
    employee.performance_reviews.last_year.average_rating >= 4.0 &&
    employee.goals.current_year.completion_rate >= 0.8
  )
  
  meets_time_requirements && 
    meets_performance_requirements && 
    department_has_opening?(employee.department)
end

Common Pitfalls

Inconsistent indentation patterns create confusion and maintenance problems. Mixed tab and space characters cause parsing issues in some editors and version control systems. Ruby code should use spaces exclusively for indentation.

Overly deep nesting indicates code organization problems beyond indentation concerns:

# Problematic deep nesting
def process_order(order)
  if order.valid?
    if order.payment_method.present?
      if order.payment_method.valid?
        if order.inventory_available?
          if order.shipping_address.valid?
            if order.customer.in_good_standing?
              # Actual processing logic buried six levels deep
              charge_payment(order)
              reserve_inventory(order)
              schedule_shipping(order)
            end
          end
        end
      end
    end
  end
end

# Improved with guard clauses
def process_order(order)
  return unless order.valid?
  return unless order.payment_method&.valid?
  return unless order.inventory_available?
  return unless order.shipping_address.valid?
  return unless order.customer.in_good_standing?
  
  charge_payment(order)
  reserve_inventory(order)
  schedule_shipping(order)
end

String interpolation within multi-line strings requires attention to preserve intended formatting:

# Indentation affects output formatting
def generate_email_body(user)
  <<~EMAIL
    Dear #{user.name},
    
    Your account has been activated successfully.
    
    Account Details:
      Username: #{user.username}
      Email: #{user.email}
      Created: #{user.created_at.strftime('%B %d, %Y')}
    
    Best regards,
    The Support Team
  EMAIL
end

Method chaining across multiple lines requires consistent indentation to maintain readability:

# Inconsistent chaining indentation
result = users
.select(&:active?)
  .map(&:email)
    .reject(&:blank?)
.sort

# Consistent chaining indentation
result = users
  .select(&:active?)
  .map(&:email)
  .reject(&:blank?)
  .sort

Reference

Indentation Standards

Context Indentation Example
Method body 2 spaces def method bodyend
Class body 2 spaces class Name contentend
Conditional blocks 2 spaces if condition actionend
Loop blocks 2 spaces while condition actionend
Case statements 2 spaces case valuewhen match actionend

Multi-line Structures

Structure Pattern Example
Hash literals Key alignment { key: value, other: value}
Array literals Element alignment [ first_element, second_element]
Method arguments Parameter alignment method( first_arg, second_arg)
Method chaining Dot alignment object .method .other_method

Whitespace Characters

Character Usage Recommendation
Space (U+0020) Standard indentation Use exclusively
Tab (U+0009) Alternative indentation Avoid in Ruby
No-break space (U+00A0) Accidental insertion Remove completely

Editor Configuration

Setting Value Purpose
indent_style space Consistent character usage
indent_size 2 Ruby community standard
insert_final_newline true POSIX compliance
trim_trailing_whitespace true Clean line endings
charset utf-8 Unicode support

Common Indentation Tools

Tool Purpose Configuration
RuboCop Style enforcement .rubocop.yml
EditorConfig Editor consistency .editorconfig
Prettier Automatic formatting .prettierrc
Standard Opinionated formatting Built-in rules

Nested Structure Limits

Structure Type Recommended Limit Refactoring Approach
Method nesting 3 levels Extract methods
Class nesting 2 levels Use modules
Conditional nesting 2 levels Guard clauses
Block nesting 3 levels Extract to methods