ag-cortex 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.
- package/.agent/commands/test-browser.md +339 -0
- package/.agent/rules/00-constitution.md +46 -0
- package/.agent/rules/project-rules.md +49 -0
- package/.agent/skills/agent-browser/SKILL.md +223 -0
- package/.agent/skills/agent-native-architecture/SKILL.md +435 -0
- package/.agent/skills/agent-native-architecture/references/action-parity-discipline.md +409 -0
- package/.agent/skills/agent-native-architecture/references/agent-execution-patterns.md +467 -0
- package/.agent/skills/agent-native-architecture/references/agent-native-testing.md +582 -0
- package/.agent/skills/agent-native-architecture/references/architecture-patterns.md +478 -0
- package/.agent/skills/agent-native-architecture/references/dynamic-context-injection.md +338 -0
- package/.agent/skills/agent-native-architecture/references/files-universal-interface.md +301 -0
- package/.agent/skills/agent-native-architecture/references/from-primitives-to-domain-tools.md +359 -0
- package/.agent/skills/agent-native-architecture/references/mcp-tool-design.md +506 -0
- package/.agent/skills/agent-native-architecture/references/mobile-patterns.md +871 -0
- package/.agent/skills/agent-native-architecture/references/product-implications.md +443 -0
- package/.agent/skills/agent-native-architecture/references/refactoring-to-prompt-native.md +317 -0
- package/.agent/skills/agent-native-architecture/references/self-modification.md +269 -0
- package/.agent/skills/agent-native-architecture/references/shared-workspace-architecture.md +680 -0
- package/.agent/skills/agent-native-architecture/references/system-prompt-design.md +250 -0
- package/.agent/skills/agent-native-reviewer/SKILL.md +246 -0
- package/.agent/skills/andrew-kane-gem-writer/SKILL.md +184 -0
- package/.agent/skills/andrew-kane-gem-writer/references/database-adapters.md +231 -0
- package/.agent/skills/andrew-kane-gem-writer/references/module-organization.md +121 -0
- package/.agent/skills/andrew-kane-gem-writer/references/rails-integration.md +183 -0
- package/.agent/skills/andrew-kane-gem-writer/references/resources.md +119 -0
- package/.agent/skills/andrew-kane-gem-writer/references/testing-patterns.md +261 -0
- package/.agent/skills/ankane-readme-writer/SKILL.md +50 -0
- package/.agent/skills/architecture-strategist/SKILL.md +52 -0
- package/.agent/skills/best-practices-researcher/SKILL.md +100 -0
- package/.agent/skills/bug-reproduction-validator/SKILL.md +67 -0
- package/.agent/skills/code-simplicity-reviewer/SKILL.md +85 -0
- package/.agent/skills/coding-tutor/.claude-plugin/plugin.json +9 -0
- package/.agent/skills/coding-tutor/README.md +37 -0
- package/.agent/skills/coding-tutor/commands/quiz-me.md +1 -0
- package/.agent/skills/coding-tutor/commands/sync-tutorials.md +25 -0
- package/.agent/skills/coding-tutor/commands/teach-me.md +1 -0
- package/.agent/skills/coding-tutor/skills/coding-tutor/SKILL.md +214 -0
- package/.agent/skills/coding-tutor/skills/coding-tutor/scripts/create_tutorial.py +202 -0
- package/.agent/skills/coding-tutor/skills/coding-tutor/scripts/index_tutorials.py +203 -0
- package/.agent/skills/coding-tutor/skills/coding-tutor/scripts/quiz_priority.py +190 -0
- package/.agent/skills/coding-tutor/skills/coding-tutor/scripts/setup_tutorials.py +132 -0
- package/.agent/skills/compound-docs/SKILL.md +510 -0
- package/.agent/skills/compound-docs/assets/critical-pattern-template.md +34 -0
- package/.agent/skills/compound-docs/assets/resolution-template.md +93 -0
- package/.agent/skills/compound-docs/references/yaml-schema.md +65 -0
- package/.agent/skills/compound-docs/schema.yaml +176 -0
- package/.agent/skills/create-agent-skills/SKILL.md +299 -0
- package/.agent/skills/create-agent-skills/references/api-security.md +226 -0
- package/.agent/skills/create-agent-skills/references/be-clear-and-direct.md +531 -0
- package/.agent/skills/create-agent-skills/references/best-practices.md +404 -0
- package/.agent/skills/create-agent-skills/references/common-patterns.md +595 -0
- package/.agent/skills/create-agent-skills/references/core-principles.md +437 -0
- package/.agent/skills/create-agent-skills/references/executable-code.md +175 -0
- package/.agent/skills/create-agent-skills/references/iteration-and-testing.md +474 -0
- package/.agent/skills/create-agent-skills/references/official-spec.md +185 -0
- package/.agent/skills/create-agent-skills/references/recommended-structure.md +168 -0
- package/.agent/skills/create-agent-skills/references/skill-structure.md +372 -0
- package/.agent/skills/create-agent-skills/references/using-scripts.md +113 -0
- package/.agent/skills/create-agent-skills/references/using-templates.md +112 -0
- package/.agent/skills/create-agent-skills/references/workflows-and-validation.md +510 -0
- package/.agent/skills/create-agent-skills/templates/router-skill.md +73 -0
- package/.agent/skills/create-agent-skills/templates/simple-skill.md +33 -0
- package/.agent/skills/create-agent-skills/workflows/add-reference.md +96 -0
- package/.agent/skills/create-agent-skills/workflows/add-script.md +93 -0
- package/.agent/skills/create-agent-skills/workflows/add-template.md +74 -0
- package/.agent/skills/create-agent-skills/workflows/add-workflow.md +120 -0
- package/.agent/skills/create-agent-skills/workflows/audit-skill.md +138 -0
- package/.agent/skills/create-agent-skills/workflows/create-domain-expertise-skill.md +605 -0
- package/.agent/skills/create-agent-skills/workflows/create-new-skill.md +191 -0
- package/.agent/skills/create-agent-skills/workflows/get-guidance.md +121 -0
- package/.agent/skills/create-agent-skills/workflows/upgrade-to-router.md +161 -0
- package/.agent/skills/create-agent-skills/workflows/verify-skill.md +204 -0
- package/.agent/skills/data-integrity-guardian/SKILL.md +70 -0
- package/.agent/skills/data-migration-expert/SKILL.md +97 -0
- package/.agent/skills/deployment-verification-agent/SKILL.md +159 -0
- package/.agent/skills/design-implementation-reviewer/SKILL.md +85 -0
- package/.agent/skills/design-iterator/SKILL.md +197 -0
- package/.agent/skills/dhh-rails-reviewer/SKILL.md +45 -0
- package/.agent/skills/dhh-rails-style/SKILL.md +184 -0
- package/.agent/skills/dhh-rails-style/references/architecture.md +653 -0
- package/.agent/skills/dhh-rails-style/references/controllers.md +303 -0
- package/.agent/skills/dhh-rails-style/references/frontend.md +510 -0
- package/.agent/skills/dhh-rails-style/references/gems.md +266 -0
- package/.agent/skills/dhh-rails-style/references/models.md +359 -0
- package/.agent/skills/dhh-rails-style/references/testing.md +338 -0
- package/.agent/skills/dspy-ruby/SKILL.md +594 -0
- package/.agent/skills/dspy-ruby/assets/config-template.rb +359 -0
- package/.agent/skills/dspy-ruby/assets/module-template.rb +326 -0
- package/.agent/skills/dspy-ruby/assets/signature-template.rb +143 -0
- package/.agent/skills/dspy-ruby/references/core-concepts.md +265 -0
- package/.agent/skills/dspy-ruby/references/optimization.md +623 -0
- package/.agent/skills/dspy-ruby/references/providers.md +305 -0
- package/.agent/skills/every-style-editor/SKILL.md +134 -0
- package/.agent/skills/every-style-editor/references/EVERY_WRITE_STYLE.md +529 -0
- package/.agent/skills/figma-design-sync/SKILL.md +166 -0
- package/.agent/skills/file-todos/SKILL.md +251 -0
- package/.agent/skills/file-todos/assets/todo-template.md +155 -0
- package/.agent/skills/framework-docs-researcher/SKILL.md +83 -0
- package/.agent/skills/frontend-design/SKILL.md +42 -0
- package/.agent/skills/gemini-imagegen/SKILL.md +237 -0
- package/.agent/skills/gemini-imagegen/requirements.txt +2 -0
- package/.agent/skills/gemini-imagegen/scripts/compose_images.py +168 -0
- package/.agent/skills/gemini-imagegen/scripts/edit_image.py +157 -0
- package/.agent/skills/gemini-imagegen/scripts/gemini_images.py +265 -0
- package/.agent/skills/gemini-imagegen/scripts/generate_image.py +147 -0
- package/.agent/skills/gemini-imagegen/scripts/multi_turn_chat.py +215 -0
- package/.agent/skills/git-history-analyzer/SKILL.md +42 -0
- package/.agent/skills/git-worktree/SKILL.md +302 -0
- package/.agent/skills/git-worktree/scripts/worktree-manager.sh +345 -0
- package/.agent/skills/julik-frontend-races-reviewer/SKILL.md +222 -0
- package/.agent/skills/kieran-python-reviewer/SKILL.md +104 -0
- package/.agent/skills/kieran-rails-reviewer/SKILL.md +86 -0
- package/.agent/skills/kieran-typescript-reviewer/SKILL.md +95 -0
- package/.agent/skills/lint/SKILL.md +16 -0
- package/.agent/skills/pattern-recognition-specialist/SKILL.md +57 -0
- package/.agent/skills/performance-oracle/SKILL.md +110 -0
- package/.agent/skills/pr-comment-resolver/SKILL.md +69 -0
- package/.agent/skills/rclone/SKILL.md +150 -0
- package/.agent/skills/rclone/scripts/check_setup.sh +60 -0
- package/.agent/skills/repo-research-analyst/SKILL.md +113 -0
- package/.agent/skills/security-sentinel/SKILL.md +93 -0
- package/.agent/skills/skill-creator/SKILL.md +209 -0
- package/.agent/skills/skill-creator/scripts/init_skill.py +304 -0
- package/.agent/skills/skill-creator/scripts/package_skill.py +112 -0
- package/.agent/skills/skill-creator/scripts/quick_validate.py +72 -0
- package/.agent/skills/spec-flow-analyzer/SKILL.md +113 -0
- package/.agent/skills/test-agent/SKILL.md +4 -0
- package/.agent/workflows/agent-native-audit.md +277 -0
- package/.agent/workflows/ask-user-question.md +21 -0
- package/.agent/workflows/changelog.md +137 -0
- package/.agent/workflows/compound.md +202 -0
- package/.agent/workflows/create-agent-skill.md +8 -0
- package/.agent/workflows/deepen-plan-research.md +334 -0
- package/.agent/workflows/deepen-plan-synthesis.md +182 -0
- package/.agent/workflows/deepen-plan.md +79 -0
- package/.agent/workflows/feature-video.md +342 -0
- package/.agent/workflows/generate-command.md +162 -0
- package/.agent/workflows/heal-skill.md +142 -0
- package/.agent/workflows/lfg.md +20 -0
- package/.agent/workflows/plan-analysis.md +67 -0
- package/.agent/workflows/plan-next-steps.md +63 -0
- package/.agent/workflows/plan-review.md +33 -0
- package/.agent/workflows/plan-synthesis.md +106 -0
- package/.agent/workflows/plan.md +49 -0
- package/.agent/workflows/report-bug.md +150 -0
- package/.agent/workflows/reproduce-bug.md +99 -0
- package/.agent/workflows/resolve-parallel.md +34 -0
- package/.agent/workflows/resolve-pr-parallel.md +49 -0
- package/.agent/workflows/resolve-todo-parallel.md +35 -0
- package/.agent/workflows/review-analysis.md +145 -0
- package/.agent/workflows/review-synthesis.md +262 -0
- package/.agent/workflows/review.md +64 -0
- package/.agent/workflows/ship.md +90 -0
- package/.agent/workflows/test-command.md +3 -0
- package/.agent/workflows/triage.md +310 -0
- package/.agent/workflows/work.md +157 -0
- package/.agent/workflows/xcode-test.md +332 -0
- package/LICENSE +22 -0
- package/README.md +49 -0
- package/bin/ag-cortex.js +54 -0
- package/lib/core.js +165 -0
- package/package.json +31 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
# Controllers - DHH Rails Style
|
|
2
|
+
|
|
3
|
+
<rest_mapping>
|
|
4
|
+
## Everything Maps to CRUD
|
|
5
|
+
|
|
6
|
+
Custom actions become new resources. Instead of verbs on existing resources, create noun resources:
|
|
7
|
+
|
|
8
|
+
```ruby
|
|
9
|
+
# Instead of this:
|
|
10
|
+
POST /cards/:id/close
|
|
11
|
+
DELETE /cards/:id/close
|
|
12
|
+
POST /cards/:id/archive
|
|
13
|
+
|
|
14
|
+
# Do this:
|
|
15
|
+
POST /cards/:id/closure # create closure
|
|
16
|
+
DELETE /cards/:id/closure # destroy closure
|
|
17
|
+
POST /cards/:id/archival # create archival
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Real examples from 37signals:**
|
|
21
|
+
```ruby
|
|
22
|
+
resources :cards do
|
|
23
|
+
resource :closure # closing/reopening
|
|
24
|
+
resource :goldness # marking important
|
|
25
|
+
resource :not_now # postponing
|
|
26
|
+
resources :assignments # managing assignees
|
|
27
|
+
end
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Each resource gets its own controller with standard CRUD actions.
|
|
31
|
+
</rest_mapping>
|
|
32
|
+
|
|
33
|
+
<controller_concerns>
|
|
34
|
+
## Concerns for Shared Behavior
|
|
35
|
+
|
|
36
|
+
Controllers use concerns extensively. Common patterns:
|
|
37
|
+
|
|
38
|
+
**CardScoped** - loads @card, @board, provides render_card_replacement
|
|
39
|
+
```ruby
|
|
40
|
+
module CardScoped
|
|
41
|
+
extend ActiveSupport::Concern
|
|
42
|
+
|
|
43
|
+
included do
|
|
44
|
+
before_action :set_card
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
def set_card
|
|
49
|
+
@card = Card.find(params[:card_id])
|
|
50
|
+
@board = @card.board
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def render_card_replacement
|
|
54
|
+
render turbo_stream: turbo_stream.replace(@card)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
**BoardScoped** - loads @board
|
|
60
|
+
**CurrentRequest** - populates Current with request data
|
|
61
|
+
**CurrentTimezone** - wraps requests in user's timezone
|
|
62
|
+
**FilterScoped** - handles complex filtering
|
|
63
|
+
**TurboFlash** - flash messages via Turbo Stream
|
|
64
|
+
**ViewTransitions** - disables on page refresh
|
|
65
|
+
**BlockSearchEngineIndexing** - sets X-Robots-Tag header
|
|
66
|
+
**RequestForgeryProtection** - Sec-Fetch-Site CSRF (modern browsers)
|
|
67
|
+
</controller_concerns>
|
|
68
|
+
|
|
69
|
+
<authorization_patterns>
|
|
70
|
+
## Authorization Patterns
|
|
71
|
+
|
|
72
|
+
Controllers check permissions via before_action, models define what permissions mean:
|
|
73
|
+
|
|
74
|
+
```ruby
|
|
75
|
+
# Controller concern
|
|
76
|
+
module Authorization
|
|
77
|
+
extend ActiveSupport::Concern
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
def ensure_can_administer
|
|
81
|
+
head :forbidden unless Current.user.admin?
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def ensure_is_staff_member
|
|
85
|
+
head :forbidden unless Current.user.staff?
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Usage
|
|
90
|
+
class BoardsController < ApplicationController
|
|
91
|
+
before_action :ensure_can_administer, only: [:destroy]
|
|
92
|
+
end
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
**Model-level authorization:**
|
|
96
|
+
```ruby
|
|
97
|
+
class Board < ApplicationRecord
|
|
98
|
+
def editable_by?(user)
|
|
99
|
+
user.admin? || user == creator
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def publishable_by?(user)
|
|
103
|
+
editable_by?(user) && !published?
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Keep authorization simple, readable, colocated with domain.
|
|
109
|
+
</authorization_patterns>
|
|
110
|
+
|
|
111
|
+
<security_concerns>
|
|
112
|
+
## Security Concerns
|
|
113
|
+
|
|
114
|
+
**Sec-Fetch-Site CSRF Protection:**
|
|
115
|
+
Modern browsers send Sec-Fetch-Site header. Use it for defense in depth:
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
module RequestForgeryProtection
|
|
119
|
+
extend ActiveSupport::Concern
|
|
120
|
+
|
|
121
|
+
included do
|
|
122
|
+
before_action :verify_request_origin
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
private
|
|
126
|
+
def verify_request_origin
|
|
127
|
+
return if request.get? || request.head?
|
|
128
|
+
return if %w[same-origin same-site].include?(
|
|
129
|
+
request.headers["Sec-Fetch-Site"]&.downcase
|
|
130
|
+
)
|
|
131
|
+
# Fall back to token verification for older browsers
|
|
132
|
+
verify_authenticity_token
|
|
133
|
+
end
|
|
134
|
+
end
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
**Rate Limiting (Rails 8+):**
|
|
138
|
+
```ruby
|
|
139
|
+
class MagicLinksController < ApplicationController
|
|
140
|
+
rate_limit to: 10, within: 15.minutes, only: :create
|
|
141
|
+
end
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
Apply to: auth endpoints, email sending, external API calls, resource creation.
|
|
145
|
+
</security_concerns>
|
|
146
|
+
|
|
147
|
+
<request_context>
|
|
148
|
+
## Request Context Concerns
|
|
149
|
+
|
|
150
|
+
**CurrentRequest** - populates Current with HTTP metadata:
|
|
151
|
+
```ruby
|
|
152
|
+
module CurrentRequest
|
|
153
|
+
extend ActiveSupport::Concern
|
|
154
|
+
|
|
155
|
+
included do
|
|
156
|
+
before_action :set_current_request
|
|
157
|
+
end
|
|
158
|
+
|
|
159
|
+
private
|
|
160
|
+
def set_current_request
|
|
161
|
+
Current.request_id = request.request_id
|
|
162
|
+
Current.user_agent = request.user_agent
|
|
163
|
+
Current.ip_address = request.remote_ip
|
|
164
|
+
Current.referrer = request.referrer
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
**CurrentTimezone** - wraps requests in user's timezone:
|
|
170
|
+
```ruby
|
|
171
|
+
module CurrentTimezone
|
|
172
|
+
extend ActiveSupport::Concern
|
|
173
|
+
|
|
174
|
+
included do
|
|
175
|
+
around_action :set_timezone
|
|
176
|
+
helper_method :timezone_from_cookie
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
private
|
|
180
|
+
def set_timezone
|
|
181
|
+
Time.use_zone(timezone_from_cookie) { yield }
|
|
182
|
+
end
|
|
183
|
+
|
|
184
|
+
def timezone_from_cookie
|
|
185
|
+
cookies[:timezone] || "UTC"
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
**SetPlatform** - detects mobile/desktop:
|
|
191
|
+
```ruby
|
|
192
|
+
module SetPlatform
|
|
193
|
+
extend ActiveSupport::Concern
|
|
194
|
+
|
|
195
|
+
included do
|
|
196
|
+
helper_method :platform
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
def platform
|
|
200
|
+
@platform ||= request.user_agent&.match?(/Mobile|Android/) ? :mobile : :desktop
|
|
201
|
+
end
|
|
202
|
+
end
|
|
203
|
+
```
|
|
204
|
+
</request_context>
|
|
205
|
+
|
|
206
|
+
<turbo_responses>
|
|
207
|
+
## Turbo Stream Responses
|
|
208
|
+
|
|
209
|
+
Use Turbo Streams for partial updates:
|
|
210
|
+
|
|
211
|
+
```ruby
|
|
212
|
+
class Cards::ClosuresController < ApplicationController
|
|
213
|
+
include CardScoped
|
|
214
|
+
|
|
215
|
+
def create
|
|
216
|
+
@card.close
|
|
217
|
+
render_card_replacement
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
def destroy
|
|
221
|
+
@card.reopen
|
|
222
|
+
render_card_replacement
|
|
223
|
+
end
|
|
224
|
+
end
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
For complex updates, use morphing:
|
|
228
|
+
```ruby
|
|
229
|
+
render turbo_stream: turbo_stream.morph(@card)
|
|
230
|
+
```
|
|
231
|
+
</turbo_responses>
|
|
232
|
+
|
|
233
|
+
<api_patterns>
|
|
234
|
+
## API Design
|
|
235
|
+
|
|
236
|
+
Same controllers, different format. Convention for responses:
|
|
237
|
+
|
|
238
|
+
```ruby
|
|
239
|
+
def create
|
|
240
|
+
@card = Card.create!(card_params)
|
|
241
|
+
|
|
242
|
+
respond_to do |format|
|
|
243
|
+
format.html { redirect_to @card }
|
|
244
|
+
format.json { head :created, location: @card }
|
|
245
|
+
end
|
|
246
|
+
end
|
|
247
|
+
|
|
248
|
+
def update
|
|
249
|
+
@card.update!(card_params)
|
|
250
|
+
|
|
251
|
+
respond_to do |format|
|
|
252
|
+
format.html { redirect_to @card }
|
|
253
|
+
format.json { head :no_content }
|
|
254
|
+
end
|
|
255
|
+
end
|
|
256
|
+
|
|
257
|
+
def destroy
|
|
258
|
+
@card.destroy
|
|
259
|
+
|
|
260
|
+
respond_to do |format|
|
|
261
|
+
format.html { redirect_to cards_path }
|
|
262
|
+
format.json { head :no_content }
|
|
263
|
+
end
|
|
264
|
+
end
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
**Status codes:**
|
|
268
|
+
- Create: 201 Created + Location header
|
|
269
|
+
- Update: 204 No Content
|
|
270
|
+
- Delete: 204 No Content
|
|
271
|
+
- Bearer token authentication
|
|
272
|
+
</api_patterns>
|
|
273
|
+
|
|
274
|
+
<http_caching>
|
|
275
|
+
## HTTP Caching
|
|
276
|
+
|
|
277
|
+
Extensive use of ETags and conditional GETs:
|
|
278
|
+
|
|
279
|
+
```ruby
|
|
280
|
+
class CardsController < ApplicationController
|
|
281
|
+
def show
|
|
282
|
+
@card = Card.find(params[:id])
|
|
283
|
+
fresh_when etag: [@card, Current.user.timezone]
|
|
284
|
+
end
|
|
285
|
+
|
|
286
|
+
def index
|
|
287
|
+
@cards = @board.cards.preloaded
|
|
288
|
+
fresh_when etag: [@cards, @board.updated_at]
|
|
289
|
+
end
|
|
290
|
+
end
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
Key insight: Times render server-side in user's timezone, so timezone must affect the ETag to prevent serving wrong times to other timezones.
|
|
294
|
+
|
|
295
|
+
**ApplicationController global etag:**
|
|
296
|
+
```ruby
|
|
297
|
+
class ApplicationController < ActionController::Base
|
|
298
|
+
etag { "v1" } # Bump to invalidate all caches
|
|
299
|
+
end
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
Use `touch: true` on associations for cache invalidation.
|
|
303
|
+
</http_caching>
|