rails 2.3.2 → 2.3.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of rails might be problematic. Click here for more details.

Files changed (118) hide show
  1. data/CHANGELOG +4 -0
  2. data/Rakefile +11 -9
  3. data/configs/routes.rb +1 -1
  4. data/guides/files/javascripts/code_highlighter.js +188 -0
  5. data/guides/files/javascripts/guides.js +8 -0
  6. data/guides/files/javascripts/highlighters.js +90 -0
  7. data/guides/files/stylesheets/main.css +441 -0
  8. data/guides/files/stylesheets/print.css +52 -0
  9. data/guides/files/stylesheets/reset.css +43 -0
  10. data/guides/files/stylesheets/style.css +13 -0
  11. data/guides/files/stylesheets/syntax.css +31 -0
  12. data/guides/images/belongs_to.png +0 -0
  13. data/guides/images/book_icon.gif +0 -0
  14. data/guides/images/bullet.gif +0 -0
  15. data/guides/images/chapters_icon.gif +0 -0
  16. data/guides/images/check_bullet.gif +0 -0
  17. data/guides/images/credits_pic_blank.gif +0 -0
  18. data/guides/images/csrf.png +0 -0
  19. data/guides/images/customized_error_messages.png +0 -0
  20. data/guides/images/error_messages.png +0 -0
  21. data/guides/images/feature_tile.gif +0 -0
  22. data/guides/images/footer_tile.gif +0 -0
  23. data/guides/images/fxn.jpg +0 -0
  24. data/guides/images/grey_bullet.gif +0 -0
  25. data/guides/images/habtm.png +0 -0
  26. data/guides/images/has_many.png +0 -0
  27. data/guides/images/has_many_through.png +0 -0
  28. data/guides/images/has_one.png +0 -0
  29. data/guides/images/has_one_through.png +0 -0
  30. data/guides/images/header_backdrop.png +0 -0
  31. data/guides/images/header_tile.gif +0 -0
  32. data/guides/images/i18n/demo_localized_pirate.png +0 -0
  33. data/guides/images/i18n/demo_translated_en.png +0 -0
  34. data/guides/images/i18n/demo_translated_pirate.png +0 -0
  35. data/guides/images/i18n/demo_translation_missing.png +0 -0
  36. data/guides/images/i18n/demo_untranslated.png +0 -0
  37. data/guides/images/icons/README +5 -0
  38. data/guides/images/icons/callouts/1.png +0 -0
  39. data/guides/images/icons/callouts/10.png +0 -0
  40. data/guides/images/icons/callouts/11.png +0 -0
  41. data/guides/images/icons/callouts/12.png +0 -0
  42. data/guides/images/icons/callouts/13.png +0 -0
  43. data/guides/images/icons/callouts/14.png +0 -0
  44. data/guides/images/icons/callouts/15.png +0 -0
  45. data/guides/images/icons/callouts/2.png +0 -0
  46. data/guides/images/icons/callouts/3.png +0 -0
  47. data/guides/images/icons/callouts/4.png +0 -0
  48. data/guides/images/icons/callouts/5.png +0 -0
  49. data/guides/images/icons/callouts/6.png +0 -0
  50. data/guides/images/icons/callouts/7.png +0 -0
  51. data/guides/images/icons/callouts/8.png +0 -0
  52. data/guides/images/icons/callouts/9.png +0 -0
  53. data/guides/images/icons/caution.png +0 -0
  54. data/guides/images/icons/example.png +0 -0
  55. data/guides/images/icons/home.png +0 -0
  56. data/guides/images/icons/important.png +0 -0
  57. data/guides/images/icons/next.png +0 -0
  58. data/guides/images/icons/note.png +0 -0
  59. data/guides/images/icons/prev.png +0 -0
  60. data/guides/images/icons/tip.png +0 -0
  61. data/guides/images/icons/up.png +0 -0
  62. data/guides/images/icons/warning.png +0 -0
  63. data/guides/images/nav_arrow.gif +0 -0
  64. data/guides/images/polymorphic.png +0 -0
  65. data/guides/images/posts_index.png +0 -0
  66. data/guides/images/rails_guides_logo.gif +0 -0
  67. data/guides/images/rails_logo_remix.gif +0 -0
  68. data/guides/images/rails_welcome.png +0 -0
  69. data/guides/images/session_fixation.png +0 -0
  70. data/guides/images/tab_grey.gif +0 -0
  71. data/guides/images/tab_info.gif +0 -0
  72. data/guides/images/tab_note.gif +0 -0
  73. data/guides/images/tab_red.gif +0 -0
  74. data/guides/images/tab_yellow.gif +0 -0
  75. data/guides/images/tab_yellow.png +0 -0
  76. data/guides/images/validation_error_messages.png +0 -0
  77. data/guides/rails_guides.rb +42 -0
  78. data/guides/rails_guides/generator.rb +138 -0
  79. data/guides/rails_guides/helpers.rb +34 -0
  80. data/guides/rails_guides/indexer.rb +55 -0
  81. data/guides/rails_guides/textile_extensions.rb +41 -0
  82. data/guides/source/2_2_release_notes.textile +422 -0
  83. data/guides/source/2_3_release_notes.textile +610 -0
  84. data/guides/source/action_controller_overview.textile +776 -0
  85. data/guides/source/action_mailer_basics.textile +424 -0
  86. data/guides/source/active_record_basics.textile +135 -0
  87. data/guides/source/active_record_querying.textile +969 -0
  88. data/guides/source/activerecord_validations_callbacks.textile +1086 -0
  89. data/guides/source/association_basics.textile +1781 -0
  90. data/guides/source/caching_with_rails.textile +524 -0
  91. data/guides/source/command_line.textile +589 -0
  92. data/guides/source/configuring.textile +234 -0
  93. data/guides/source/contribute.textile +71 -0
  94. data/guides/source/contributing_to_rails.textile +239 -0
  95. data/guides/source/credits.erb.textile +52 -0
  96. data/guides/source/debugging_rails_applications.textile +709 -0
  97. data/guides/source/form_helpers.textile +766 -0
  98. data/guides/source/getting_started.textile +1297 -0
  99. data/guides/source/i18n.textile +912 -0
  100. data/guides/source/index.erb.textile +124 -0
  101. data/guides/source/layout.html.erb +103 -0
  102. data/guides/source/layouts_and_rendering.textile +979 -0
  103. data/guides/source/migrations.textile +591 -0
  104. data/guides/source/nested_model_forms.textile +222 -0
  105. data/guides/source/performance_testing.textile +531 -0
  106. data/guides/source/plugins.textile +1512 -0
  107. data/guides/source/rails_on_rack.textile +309 -0
  108. data/guides/source/routing.textile +903 -0
  109. data/guides/source/security.textile +986 -0
  110. data/guides/source/testing.textile +951 -0
  111. data/lib/commands/performance/profiler.rb +1 -1
  112. data/lib/initializer.rb +27 -4
  113. data/lib/rails/gem_dependency.rb +35 -6
  114. data/lib/rails/rack/metal.rb +1 -1
  115. data/lib/rails/version.rb +1 -1
  116. data/lib/tasks/gems.rake +19 -6
  117. data/lib/test_help.rb +4 -1
  118. metadata +123 -7
