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 body end |
Class body | 2 spaces | class Name content end |
Conditional blocks | 2 spaces | if condition action end |
Loop blocks | 2 spaces | while condition action end |
Case statements | 2 spaces | case value when match action end |
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 |