Rails Error Prevention
Comprehensive checklist and prevention strategies for common Ruby on Rails errors. Use this skill BEFORE writing any Rails code to prevent ViewComponent template errors, ActiveRecord query mistakes, method exposure issues, N+1 queries, and other common Rails pitfalls.
$ 安裝
git clone https://github.com/Kaakati/rails-enterprise-dev /tmp/rails-enterprise-dev && cp -r /tmp/rails-enterprise-dev/plugins/rails-enterprise-dev/skills/rails-error-prevention ~/.claude/skills/rails-enterprise-dev// tip: Run this command in your terminal to install the skill
name: "Rails Error Prevention" description: "Comprehensive checklist and prevention strategies for common Ruby on Rails errors. Use this skill BEFORE writing any Rails code to prevent ViewComponent template errors, ActiveRecord query mistakes, method exposure issues, N+1 queries, and other common Rails pitfalls."
Rails Error Prevention Skill
This skill provides preventative checklists and error patterns for common Rails mistakes. Review relevant sections BEFORE writing code.
When to Use This Skill
- Before creating ViewComponents
- Before writing ActiveRecord queries with GROUP BY or joins
- Before writing view code that calls component methods
- Before creating controller actions
- When debugging undefined method errors
- When debugging template not found errors
- When debugging ActiveRecord grouping errors
Critical Rule: Method Exposure Verification
This is the #1 cause of runtime errors in Rails applications.
WRONG ASSUMPTION: Service has method → View can call it through component
CORRECT RULE: Service has method + Component exposes it = View can call it
Verification Process
# Step 1: List all methods view will call on component
grep -oE '@[a-z_]+\.[a-z_]+' app/views/{path}/*.erb | sort -u
# Step 2: List all public methods in component
grep -E '^\s+def [a-z_]+' app/components/{component}_component.rb
# Step 3: Compare - any view call without component method = BUG
# Missing methods MUST be added to component BEFORE writing view code
Patterns to Fix Missing Methods
# Pattern 1: Delegation
class Metrics::DashboardComponent < ViewComponent::Base
delegate :calculate_lifetime_tasks,
:calculate_lifetime_success_rate,
to: :@service
def initialize(service:)
@service = service
end
end
# Pattern 2: Wrapper methods (preferred for transformation)
class Metrics::DashboardComponent < ViewComponent::Base
def initialize(service:)
@service = service
end
def lifetime_tasks
@service.calculate_lifetime_tasks
end
def lifetime_success_rate
@service.calculate_lifetime_success_rate
end
end
# Pattern 3: Expose service directly (use sparingly)
class Metrics::DashboardComponent < ViewComponent::Base
attr_reader :service
def initialize(service:)
@service = service
end
end
# View then calls: dashboard.service.calculate_lifetime_tasks
ViewComponent Errors
Template Not Found
Error Pattern:
Couldn't find a template file or inline render method for {Component}
Root Causes:
- Missing template file (component.html.erb)
- Template in wrong location
- Class name doesn't match file path
- Using render without inline template defined
Prevention Checklist:
# Before creating component:
ls app/components/ # Check structure
head -50 app/components/*_component.rb | head -1 # Review existing pattern
# Verify naming: ComponentName -> component_name/
Required Files:
app/components/{namespace}/{component_name}_component.rb
app/components/{namespace}/{component_name}_component.html.erb
Correct Patterns:
# Option A: Separate template file
# app/components/metrics/kpi_card_component.rb
class Metrics::KpiCardComponent < ViewComponent::Base
def initialize(title:, value:)
@title = title
@value = value
end
end
# app/components/metrics/kpi_card_component.html.erb
# <div class="kpi-card">
# <h3><%= @title %></h3>
# <span><%= @value %></span>
# </div>
# Option B: Inline template (call method)
class Metrics::KpiCardComponent < ViewComponent::Base
def initialize(title:, value:)
@title = title
@value = value
end
def call
content_tag :div, class: "kpi-card" do
safe_join([
content_tag(:h3, @title),
content_tag(:span, @value)
])
end
end
end
Helper Method Errors
Error Pattern:
undefined local variable or method '{method}' for an instance of {Component}
Did you mean `helpers.{method}`?
Root Cause: Calling view helper directly without helpers. prefix
Prevention Rules:
- ViewComponents do NOT have direct access to view helpers
- Must use
helpers.method_namefor any Rails helper
Helpers Requiring Prefix:
# WRONG # CORRECT
link_to(...) helpers.link_to(...)
image_tag(...) helpers.image_tag(...)
url_for(...) helpers.url_for(...)
form_with(...) helpers.form_with(...)
number_to_currency(...) helpers.number_to_currency(...)
time_ago_in_words(...) helpers.time_ago_in_words(...)
truncate(...) helpers.truncate(...)
pluralize(...) helpers.pluralize(...)
content_tag(...) helpers.content_tag(...)
tag(...) helpers.tag(...)
content_for(...) helpers.content_for(...)
Alternative - Delegate Helpers:
class Metrics::KpiCardComponent < ViewComponent::Base
delegate :number_to_currency, :link_to, to: :helpers
def formatted_value
number_to_currency(@value) # Now works without prefix
end
end
Content Block Errors
Error Pattern:
content is not defined
Prevention: Always check content? before using content
<% if content? %>
<%= content %>
<% end %>
ActiveRecord Errors
Grouping Error (PostgreSQL)
Error Pattern:
PG::GroupingError: ERROR: column "{table}.{column}" must appear in the GROUP BY clause
Root Causes:
- SELECT includes columns not in GROUP BY or aggregate functions
- Using
.selectwith.groupbut including non-grouped columns - Eager loading (
includes/preload) with GROUP BY - Using
.pluckor.selectwith associations and GROUP BY
Prevention Rules:
- Every non-aggregated column in SELECT must be in GROUP BY
- NEVER combine
includes/preload/eager_loadwith GROUP BY - Use
.selectonly for grouped columns and aggregates - If you need associated data with grouped results, query separately
Examples:
# WRONG - selecting all columns with group
Task.includes(:user).group(:task_type).count
# This tries to select tasks.* which fails
# WRONG - selecting id with group
Task.select(:task_type, :id).group(:task_type).count
# id is not grouped or aggregated
# CORRECT - only select grouped columns and aggregates
Task.group(:task_type).count
# => { "type_a" => 5, "type_b" => 3 }
# CORRECT - explicit select with only valid columns
Task.select(:task_type, 'COUNT(*) as count').group(:task_type)
# CORRECT - if you need associated data, query separately
type_counts = Task.group(:task_type).count
tasks_by_type = type_counts.keys.each_with_object({}) do |type, hash|
hash[type] = Task.where(task_type: type).includes(:user).limit(5)
end
# CORRECT - using pluck for simple aggregations
Task.group(:task_type).pluck(:task_type, 'COUNT(*)')
# => [["type_a", 5], ["type_b", 3]]
Before Writing GROUP BY Query:
# Detection command
grep -r '\.group(' app/ --include='*.rb' -A3 -B3
grep -r '\.includes.*\.group\|.group.*\.includes' app/ --include='*.rb'
N+1 Queries
Detection: Multiple queries for same association in logs
Prevention:
# WRONG - N+1
tasks.each { |t| puts t.user.name }
# CORRECT
tasks.includes(:user).each { |t| puts t.user.name }
Ambiguous Column
Error Pattern:
PG::AmbiguousColumn: ERROR: column reference "{column}" is ambiguous
Prevention: Always qualify columns when joining
# WRONG
User.joins(:tasks).where(status: 'active') # Both have status?
# CORRECT
User.joins(:tasks).where(users: { status: 'active' })
# OR
User.joins(:tasks).where('users.status = ?', 'active')
Missing Attribute
Error Pattern:
ActiveModel::MissingAttributeError: missing attribute: {attribute}
Prevention: Include all attributes needed downstream
# WRONG - later code needs 'email'
users = User.select(:id, :name)
users.each { |u| puts u.email } # ERROR!
# CORRECT
users = User.select(:id, :name, :email)
Controller Errors
Missing Template
Error Pattern:
ActionView::MissingTemplate
Prevention:
- Every non-redirect action needs a view OR explicit render
- View path must match controller namespace
# Before creating controller action:
ls app/views/{controller}/
Unknown Action
Error Pattern:
AbstractController::ActionNotFound
Root Causes:
- Route points to non-existent action
- Action is private/protected (must be public)
# Verification
rails routes | grep {controller}
grep -n 'def ' app/controllers/{controller}_controller.rb
Nil Errors
Error Pattern:
undefined method '{method}' for nil:NilClass
Prevention Patterns:
# Safe navigation
user&.profile&.settings&.theme
# Guard clause
return unless user&.profile
# With default
user&.profile&.settings&.theme || 'default'
# Early return
def process_user(user)
return if user.nil?
# ... rest of method
end
Argument Errors
Error Pattern:
ArgumentError: wrong number of arguments (given X, expected Y)
Prevention:
# Before modifying method signature
grep -r 'method_name' app/ --include='*.rb'
# Update all call sites
Rules:
- Prefer keyword arguments for methods with 2+ params
- Use default values for optional params
Prevention Checklists
Before Creating ViewComponent
[ ] Check app/components/ structure
[ ] Review existing component for template pattern (inline vs file)
[ ] Verify naming: Namespace::ComponentNameComponent
[ ] Create both .rb AND .html.erb files (unless using inline)
[ ] List ALL methods view will need
[ ] Implement ALL needed methods in component
[ ] Prefix ALL Rails helpers with 'helpers.' or delegate them
Before Writing View Code
[ ] List ALL methods view will call on component
[ ] For EACH method, verify it exists in component class
[ ] If method missing, ADD to component BEFORE view code
[ ] Verify underlying service/model has implementation
Before Writing ActiveRecord Query with GROUP BY
[ ] List ALL columns in SELECT
[ ] Verify each is in GROUP BY or aggregate function
[ ] Remove includes/preload/eager_load
[ ] Test query in rails console first
Before Writing ActiveRecord Query with Joins
[ ] Qualify ambiguous columns with table name
[ ] Consider if includes is better for the use case
Before Creating Controller Action
[ ] View exists for non-redirect actions?
[ ] Routes point to public methods?
[ ] All @variables view needs are set?
Before Any Code
[ ] Inspect existing similar implementations
[ ] Check naming conventions in codebase
[ ] Verify all dependencies exist (gems, files, routes)
[ ] Verify method exposure across all layers (view→component→service)
Quick Debugging Commands
# Find where method is called
grep -r 'method_name' app/ --include='*.rb'
# Find method definition
grep -rn 'def method_name' app/ --include='*.rb'
# Check method visibility
grep -B5 'def method_name' app/path/to/file.rb
# List component methods
grep -E '^\s+def [a-z_]+' app/components/path_component.rb
# List service methods
grep -E '^\s+def [a-z_]+' app/services/path.rb
# Compare view calls vs component methods
grep -oE '@\w+\.\w+' app/views/path/*.erb | sort -u
Repository
