rails_claude_skills 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (87) hide show
  1. checksums.yaml +7 -0
  2. data/.github/ISSUE_TEMPLATE/bug_report.yml +134 -0
  3. data/.github/ISSUE_TEMPLATE/config.yml +11 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.yml +129 -0
  5. data/.github/ISSUE_TEMPLATE/question.yml +90 -0
  6. data/.github/dependabot.yml +19 -0
  7. data/.github/workflows/ci.yml +77 -0
  8. data/.github/workflows/release.yml +66 -0
  9. data/.rubocop.yml +52 -0
  10. data/CHANGELOG.md +94 -0
  11. data/CLAUDE.md +332 -0
  12. data/CODE_OF_CONDUCT.md +134 -0
  13. data/CONTRIBUTING.md +580 -0
  14. data/LICENSE.txt +21 -0
  15. data/README.md +544 -0
  16. data/Rakefile +8 -0
  17. data/lib/generators/claude/agent/agent_generator.rb +71 -0
  18. data/lib/generators/claude/agent/templates/agent.md.tt +62 -0
  19. data/lib/generators/claude/command/command_generator.rb +50 -0
  20. data/lib/generators/claude/command/templates/command.md.tt +28 -0
  21. data/lib/generators/claude/commands_library/create-pr.md +27 -0
  22. data/lib/generators/claude/commands_library/dbchange.md +19 -0
  23. data/lib/generators/claude/commands_library/quality.md +20 -0
  24. data/lib/generators/claude/commands_library/stimulus.md +19 -0
  25. data/lib/generators/claude/commands_library/turbo-feature.md +17 -0
  26. data/lib/generators/claude/install/install_generator.rb +211 -0
  27. data/lib/generators/claude/install/templates/README.md.tt +59 -0
  28. data/lib/generators/claude/install/templates/USAGE +28 -0
  29. data/lib/generators/claude/install/templates/agents/api-dev.md.tt +46 -0
  30. data/lib/generators/claude/install/templates/agents/fullstack-dev.md.tt +48 -0
  31. data/lib/generators/claude/install/templates/agents/rails-developer.md.tt +40 -0
  32. data/lib/generators/claude/install/templates/settings.local.json.tt +13 -0
  33. data/lib/generators/claude/rule/rule_generator.rb +175 -0
  34. data/lib/generators/claude/rule/templates/rule.md.tt +7 -0
  35. data/lib/generators/claude/rules_library/code-style.md +37 -0
  36. data/lib/generators/claude/rules_library/database.md +47 -0
  37. data/lib/generators/claude/rules_library/hotwire.md +56 -0
  38. data/lib/generators/claude/rules_library/security.md +54 -0
  39. data/lib/generators/claude/rules_library/testing.md +47 -0
  40. data/lib/generators/claude/skill/skill_generator.rb +196 -0
  41. data/lib/generators/claude/skill/templates/SKILL.md.tt +27 -0
  42. data/lib/generators/claude/skills_library/create-task-files/SKILL.md +311 -0
  43. data/lib/generators/claude/skills_library/create-task-files/templates/bug.md +60 -0
  44. data/lib/generators/claude/skills_library/create-task-files/templates/epic.md +47 -0
  45. data/lib/generators/claude/skills_library/create-task-files/templates/issue.md +45 -0
  46. data/lib/generators/claude/skills_library/create-task-files/templates/user-story.md +57 -0
  47. data/lib/generators/claude/skills_library/minitest-testing/SKILL.md +398 -0
  48. data/lib/generators/claude/skills_library/minitest-testing/references/examples.md +889 -0
  49. data/lib/generators/claude/skills_library/plan-feature/SKILL.md +253 -0
  50. data/lib/generators/claude/skills_library/rails-api-controllers/SKILL.md +1041 -0
  51. data/lib/generators/claude/skills_library/rails-api-controllers/references/api-documentation.md +422 -0
  52. data/lib/generators/claude/skills_library/rails-api-controllers/references/serialization.md +456 -0
  53. data/lib/generators/claude/skills_library/rails-auth-with-devise/SKILL.md +191 -0
  54. data/lib/generators/claude/skills_library/rails-auth-with-devise/references/advanced.md +331 -0
  55. data/lib/generators/claude/skills_library/rails-auth-with-devise/references/api-auth.md +266 -0
  56. data/lib/generators/claude/skills_library/rails-auth-with-devise/references/omniauth.md +194 -0
  57. data/lib/generators/claude/skills_library/rails-authorization-cancancan/SKILL.md +603 -0
  58. data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/api-authorization.md +543 -0
  59. data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/complex-permissions.md +572 -0
  60. data/lib/generators/claude/skills_library/rails-authorization-cancancan/references/multi-tenancy.md +373 -0
  61. data/lib/generators/claude/skills_library/rails-controllers/SKILL.md +514 -0
  62. data/lib/generators/claude/skills_library/rails-debugging/SKILL.md +260 -0
  63. data/lib/generators/claude/skills_library/rails-deployment/SKILL.md +437 -0
  64. data/lib/generators/claude/skills_library/rails-deployment/references/examples.md +901 -0
  65. data/lib/generators/claude/skills_library/rails-hotwire/SKILL.md +367 -0
  66. data/lib/generators/claude/skills_library/rails-jobs/MISSION_CONTROL_SETUP.md +639 -0
  67. data/lib/generators/claude/skills_library/rails-jobs/SKILL.md +704 -0
  68. data/lib/generators/claude/skills_library/rails-mailers/SKILL.md +549 -0
  69. data/lib/generators/claude/skills_library/rails-models/SKILL.md +379 -0
  70. data/lib/generators/claude/skills_library/rails-pagination-kaminari/SKILL.md +622 -0
  71. data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/api-pagination.md +523 -0
  72. data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/custom-themes.md +498 -0
  73. data/lib/generators/claude/skills_library/rails-pagination-kaminari/references/performance.md +478 -0
  74. data/lib/generators/claude/skills_library/rails-views/SKILL.md +508 -0
  75. data/lib/generators/claude/skills_library/refine-requirements/SKILL.md +226 -0
  76. data/lib/generators/claude/skills_library/refine-requirements/references/examples.md +344 -0
  77. data/lib/generators/claude/skills_library/refine-requirements/references/reference.md +298 -0
  78. data/lib/generators/claude/skills_library/rspec-testing/SKILL.md +572 -0
  79. data/lib/generators/claude/skills_library/rspec-testing/references/better_specs_guide.md +273 -0
  80. data/lib/generators/claude/skills_library/rspec-testing/references/thoughtbot_patterns.md +407 -0
  81. data/lib/generators/claude/skills_library/tailwindcss/SKILL.md +371 -0
  82. data/lib/generators/claude/views/views_generator.rb +113 -0
  83. data/lib/rails_claude_skills/railtie.rb +16 -0
  84. data/lib/rails_claude_skills/version.rb +5 -0
  85. data/lib/rails_claude_skills.rb +27 -0
  86. data/sig/rails_claude_skills.rbs +4 -0
  87. metadata +199 -0
