$npx -y skills add Jeffallan/claude-skills --skill rails-expertRails 7+ specialist that optimizes Active Record queries with includes/eager_load, implements Turbo Frames and Turbo Streams for partial page updates, configures Action Cable for WebSocket connections, sets up Sidekiq workers for background job processing, and writes comprehensiv
| 1 | # Rails Expert |
| 2 | |
| 3 | ## Core Workflow |
| 4 | |
| 5 | 1. **Analyze requirements** — Identify models, routes, real-time needs, background jobs |
| 6 | 2. **Scaffold resources** — `rails generate model User name:string email:string`, `rails generate controller Users` |
| 7 | 3. **Run migrations** — `rails db:migrate` and verify schema with `rails db:schema:dump` |
| 8 | - If migration fails: inspect `db/schema.rb` for conflicts, rollback with `rails db:rollback`, fix and retry |
| 9 | 4. **Implement** — Write controllers, models, add Hotwire (see Reference Guide below) |
| 10 | 5. **Validate** — `bundle exec rspec` must pass; `bundle exec rubocop` for style |
| 11 | - If specs fail: check error output, fix failing examples, re-run with `--format documentation` for detail |
| 12 | - If N+1 queries surface during review: add `includes`/`eager_load` (see Common Patterns) and re-run specs |
| 13 | 6. **Optimize** — Audit for N+1 queries, add missing indexes, add caching |
| 14 | |
| 15 | ## Reference Guide |
| 16 | |
| 17 | Load detailed guidance based on context: |
| 18 | |
| 19 | | Topic | Reference | Load When | |
| 20 | |-------|-----------|-----------| |
| 21 | | Hotwire/Turbo | `references/hotwire-turbo.md` | Turbo Frames, Streams, Stimulus controllers | |
| 22 | | Active Record | `references/active-record.md` | Models, associations, queries, performance | |
| 23 | | Background Jobs | `references/background-jobs.md` | Sidekiq, job design, queues, error handling | |
| 24 | | Testing | `references/rspec-testing.md` | Model/request/system specs, factories | |
| 25 | | API Development | `references/api-development.md` | API-only mode, serialization, authentication | |
| 26 | |
| 27 | ## Common Patterns |
| 28 | |
| 29 | ### N+1 Prevention with includes/eager_load |
| 30 | |
| 31 | ```ruby |
| 32 | # BAD — triggers N+1 |
| 33 | posts = Post.all |
| 34 | posts.each { |post| puts post.author.name } |
| 35 | |
| 36 | # GOOD — eager load association |
| 37 | posts = Post.includes(:author).all |
| 38 | posts.each { |post| puts post.author.name } |
| 39 | |
| 40 | # GOOD — eager_load forces a JOIN (useful when filtering on association) |
| 41 | posts = Post.eager_load(:author).where(authors: { verified: true }) |
| 42 | ``` |
| 43 | |
| 44 | ### Turbo Frame Setup (partial page update) |
| 45 | |
| 46 | ```erb |
| 47 | <%# app/views/posts/index.html.erb %> |
| 48 | <%= turbo_frame_tag "posts" do %> |
| 49 | <%= render @posts %> |
| 50 | <%= link_to "Load More", posts_path(page: @next_page) %> |
| 51 | <% end %> |
| 52 | |
| 53 | <%# app/views/posts/_post.html.erb %> |
| 54 | <%= turbo_frame_tag dom_id(post) do %> |
| 55 | <h2><%= post.title %></h2> |
| 56 | <%= link_to "Edit", edit_post_path(post) %> |
| 57 | <% end %> |
| 58 | ``` |
| 59 | |
| 60 | ```ruby |
| 61 | # app/controllers/posts_controller.rb |
| 62 | def index |
| 63 | @posts = Post.includes(:author).page(params[:page]) |
| 64 | @next_page = @posts.next_page |
| 65 | end |
| 66 | ``` |
| 67 | |
| 68 | ### Sidekiq Worker Template |
| 69 | |
| 70 | ```ruby |
| 71 | # app/jobs/send_welcome_email_job.rb |
| 72 | class SendWelcomeEmailJob < ApplicationJob |
| 73 | queue_as :default |
| 74 | sidekiq_options retry: 3, dead: false |
| 75 | |
| 76 | def perform(user_id) |
| 77 | user = User.find(user_id) |
| 78 | UserMailer.welcome(user).deliver_now |
| 79 | rescue ActiveRecord::RecordNotFound => e |
| 80 | Rails.logger.warn("SendWelcomeEmailJob: user #{user_id} not found — #{e.message}") |
| 81 | # Do not re-raise; record is gone, no point retrying |
| 82 | end |
| 83 | end |
| 84 | |
| 85 | # Enqueue from controller or model callback |
| 86 | SendWelcomeEmailJob.perform_later(user.id) |
| 87 | ``` |
| 88 | |
| 89 | ### Strong Parameters (controller template) |
| 90 | |
| 91 | ```ruby |
| 92 | # app/controllers/posts_controller.rb |
| 93 | class PostsController < ApplicationController |
| 94 | before_action :set_post, only: %i[show edit update destroy] |
| 95 | |
| 96 | def create |
| 97 | @post = Post.new(post_params) |
| 98 | if @post.save |
| 99 | redirect_to @post, notice: "Post created." |
| 100 | else |
| 101 | render :new, status: :unprocessable_entity |
| 102 | end |
| 103 | end |
| 104 | |
| 105 | private |
| 106 | |
| 107 | def set_post |
| 108 | @post = Post.find(params[:id]) |
| 109 | end |
| 110 | |
| 111 | def post_params |
| 112 | params.require(:post).permit(:title, :body, :published_at) |
| 113 | end |
| 114 | end |
| 115 | ``` |
| 116 | |
| 117 | ## Constraints |
| 118 | |
| 119 | ### MUST DO |
| 120 | - Prevent N+1 queries with `includes`/`eager_load` on every collection query involving associations |
| 121 | - Write comprehensive specs targeting >95% coverage |
| 122 | - Use service objects for complex business logic; keep controllers thin |
| 123 | - Add database indexes for every column used in `WHERE`, `ORDER BY`, or `JOIN` |
| 124 | - Offload slow operations to Sidekiq — never run them synchronously in a request cycle |
| 125 | |
| 126 | ### MUST NOT DO |
| 127 | - Skip migrations for schema changes |
| 128 | - Use raw SQL without sanitization (`sanitize_sql` or parameterized |