claude-agent-framework 1.0.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/README.md +128 -0
- package/bin/claude-framework +3 -0
- package/framework/agents/design-lead.md +240 -0
- package/framework/agents/product-owner.md +179 -0
- package/framework/agents/tech-lead.md +226 -0
- package/framework/commands/ayuda.md +127 -0
- package/framework/commands/a/303/261adir.md +98 -0
- package/framework/commands/backup.md +397 -0
- package/framework/commands/cambiar.md +110 -0
- package/framework/commands/cloud.md +457 -0
- package/framework/commands/code.md +142 -0
- package/framework/commands/debug.md +334 -0
- package/framework/commands/deploy.md +383 -0
- package/framework/commands/deshacer.md +120 -0
- package/framework/commands/estado.md +218 -0
- package/framework/commands/explica.md +227 -0
- package/framework/commands/feature.md +120 -0
- package/framework/commands/git.md +427 -0
- package/framework/commands/historial.md +202 -0
- package/framework/commands/learn.md +408 -0
- package/framework/commands/movil.md +245 -0
- package/framework/commands/nuevo.md +118 -0
- package/framework/commands/plan.md +134 -0
- package/framework/commands/prd.md +113 -0
- package/framework/commands/probar.md +148 -0
- package/framework/commands/revisar.md +208 -0
- package/framework/commands/seeds.md +230 -0
- package/framework/commands/seguridad.md +226 -0
- package/framework/commands/tasks.md +157 -0
- package/framework/skills/architecture/algorithms.md +970 -0
- package/framework/skills/architecture/clean-code.md +1080 -0
- package/framework/skills/architecture/design-patterns.md +1984 -0
- package/framework/skills/architecture/functional-programming.md +972 -0
- package/framework/skills/architecture/solid.md +991 -0
- package/framework/skills/cloud/cloud-aws.md +848 -0
- package/framework/skills/cloud/cloud-azure.md +931 -0
- package/framework/skills/cloud/cloud-gcp.md +848 -0
- package/framework/skills/cloud/message-queues.md +1229 -0
- package/framework/skills/core/accessibility.md +401 -0
- package/framework/skills/core/api.md +474 -0
- package/framework/skills/core/authentication.md +306 -0
- package/framework/skills/core/authorization.md +388 -0
- package/framework/skills/core/background-jobs.md +341 -0
- package/framework/skills/core/caching.md +473 -0
- package/framework/skills/core/code-review.md +341 -0
- package/framework/skills/core/controllers.md +290 -0
- package/framework/skills/core/cua.md +285 -0
- package/framework/skills/core/documentation.md +472 -0
- package/framework/skills/core/file-uploads.md +351 -0
- package/framework/skills/core/hotwire-native.md +296 -0
- package/framework/skills/core/hotwire.md +278 -0
- package/framework/skills/core/i18n.md +334 -0
- package/framework/skills/core/imports-exports.md +750 -0
- package/framework/skills/core/infrastructure.md +337 -0
- package/framework/skills/core/models.md +228 -0
- package/framework/skills/core/notifications.md +672 -0
- package/framework/skills/core/payments.md +581 -0
- package/framework/skills/core/performance.md +361 -0
- package/framework/skills/core/rails-scaffold.md +131 -0
- package/framework/skills/core/search.md +518 -0
- package/framework/skills/core/security.md +565 -0
- package/framework/skills/core/seeds.md +307 -0
- package/framework/skills/core/seo.md +542 -0
- package/framework/skills/core/testing.md +393 -0
- package/framework/skills/core/views.md +260 -0
- package/framework/skills/core/websockets.md +564 -0
- package/framework/skills/data/advanced-sql.md +1204 -0
- package/framework/skills/data/nosql.md +1141 -0
- package/framework/skills/devops/containers-advanced.md +1237 -0
- package/framework/skills/devops/debugging.md +834 -0
- package/framework/skills/devops/git-workflow.md +752 -0
- package/framework/skills/devops/networking.md +932 -0
- package/framework/skills/devops/shell-scripting.md +1132 -0
- package/framework/sub-agents/architecture-patterns-agent.md +1450 -0
- package/framework/sub-agents/cloud-agent.md +677 -0
- package/framework/sub-agents/data.md +504 -0
- package/framework/sub-agents/debugging-agent.md +554 -0
- package/framework/sub-agents/devops.md +483 -0
- package/framework/sub-agents/docs.md +176 -0
- package/framework/sub-agents/frontend-dev.md +349 -0
- package/framework/sub-agents/git-workflow-agent.md +697 -0
- package/framework/sub-agents/integrations.md +630 -0
- package/framework/sub-agents/native-dev.md +434 -0
- package/framework/sub-agents/qa.md +138 -0
- package/framework/sub-agents/rails-dev.md +375 -0
- package/framework/sub-agents/security.md +526 -0
- package/framework/sub-agents/ui.md +437 -0
- package/framework/sub-agents/ux.md +284 -0
- package/framework/templates/api-spec.md +500 -0
- package/framework/templates/component-spec.md +248 -0
- package/framework/templates/feature.json +13 -0
- package/framework/templates/model-spec.md +318 -0
- package/framework/templates/prd-template.md +80 -0
- package/framework/templates/task-plan.md +122 -0
- package/framework/templates/task-user-story.md +52 -0
- package/framework/templates/technical-spec.md +260 -0
- package/framework/templates/user-story.md +95 -0
- package/package.json +42 -0
- package/project-templates/CLAUDE.md +42 -0
- package/project-templates/contexts/architecture.md +25 -0
- package/project-templates/contexts/conventions.md +46 -0
- package/project-templates/contexts/design-system.md +47 -0
- package/project-templates/contexts/requirements.md +38 -0
- package/project-templates/contexts/stack.md +30 -0
- package/project-templates/history/active/models.md +11 -0
- package/project-templates/history/changelog.md +15 -0
- package/project-templates/workspace/.gitkeep +0 -0
- package/src/cli.js +52 -0
- package/src/init.js +104 -0
- package/src/status.js +75 -0
- package/src/update.js +88 -0
|
@@ -0,0 +1,473 @@
|
|
|
1
|
+
# Skill: Caching
|
|
2
|
+
|
|
3
|
+
## Purpose
|
|
4
|
+
|
|
5
|
+
Implementar estrategias de caché en Rails para mejorar el rendimiento de aplicaciones usando Solid Cache (Rails 8) y otras técnicas.
|
|
6
|
+
|
|
7
|
+
## Setup
|
|
8
|
+
|
|
9
|
+
### Solid Cache (Rails 8)
|
|
10
|
+
|
|
11
|
+
```ruby
|
|
12
|
+
# Gemfile (ya incluido en Rails 8)
|
|
13
|
+
gem "solid_cache"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Generar migraciones
|
|
18
|
+
bin/rails solid_cache:install
|
|
19
|
+
|
|
20
|
+
# Ejecutar migraciones
|
|
21
|
+
bin/rails db:migrate
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```ruby
|
|
25
|
+
# config/environments/production.rb
|
|
26
|
+
config.cache_store = :solid_cache_store
|
|
27
|
+
|
|
28
|
+
# config/environments/development.rb
|
|
29
|
+
# Habilitar caché en desarrollo (toggle)
|
|
30
|
+
# rails dev:cache
|
|
31
|
+
if Rails.root.join("tmp/caching-dev.txt").exist?
|
|
32
|
+
config.cache_store = :solid_cache_store
|
|
33
|
+
config.public_file_server.headers = {
|
|
34
|
+
"Cache-Control" => "public, max-age=#{2.days.to_i}"
|
|
35
|
+
}
|
|
36
|
+
else
|
|
37
|
+
config.cache_store = :null_store
|
|
38
|
+
end
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Redis Cache (alternativa)
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
# config/environments/production.rb
|
|
45
|
+
config.cache_store = :redis_cache_store, {
|
|
46
|
+
url: ENV.fetch("REDIS_URL") { "redis://localhost:6379/0" },
|
|
47
|
+
expires_in: 1.day,
|
|
48
|
+
namespace: "cache"
|
|
49
|
+
}
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Fragment Caching
|
|
53
|
+
|
|
54
|
+
### Cache básico
|
|
55
|
+
|
|
56
|
+
```erb
|
|
57
|
+
<%# Caché de un bloque %>
|
|
58
|
+
<% cache @post do %>
|
|
59
|
+
<article class="post">
|
|
60
|
+
<h2><%= @post.title %></h2>
|
|
61
|
+
<div><%= @post.body %></div>
|
|
62
|
+
<p>By <%= @post.author.name %></p>
|
|
63
|
+
</article>
|
|
64
|
+
<% end %>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Cache con dependencias
|
|
68
|
+
|
|
69
|
+
```erb
|
|
70
|
+
<%# La caché se invalida cuando cambia el post O sus comentarios %>
|
|
71
|
+
<% cache [@post, @post.comments.maximum(:updated_at)] do %>
|
|
72
|
+
<article>
|
|
73
|
+
<h2><%= @post.title %></h2>
|
|
74
|
+
<%= render @post.comments %>
|
|
75
|
+
</article>
|
|
76
|
+
<% end %>
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
### Cache de colecciones
|
|
80
|
+
|
|
81
|
+
```erb
|
|
82
|
+
<%# Caché individual por item %>
|
|
83
|
+
<%= render partial: "posts/post", collection: @posts, cached: true %>
|
|
84
|
+
|
|
85
|
+
<%# El partial necesita estar preparado %>
|
|
86
|
+
<%# app/views/posts/_post.html.erb %>
|
|
87
|
+
<% cache post do %>
|
|
88
|
+
<article id="<%= dom_id(post) %>">
|
|
89
|
+
<%= post.title %>
|
|
90
|
+
</article>
|
|
91
|
+
<% end %>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Cache con versión
|
|
95
|
+
|
|
96
|
+
```erb
|
|
97
|
+
<%# Útil para invalidar manualmente %>
|
|
98
|
+
<% cache ["v2", @post] do %>
|
|
99
|
+
<%= render @post %>
|
|
100
|
+
<% end %>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## Russian Doll Caching
|
|
104
|
+
|
|
105
|
+
```erb
|
|
106
|
+
<%# Cache anidada - se invalida de adentro hacia afuera %>
|
|
107
|
+
<% cache @post do %>
|
|
108
|
+
<article>
|
|
109
|
+
<h2><%= @post.title %></h2>
|
|
110
|
+
|
|
111
|
+
<% cache [@post, "comments"] do %>
|
|
112
|
+
<div class="comments">
|
|
113
|
+
<% @post.comments.each do |comment| %>
|
|
114
|
+
<% cache comment do %>
|
|
115
|
+
<%= render comment %>
|
|
116
|
+
<% end %>
|
|
117
|
+
<% end %>
|
|
118
|
+
</div>
|
|
119
|
+
<% end %>
|
|
120
|
+
</article>
|
|
121
|
+
<% end %>
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Low-Level Caching
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
# Caché manual de valores
|
|
128
|
+
class Post < ApplicationRecord
|
|
129
|
+
def comment_count
|
|
130
|
+
Rails.cache.fetch("#{cache_key_with_version}/comment_count") do
|
|
131
|
+
comments.count
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def expensive_calculation
|
|
136
|
+
Rails.cache.fetch("#{cache_key_with_version}/calculation", expires_in: 1.hour) do
|
|
137
|
+
# Cálculo costoso...
|
|
138
|
+
heavy_computation
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Con bloque y opciones
|
|
144
|
+
def self.popular_posts
|
|
145
|
+
Rails.cache.fetch("posts/popular", expires_in: 30.minutes) do
|
|
146
|
+
where("views_count > ?", 1000)
|
|
147
|
+
.order(views_count: :desc)
|
|
148
|
+
.limit(10)
|
|
149
|
+
.to_a # Importante: materializar la query
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
# Forzar recalcular
|
|
154
|
+
Rails.cache.fetch("key", force: true) do
|
|
155
|
+
# Siempre ejecuta el bloque
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Escribir y leer directamente
|
|
159
|
+
Rails.cache.write("key", value, expires_in: 1.hour)
|
|
160
|
+
Rails.cache.read("key")
|
|
161
|
+
Rails.cache.delete("key")
|
|
162
|
+
Rails.cache.exist?("key")
|
|
163
|
+
|
|
164
|
+
# Incrementar/decrementar
|
|
165
|
+
Rails.cache.increment("visits/#{post.id}")
|
|
166
|
+
Rails.cache.decrement("stock/#{product.id}")
|
|
167
|
+
|
|
168
|
+
# Multi-get
|
|
169
|
+
Rails.cache.read_multi("key1", "key2", "key3")
|
|
170
|
+
Rails.cache.write_multi({ "key1" => "value1", "key2" => "value2" })
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Query Caching
|
|
174
|
+
|
|
175
|
+
```ruby
|
|
176
|
+
# Rails cachea queries idénticas dentro de una request
|
|
177
|
+
# Esto es automático, pero puedes forzarlo:
|
|
178
|
+
|
|
179
|
+
ActiveRecord::Base.cache do
|
|
180
|
+
# Todas las queries aquí se cachean
|
|
181
|
+
User.find(1)
|
|
182
|
+
User.find(1) # No hace query, usa cache
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# Para queries específicas
|
|
186
|
+
def expensive_query
|
|
187
|
+
Rails.cache.fetch("expensive_query", expires_in: 5.minutes) do
|
|
188
|
+
User.includes(:posts, :comments)
|
|
189
|
+
.where(active: true)
|
|
190
|
+
.order(created_at: :desc)
|
|
191
|
+
.to_a # Materializar para cachear
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Counter Caching
|
|
197
|
+
|
|
198
|
+
```ruby
|
|
199
|
+
# db/migrate/xxx_add_comments_count_to_posts.rb
|
|
200
|
+
class AddCommentsCountToPosts < ActiveRecord::Migration[8.0]
|
|
201
|
+
def change
|
|
202
|
+
add_column :posts, :comments_count, :integer, default: 0, null: false
|
|
203
|
+
|
|
204
|
+
# Actualizar valores existentes
|
|
205
|
+
reversible do |dir|
|
|
206
|
+
dir.up do
|
|
207
|
+
Post.find_each do |post|
|
|
208
|
+
Post.reset_counters(post.id, :comments)
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
end
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# app/models/comment.rb
|
|
216
|
+
class Comment < ApplicationRecord
|
|
217
|
+
belongs_to :post, counter_cache: true
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Ahora post.comments.count usa el counter
|
|
221
|
+
# En lugar de COUNT(*) en la BD
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
## HTTP Caching
|
|
225
|
+
|
|
226
|
+
### ETag y Last-Modified
|
|
227
|
+
|
|
228
|
+
```ruby
|
|
229
|
+
class PostsController < ApplicationController
|
|
230
|
+
def show
|
|
231
|
+
@post = Post.find(params[:id])
|
|
232
|
+
|
|
233
|
+
# Usar ETag
|
|
234
|
+
if stale?(@post)
|
|
235
|
+
respond_to do |format|
|
|
236
|
+
format.html
|
|
237
|
+
format.json { render json: @post }
|
|
238
|
+
end
|
|
239
|
+
end
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
def index
|
|
243
|
+
@posts = Post.published.includes(:author)
|
|
244
|
+
|
|
245
|
+
# Con última modificación
|
|
246
|
+
if stale?(last_modified: @posts.maximum(:updated_at))
|
|
247
|
+
respond_to do |format|
|
|
248
|
+
format.html
|
|
249
|
+
format.json { render json: @posts }
|
|
250
|
+
end
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
|
254
|
+
```
|
|
255
|
+
|
|
256
|
+
### Cache-Control Headers
|
|
257
|
+
|
|
258
|
+
```ruby
|
|
259
|
+
class PostsController < ApplicationController
|
|
260
|
+
def show
|
|
261
|
+
@post = Post.find(params[:id])
|
|
262
|
+
|
|
263
|
+
# Cache público (CDN puede cachear)
|
|
264
|
+
expires_in 1.hour, public: true
|
|
265
|
+
|
|
266
|
+
# Cache privado (solo browser)
|
|
267
|
+
expires_in 30.minutes, public: false
|
|
268
|
+
|
|
269
|
+
# No cachear
|
|
270
|
+
expires_now
|
|
271
|
+
end
|
|
272
|
+
end
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
## Memoization
|
|
276
|
+
|
|
277
|
+
```ruby
|
|
278
|
+
class User < ApplicationRecord
|
|
279
|
+
# Memoization simple
|
|
280
|
+
def full_name
|
|
281
|
+
@full_name ||= "#{first_name} #{last_name}"
|
|
282
|
+
end
|
|
283
|
+
|
|
284
|
+
# Con argumentos (usar hash)
|
|
285
|
+
def posts_in_year(year)
|
|
286
|
+
@posts_by_year ||= {}
|
|
287
|
+
@posts_by_year[year] ||= posts.where("YEAR(created_at) = ?", year)
|
|
288
|
+
end
|
|
289
|
+
|
|
290
|
+
# Resetear memoization
|
|
291
|
+
def reload
|
|
292
|
+
@full_name = nil
|
|
293
|
+
@posts_by_year = nil
|
|
294
|
+
super
|
|
295
|
+
end
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
# Para memoization más compleja
|
|
299
|
+
class ExpensiveService
|
|
300
|
+
include ActiveSupport::Memoizable
|
|
301
|
+
|
|
302
|
+
def expensive_call
|
|
303
|
+
# ...cálculo costoso
|
|
304
|
+
end
|
|
305
|
+
memoize :expensive_call
|
|
306
|
+
end
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
## Cache Invalidation
|
|
310
|
+
|
|
311
|
+
### Touch para propagar cambios
|
|
312
|
+
|
|
313
|
+
```ruby
|
|
314
|
+
class Comment < ApplicationRecord
|
|
315
|
+
belongs_to :post, touch: true
|
|
316
|
+
# Cuando se actualiza un comentario, post.updated_at también se actualiza
|
|
317
|
+
end
|
|
318
|
+
|
|
319
|
+
class Post < ApplicationRecord
|
|
320
|
+
belongs_to :author, class_name: "User", touch: true
|
|
321
|
+
has_many :comments
|
|
322
|
+
end
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
### Invalidación manual
|
|
326
|
+
|
|
327
|
+
```ruby
|
|
328
|
+
# Borrar una clave
|
|
329
|
+
Rails.cache.delete("posts/popular")
|
|
330
|
+
|
|
331
|
+
# Borrar por patrón (solo Redis)
|
|
332
|
+
Rails.cache.delete_matched("posts/*")
|
|
333
|
+
|
|
334
|
+
# Versionar para invalidar
|
|
335
|
+
class Post < ApplicationRecord
|
|
336
|
+
after_commit :bump_cache_version
|
|
337
|
+
|
|
338
|
+
def cache_version
|
|
339
|
+
Rails.cache.read("posts/version") || 1
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
private
|
|
343
|
+
|
|
344
|
+
def bump_cache_version
|
|
345
|
+
Rails.cache.increment("posts/version")
|
|
346
|
+
end
|
|
347
|
+
end
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
### Callbacks de cache
|
|
351
|
+
|
|
352
|
+
```ruby
|
|
353
|
+
class Post < ApplicationRecord
|
|
354
|
+
after_commit :clear_cache
|
|
355
|
+
|
|
356
|
+
private
|
|
357
|
+
|
|
358
|
+
def clear_cache
|
|
359
|
+
Rails.cache.delete("posts/popular")
|
|
360
|
+
Rails.cache.delete("posts/recent")
|
|
361
|
+
Rails.cache.delete("homepage/stats")
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
## Caché de vistas parciales con conditional GET
|
|
367
|
+
|
|
368
|
+
```ruby
|
|
369
|
+
class PostsController < ApplicationController
|
|
370
|
+
def index
|
|
371
|
+
@posts = Post.published.recent
|
|
372
|
+
|
|
373
|
+
fresh_when(
|
|
374
|
+
etag: @posts,
|
|
375
|
+
last_modified: @posts.maximum(:updated_at),
|
|
376
|
+
public: true
|
|
377
|
+
)
|
|
378
|
+
end
|
|
379
|
+
end
|
|
380
|
+
```
|
|
381
|
+
|
|
382
|
+
## Action Caching (páginas completas)
|
|
383
|
+
|
|
384
|
+
```ruby
|
|
385
|
+
# Para páginas que no varían por usuario
|
|
386
|
+
class PagesController < ApplicationController
|
|
387
|
+
caches_action :about, expires_in: 1.day
|
|
388
|
+
|
|
389
|
+
def about
|
|
390
|
+
# Esta acción se cachea completa
|
|
391
|
+
end
|
|
392
|
+
end
|
|
393
|
+
|
|
394
|
+
# Con condiciones
|
|
395
|
+
class PostsController < ApplicationController
|
|
396
|
+
caches_action :show,
|
|
397
|
+
expires_in: 30.minutes,
|
|
398
|
+
if: -> { !user_signed_in? } # Solo para visitantes
|
|
399
|
+
|
|
400
|
+
def show
|
|
401
|
+
@post = Post.find(params[:id])
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
## Monitoring y debugging
|
|
407
|
+
|
|
408
|
+
```ruby
|
|
409
|
+
# config/environments/development.rb
|
|
410
|
+
# Ver información de cache en logs
|
|
411
|
+
config.action_controller.enable_fragment_cache_logging = true
|
|
412
|
+
|
|
413
|
+
# En console
|
|
414
|
+
Rails.cache.stats # Solo Redis
|
|
415
|
+
|
|
416
|
+
# Ver hits/misses
|
|
417
|
+
ActiveSupport::Notifications.subscribe("cache_read.active_support") do |*args|
|
|
418
|
+
event = ActiveSupport::Notifications::Event.new(*args)
|
|
419
|
+
Rails.logger.debug "Cache #{event.payload[:hit] ? 'HIT' : 'MISS'}: #{event.payload[:key]}"
|
|
420
|
+
end
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
## Estrategias por tipo de contenido
|
|
424
|
+
|
|
425
|
+
### Contenido estático (rara vez cambia)
|
|
426
|
+
|
|
427
|
+
```ruby
|
|
428
|
+
# Cache largo, versionar manualmente
|
|
429
|
+
Rails.cache.fetch("site/footer/v1", expires_in: 1.week) do
|
|
430
|
+
render_to_string(partial: "shared/footer")
|
|
431
|
+
end
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
### Contenido de usuario (cambia frecuentemente)
|
|
435
|
+
|
|
436
|
+
```erb
|
|
437
|
+
<%# Cache corto, con key específico del usuario %>
|
|
438
|
+
<% cache [current_user, "dashboard", Date.current] do %>
|
|
439
|
+
<%= render "dashboard/stats" %>
|
|
440
|
+
<% end %>
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
### Datos agregados (conteos, estadísticas)
|
|
444
|
+
|
|
445
|
+
```ruby
|
|
446
|
+
# Precalcular y cachear
|
|
447
|
+
class StatsJob < ApplicationJob
|
|
448
|
+
def perform
|
|
449
|
+
stats = {
|
|
450
|
+
total_users: User.count,
|
|
451
|
+
total_posts: Post.count,
|
|
452
|
+
posts_today: Post.where("created_at > ?", Date.current.beginning_of_day).count
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
Rails.cache.write("site/stats", stats, expires_in: 5.minutes)
|
|
456
|
+
end
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
# Ejecutar periódicamente con Solid Queue
|
|
460
|
+
# O en un scheduler
|
|
461
|
+
```
|
|
462
|
+
|
|
463
|
+
## Checklist
|
|
464
|
+
|
|
465
|
+
- [ ] Solid Cache o Redis configurado
|
|
466
|
+
- [ ] Fragment caching en vistas pesadas
|
|
467
|
+
- [ ] Russian doll caching para anidados
|
|
468
|
+
- [ ] Counter cache para conteos frecuentes
|
|
469
|
+
- [ ] Touch en belongs_to para invalidación
|
|
470
|
+
- [ ] HTTP caching con ETags
|
|
471
|
+
- [ ] Memoization en modelos
|
|
472
|
+
- [ ] Monitoring de hit/miss rate
|
|
473
|
+
- [ ] Cache warming en deploy (opcional)
|