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,603 @@
1
+ ---
2
+ name: rails-authorization-cancancan
3
+ description: "Authorization and permissions management for Ruby on Rails applications using CanCanCan. Use when: (1) Implementing role-based access control (RBAC), (2) Defining user permissions and abilities, (3) Restricting resource access in controllers, (4) Filtering queries based on user permissions, (5) Hiding/showing UI elements based on authorization, (6) Testing authorization logic, (7) Managing admin vs user vs guest permissions, (8) Implementing attribute-based access control"
4
+ ---
5
+
6
+ # Rails Authorization with CanCanCan
7
+
8
+ CanCanCan is a popular authorization library for Rails that restricts what resources a given user is allowed to access. It centralizes all permission logic in a single Ability class, keeping authorization rules DRY and maintainable.
9
+
10
+ ## Quick Setup
11
+
12
+ ```bash
13
+ # Add to Gemfile
14
+ bundle add cancancan
15
+
16
+ # Generate Ability class
17
+ rails generate cancan:ability
18
+ ```
19
+
20
+ This creates `app/models/ability.rb` where all authorization rules are defined.
21
+
22
+ ## Core Concepts
23
+
24
+ ### Defining Abilities
25
+
26
+ The `Ability` class centralizes all permission logic:
27
+
28
+ ```ruby
29
+ # app/models/ability.rb
30
+ class Ability
31
+ include CanCan::Ability
32
+
33
+ def initialize(user)
34
+ # Guest users (not signed in)
35
+ can :read, Post, published: true
36
+ can :read, Comment
37
+
38
+ # Signed-in users
39
+ return unless user.present?
40
+
41
+ can :read, Post
42
+ can :create, Post
43
+ can :update, Post, user_id: user.id
44
+ can :destroy, Post, user_id: user.id
45
+
46
+ can :create, Comment
47
+ can :update, Comment, user_id: user.id
48
+ can :destroy, Comment, user_id: user.id
49
+
50
+ # Admin users
51
+ return unless user.admin?
52
+
53
+ can :manage, :all # Can do anything
54
+ end
55
+ end
56
+ ```
57
+
58
+ **Best Practice**: Structure rules hierarchically (guest → user → admin) for clarity.
59
+
60
+ ## Actions and Resources
61
+
62
+ ### Standard CRUD Actions
63
+
64
+ ```ruby
65
+ :read # :index and :show
66
+ :create # :new and :create
67
+ :update # :edit and :update
68
+ :destroy # :destroy
69
+
70
+ :manage # All actions (use carefully!)
71
+ ```
72
+
73
+ ### Custom Actions
74
+
75
+ ```ruby
76
+ can :publish, Post
77
+ can :archive, Post
78
+ can :approve, Comment
79
+ ```
80
+
81
+ ### Multiple Resources
82
+
83
+ ```ruby
84
+ can :read, [Post, Comment, Category]
85
+ can :manage, [User, Post], user_id: user.id
86
+ ```
87
+
88
+ ## Ability Conditions
89
+
90
+ ### Hash Conditions
91
+
92
+ ```ruby
93
+ # Simple equality
94
+ can :update, Post, user_id: user.id
95
+
96
+ # Multiple conditions (AND logic)
97
+ can :read, Post, published: true, category_id: user.accessible_category_ids
98
+
99
+ # SQL fragment (use sparingly)
100
+ can :read, Post, ["published_at <= ?", Time.zone.now]
101
+ ```
102
+
103
+ ### Block Conditions
104
+
105
+ ```ruby
106
+ # Complex logic
107
+ can :update, Post do |post|
108
+ post.user_id == user.id || user.admin?
109
+ end
110
+
111
+ # With associations
112
+ can :read, Post do |post|
113
+ post.published? || post.user_id == user.id
114
+ end
115
+
116
+ # Accessing current user
117
+ can :destroy, Comment do |comment|
118
+ comment.user_id == user.id && comment.created_at > 15.minutes.ago
119
+ end
120
+ ```
121
+
122
+ **Important**: Block conditions cannot be used with `accessible_by` for database queries. Use hash conditions when you need to filter collections.
123
+
124
+ ### Combining Conditions
125
+
126
+ ```ruby
127
+ # Multiple can statements are OR'd together
128
+ can :read, Post, published: true # Public posts
129
+ can :read, Post, user_id: user.id # Own posts
130
+ # User can read posts that are EITHER published OR owned by them
131
+ ```
132
+
133
+ ## Controller Integration
134
+
135
+ ### Manual Authorization
136
+
137
+ ```ruby
138
+ class PostsController < ApplicationController
139
+ def show
140
+ @post = Post.find(params[:id])
141
+ authorize! :read, @post # Raises CanCan::AccessDenied if not authorized
142
+ end
143
+
144
+ def update
145
+ @post = Post.find(params[:id])
146
+ authorize! :update, @post
147
+
148
+ if @post.update(post_params)
149
+ redirect_to @post
150
+ else
151
+ render :edit
152
+ end
153
+ end
154
+ end
155
+ ```
156
+
157
+ ### Automatic Loading and Authorization
158
+
159
+ ```ruby
160
+ class PostsController < ApplicationController
161
+ load_and_authorize_resource
162
+
163
+ def index
164
+ # @posts automatically loaded with accessible_by
165
+ end
166
+
167
+ def show
168
+ # @post automatically loaded and authorized
169
+ end
170
+
171
+ def create
172
+ # @post initialized and authorized
173
+ if @post.save
174
+ redirect_to @post
175
+ else
176
+ render :new
177
+ end
178
+ end
179
+
180
+ def update
181
+ # @post loaded and authorized
182
+ if @post.update(post_params)
183
+ redirect_to @post
184
+ else
185
+ render :edit
186
+ end
187
+ end
188
+ end
189
+ ```
190
+
191
+ **Benefits**: Eliminates repetitive authorization code across RESTful actions.
192
+
193
+ ### Load and Authorize Options
194
+
195
+ ```ruby
196
+ # Specific actions only
197
+ load_and_authorize_resource only: [:show, :edit, :update, :destroy]
198
+ load_and_authorize_resource except: [:index]
199
+
200
+ # Different resource name
201
+ load_and_authorize_resource :article
202
+
203
+ # Custom find method
204
+ load_and_authorize_resource find_by: :slug
205
+
206
+ # Nested resources
207
+ class CommentsController < ApplicationController
208
+ load_and_authorize_resource :post
209
+ load_and_authorize_resource :comment, through: :post
210
+ end
211
+
212
+ # Skip loading (only authorize)
213
+ authorize_resource
214
+
215
+ # Skip authorization for specific actions
216
+ skip_authorize_resource only: [:index]
217
+ ```
218
+
219
+ ## Fetching Authorized Records
220
+
221
+ ### accessible_by
222
+
223
+ Retrieve only records the user can access:
224
+
225
+ ```ruby
226
+ # In controller
227
+ def index
228
+ @posts = Post.accessible_by(current_ability)
229
+ end
230
+
231
+ # With specific action
232
+ @posts = Post.accessible_by(current_ability, :read)
233
+ @editable_posts = Post.accessible_by(current_ability, :update)
234
+
235
+ # Chainable with ActiveRecord
236
+ @published_posts = Post.published.accessible_by(current_ability)
237
+ @posts = Post.accessible_by(current_ability).where(category_id: params[:category_id])
238
+ ```
239
+
240
+ **Performance**: Uses SQL conditions from ability rules for efficient database queries.
241
+
242
+ ## View Helpers
243
+
244
+ ### Conditional UI Elements
245
+
246
+ ```ruby
247
+ # Check single permission
248
+ <% if can? :update, @post %>
249
+ <%= link_to 'Edit', edit_post_path(@post) %>
250
+ <% end %>
251
+
252
+ <% if can? :destroy, @post %>
253
+ <%= link_to 'Delete', @post, method: :delete, data: { confirm: 'Are you sure?' } %>
254
+ <% end %>
255
+
256
+ # Negative check
257
+ <% if cannot? :update, @post %>
258
+ <p>You cannot edit this post</p>
259
+ <% end %>
260
+
261
+ # Multiple permissions
262
+ <% if can?(:update, @post) || can?(:destroy, @post) %>
263
+ <div class="post-actions">
264
+ <%= link_to 'Edit', edit_post_path(@post) if can? :update, @post %>
265
+ <%= link_to 'Delete', @post, method: :delete if can? :destroy, @post %>
266
+ </div>
267
+ <% end %>
268
+
269
+ # Check on class (useful in index views)
270
+ <% if can? :create, Post %>
271
+ <%= link_to 'New Post', new_post_path %>
272
+ <% end %>
273
+ ```
274
+
275
+ ### Navigation Menus
276
+
277
+ ```ruby
278
+ <nav>
279
+ <%= link_to 'Posts', posts_path if can? :read, Post %>
280
+ <%= link_to 'New Post', new_post_path if can? :create, Post %>
281
+ <%= link_to 'Admin', admin_path if can? :manage, :all %>
282
+ </nav>
283
+ ```
284
+
285
+ ## Handling Unauthorized Access
286
+
287
+ ### Exception Rescue
288
+
289
+ ```ruby
290
+ # app/controllers/application_controller.rb
291
+ class ApplicationController < ActionController::Base
292
+ rescue_from CanCan::AccessDenied do |exception|
293
+ respond_to do |format|
294
+ format.html { redirect_to root_path, alert: exception.message }
295
+ format.json { render json: { error: exception.message }, status: :forbidden }
296
+ end
297
+ end
298
+ end
299
+ ```
300
+
301
+ ### Custom Error Messages
302
+
303
+ ```ruby
304
+ # In Ability class
305
+ can :update, Post, user_id: user.id do |post|
306
+ post.user_id == user.id
307
+ end
308
+
309
+ # In controller with custom message
310
+ authorize! :update, @post, message: "You can only edit your own posts"
311
+ ```
312
+
313
+ ### Flash Messages
314
+
315
+ ```ruby
316
+ rescue_from CanCan::AccessDenied do |exception|
317
+ redirect_to root_path, alert: "Access denied: #{exception.message}"
318
+ end
319
+ ```
320
+
321
+ ## Common Patterns
322
+
323
+ ### Role-Based Authorization
324
+
325
+ ```ruby
326
+ # app/models/user.rb
327
+ class User < ApplicationRecord
328
+ ROLES = %w[guest user moderator admin].freeze
329
+
330
+ enum role: { guest: 0, user: 1, moderator: 2, admin: 3 }
331
+
332
+ def role?(check_role)
333
+ role.to_sym == check_role.to_sym
334
+ end
335
+ end
336
+
337
+ # app/models/ability.rb
338
+ class Ability
339
+ include CanCan::Ability
340
+
341
+ def initialize(user)
342
+ user ||= User.new # Guest user
343
+
344
+ if user.admin?
345
+ can :manage, :all
346
+ elsif user.moderator?
347
+ can :manage, Post
348
+ can :manage, Comment
349
+ can :read, User
350
+ elsif user.user?
351
+ can :read, :all
352
+ can :create, Post
353
+ can :manage, Post, user_id: user.id
354
+ can :manage, Comment, user_id: user.id
355
+ else
356
+ can :read, Post, published: true
357
+ end
358
+ end
359
+ end
360
+ ```
361
+
362
+ ### Organization/Tenant-Based Authorization
363
+
364
+ ```ruby
365
+ class Ability
366
+ include CanCan::Ability
367
+
368
+ def initialize(user)
369
+ return unless user.present?
370
+
371
+ # User can manage resources in their organization
372
+ can :manage, Post, organization_id: user.organization_id
373
+ can :manage, Comment, post: { organization_id: user.organization_id }
374
+
375
+ # Admin can manage organization settings
376
+ can :manage, Organization, id: user.organization_id if user.admin?
377
+ end
378
+ end
379
+ ```
380
+
381
+ ### Time-Based Authorization
382
+
383
+ ```ruby
384
+ class Ability
385
+ include CanCan::Ability
386
+
387
+ def initialize(user)
388
+ return unless user.present?
389
+
390
+ # Can edit posts within 1 hour of creation
391
+ can :update, Post do |post|
392
+ post.user_id == user.id && post.created_at > 1.hour.ago
393
+ end
394
+
395
+ # Can read posts after publication date
396
+ can :read, Post, ["published_at <= ?", Time.current]
397
+ end
398
+ end
399
+ ```
400
+
401
+ ### Attribute-Based Authorization
402
+
403
+ ```ruby
404
+ class Ability
405
+ include CanCan::Ability
406
+
407
+ def initialize(user)
408
+ return unless user.present?
409
+
410
+ # Users can update specific attributes of their own posts
411
+ can [:update], Post, user_id: user.id
412
+
413
+ # Only admins can change published status
414
+ cannot :update, Post, :published unless user.admin?
415
+
416
+ # Users can update their profile but not role
417
+ can :update, User, id: user.id
418
+ cannot :update, User, :role
419
+ end
420
+ end
421
+ ```
422
+
423
+ ## Strong Parameters with CanCanCan
424
+
425
+ ```ruby
426
+ # app/controllers/posts_controller.rb
427
+ def post_params
428
+ params.require(:post).permit(:title, :body, :published)
429
+ end
430
+
431
+ # Restrict based on abilities
432
+ def post_params
433
+ params.require(:post).permit(
434
+ current_user.admin? ? [:title, :body, :published] : [:title, :body]
435
+ )
436
+ end
437
+ ```
438
+
439
+ ## Testing
440
+
441
+ ### RSpec Setup
442
+
443
+ ```ruby
444
+ # spec/support/cancan.rb
445
+ RSpec.configure do |config|
446
+ config.include CanCan::Ability
447
+ end
448
+ ```
449
+
450
+ ### Testing Abilities
451
+
452
+ ```ruby
453
+ # spec/models/ability_spec.rb
454
+ require 'rails_helper'
455
+ require 'cancan/matchers'
456
+
457
+ RSpec.describe Ability, type: :model do
458
+ subject(:ability) { Ability.new(user) }
459
+
460
+ describe 'Guest user' do
461
+ let(:user) { nil }
462
+
463
+ it { is_expected.to be_able_to(:read, Post.new(published: true)) }
464
+ it { is_expected.not_to be_able_to(:create, Post) }
465
+ it { is_expected.not_to be_able_to(:update, Post) }
466
+ end
467
+
468
+ describe 'Regular user' do
469
+ let(:user) { create(:user) }
470
+ let(:own_post) { create(:post, user: user) }
471
+ let(:other_post) { create(:post) }
472
+
473
+ it { is_expected.to be_able_to(:read, Post) }
474
+ it { is_expected.to be_able_to(:create, Post) }
475
+ it { is_expected.to be_able_to(:update, own_post) }
476
+ it { is_expected.not_to be_able_to(:update, other_post) }
477
+ it { is_expected.to be_able_to(:destroy, own_post) }
478
+ it { is_expected.not_to be_able_to(:destroy, other_post) }
479
+ end
480
+
481
+ describe 'Admin user' do
482
+ let(:user) { create(:user, admin: true) }
483
+
484
+ it { is_expected.to be_able_to(:manage, :all) }
485
+ end
486
+ end
487
+ ```
488
+
489
+ ### Testing Controllers
490
+
491
+ ```ruby
492
+ # spec/controllers/posts_controller_spec.rb
493
+ RSpec.describe PostsController, type: :controller do
494
+ let(:user) { create(:user) }
495
+ let(:other_user) { create(:user) }
496
+ let(:post) { create(:post, user: user) }
497
+
498
+ before { sign_in user }
499
+
500
+ describe 'GET #edit' do
501
+ context 'when editing own post' do
502
+ it 'allows access' do
503
+ get :edit, params: { id: post.id }
504
+ expect(response).to have_http_status(:ok)
505
+ end
506
+ end
507
+
508
+ context 'when editing other user post' do
509
+ let(:other_post) { create(:post, user: other_user) }
510
+
511
+ it 'denies access' do
512
+ expect {
513
+ get :edit, params: { id: other_post.id }
514
+ }.to raise_error(CanCan::AccessDenied)
515
+ end
516
+ end
517
+ end
518
+ end
519
+ ```
520
+
521
+ ### Testing accessible_by
522
+
523
+ ```ruby
524
+ RSpec.describe 'Post access', type: :model do
525
+ let(:user) { create(:user) }
526
+ let(:admin) { create(:user, admin: true) }
527
+ let!(:published_post) { create(:post, published: true) }
528
+ let!(:draft_post) { create(:post, published: false, user: user) }
529
+ let!(:other_draft) { create(:post, published: false) }
530
+
531
+ it 'returns correct posts for user' do
532
+ ability = Ability.new(user)
533
+ accessible = Post.accessible_by(ability)
534
+
535
+ expect(accessible).to include(published_post, draft_post)
536
+ expect(accessible).not_to include(other_draft)
537
+ end
538
+
539
+ it 'returns all posts for admin' do
540
+ ability = Ability.new(admin)
541
+ accessible = Post.accessible_by(ability)
542
+
543
+ expect(accessible).to include(published_post, draft_post, other_draft)
544
+ end
545
+ end
546
+ ```
547
+
548
+ ## Performance Considerations
549
+
550
+ ### Use Hash Conditions for Collections
551
+
552
+ ```ruby
553
+ # Good - generates SQL query
554
+ can :read, Post, user_id: user.id
555
+ @posts = Post.accessible_by(current_ability)
556
+
557
+ # Bad - cannot generate SQL, will raise error
558
+ can :read, Post do |post|
559
+ post.user_id == user.id
560
+ end
561
+ @posts = Post.accessible_by(current_ability) # Error!
562
+ ```
563
+
564
+ ### Eager Loading
565
+
566
+ ```ruby
567
+ # Prevent N+1 queries
568
+ @posts = Post.accessible_by(current_ability).includes(:user, :comments)
569
+ ```
570
+
571
+ ### Caching Abilities
572
+
573
+ ```ruby
574
+ # Cache ability checks in instance variable
575
+ def current_ability
576
+ @current_ability ||= Ability.new(current_user)
577
+ end
578
+ ```
579
+
580
+ ## Integration with Pundit
581
+
582
+ If migrating from Pundit or using both:
583
+
584
+ ```ruby
585
+ # CanCanCan uses a single Ability class
586
+ # Pundit uses policy classes per model
587
+
588
+ # They can coexist, but choose one primary approach
589
+ # CanCanCan: Centralized, better for simple RBAC
590
+ # Pundit: Decentralized, better for complex domain logic
591
+ ```
592
+
593
+ ## Advanced Patterns
594
+
595
+ For more complex scenarios, see:
596
+ - **Multi-tenancy**: [references/multi-tenancy.md](references/multi-tenancy.md)
597
+ - **API authorization**: [references/api-authorization.md](references/api-authorization.md)
598
+ - **Complex permissions**: [references/complex-permissions.md](references/complex-permissions.md)
599
+
600
+ ## Resources
601
+
602
+ - [CanCanCan GitHub](https://github.com/CanCanCommunity/cancancan)
603
+ - [CanCanCan Wiki](https://github.com/CanCanCommunity/cancancan/wiki)