@@ -0,0 +1,514 @@
1
+ ---
2
+ name: rails-controllers
3
+ description: Controller actions, routing, REST conventions, filters, and response handling
4
+ version: 1.0.0
5
+ rails_version: ">= 7.0"
6
+ tags:
7
+ - controllers
8
+ - routing
9
+ - actions
10
+ - rest
11
+ ---
12
+
13
+ # Rails Controllers
14
+
15
+ ## Quick Reference
16
+
17
+ | Pattern | Example |
18
+ |---------|---------|
19
+ | **Generate** | `rails g controller Posts index show` |
20
+ | **Route** | `resources :posts` |
21
+ | **Action** | `def show; @post = Post.find(params[:id]); end` |
22
+ | **Render** | `render :edit` |
23
+ | **Redirect** | `redirect_to posts_path` |
24
+ | **Filter** | `before_action :authenticate_user!` |
25
+ | **Strong Params** | `params.require(:post).permit(:title, :body)` |
26
+
27
+ ## Controller Structure
28
+
29
+ ```ruby
30
+ class PostsController < ApplicationController
31
+ before_action :set_post, only: [:show, :edit, :update, :destroy]
32
+ before_action :authenticate_user!, except: [:index, :show]
33
+
34
+ # GET /posts
35
+ def index
36
+ @posts = Post.all.order(created_at: :desc)
37
+ end
38
+
39
+ # GET /posts/:id
40
+ def show
41
+ # @post set by before_action
42
+ end
43
+
44
+ # GET /posts/new
45
+ def new
46
+ @post = Post.new
47
+ end
48
+
49
+ # POST /posts
50
+ def create
51
+ @post = Post.new(post_params)
52
+
53
+ if @post.save
54
+ redirect_to @post, notice: 'Post created successfully.'
55
+ else
56
+ render :new, status: :unprocessable_entity
57
+ end
58
+ end
59
+
60
+ # GET /posts/:id/edit
61
+ def edit
62
+ # @post set by before_action
63
+ end
64
+
65
+ # PATCH/PUT /posts/:id
66
+ def update
67
+ if @post.update(post_params)
68
+ redirect_to @post, notice: 'Post updated successfully.'
69
+ else
70
+ render :edit, status: :unprocessable_entity
71
+ end
72
+ end
73
+
74
+ # DELETE /posts/:id
75
+ def destroy
76
+ @post.destroy
77
+ redirect_to posts_path, notice: 'Post deleted successfully.'
78
+ end
79
+
80
+ private
81
+
82
+ def set_post
83
+ @post = Post.find(params[:id])
84
+ end
85
+
86
+ def post_params
87
+ params.require(:post).permit(:title, :body, :published)
88
+ end
89
+ end
90
+ ```
91
+
92
+ ## Routing
93
+
94
+ ### RESTful Routes
95
+
96
+ ```ruby
97
+ # config/routes.rb
98
+ Rails.application.routes.draw do
99
+ # Creates 7 standard routes (index, show, new, create, edit, update, destroy)
100
+ resources :posts
101
+
102
+ # Limit actions
103
+ resources :posts, only: [:index, :show]
104
+ resources :posts, except: [:destroy]
105
+
106
+ # Nested resources
107
+ resources :authors do
108
+ resources :posts
109
+ end
110
+ # URLs: /authors/:author_id/posts
111
+
112
+ # Shallow nesting (recommended for deep nesting)
113
+ resources :authors do
114
+ resources :posts, shallow: true
115
+ end
116
+ # URLs: /authors/:author_id/posts (collection)
117
+ # /posts/:id (member)
118
+
119
+ # Custom member and collection routes
120
+ resources :posts do
121
+ member do
122
+ post :publish
123
+ post :unpublish
124
+ end
125
+
126
+ collection do
127
+ get :archived
128
+ end
129
+ end
130
+ # URLs: POST /posts/:id/publish
131
+ # GET /posts/archived
132
+
133
+ # Singular resource
134
+ resource :profile, only: [:show, :edit, :update]
135
+ # URLs: /profile (no :id needed)
136
+ end
137
+ ```
138
+
139
+ ### Custom Routes
140
+
141
+ ```ruby
142
+ # Named routes
143
+ get 'about', to: 'pages#about', as: :about
144
+ # Usage: about_path
145
+
146
+ # Root route
147
+ root 'posts#index'
148
+
149
+ # Redirect
150
+ get '/old-path', to: redirect('/new-path')
151
+
152
+ # Constraints
153
+ get 'posts/:id', to: 'posts#show', constraints: { id: /\d+/ }
154
+
155
+ # Namespace
156
+ namespace :admin do
157
+ resources :posts
158
+ end
159
+ # URLs: /admin/posts
160
+ # Controller: Admin::PostsController
161
+
162
+ # Scope
163
+ scope module: 'admin' do
164
+ resources :posts
165
+ end
166
+ # URLs: /posts
167
+ # Controller: Admin::PostsController
168
+
169
+ # Concern for reusable routes
170
+ concern :commentable do
171
+ resources :comments
172
+ end
173
+
174
+ resources :posts, concerns: :commentable
175
+ resources :photos, concerns: :commentable
176
+ ```
177
+
178
+ ## Filters (Callbacks)
179
+
180
+ ```ruby
181
+ class ApplicationController < ActionController::Base
182
+ before_action :authenticate_user!
183
+ before_action :set_locale
184
+ around_action :log_request
185
+ after_action :track_analytics
186
+
187
+ private
188
+
189
+ def set_locale
190
+ I18n.locale = params[:locale] || I18n.default_locale
191
+ end
192
+
193
+ def log_request
194
+ start_time = Time.current
195
+ yield
196
+ duration = Time.current - start_time
197
+ Rails.logger.info "Request took #{duration}s"
198
+ end
199
+ end
200
+
201
+ class PostsController < ApplicationController
202
+ skip_before_action :authenticate_user!, only: [:index, :show]
203
+ before_action :set_post, only: [:show, :edit, :update, :destroy]
204
+ before_action :authorize_post, only: [:edit, :update, :destroy]
205
+
206
+ private
207
+
208
+ def authorize_post
209
+ unless @post.author == current_user
210
+ redirect_to root_path, alert: 'Not authorized'
211
+ end
212
+ end
213
+ end
214
+ ```
215
+
216
+ ## Strong Parameters
217
+
218
+ ```ruby
219
+ class PostsController < ApplicationController
220
+ private
221
+
222
+ # Basic
223
+ def post_params
224
+ params.require(:post).permit(:title, :body, :published)
225
+ end
226
+
227
+ # Arrays
228
+ def post_params
229
+ params.require(:post).permit(:title, :body, tag_ids: [])
230
+ end
231
+
232
+ # Nested attributes
233
+ def post_params
234
+ params.require(:post).permit(
235
+ :title,
236
+ :body,
237
+ comments_attributes: [:id, :content, :_destroy]
238
+ )
239
+ end
240
+
241
+ # Conditional
242
+ def post_params
243
+ permitted = [:title, :body]
244
+ permitted << :published if current_user.admin?
245
+ params.require(:post).permit(permitted)
246
+ end
247
+ end
248
+ ```
249
+
250
+ ## Rendering and Redirecting
251
+
252
+ ```ruby
253
+ class PostsController < ApplicationController
254
+ def show
255
+ @post = Post.find(params[:id])
256
+
257
+ # Implicit render: renders views/posts/show.html.erb
258
+
259
+ # Explicit template
260
+ # render :show
261
+
262
+ # Different template
263
+ # render :custom_template
264
+
265
+ # Partial
266
+ # render partial: 'post', locals: { post: @post }
267
+
268
+ # JSON
269
+ # render json: @post
270
+
271
+ # Plain text
272
+ # render plain: "Hello"
273
+
274
+ # Status codes
275
+ # render :show, status: :ok
276
+ # render :new, status: :unprocessable_entity
277
+ # render json: { error: 'Not found' }, status: :not_found
278
+ end
279
+
280
+ def create
281
+ @post = Post.new(post_params)
282
+
283
+ if @post.save
284
+ # Redirect to show page
285
+ redirect_to @post
286
+
287
+ # With flash message
288
+ # redirect_to @post, notice: 'Created!'
289
+
290
+ # With custom path
291
+ # redirect_to posts_path
292
+
293
+ # Back to previous page
294
+ # redirect_back(fallback_location: root_path)
295
+ else
296
+ render :new, status: :unprocessable_entity
297
+ end
298
+ end
299
+ end
300
+ ```
301
+
302
+ ## Flash Messages
303
+
304
+ ```ruby
305
+ class PostsController < ApplicationController
306
+ def create
307
+ @post = Post.new(post_params)
308
+
309
+ if @post.save
310
+ # Set flash for next request
311
+ flash[:notice] = 'Post created!'
312
+ # Or shorter:
313
+ redirect_to @post, notice: 'Post created!'
314
+
315
+ # Different flash types
316
+ # flash[:success] = 'Success!'
317
+ # flash[:error] = 'Error!'
318
+ # flash[:alert] = 'Alert!'
319
+ # flash[:warning] = 'Warning!'
320
+ else
321
+ # flash.now for current request
322
+ flash.now[:alert] = 'Could not create post'
323
+ render :new
324
+ end
325
+ end
326
+
327
+ def update
328
+ # Keep flash for next request
329
+ flash.keep
330
+ redirect_to @post
331
+ end
332
+ end
333
+ ```
334
+
335
+ ## Response Formats
336
+
337
+ ```ruby
338
+ class PostsController < ApplicationController
339
+ def show
340
+ @post = Post.find(params[:id])
341
+
342
+ respond_to do |format|
343
+ format.html # renders show.html.erb
344
+ format.json { render json: @post }
345
+ format.xml { render xml: @post }
346
+ format.pdf { render pdf: generate_pdf(@post) }
347
+ end
348
+ end
349
+
350
+ def create
351
+ @post = Post.new(post_params)
352
+
353
+ respond_to do |format|
354
+ if @post.save
355
+ format.html { redirect_to @post, notice: 'Created!' }
356
+ format.json { render json: @post, status: :created }
357
+ else
358
+ format.html { render :new, status: :unprocessable_entity }
359
+ format.json { render json: @post.errors, status: :unprocessable_entity }
360
+ end
361
+ end
362
+ end
363
+ end
364
+ ```
365
+
366
+ ## Controller Concerns
367
+
368
+ ```ruby
369
+ # app/controllers/concerns/authenticatable.rb
370
+ module Authenticatable
371
+ extend ActiveSupport::Concern
372
+
373
+ included do
374
+ before_action :authenticate_user!
375
+ helper_method :current_user, :logged_in?
376
+ end
377
+
378
+ def current_user
379
+ @current_user ||= User.find_by(id: session[:user_id])
380
+ end
381
+
382
+ def logged_in?
383
+ current_user.present?
384
+ end
385
+
386
+ def authenticate_user!
387
+ unless logged_in?
388
+ redirect_to login_path, alert: 'Please log in'
389
+ end
390
+ end
391
+ end
392
+
393
+ # Usage
394
+ class PostsController < ApplicationController
395
+ include Authenticatable
396
+ end
397
+ ```
398
+
399
+ ## Error Handling
400
+
401
+ ```ruby
402
+ class ApplicationController < ActionController::Base
403
+ rescue_from ActiveRecord::RecordNotFound, with: :record_not_found
404
+ rescue_from ActionController::ParameterMissing, with: :parameter_missing
405
+
406
+ private
407
+
408
+ def record_not_found
409
+ render file: "#{Rails.root}/public/404.html", status: :not_found
410
+ end
411
+
412
+ def parameter_missing
413
+ render json: { error: 'Missing parameter' }, status: :bad_request
414
+ end
415
+ end
416
+
417
+ class PostsController < ApplicationController
418
+ def show
419
+ @post = Post.find(params[:id])
420
+ rescue ActiveRecord::RecordNotFound
421
+ redirect_to posts_path, alert: 'Post not found'
422
+ end
423
+ end
424
+ ```
425
+
426
+ ## Session and Cookies
427
+
428
+ ```ruby
429
+ class SessionsController < ApplicationController
430
+ def create
431
+ user = User.find_by(email: params[:email])
432
+
433
+ if user&.authenticate(params[:password])
434
+ # Set session
435
+ session[:user_id] = user.id
436
+
437
+ # Set cookie
438
+ cookies[:user_name] = user.name
439
+
440
+ # Signed cookie (tamper-proof)
441
+ cookies.signed[:user_id] = user.id
442
+
443
+ # Encrypted cookie
444
+ cookies.encrypted[:user_data] = { id: user.id, role: user.role }
445
+
446
+ # Permanent cookie (20 years)
447
+ cookies.permanent[:remember_token] = user.remember_token
448
+
449
+ redirect_to root_path
450
+ else
451
+ flash.now[:alert] = 'Invalid credentials'
452
+ render :new
453
+ end
454
+ end
455
+
456
+ def destroy
457
+ session.delete(:user_id)
458
+ cookies.delete(:user_name)
459
+ redirect_to root_path
460
+ end
461
+ end
462
+ ```
463
+
464
+ ## Best Practices
465
+
466
+ 1. **Keep controllers thin** - Move business logic to models or service objects
467
+ 2. **Use before_action** for common setup code
468
+ 3. **Always use strong parameters** for security
469
+ 4. **Return proper HTTP status codes**
470
+ 5. **Use concerns** for shared controller behavior
471
+ 6. **Follow REST conventions** when possible
472
+ 7. **Handle errors gracefully** with rescue_from
473
+ 8. **Use flash messages** for user feedback
474
+ 9. **Set instance variables** only for view rendering
475
+ 10. **Avoid complex queries** in controllers - use scopes or query objects
476
+
477
+ ## Common Patterns
478
+
479
+ ### Service Objects for Complex Actions
480
+
481
+ ```ruby
482
+ class PostsController < ApplicationController
483
+ def create
484
+ result = Posts::CreateService.call(
485
+ params: post_params,
486
+ user: current_user
487
+ )
488
+
489
+ if result.success?
490
+ redirect_to result.post, notice: 'Created!'
491
+ else
492
+ @post = result.post
493
+ flash.now[:alert] = result.error
494
+ render :new
495
+ end
496
+ end
497
+ end
498
+ ```
499
+
500
+ ### Query Objects for Complex Queries
501
+
502
+ ```ruby
503
+ class PostsController < ApplicationController
504
+ def index
505
+ @posts = PostsQuery.new(params).call
506
+ end
507
+ end
508
+ ```
509
+
510
+ ## References
511
+
512
+ - [Rails Guides - Controllers](https://guides.rubyonrails.org/action_controller_overview.html)
513
+ - [Rails Guides - Routing](https://guides.rubyonrails.org/routing.html)
514
+ - [Rails API - ActionController](https://api.rubyonrails.org/classes/ActionController/Base.html)