source_monitor 0.2.0 → 0.3.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 (196) hide show
  1. checksums.yaml +4 -4
  2. data/.claude/agents/rails-concern.md +464 -0
  3. data/.claude/agents/rails-controller.md +424 -0
  4. data/.claude/agents/rails-hotwire.md +446 -0
  5. data/.claude/agents/rails-implement.md +374 -0
  6. data/.claude/agents/rails-job.md +334 -0
  7. data/.claude/agents/rails-lint.md +294 -0
  8. data/.claude/agents/rails-mailer.md +371 -0
  9. data/.claude/agents/rails-migration.md +449 -0
  10. data/.claude/agents/rails-model.md +420 -0
  11. data/.claude/agents/rails-policy.md +443 -0
  12. data/.claude/agents/rails-presenter.md +427 -0
  13. data/.claude/agents/rails-query.md +412 -0
  14. data/.claude/agents/rails-review.md +490 -0
  15. data/.claude/agents/rails-service.md +458 -0
  16. data/.claude/agents/rails-state-records.md +465 -0
  17. data/.claude/agents/rails-tdd.md +314 -0
  18. data/.claude/agents/rails-test.md +441 -0
  19. data/.claude/agents/rails-view-component.md +418 -0
  20. data/.claude/hooks/block-secrets.sh +52 -0
  21. data/.claude/settings.json +85 -0
  22. data/.claude/skills/action-cable-patterns/SKILL.md +296 -0
  23. data/.claude/skills/action-mailer-patterns/SKILL.md +295 -0
  24. data/.claude/skills/active-storage-setup/SKILL.md +311 -0
  25. data/.claude/skills/api-versioning/SKILL.md +294 -0
  26. data/.claude/skills/authentication-flow/SKILL.md +335 -0
  27. data/.claude/skills/authentication-flow/reference/current.md +248 -0
  28. data/.claude/skills/authentication-flow/reference/passwordless.md +253 -0
  29. data/.claude/skills/authentication-flow/reference/sessions.md +201 -0
  30. data/.claude/skills/authorization-pundit/SKILL.md +462 -0
  31. data/.claude/skills/caching-strategies/SKILL.md +350 -0
  32. data/.claude/skills/database-migrations/SKILL.md +354 -0
  33. data/.claude/skills/form-object-patterns/SKILL.md +399 -0
  34. data/.claude/skills/hotwire-patterns/SKILL.md +247 -0
  35. data/.claude/skills/hotwire-patterns/reference/stimulus.md +307 -0
  36. data/.claude/skills/hotwire-patterns/reference/tailwind-integration.md +112 -0
  37. data/.claude/skills/hotwire-patterns/reference/turbo-frames.md +158 -0
  38. data/.claude/skills/hotwire-patterns/reference/turbo-streams.md +218 -0
  39. data/.claude/skills/i18n-patterns/SKILL.md +320 -0
  40. data/.claude/skills/install/SKILL.md +367 -0
  41. data/.claude/skills/performance-optimization/SKILL.md +311 -0
  42. data/.claude/skills/rails-architecture/SKILL.md +259 -0
  43. data/.claude/skills/rails-architecture/reference/error-handling.md +333 -0
  44. data/.claude/skills/rails-architecture/reference/event-tracking.md +142 -0
  45. data/.claude/skills/rails-architecture/reference/layer-interactions.md +417 -0
  46. data/.claude/skills/rails-architecture/reference/multi-tenancy.md +152 -0
  47. data/.claude/skills/rails-architecture/reference/query-patterns.md +342 -0
  48. data/.claude/skills/rails-architecture/reference/service-patterns.md +286 -0
  49. data/.claude/skills/rails-architecture/reference/state-records.md +250 -0
  50. data/.claude/skills/rails-architecture/reference/testing-strategy.md +326 -0
  51. data/.claude/skills/rails-concern/SKILL.md +399 -0
  52. data/.claude/skills/rails-controller/SKILL.md +336 -0
  53. data/.claude/skills/rails-model-generator/SKILL.md +321 -0
  54. data/.claude/skills/rails-model-generator/reference/validations.md +298 -0
  55. data/.claude/skills/rails-presenter/SKILL.md +274 -0
  56. data/.claude/skills/rails-query-object/SKILL.md +289 -0
  57. data/.claude/skills/rails-service-object/SKILL.md +349 -0
  58. data/.claude/skills/solid-queue-setup/SKILL.md +307 -0
  59. data/.claude/skills/tdd-cycle/SKILL.md +359 -0
  60. data/.claude/skills/viewcomponent-patterns/SKILL.md +333 -0
  61. data/.gitignore +1 -0
  62. data/.rubocop.yml +2 -0
  63. data/.ruby-version +1 -1
  64. data/.vbw-planning/.notification-log.jsonl +192 -0
  65. data/.vbw-planning/.session-log.jsonl +871 -0
  66. data/.vbw-planning/PROJECT.md +51 -0
  67. data/.vbw-planning/REQUIREMENTS.md +50 -0
  68. data/.vbw-planning/SHIPPED.md +28 -0
  69. data/.vbw-planning/codebase/ARCHITECTURE.md +147 -0
  70. data/.vbw-planning/codebase/CONCERNS.md +99 -0
  71. data/.vbw-planning/codebase/CONVENTIONS.md +97 -0
  72. data/.vbw-planning/codebase/DEPENDENCIES.md +100 -0
  73. data/.vbw-planning/codebase/INDEX.md +86 -0
  74. data/.vbw-planning/codebase/META.md +42 -0
  75. data/.vbw-planning/codebase/PATTERNS.md +262 -0
  76. data/.vbw-planning/codebase/STACK.md +101 -0
  77. data/.vbw-planning/codebase/STRUCTURE.md +324 -0
  78. data/.vbw-planning/codebase/TESTING.md +154 -0
  79. data/.vbw-planning/config.json +12 -0
  80. data/.vbw-planning/discovery.json +24 -0
  81. data/.vbw-planning/milestones/default/ROADMAP.md +115 -0
  82. data/.vbw-planning/milestones/default/STATE.md +83 -0
  83. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01-SUMMARY.md +56 -0
  84. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-01.md +187 -0
  85. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02-SUMMARY.md +64 -0
  86. data/.vbw-planning/milestones/default/phases/01-coverage-analysis-quick-wins/PLAN-02.md +137 -0
  87. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01-SUMMARY.md +67 -0
  88. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-01.md +142 -0
  89. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02-SUMMARY.md +64 -0
  90. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-02.md +138 -0
  91. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03-SUMMARY.md +85 -0
  92. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-03.md +147 -0
  93. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04-SUMMARY.md +63 -0
  94. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-04.md +129 -0
  95. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05-SUMMARY.md +74 -0
  96. data/.vbw-planning/milestones/default/phases/02-critical-path-test-coverage/PLAN-05.md +154 -0
  97. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION-wave1.md +303 -0
  98. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/03-VERIFICATION.md +510 -0
  99. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01-SUMMARY.md +61 -0
  100. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-01.md +161 -0
  101. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02-SUMMARY.md +66 -0
  102. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-02.md +132 -0
  103. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03-SUMMARY.md +59 -0
  104. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-03.md +171 -0
  105. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04-SUMMARY.md +56 -0
  106. data/.vbw-planning/milestones/default/phases/03-large-file-refactoring/PLAN-04.md +152 -0
  107. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/04-CONTEXT.md +33 -0
  108. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01-SUMMARY.md +42 -0
  109. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-01.md +119 -0
  110. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02-SUMMARY.md +52 -0
  111. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-02.md +195 -0
  112. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03-SUMMARY.md +79 -0
  113. data/.vbw-planning/milestones/default/phases/04-code-quality-conventions-cleanup/PLAN-03.md +130 -0
  114. data/CHANGELOG.md +28 -0
  115. data/CLAUDE.md +179 -0
  116. data/Gemfile +8 -0
  117. data/Gemfile.lock +114 -101
  118. data/Rakefile +2 -0
  119. data/app/assets/builds/source_monitor/application.css +2076 -0
  120. data/app/assets/builds/source_monitor/application.js +2758 -0
  121. data/app/assets/builds/source_monitor/application.js.map +7 -0
  122. data/app/controllers/source_monitor/application_controller.rb +2 -0
  123. data/app/controllers/source_monitor/health_controller.rb +2 -0
  124. data/app/controllers/source_monitor/import_sessions/bulk_configuration.rb +106 -0
  125. data/app/controllers/source_monitor/import_sessions/entry_annotation.rb +187 -0
  126. data/app/controllers/source_monitor/import_sessions/health_check_management.rb +112 -0
  127. data/app/controllers/source_monitor/import_sessions/opml_parser.rb +130 -0
  128. data/app/controllers/source_monitor/import_sessions_controller.rb +6 -507
  129. data/app/controllers/source_monitor/items_controller.rb +2 -0
  130. data/app/controllers/source_monitor/sources_controller.rb +0 -14
  131. data/app/helpers/source_monitor/application_helper.rb +4 -112
  132. data/app/helpers/source_monitor/health_badge_helper.rb +69 -0
  133. data/app/helpers/source_monitor/table_sort_helper.rb +53 -0
  134. data/app/jobs/source_monitor/application_job.rb +2 -0
  135. data/app/models/source_monitor/application_record.rb +2 -0
  136. data/app/models/source_monitor/log_entry.rb +0 -2
  137. data/config/coverage_baseline.json +217 -1862
  138. data/config/routes.rb +2 -0
  139. data/db/migrate/20251009103000_add_feed_content_readability_to_sources.rb +2 -0
  140. data/db/migrate/20251014171659_add_performance_indexes.rb +2 -0
  141. data/db/migrate/20251014172525_add_fetch_status_check_constraint.rb +2 -0
  142. data/db/migrate/20251108120116_refresh_fetch_status_constraint.rb +2 -0
  143. data/db/migrate/20260210204022_add_composite_index_to_log_entries.rb +17 -0
  144. data/lib/source_monitor/assets/bundler.rb +2 -0
  145. data/lib/source_monitor/assets.rb +2 -0
  146. data/lib/source_monitor/configuration/authentication_settings.rb +62 -0
  147. data/lib/source_monitor/configuration/events.rb +60 -0
  148. data/lib/source_monitor/configuration/fetching_settings.rb +27 -0
  149. data/lib/source_monitor/configuration/health_settings.rb +27 -0
  150. data/lib/source_monitor/configuration/http_settings.rb +43 -0
  151. data/lib/source_monitor/configuration/model_definition.rb +108 -0
  152. data/lib/source_monitor/configuration/models.rb +36 -0
  153. data/lib/source_monitor/configuration/realtime_settings.rb +95 -0
  154. data/lib/source_monitor/configuration/retention_settings.rb +45 -0
  155. data/lib/source_monitor/configuration/scraper_registry.rb +67 -0
  156. data/lib/source_monitor/configuration/scraping_settings.rb +39 -0
  157. data/lib/source_monitor/configuration/validation_definition.rb +32 -0
  158. data/lib/source_monitor/configuration.rb +12 -579
  159. data/lib/source_monitor/dashboard/queries/recent_activity_query.rb +138 -0
  160. data/lib/source_monitor/dashboard/queries/stats_query.rb +71 -0
  161. data/lib/source_monitor/dashboard/queries.rb +2 -195
  162. data/lib/source_monitor/engine.rb +2 -0
  163. data/lib/source_monitor/fetching/feed_fetcher/adaptive_interval.rb +141 -0
  164. data/lib/source_monitor/fetching/feed_fetcher/entry_processor.rb +89 -0
  165. data/lib/source_monitor/fetching/feed_fetcher/source_updater.rb +200 -0
  166. data/lib/source_monitor/fetching/feed_fetcher.rb +37 -379
  167. data/lib/source_monitor/items/item_creator/content_extractor.rb +113 -0
  168. data/lib/source_monitor/items/item_creator/entry_parser/media_extraction.rb +96 -0
  169. data/lib/source_monitor/items/item_creator/entry_parser.rb +294 -0
  170. data/lib/source_monitor/items/item_creator.rb +28 -455
  171. data/lib/source_monitor/setup/bundle_installer.rb +2 -0
  172. data/lib/source_monitor/setup/cli.rb +2 -0
  173. data/lib/source_monitor/setup/dependency_checker.rb +2 -0
  174. data/lib/source_monitor/setup/detectors.rb +2 -0
  175. data/lib/source_monitor/setup/gemfile_editor.rb +2 -0
  176. data/lib/source_monitor/setup/initializer_patcher.rb +2 -0
  177. data/lib/source_monitor/setup/install_generator.rb +2 -0
  178. data/lib/source_monitor/setup/migration_installer.rb +2 -0
  179. data/lib/source_monitor/setup/node_installer.rb +2 -0
  180. data/lib/source_monitor/setup/prompter.rb +2 -0
  181. data/lib/source_monitor/setup/requirements.rb +2 -0
  182. data/lib/source_monitor/setup/shell_runner.rb +2 -0
  183. data/lib/source_monitor/setup/verification/action_cable_verifier.rb +2 -0
  184. data/lib/source_monitor/setup/verification/printer.rb +2 -0
  185. data/lib/source_monitor/setup/verification/result.rb +2 -0
  186. data/lib/source_monitor/setup/verification/runner.rb +2 -0
  187. data/lib/source_monitor/setup/verification/solid_queue_verifier.rb +2 -0
  188. data/lib/source_monitor/setup/verification/telemetry_logger.rb +2 -0
  189. data/lib/source_monitor/setup/workflow.rb +2 -0
  190. data/lib/source_monitor/version.rb +3 -1
  191. data/lib/source_monitor.rb +140 -58
  192. data/lib/tasks/source_monitor_assets.rake +2 -0
  193. data/lib/tasks/source_monitor_setup.rake +2 -0
  194. data/lib/tasks/source_monitor_tasks.rake +2 -0
  195. data/source_monitor.gemspec +3 -1
  196. metadata +144 -4