@@ -0,0 +1,524 @@
1
+ h2. Caching with Rails: An overview
2
+
3
+ Everyone caches. This guide will teach you what you need to know about
4
+ avoiding that expensive round-trip to your database and returning what you
5
+ need to return to those hungry web clients in the shortest time possible.
6
+
7
+ After reading this guide, you should be able to use and configure:
8
+
9
+ * Page, action, and fragment caching
10
+ * Sweepers
11
+ * Alternative cache stores
12
+ * Conditional GET support
13
+
14
+ endprologue.
15
+
16
+ h3. Basic Caching
17
+
18
+ This is an introduction to the three types of caching techniques that Rails
19
+ provides by default without the use of any third party plugins.
20
+
21
+ To get started make sure +config.action_controller.perform_caching+ is set
22
+ to +true+ for your environment. This flag is normally set in the
23
+ corresponding config/environments/*.rb. By default, caching is disabled for development and test, and enabled for production.
24
+
25
+ <ruby>
26
+ config.action_controller.perform_caching = true
27
+ </ruby>
28
+
29
+ h4. Page Caching
30
+
31
+ Page caching is a Rails mechanism which allows the request for a generated
32
+ page to be fulfilled by the webserver, without ever having to go through the
33
+ Rails stack at all. Obviously, this is super-fast. Unfortunately, it can't be
34
+ applied to every situation (such as pages that need authentication) and since
35
+ the webserver is literally just serving a file from the filesystem, cache
36
+ expiration is an issue that needs to be dealt with.
37
+
38
+ So, how do you enable this super-fast cache behavior? Suppose you
39
+ have a controller called +ProductsController+ and an +index+ action that lists all
40
+ the products. You could enable caching for this action like this:
41
+
42
+ <ruby>
43
+ class ProductsController < ActionController
44
+
45
+ caches_page :index
46
+
47
+ def index; end
48
+
49
+ end
50
+ </ruby>
51
+
52
+ The first time anyone requests products/index, Rails will generate a file
53
+ called +index.html+. If a web server see this file, it will be served in response to the
54
+ next request for products/index, without your Rails application being called.
55
+
56
+ By default, the page cache directory is set to Rails.public_path (which is
57
+ usually set to +File.join(self.root, "public")+ - that is, the public directory under your Rails application's root). This can be configured by
58
+ changing the configuration setting +config.action_controller.page_cache_directory+.
59
+ Changing the default from /public helps avoid naming conflicts, since you may
60
+ want to put other static html in /public, but changing this will require web
61
+ server reconfiguration to let the web server know where to serve the cached
62
+ files from.
63
+
64
+ The page caching mechanism will automatically add a +.html+ extension to
65
+ requests for pages that do not have an extension to make it easy for the
66
+ webserver to find those pages. This can be configured by changing the
67
+ configuration setting +config.action_controller.page_cache_extension+.
68
+
69
+ In order to expire this page when a new product is added you could extend the products controller like this:
70
+
71
+ <ruby>
72
+ class ProductsController < ActionController
73
+
74
+ caches_page :index
75
+
76
+ def index; end
77
+
78
+ def create
79
+ expire_page :action => :index
80
+ end
81
+
82
+ end
83
+ </ruby>
84
+
85
+ If you want a more complicated expiration scheme, you can use cache sweepers
86
+ to expire cached objects when things change. This is covered in the section on Sweepers.
87
+
88
+ Note: Page caching ignores all parameters, so /products/list?page=1 will be written out to the filesystem as /products/list.html and if someone requests /products/list?page=2, they will be returned the same result as page=1. Be careful when page caching GET parameters in the URL!
89
+
90
+ h4. Action Caching
91
+
92
+ One of the issues with page caching is that you cannot use it for pages that
93
+ require checking code to determine whether the user should be permitted access. This is where Action Caching comes in.
94
+ action caching works like page caching except for the fact that the incoming
95
+ web request does go from the web server to the Rails stack and Action Pack so
96
+ that before filters can be run on it before the cache is served. This allows you to use
97
+ authentication and other restrictions while still serving the
98
+ result of the output from a cached copy.
99
+
100
+ Clearing the cache works in the exact same way as with page caching.
101
+
102
+ Let's say you only wanted authenticated users to edit or create a Product
103
+ object, but still cache those pages:
104
+
105
+ <ruby>
106
+ class ProductsController < ActionController
107
+
108
+ before_filter :authenticate, :only => [ :edit, :create ]
109
+ caches_page :index
110
+ caches_action :edit
111
+
112
+ def index; end
113
+
114
+ def create
115
+ expire_page :action => :index
116
+ expire_action :action => :edit
117
+ end
118
+
119
+ def edit; end
120
+
121
+ end
122
+ </ruby>
123
+
124
+ You can also use +:if+ (or +:unless+) to pass a Proc that specifies when the
125
+ action should be cached. Also, you can use +:layout => false+ to cache without
126
+ layout so that dynamic information in the layout such as the name of the logged-in user
127
+ or the number of items in the cart can be left uncached. This feature is
128
+ available as of Rails 2.2.
129
+
130
+ You can modify the default action cache path by passing a +:cache_path+ option.
131
+ This will be passed directly to +ActionCachePath.path_for+. This is handy for
132
+ actions with multiple possible routes that should be cached differently. If
133
+ a block is given, it is called with the current controller instance.
134
+
135
+ Finally, if you are using memcached, you can also pass +:expires_in+. In fact,
136
+ all parameters not used by +caches_action+ are sent to the underlying cache
137
+ store.
138
+
139
+ h4. Fragment Caching
140
+
141
+ Life would be perfect if we could get away with caching the entire contents of
142
+ a page or action and serving it out to the world. Unfortunately, dynamic web
143
+ applications usually build pages with a variety of components not all of which
144
+ have the same caching characteristics. In order to address such a dynamically
145
+ created page where different parts of the page need to be cached and expired
146
+ differently Rails provides a mechanism called Fragment Caching.
147
+
148
+ Fragment Caching allows a fragment of view logic to be wrapped in a cache
149
+ block and served out of the cache store when the next request comes in.
150
+
151
+ As an example, if you wanted to show all the orders placed on your website
152
+ in real time and didn't want to cache that part of the page, but did want
153
+ to cache the part of the page which lists all products available, you
154
+ could use this piece of code:
155
+
156
+ <ruby>
157
+ <% Order.find_recent.each do |o| %>
158
+ <%= o.buyer.name %> bought <% o.product.name %>
159
+ <% end %>
160
+
161
+ <% cache do %>
162
+ All available products:
163
+ <% Product.find(:all).each do |p| %>
164
+ <%= link_to p.name, product_url(p) %>
165
+ <% end %>
166
+ <% end %>
167
+ </ruby>
168
+
169
+ The cache block in our example will bind to the action that called it and is
170
+ written out to the same place as the action cache, which means that if you
171
+ want to cache multiple fragments per action, you should provide an +action_suffix+ to the cache call:
172
+
173
+ <ruby>
174
+ <% cache(:action => 'recent', :action_suffix => 'all_prods') do %>
175
+ All available products:
176
+ </ruby>
177
+
178
+ You can expire the cache using the +expire_fragment+ method, like so:
179
+
180
+ <ruby>
181
+ expire_fragment(:controller => 'products', :action => 'recent',
182
+ :action_suffix => 'all_prods)
183
+ </ruby>
184
+
185
+ If you don't want the cache block to bind to the action that called it, you can
186
+ also use globally keyed fragments. To do this, call the +cache+ method with a key, like
187
+ so:
188
+
189
+ <ruby>
190
+ <% cache(:key =>
191
+ ['all_available_products', @latest_product.created_at].join(':')) do %>
192
+ All available products:
193
+ <% end %>
194
+ </ruby>
195
+
196
+ This fragment is then available to all actions in the +ProductsController+ using
197
+ the key and can be expired the same way:
198
+
199
+ <ruby>
200
+ expire_fragment(:key =>
201
+ ['all_available_products', @latest_product.created_at].join(':'))
202
+ </ruby>
203
+
204
+ h4. Sweepers
205
+
206
+ Cache sweeping is a mechanism which allows you to get around having a ton of
207
+ +expire_{page,action,fragment}+ calls in your code. It does this by moving all the work
208
+ required to expire cached content into na +ActionController::Caching::Sweeper+
209
+ class. This class is an Observer that looks for changes to an object via callbacks,
210
+ and when a change occurs it expires the caches associated with that object in
211
+ an around or after filter.
212
+
213
+ Continuing with our Product controller example, we could rewrite it with a
214
+ sweeper like this:
215
+
216
+ <ruby>
217
+ class StoreSweeper < ActionController::Caching::Sweeper
218
+ # This sweeper is going to keep an eye on the Product model
219
+ observe Product
220
+
221
+ # If our sweeper detects that a Product was created call this
222
+ def after_create(product)
223
+ expire_cache_for(product)
224
+ end
225
+
226
+ # If our sweeper detects that a Product was updated call this
227
+ def after_update(product)
228
+ expire_cache_for(product)
229
+ end
230
+
231
+ # If our sweeper detects that a Product was deleted call this
232
+ def after_destroy(product)
233
+ expire_cache_for(product)
234
+ end
235
+
236
+ private
237
+ def expire_cache_for(record)
238
+ # Expire the list page now that we added a new product
239
+ expire_page(:controller => '#{record}', :action => 'list')
240
+
241
+ # Expire a fragment
242
+ expire_fragment(:controller => '#{record}',
243
+ :action => 'recent', :action_suffix => 'all_products')
244
+ end
245
+ end
246
+ </ruby>
247
+
248
+ The sweeper has to be added to the controller that will use it. So, if we wanted to expire the cached content for the
249
+ list and edit actions when the create action was called, we could do the
250
+ following:
251
+
252
+ <ruby>
253
+ class ProductsController < ActionController
254
+
255
+ before_filter :authenticate, :only => [ :edit, :create ]
256
+ caches_page :list
257
+ caches_action :edit
258
+ cache_sweeper :store_sweeper, :only => [ :create ]
259
+
260
+ def list; end
261
+
262
+ def create
263
+ expire_page :action => :list
264
+ expire_action :action => :edit
265
+ end
266
+
267
+ def edit; end
268
+
269
+ end
270
+ </ruby>
271
+
272
+ h4. SQL Caching
273
+
274
+ Query caching is a Rails feature that caches the result set returned by each
275
+ query. If Rails encounters the same query again during the current request, it
276
+ will used the cached result set as opposed to running the query against the
277
+ database.
278
+
279
+ For example:
280
+
281
+ <ruby>
282
+ class ProductsController < ActionController
283
+
284
+ before_filter :authenticate, :only => [ :edit, :create ]
285
+ caches_page :list
286
+ caches_action :edit
287
+ cache_sweeper :store_sweeper, :only => [ :create ]
288
+
289
+ def list
290
+ # Run a find query
291
+ Product.find(:all)
292
+
293
+ ...
294
+
295
+ # Run the same query again
296
+ Product.find(:all)
297
+ end
298
+
299
+ def create
300
+ expire_page :action => :list
301
+ expire_action :action => :edit
302
+ end
303
+
304
+ def edit; end
305
+
306
+ end
307
+ </ruby>
308
+
309
+ In the 'list' action above, the result set returned by the first
310
+ Product.find(:all) will be cached and will be used to avoid querying the
311
+ database again the second time that finder is called.
312
+
313
+ Query caches are created at the start of an action and destroyed at the end of
314
+ that action and thus persist only for the duration of the action.
315
+
316
+ h4. Cache Stores
317
+
318
+ Rails (as of 2.1) provides different stores for the cached data created by action and
319
+ fragment caches. Page caches are always stored on disk.
320
+
321
+ Rails 2.1 and above provide ActiveSupport::Cache::Store which can be used to
322
+ cache strings. Some cache store implementations, like MemoryStore, are able to
323
+ cache arbitrary Ruby objects, but don't count on every cache store to be able
324
+ to do that.
325
+
326
+ The default cache stores provided with Rails include:
327
+
328
+ 1) ActiveSupport::Cache::MemoryStore: A cache store implementation which stores
329
+ everything into memory in the same process. If you're running multiple Ruby on
330
+ Rails server processes (which is the case if you're using mongrel_cluster or
331
+ Phusion Passenger), then this means that your Rails server process instances
332
+ won't be able to share cache data with each other. If your application never
333
+ performs manual cache item expiry (e.g. when you‘re using generational cache
334
+ keys), then using +MemoryStore+ is ok. Otherwise, consider carefully whether you
335
+ should be using this cache store.
336
+
337
+ +MemoryStore+ is not only able to store strings, but also arbitrary Ruby objects.
338
+
339
+ +MemoryStore+ is not thread-safe. Use +SynchronizedMemoryStore+ instead if you
340
+ need thread-safety.
341
+
342
+
343
+ <ruby>
344
+ ActionController::Base.cache_store = :memory_store
345
+ </ruby>
346
+
347
+ 2) ActiveSupport::Cache::FileStore: Cached data is stored on the disk. This is
348
+ the default store and the default path for this store is: /tmp/cache. Works
349
+ well for all types of environments and allows all processes running from the
350
+ same application directory to access the cached content. If /tmp/cache does not
351
+ exist, the default store becomes MemoryStore.
352
+
353
+ <ruby>
354
+ ActionController::Base.cache_store = :file_store, "/path/to/cache/directory"
355
+ </ruby>
356
+
357
+ 3) ActiveSupport::Cache::DRbStore: Cached data is stored in a separate shared
358
+ DRb process that all servers communicate with. This works for all environments
359
+ and only keeps one cache around for all processes, but requires that you run
360
+ and manage a separate DRb process.
361
+
362
+ <ruby>
363
+ ActionController::Base.cache_store = :drb_store, "druby://localhost:9192"
364
+ </ruby>
365
+
366
+ 4) MemCached store: Works like DRbStore, but uses Danga's MemCache instead.
367
+ Rails uses the bundled memcached-client gem by default. This is currently the
368
+ most popular cache store for production websites.
369
+
370
+ Special features:
371
+
372
+ * Clustering and load balancing. One can specify multiple memcached servers,
373
+ and MemCacheStore will load balance between all available servers. If a
374
+ server goes down, then MemCacheStore will ignore it until it goes back
375
+ online.
376
+ * Time-based expiry support. See +write+ and the +:expires_in+ option.
377
+ * Per-request in memory cache for all communication with the MemCache server(s).
378
+
379
+ It also accepts a hash of additional options:
380
+
381
+ * +:namespace+- specifies a string that will automatically be prepended to keys when accessing the memcached store.
382
+ * +:readonly+- a boolean value that when set to true will make the store read-only, with an error raised on any attempt to write.
383
+ * +:multithread+ - a boolean value that adds thread safety to read/write operations - it is unlikely you'll need to use this option as the Rails threadsafe! method offers the same functionality.
384
+
385
+ The read and write methods of the MemCacheStore accept an options hash too.
386
+ When reading you can specify +:raw => true+ to prevent the object being
387
+ marshaled
388
+ (by default this is false which means the raw value in the cache is passed to
389
+ +Marshal.load+ before being returned to you.)
390
+
391
+ When writing to the cache it is also possible to specify +:raw => true+. This means
392
+ that the value is not passed to +Marshal.dump+ before being stored in the cache (by
393
+ default this is false).
394
+
395
+ The write method also accepts an +:unless_exist+ flag which determines whether
396
+ the memcached add (when true) or set (when false) method is used to store the
397
+ item in the cache and an +:expires_in+ option that specifies the time-to-live
398
+ for the cached item in seconds.
399
+
400
+
401
+ <ruby>
402
+ ActionController::Base.cache_store = :mem_cache_store, "localhost"
403
+ </ruby>
404
+
405
+ 5) ActiveSupport::Cache::SynchronizedMemoryStore: Like ActiveSupport::Cache::MemoryStore but thread-safe.
406
+
407
+
408
+ <ruby>
409
+ ActionController::Base.cache_store = :synchronized_memory_store
410
+ </ruby>
411
+
412
+ 6) ActiveSupport::Cache::CompressedMemCacheStore: Works just like the regular
413
+ MemCacheStore but uses GZip to decompress/compress on read/write.
414
+
415
+
416
+ <ruby>
417
+ ActionController::Base.cache_store = :compressed_mem_cache_store, "localhost"
418
+ </ruby>
419
+
420
+ 7) Custom store: You can define your own cache store (new in Rails 2.1)
421
+
422
+
423
+ <ruby>
424
+ ActionController::Base.cache_store = MyOwnStore.new("parameter")
425
+ </ruby>
426
+
427
+ NOTE: +config.cache_store+ can be used in place of
428
+ +ActionController::Base.cache_store+ in the +Rails::Initializer.run+ block in
429
+ environment.rb.
430
+
431
+ In addition to all of this, Rails also adds the +ActiveRecord::Base#cache_key+
432
+ method that generates a key using the class name, id and updated_at timestamp
433
+ (if available).
434
+
435
+ An example:
436
+
437
+ <ruby>
438
+ Rails.cache.read("city") # => nil
439
+ Rails.cache.write("city", "Duckburgh")
440
+ Rails.cache.read("city") # => "Duckburgh"
441
+ </ruby>
442
+
443
+ h3. Conditional GET Support
444
+
445
+ Conditional GETs are a feature of the HTTP specification that provide a way for web
446
+ servers to tell browsers that the response to a GET request hasn't changed
447
+ since the last request and can be safely pulled from the browser cache.
448
+
449
+ They work by using the HTTP_IF_NONE_MATCH and HTTP_IF_MODIFIED_SINCE headers to
450
+ pass back and forth both a unique content identifier and the timestamp of when
451
+ the content was last changed. If the browser makes a request where the content
452
+ identifier (etag) or last modified since timestamp matches the server’s version
453
+ then the server only needs to send back an empty response with a not modified
454
+ status.
455
+
456
+ It is the server's (i.e. our) responsibility to look for a last modified
457
+ timestamp and the if-none-match header and determine whether or not to send
458
+ back the full response. With conditional-get support in rails this is a pretty
459
+ easy task:
460
+
461
+ <ruby>
462
+ class ProductsController < ApplicationController
463
+
464
+ def show
465
+ @product = Product.find(params[:id])
466
+
467
+ # If the request is stale according to the given timestamp and etag value
468
+ # (i.e. it needs to be processed again) then execute this block
469
+ if stale?(:last_modified => @product.updated_at.utc, :etag => @product)
470
+ respond_to do |wants|
471
+ # ... normal response processing
472
+ end
473
+ end
474
+
475
+ # If the request is fresh (i.e. it's not modified) then you don't need to do
476
+ # anything. The default render checks for this using the parameters
477
+ # used in the previous call to stale? and will automatically send a
478
+ # :not_modified. So that's it, you're done.
479
+ end
480
+ </ruby>
481
+
482
+ If you don't have any special response processing and are using the default
483
+ rendering mechanism (i.e. you're not using respond_to or calling render
484
+ yourself) then you’ve got an easy helper in fresh_when:
485
+
486
+ <ruby>
487
+ class ProductsController < ApplicationController
488
+
489
+ # This will automatically send back a :not_modified if the request is fresh,
490
+ # and will render the default template (product.*) if it's stale.
491
+
492
+ def show
493
+ @product = Product.find(params[:id])
494
+ fresh_when :last_modified => @product.published_at.utc, :etag => @article
495
+ end
496
+ end
497
+ </ruby>
498
+
499
+ h3. Advanced Caching
500
+
501
+ Along with the built-in mechanisms outlined above, a number of excellent
502
+ plugins exist to help with finer grained control over caching. These include
503
+ Chris Wanstrath's excellent cache_fu plugin (more info "here": http://errtheblog.com/posts/57-kickin-ass-w-cachefu) and Evan Weaver's
504
+ interlock plugin (more info "here": http://blog.evanweaver.com/articles/2007/12/13/better-rails-caching/). Both
505
+ of these plugins play nice with memcached and are a must-see for anyone
506
+ seriously considering optimizing their caching needs.
507
+
508
+ Also the new "Cache money":http://github.com/nkallen/cache-money/tree/master plugin is supposed to be mad cool.
509
+
510
+ h3. References
511
+
512
+ * "RailsEnvy, Rails Caching Tutorial, Part 1":http://www.railsenvy.com/2007/2/28/rails-caching-tutorial
513
+ * "RailsEnvy, Rails Caching Tutorial, Part 1":http://www.railsenvy.com/2007/3/20/ruby-on-rails-caching-tutorial-part-2
514
+ * "ActiveSupport::Cache documentation":http://api.rubyonrails.org/classes/ActiveSupport/Cache.html
515
+ * "Rails 2.1 integrated caching tutorial":http://thewebfellas.com/blog/2008/6/9/rails-2-1-now-with-better-integrated-caching
516
+
517
+ h3. Changelog
518
+
519
+ "Lighthouse ticket":http://rails.lighthouseapp.com/projects/16213-rails-guides/tickets/10-guide-to-caching
520
+
521
+ * February 22, 2009: Beefed up the section on cache_stores
522
+ * December 27, 2008: Typo fixes
523
+ * November 23, 2008: Incremental updates with various suggested changes and formatting cleanup
524
+ * September 15, 2008: Initial version by Aditya Chadha