@@ -0,0 +1,335 @@
1
+ ---
2
+ name: authentication-flow
3
+ description: Implements authentication using Rails 8 built-in generator. Use when setting up user authentication, login/logout, session management, password reset flows, or securing controllers.
4
+ allowed-tools: Read, Write, Edit, Bash, Glob, Grep
5
+ ---
6
+
7
+ # Rails 8 Authentication
8
+
9
+ ## Overview
10
+
11
+ Rails 8 includes a built-in authentication generator that creates a complete, secure authentication system without external gems.
12
+
13
+ ## Quick Start
14
+
15
+ ```bash
16
+ # Generate authentication
17
+ bin/rails generate authentication
18
+
19
+ # Run migrations
20
+ bin/rails db:migrate
21
+ ```
22
+
23
+ This creates:
24
+ - `User` model with `has_secure_password`
25
+ - `Session` model for secure sessions
26
+ - `Current` model for request-local storage
27
+ - Authentication concern for controllers
28
+ - Session and Password controllers
29
+ - Login/logout views
30
+
31
+ ## Generated Structure
32
+
33
+ ```
34
+ app/
35
+ ├── models/
36
+ │ ├── user.rb # User with has_secure_password
37
+ │ ├── session.rb # Session tracking
38
+ │ └── current.rb # Current.user accessor
39
+ ├── controllers/
40
+ │ ├── sessions_controller.rb # Login/logout
41
+ │ ├── passwords_controller.rb # Password reset
42
+ │ └── concerns/
43
+ │ └── authentication.rb # Auth helpers
44
+ └── views/
45
+ ├── sessions/
46
+ │ └── new.html.erb # Login form
47
+ └── passwords/
48
+ ├── new.html.erb # Forgot password
49
+ └── edit.html.erb # Reset password
50
+ ```
51
+
52
+ ## Core Components
53
+
54
+ ### User Model
55
+
56
+ ```ruby
57
+ # app/models/user.rb
58
+ class User < ApplicationRecord
59
+ has_secure_password
60
+ has_many :sessions, dependent: :destroy
61
+
62
+ normalizes :email_address, with: -> { _1.strip.downcase }
63
+
64
+ validates :email_address, presence: true, uniqueness: true,
65
+ format: { with: URI::MailTo::EMAIL_REGEXP }
66
+ end
67
+ ```
68
+
69
+ ### Session Model
70
+
71
+ ```ruby
72
+ # app/models/session.rb
73
+ class Session < ApplicationRecord
74
+ belongs_to :user
75
+
76
+ before_create { self.token = SecureRandom.urlsafe_base64(32) }
77
+
78
+ def self.find_by_token(token)
79
+ find_by(token: token) if token.present?
80
+ end
81
+ end
82
+ ```
83
+
84
+ ### Current Model
85
+
86
+ ```ruby
87
+ # app/models/current.rb
88
+ class Current < ActiveSupport::CurrentAttributes
89
+ attribute :session
90
+ delegate :user, to: :session, allow_nil: true
91
+ end
92
+ ```
93
+
94
+ ### Authentication Concern
95
+
96
+ ```ruby
97
+ # app/controllers/concerns/authentication.rb
98
+ module Authentication
99
+ extend ActiveSupport::Concern
100
+
101
+ included do
102
+ before_action :require_authentication
103
+ helper_method :authenticated?
104
+ end
105
+
106
+ class_methods do
107
+ def allow_unauthenticated_access(**options)
108
+ skip_before_action :require_authentication, **options
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ def authenticated?
115
+ Current.session.present?
116
+ end
117
+
118
+ def require_authentication
119
+ resume_session || request_authentication
120
+ end
121
+
122
+ def resume_session
123
+ if session_token = cookies.signed[:session_token]
124
+ if session = Session.find_by_token(session_token)
125
+ Current.session = session
126
+ end
127
+ end
128
+ end
129
+
130
+ def request_authentication
131
+ redirect_to new_session_path
132
+ end
133
+
134
+ def start_new_session_for(user)
135
+ session = user.sessions.create!
136
+ cookies.signed.permanent[:session_token] = { value: session.token, httponly: true }
137
+ Current.session = session
138
+ end
139
+
140
+ def terminate_session
141
+ Current.session&.destroy
142
+ cookies.delete(:session_token)
143
+ end
144
+ end
145
+ ```
146
+
147
+ ## Usage Patterns
148
+
149
+ ### Protecting Controllers
150
+
151
+ ```ruby
152
+ class ApplicationController < ActionController::Base
153
+ include Authentication
154
+ # All actions require authentication by default
155
+ end
156
+
157
+ class HomeController < ApplicationController
158
+ allow_unauthenticated_access only: [:index, :about]
159
+ end
160
+ ```
161
+
162
+ ### Accessing Current User
163
+
164
+ ```ruby
165
+ # In controllers and views
166
+ Current.user
167
+ Current.user.email_address
168
+ ```
169
+
170
+ ### Login Flow
171
+
172
+ ```ruby
173
+ # app/controllers/sessions_controller.rb
174
+ class SessionsController < ApplicationController
175
+ allow_unauthenticated_access only: [:new, :create]
176
+
177
+ def new
178
+ end
179
+
180
+ def create
181
+ if user = User.authenticate_by(email_address: params[:email_address],
182
+ password: params[:password])
183
+ start_new_session_for(user)
184
+ redirect_to root_path, notice: "Signed in successfully"
185
+ else
186
+ flash.now[:alert] = "Invalid email or password"
187
+ render :new, status: :unprocessable_entity
188
+ end
189
+ end
190
+
191
+ def destroy
192
+ terminate_session
193
+ redirect_to root_path, notice: "Signed out"
194
+ end
195
+ end
196
+ ```
197
+
198
+ ## Testing Authentication
199
+
200
+ ### Test Helper
201
+
202
+ ```ruby
203
+ # test/test_helper.rb
204
+ class ActionDispatch::IntegrationTest
205
+ def sign_in(user)
206
+ session = user.sessions.create!
207
+ cookies[:session_token] = session.token
208
+ end
209
+
210
+ def sign_out
211
+ cookies.delete(:session_token)
212
+ end
213
+ end
214
+ ```
215
+
216
+ ### Session Controller Tests
217
+
218
+ ```ruby
219
+ # test/controllers/sessions_controller_test.rb
220
+ require "test_helper"
221
+
222
+ class SessionsControllerTest < ActionDispatch::IntegrationTest
223
+ setup do
224
+ @user = users(:one)
225
+ end
226
+
227
+ test "GET new renders login form" do
228
+ get new_session_path
229
+ assert_response :success
230
+ end
231
+
232
+ test "POST create with valid credentials signs in user" do
233
+ post session_path, params: {
234
+ email_address: @user.email_address,
235
+ password: "password123"
236
+ }
237
+
238
+ assert_redirected_to root_path
239
+ assert cookies[:session_token].present?
240
+ end
241
+
242
+ test "POST create with invalid credentials shows error" do
243
+ post session_path, params: {
244
+ email_address: @user.email_address,
245
+ password: "wrong"
246
+ }
247
+
248
+ assert_response :unprocessable_entity
249
+ end
250
+
251
+ test "DELETE destroy signs out user" do
252
+ sign_in @user
253
+
254
+ delete session_path
255
+
256
+ assert_redirected_to root_path
257
+ assert_nil cookies[:session_token]
258
+ end
259
+ end
260
+ ```
261
+
262
+ ### Protected Route Tests
263
+
264
+ ```ruby
265
+ # test/controllers/posts_controller_test.rb
266
+ require "test_helper"
267
+
268
+ class PostsControllerTest < ActionDispatch::IntegrationTest
269
+ setup do
270
+ @user = users(:one)
271
+ end
272
+
273
+ test "redirects to login when not authenticated" do
274
+ get posts_path
275
+ assert_redirected_to new_session_path
276
+ end
277
+
278
+ test "shows posts when authenticated" do
279
+ sign_in @user
280
+
281
+ get posts_path
282
+ assert_response :success
283
+ end
284
+ end
285
+ ```
286
+
287
+ ## References
288
+
289
+ - See [sessions.md](reference/sessions.md) for session management details
290
+ - See [current.md](reference/current.md) for Current attributes patterns
291
+ - See [passwordless.md](reference/passwordless.md) for magic link authentication
292
+
293
+ ## Common Customizations
294
+
295
+ ### Remember Me
296
+
297
+ ```ruby
298
+ def start_new_session_for(user, remember: false)
299
+ session = user.sessions.create!
300
+ cookie_options = { value: session.token, httponly: true }
301
+ cookie_options[:expires] = 2.weeks.from_now if remember
302
+ cookies.signed.permanent[:session_token] = cookie_options
303
+ Current.session = session
304
+ end
305
+ ```
306
+
307
+ ### Multiple Sessions Tracking
308
+
309
+ ```ruby
310
+ def active_sessions
311
+ sessions.where("created_at > ?", 30.days.ago)
312
+ end
313
+
314
+ def terminate_all_sessions_except(current_session)
315
+ sessions.where.not(id: current_session.id).destroy_all
316
+ end
317
+ ```
318
+
319
+ ### Rate Limiting
320
+
321
+ ```ruby
322
+ # app/controllers/sessions_controller.rb
323
+ rate_limit to: 10, within: 3.minutes, only: :create,
324
+ with: -> { redirect_to new_session_path, alert: "Too many attempts" }
325
+ ```
326
+
327
+ ## Checklist
328
+
329
+ - [ ] Authentication generator run
330
+ - [ ] Test helper with `sign_in`/`sign_out` methods
331
+ - [ ] Session controller tests written
332
+ - [ ] Protected route tests written
333
+ - [ ] Rate limiting on login
334
+ - [ ] `allow_unauthenticated_access` on public pages
335
+ - [ ] All tests GREEN
@@ -0,0 +1,248 @@
1
+ # Current Attributes Reference
2
+
3
+ ## Concept
4
+
5
+ `Current` uses `ActiveSupport::CurrentAttributes` to provide request-local storage, making request-specific data available throughout the application without passing it explicitly.
6
+
7
+ ## Basic Setup
8
+
9
+ ```ruby
10
+ # app/models/current.rb
11
+ class Current < ActiveSupport::CurrentAttributes
12
+ # Attributes stored per-request
13
+ attribute :session
14
+ attribute :user_agent
15
+ attribute :ip_address
16
+ attribute :request_id
17
+
18
+ # Delegate to session for convenience
19
+ delegate :user, to: :session, allow_nil: true
20
+
21
+ # Resets automatically between requests
22
+ resets { Time.zone = nil }
23
+ end
24
+ ```
25
+
26
+ ## Setting Current Attributes
27
+
28
+ ### In ApplicationController
29
+
30
+ ```ruby
31
+ # app/controllers/application_controller.rb
32
+ class ApplicationController < ActionController::Base
33
+ before_action :set_current_attributes
34
+
35
+ private
36
+
37
+ def set_current_attributes
38
+ Current.user_agent = request.user_agent
39
+ Current.ip_address = request.remote_ip
40
+ Current.request_id = request.request_id
41
+ end
42
+ end
43
+ ```
44
+
45
+ ### In Authentication
46
+
47
+ ```ruby
48
+ # app/controllers/concerns/authentication.rb
49
+ def resume_session
50
+ if token = cookies.signed[:session_token]
51
+ if session = Session.find_by_token(token)
52
+ Current.session = session # Sets Current.session
53
+ end
54
+ end
55
+ end
56
+ ```
57
+
58
+ ## Accessing Current
59
+
60
+ ### In Controllers
61
+
62
+ ```ruby
63
+ class PostsController < ApplicationController
64
+ def create
65
+ @post = Current.user.posts.build(post_params)
66
+ # ...
67
+ end
68
+
69
+ def index
70
+ @posts = Current.user.posts
71
+ end
72
+ end
73
+ ```
74
+
75
+ ### In Views
76
+
77
+ ```erb
78
+ <% if Current.user %>
79
+ Logged in as: <%= Current.user.email_address %>
80
+ <%= link_to "Sign out", session_path, method: :delete %>
81
+ <% else %>
82
+ <%= link_to "Sign in", new_session_path %>
83
+ <% end %>
84
+ ```
85
+
86
+ ### In Models (Use Sparingly)
87
+
88
+ ```ruby
89
+ class Post < ApplicationRecord
90
+ belongs_to :user
91
+
92
+ before_create :set_author
93
+
94
+ private
95
+
96
+ def set_author
97
+ self.user ||= Current.user
98
+ end
99
+ end
100
+ ```
101
+
102
+ **Warning**: Using `Current` in models couples them to the request context. This makes testing harder and breaks in background jobs. Prefer passing the user explicitly.
103
+
104
+ ### In Mailers
105
+
106
+ ```ruby
107
+ class NotificationMailer < ApplicationMailer
108
+ def alert(user, message)
109
+ @user = user
110
+ @message = message
111
+ @request_id = Current.request_id # For logging correlation
112
+
113
+ mail(to: user.email)
114
+ end
115
+ end
116
+ ```
117
+
118
+ ### In Jobs (Careful!)
119
+
120
+ ```ruby
121
+ class AuditLogJob < ApplicationJob
122
+ def perform(action:, user_id:, ip_address:, request_id:)
123
+ # Don't rely on Current - it's reset between requests
124
+ # Pass values explicitly
125
+ AuditLog.create!(
126
+ action: action,
127
+ user_id: user_id,
128
+ ip_address: ip_address,
129
+ request_id: request_id
130
+ )
131
+ end
132
+ end
133
+
134
+ # Enqueue with current values
135
+ AuditLogJob.perform_later(
136
+ action: "created_post",
137
+ user_id: Current.user.id,
138
+ ip_address: Current.ip_address,
139
+ request_id: Current.request_id
140
+ )
141
+ ```
142
+
143
+ ## Common Attributes
144
+
145
+ ```ruby
146
+ class Current < ActiveSupport::CurrentAttributes
147
+ # Authentication
148
+ attribute :session
149
+ delegate :user, to: :session, allow_nil: true
150
+
151
+ # Request metadata
152
+ attribute :request_id
153
+ attribute :user_agent
154
+ attribute :ip_address
155
+
156
+ # Timezone (per-user)
157
+ attribute :time_zone
158
+
159
+ # Feature flags
160
+ attribute :feature_flags
161
+
162
+ # Request tracking
163
+ attribute :request_start_time
164
+ end
165
+ ```
166
+
167
+ ## Callbacks
168
+
169
+ ```ruby
170
+ class Current < ActiveSupport::CurrentAttributes
171
+ attribute :session, :time_zone
172
+
173
+ # Called when session is set
174
+ after_reset do
175
+ Time.zone = nil
176
+ end
177
+
178
+ # Apply user's timezone when session is set
179
+ def session=(session)
180
+ super
181
+ self.time_zone = session&.user&.time_zone
182
+ Time.zone = time_zone if time_zone
183
+ end
184
+ end
185
+ ```
186
+
187
+ ## Testing
188
+
189
+ ### Stub Current in Tests
190
+
191
+ ```ruby
192
+ # test/support/current_helpers.rb
193
+ module CurrentHelpers
194
+ def with_current_user(user)
195
+ session = user.sessions.create!
196
+ Current.session = session
197
+ yield
198
+ ensure
199
+ Current.reset
200
+ end
201
+ end
202
+
203
+ ```
204
+
205
+ ### In Tests
206
+
207
+ ```ruby
208
+ # test/models/post_test.rb
209
+ require "test_helper"
210
+
211
+ class PostTest < ActiveSupport::TestCase
212
+ test "sets author from Current.user" do
213
+ user = users(:one)
214
+ with_current_user(user) do
215
+ post = Post.create!(title: "Test")
216
+ assert_equal user, post.user
217
+ end
218
+ end
219
+ end
220
+ ```
221
+
222
+ ### Controller Tests
223
+
224
+ ```ruby
225
+ # test/controllers/posts_controller_test.rb
226
+ require "test_helper"
227
+
228
+ class PostsControllerTest < ActionDispatch::IntegrationTest
229
+ setup do
230
+ @user = users(:one)
231
+ sign_in_as @user # Sets Current.session
232
+ end
233
+
234
+ test "uses current user" do
235
+ post posts_path, params: { post: { title: "Test" } }
236
+ assert_equal @user, Post.last.user
237
+ end
238
+ end
239
+ ```
240
+
241
+ ## Best Practices
242
+
243
+ 1. **Controllers/Views**: Safe to use `Current.user` freely
244
+ 2. **Models**: Pass user explicitly when possible
245
+ 3. **Jobs**: Never rely on Current - pass values explicitly
246
+ 4. **Mailers**: Can use for metadata, but pass main data explicitly
247
+ 5. **Services**: Accept user as parameter, don't assume Current
248
+ 6. **Tests**: Reset Current between examples