josevalim-easy_http_cache 2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +21 -0
- data/README +174 -0
- data/Rakefile +12 -0
- data/init.rb +1 -0
- data/lib/easy_http_cache.rb +140 -0
- data/test/easy_http_cache_test.rb +239 -0
- metadata +59 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
Copyright (c) 2006 Coda Hale
|
2
|
+
Copyright (c) 2008 José Valim (jose.valim at gmail dot com)
|
3
|
+
|
4
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
5
|
+
a copy of this software and associated documentation files (the
|
6
|
+
"Software"), to deal in the Software without restriction, including
|
7
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
8
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
9
|
+
permit persons to whom the Software is furnished to do so, subject to
|
10
|
+
the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
19
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
20
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
21
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,174 @@
|
|
1
|
+
Copyright (c) 2008 José Valim (jose.valim at gmail dot com)
|
2
|
+
Site: http://www.pagestacker.com/
|
3
|
+
Blog: http://josevalim.blogspot.com/
|
4
|
+
License: MIT
|
5
|
+
Version: 2.0
|
6
|
+
|
7
|
+
You can also read this README in pretty html at the GitHub project Wiki page
|
8
|
+
|
9
|
+
http://github.com/josevalim/easy_http_cache/wikis/home
|
10
|
+
|
11
|
+
Warning
|
12
|
+
-------
|
13
|
+
|
14
|
+
Since version 2.0, this plugin/gem has drastically changed to fit Rails 2.2
|
15
|
+
http cache goodness. :expires_in, :expires_at and :control options were
|
16
|
+
removed, so if you want to use previous versions, see "Previous versions"
|
17
|
+
below.
|
18
|
+
|
19
|
+
Description
|
20
|
+
-----------
|
21
|
+
|
22
|
+
Allows Rails applications to do conditional cache easily and in a DRY way
|
23
|
+
(without messing up your actions):
|
24
|
+
|
25
|
+
class ListsController < ApplicationController
|
26
|
+
http_cache :index, :show
|
27
|
+
end
|
28
|
+
|
29
|
+
It uses :last_modified and :etag keys, that besides Time, String or resources
|
30
|
+
accepts Proc, Method and Symbols that are evaluated within the current controller.
|
31
|
+
Read more about each option below:
|
32
|
+
|
33
|
+
:last_modified
|
34
|
+
Used to manipulate Last-Modified header. You can pass any object that responds
|
35
|
+
to :to_time. If you pass a Proc or Method or Symbols, they will be evaluated
|
36
|
+
within the current controller and :to_time will be called.
|
37
|
+
|
38
|
+
You can also pass resources and :updated_at or :updated_on will be called on
|
39
|
+
it. If you want to call a different method on your resource, you can pass it as
|
40
|
+
a symbol using the :method option.
|
41
|
+
|
42
|
+
All times will be converted to UTC. Finally, if you pass an array, it will get
|
43
|
+
the most recent time to be used.
|
44
|
+
|
45
|
+
:etag
|
46
|
+
Used to manipulate Etag header. If you pass a Proc or Method or Symbols, they
|
47
|
+
will be evaluated within the current controller.
|
48
|
+
|
49
|
+
You can also pass an array and each element will be also evaluated with needed.
|
50
|
+
|
51
|
+
:if
|
52
|
+
Only perform http cache if it returns true.
|
53
|
+
|
54
|
+
:unless
|
55
|
+
Only perform http cache if it returns false.
|
56
|
+
|
57
|
+
|
58
|
+
Install
|
59
|
+
-------
|
60
|
+
|
61
|
+
Install Easy HTTP Cache is very easy. It is stored in GitHub, so if you
|
62
|
+
have never installed a gem via GitHub run the following:
|
63
|
+
|
64
|
+
gem sources -a http://gems.github.com
|
65
|
+
|
66
|
+
Then install the gem:
|
67
|
+
|
68
|
+
sudo gem install josevalim-easy_http_cache
|
69
|
+
|
70
|
+
In RAILS_ROOT/config/environment.rb:
|
71
|
+
|
72
|
+
config.gem "josevalim-easy_http_cache", :lib => "easy_http_cache", :source => "http://gems.github.com"
|
73
|
+
|
74
|
+
If you want it as plugin, just do:
|
75
|
+
|
76
|
+
cd myapp
|
77
|
+
git clone git://github.com/josevalim/easy_http_cache.git
|
78
|
+
rm -rf vendor/plugins/easy_http_cache/.git
|
79
|
+
|
80
|
+
|
81
|
+
Previous versions
|
82
|
+
-----------------
|
83
|
+
|
84
|
+
If you are running on Rails 2.1.x, you should use v1.2.3:
|
85
|
+
|
86
|
+
cd myapp
|
87
|
+
git clone git://github.com/josevalim/easy_http_cache.git
|
88
|
+
cd vendor/plugins/easy_http_cache
|
89
|
+
git checkout v1.2.3
|
90
|
+
rm -rf ./.git
|
91
|
+
|
92
|
+
If you are using a previous version, please updagrade your app. =)
|
93
|
+
|
94
|
+
|
95
|
+
Variables
|
96
|
+
---------
|
97
|
+
|
98
|
+
You can set ENV['RAILS_CACHE_ID'] or ENV['RAILS_APP_VERSION'] to change
|
99
|
+
the ETag that will be generated, expiring all previous caches. Those variables
|
100
|
+
are also used by other cache stores (memcached, file, ...).
|
101
|
+
|
102
|
+
|
103
|
+
Examples
|
104
|
+
--------
|
105
|
+
|
106
|
+
Just as above:
|
107
|
+
|
108
|
+
class ListsController < ApplicationController
|
109
|
+
http_cache :index, :show
|
110
|
+
end
|
111
|
+
|
112
|
+
If you do not want to cache when you are showing a flash message (and you
|
113
|
+
usually want that), you can simply do:
|
114
|
+
|
115
|
+
class ListsController < ApplicationController
|
116
|
+
http_cache :index, :show, :if => Proc.new { |c| c.__send__(:flash).empty? }
|
117
|
+
end
|
118
|
+
|
119
|
+
And if you do not want JSON requests:
|
120
|
+
|
121
|
+
class ListsController < ApplicationController
|
122
|
+
http_cache :index, :show, :unless => Proc.new { |c| c.request.format.json? }
|
123
|
+
end
|
124
|
+
|
125
|
+
Or if you want to expire all http cache before 2008, just do:
|
126
|
+
|
127
|
+
class ListsController < ApplicationController
|
128
|
+
http_cache :index, :show, :last_modified => Time.utc(2008)
|
129
|
+
end
|
130
|
+
|
131
|
+
If You want to cache a list and automatically expire the cache when
|
132
|
+
it changes, just do:
|
133
|
+
|
134
|
+
class ListsController < ApplicationController
|
135
|
+
http_cache :index, :show, :last_modified => :list
|
136
|
+
|
137
|
+
protected
|
138
|
+
def list
|
139
|
+
@list ||= List.find(params[:id])
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
You can also set :etag header:
|
144
|
+
|
145
|
+
class ListsController < ApplicationController
|
146
|
+
http_cache :index, :show, :etag => :list
|
147
|
+
|
148
|
+
protected
|
149
|
+
def list
|
150
|
+
@list ||= List.find(params[:id])
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
If you are using a resource that doesn't respond to updated_at or updated_on,
|
155
|
+
you can pass a method as parameter and it will be called in your resources:
|
156
|
+
|
157
|
+
class ListsController < ApplicationController
|
158
|
+
http_cache :index, :show, :last_modified => :list, :method => :cached_at
|
159
|
+
|
160
|
+
protected
|
161
|
+
def list
|
162
|
+
@list ||= List.find(params[:id])
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
Finally, you can also pass an array at :last_modified as below:
|
167
|
+
|
168
|
+
class ListsController < ApplicationController
|
169
|
+
http_cache :index, :show,
|
170
|
+
:last_modified => [ :list, Time.utc(2007,12,27) ]
|
171
|
+
end
|
172
|
+
|
173
|
+
This will check which one is the most recent to compare with the
|
174
|
+
"Last-Modified" field sent by the client.
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Generate documentation for Footnotes plugin.'
|
6
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
7
|
+
rdoc.rdoc_dir = 'rdoc'
|
8
|
+
rdoc.title = 'Easy HTTP Cache'
|
9
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
10
|
+
rdoc.rdoc_files.include('README')
|
11
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
12
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'easy_http_cache'
|
@@ -0,0 +1,140 @@
|
|
1
|
+
module ActionController #:nodoc:
|
2
|
+
module Caching
|
3
|
+
module HttpCache
|
4
|
+
def self.included(base) #:nodoc:
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
end
|
7
|
+
|
8
|
+
module ClassMethods
|
9
|
+
# Declares that +actions+ should be cached.
|
10
|
+
#
|
11
|
+
def http_cache(*actions)
|
12
|
+
return unless perform_caching
|
13
|
+
options = actions.extract_options!
|
14
|
+
|
15
|
+
options.assert_valid_keys(
|
16
|
+
:last_modified, :method, :etag, :if, :unless
|
17
|
+
)
|
18
|
+
|
19
|
+
http_cache_filter = HttpCacheFilter.new(
|
20
|
+
:method => options.delete(:method),
|
21
|
+
:last_modified => [options.delete(:last_modified)].flatten.compact,
|
22
|
+
:etag => options.delete(:etag)
|
23
|
+
)
|
24
|
+
filter_options = {:only => actions}.merge(options)
|
25
|
+
|
26
|
+
before_filter(http_cache_filter, filter_options)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class HttpCacheFilter #:nodoc:
|
31
|
+
def initialize(options = {})
|
32
|
+
@options = options
|
33
|
+
end
|
34
|
+
|
35
|
+
def filter(controller)
|
36
|
+
# We don't go ahead if we are rendering a component
|
37
|
+
#
|
38
|
+
return if component_request?(controller)
|
39
|
+
|
40
|
+
last_modified = get_last_modified(controller)
|
41
|
+
controller.response.last_modified = last_modified if last_modified
|
42
|
+
|
43
|
+
processed_etags = get_processed_etags(controller)
|
44
|
+
controller.response.etag = processed_etags if processed_etags
|
45
|
+
|
46
|
+
if controller.request.fresh?(controller.response)
|
47
|
+
controller.__send__(:head, :not_modified)
|
48
|
+
return false
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
protected
|
53
|
+
# If :etag is an array, it processes all Methods, Procs and Symbols
|
54
|
+
# and return them as array. If it's an object, we only evaluate it.
|
55
|
+
#
|
56
|
+
# Finally, if :etag is not sent but RAILS_CACHE_ID or RAILS_APP_VERSION
|
57
|
+
# are set, we return an empty string allowing etag to be performed
|
58
|
+
# because those variables, when modified, are a valid way to expire
|
59
|
+
# all previous caches.
|
60
|
+
#
|
61
|
+
def get_processed_etags(controller)
|
62
|
+
if @options[:etag].is_a?(Array)
|
63
|
+
@options[:etag].collect do |item|
|
64
|
+
evaluate_method(item, controller)
|
65
|
+
end
|
66
|
+
elsif @options[:etag]
|
67
|
+
evaluate_method(@options[:etag], controller)
|
68
|
+
elsif ENV['RAILS_CACHE_ID'] || ENV['RAILS_APP_VERSION']
|
69
|
+
''
|
70
|
+
else
|
71
|
+
nil
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
# We perform Last-Modified HTTP Cache when the option :last_modified is sent
|
76
|
+
# or no other cache mechanism is set (then we set a very old timestamp).
|
77
|
+
#
|
78
|
+
def get_last_modified(controller)
|
79
|
+
# Then, if @options[:last_modified] is not empty, we run through the array
|
80
|
+
# processing all objects (if needed) and return the latest one to be used.
|
81
|
+
#
|
82
|
+
if !@options[:last_modified].empty?
|
83
|
+
@options[:last_modified].collect do |item|
|
84
|
+
evaluate_time(item, controller)
|
85
|
+
end.compact.sort.last
|
86
|
+
elsif @options[:etag].blank?
|
87
|
+
Time.utc(0)
|
88
|
+
else
|
89
|
+
nil
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def evaluate_method(method, controller)
|
94
|
+
case method
|
95
|
+
when Symbol
|
96
|
+
controller.__send__(method)
|
97
|
+
when Proc, Method
|
98
|
+
method.call(controller)
|
99
|
+
else
|
100
|
+
method
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Evaluate the objects sent and return time objects
|
105
|
+
#
|
106
|
+
# It process Symbols, String, Proc and Methods, get its results and then
|
107
|
+
# call :to_time, :updated_at, :updated_on on it.
|
108
|
+
#
|
109
|
+
# If the parameter :method is sent, it will try to call it on the object before
|
110
|
+
# calling :to_time, :updated_at, :updated_on.
|
111
|
+
#
|
112
|
+
def evaluate_time(method, controller)
|
113
|
+
return nil unless method
|
114
|
+
time = evaluate_method(method, controller)
|
115
|
+
|
116
|
+
time = time.__send__(@options[:method]) if @options[:method].is_a?(Symbol) && time.respond_to?(@options[:method])
|
117
|
+
|
118
|
+
if time.respond_to?(:to_time)
|
119
|
+
time.to_time.utc
|
120
|
+
elsif time.respond_to?(:updated_at)
|
121
|
+
time.updated_at.utc
|
122
|
+
elsif time.respond_to?(:updated_on)
|
123
|
+
time.updated_on.utc
|
124
|
+
else
|
125
|
+
nil
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
# We should not do http cache when we are using components
|
130
|
+
#
|
131
|
+
def component_request?(controller)
|
132
|
+
controller.instance_variable_get('@parent_controller')
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
ActionController::Base.__send__ :include, ActionController::Caching::HttpCache
|
@@ -0,0 +1,239 @@
|
|
1
|
+
# Those lines are plugin test settings
|
2
|
+
ENV["RAILS_ENV"] = "test"
|
3
|
+
require 'ostruct'
|
4
|
+
require File.dirname(__FILE__) + '/../../../../config/environment'
|
5
|
+
require File.dirname(__FILE__) + '/../lib/easy_http_cache.rb'
|
6
|
+
require 'test_help'
|
7
|
+
|
8
|
+
ActionController::Base.perform_caching = true
|
9
|
+
ActionController::Routing::Routes.draw do |map|
|
10
|
+
map.connect ':controller/:action/:id'
|
11
|
+
end
|
12
|
+
|
13
|
+
class HttpCacheTestController < ActionController::Base
|
14
|
+
http_cache :index
|
15
|
+
http_cache :show, :last_modified => 2.hours.ago, :if => Proc.new { |c| !c.request.format.json? }
|
16
|
+
http_cache :edit, :last_modified => Proc.new{ 30.minutes.ago }
|
17
|
+
http_cache :destroy, :last_modified => [2.hours.ago, Proc.new{|c| 30.minutes.ago }]
|
18
|
+
http_cache :invalid, :last_modified => [1.hours.ago, false]
|
19
|
+
|
20
|
+
http_cache :etag, :etag => 'ETAG_CACHE'
|
21
|
+
http_cache :etag_array, :etag => [ 'ETAG_CACHE', :resource ]
|
22
|
+
http_cache :resources, :last_modified => [:resource, :list, :object]
|
23
|
+
http_cache :resources_with_method, :last_modified => [:resource, :list, :object], :method => :cached_at
|
24
|
+
|
25
|
+
def index
|
26
|
+
render :text => '200 OK', :status => 200
|
27
|
+
end
|
28
|
+
|
29
|
+
alias_method :show, :index
|
30
|
+
alias_method :edit, :index
|
31
|
+
alias_method :destroy, :index
|
32
|
+
alias_method :invalid, :index
|
33
|
+
alias_method :etag, :index
|
34
|
+
alias_method :etag_array, :index
|
35
|
+
alias_method :resources, :index
|
36
|
+
alias_method :resources_with_method, :index
|
37
|
+
|
38
|
+
protected
|
39
|
+
|
40
|
+
def resource
|
41
|
+
resource = OpenStruct.new
|
42
|
+
resource.instance_eval do
|
43
|
+
def to_param
|
44
|
+
12345
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
resource.updated_at = 2.hours.ago
|
49
|
+
resource
|
50
|
+
end
|
51
|
+
|
52
|
+
def list
|
53
|
+
list = OpenStruct.new
|
54
|
+
list.updated_on = 30.minutes.ago
|
55
|
+
list
|
56
|
+
end
|
57
|
+
|
58
|
+
def object
|
59
|
+
object = OpenStruct.new
|
60
|
+
object.cached_at = 15.minutes.ago
|
61
|
+
object
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class HttpCacheTest < Test::Unit::TestCase
|
66
|
+
def setup
|
67
|
+
reset!
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_last_modified_http_cache
|
71
|
+
last_modified_http_cache(:show, 1.hour.ago, 3.hours.ago)
|
72
|
+
end
|
73
|
+
|
74
|
+
def test_last_modified_http_cache_with_proc
|
75
|
+
last_modified_http_cache(:edit, 15.minutes.ago, 45.minutes.ago)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_last_modified_http_cache_with_array
|
79
|
+
last_modified_http_cache(:destroy, 15.minutes.ago, 45.minutes.ago)
|
80
|
+
end
|
81
|
+
|
82
|
+
def test_last_modified_http_cache_with_resources
|
83
|
+
last_modified_http_cache(:resources, 15.minutes.ago, 45.minutes.ago)
|
84
|
+
end
|
85
|
+
|
86
|
+
def test_last_modified_http_cache_with_resources_with_method
|
87
|
+
last_modified_http_cache(:resources_with_method, 10.minutes.ago, 20.minutes.ago)
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_last_modified_http_cache_discards_invalid_input
|
91
|
+
last_modified_http_cache(:invalid, 30.minutes.ago, 90.minutes.ago)
|
92
|
+
end
|
93
|
+
|
94
|
+
def test_http_cache_without_input
|
95
|
+
get :index
|
96
|
+
assert_headers('200 OK', 'private, max-age=0, must-revalidate', 'Last-Modified', Time.utc(0).httpdate)
|
97
|
+
reset!
|
98
|
+
|
99
|
+
@request.env['HTTP_IF_MODIFIED_SINCE'] = 1.hour.ago.httpdate
|
100
|
+
get :index
|
101
|
+
assert_headers('304 Not Modified', 'private, max-age=0, must-revalidate', 'Last-Modified', Time.utc(0).httpdate)
|
102
|
+
reset!
|
103
|
+
|
104
|
+
@request.env['HTTP_IF_MODIFIED_SINCE'] = 3.hours.ago.httpdate
|
105
|
+
get :index
|
106
|
+
assert_headers('304 Not Modified', 'private, max-age=0, must-revalidate', 'Last-Modified', Time.utc(0).httpdate)
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_http_cache_with_conditional_options
|
110
|
+
@request.env['HTTP_ACCEPT'] = 'application/json'
|
111
|
+
get :show
|
112
|
+
assert_nil @response.headers['Last-Modified']
|
113
|
+
reset!
|
114
|
+
|
115
|
+
@request.env['HTTP_ACCEPT'] = 'application/json'
|
116
|
+
@request.env['HTTP_IF_MODIFIED_SINCE'] = 1.hour.ago.httpdate
|
117
|
+
get :show
|
118
|
+
assert_equal '200 OK', @response.headers['Status']
|
119
|
+
end
|
120
|
+
|
121
|
+
def test_http_cache_without_input_with_env_variable
|
122
|
+
ENV['RAILS_APP_VERSION'] = '1.2.3'
|
123
|
+
|
124
|
+
get :index
|
125
|
+
assert_headers('200 OK', 'private, max-age=0, must-revalidate', 'Last-Modified', Time.utc(0).httpdate)
|
126
|
+
reset!
|
127
|
+
|
128
|
+
etag_http_cache(:index, '')
|
129
|
+
end
|
130
|
+
|
131
|
+
def test_etag_http_cache
|
132
|
+
etag_http_cache(:etag, 'ETAG_CACHE')
|
133
|
+
end
|
134
|
+
|
135
|
+
def test_etag_http_cache_with_array
|
136
|
+
etag_http_cache(:etag_array, ['ETAG_CACHE', 12345])
|
137
|
+
end
|
138
|
+
|
139
|
+
def test_etag_http_cache_with_env_variable
|
140
|
+
ENV['RAILS_APP_VERSION'] = '1.2.3'
|
141
|
+
etag_http_cache(:etag, 'ETAG_CACHE')
|
142
|
+
end
|
143
|
+
|
144
|
+
def test_should_not_cache_when_rendering_components
|
145
|
+
set_parent_controller!
|
146
|
+
get :show
|
147
|
+
assert_headers('200 OK', 'no-cache')
|
148
|
+
|
149
|
+
set_parent_controller!
|
150
|
+
@request.env['HTTP_IF_MODIFIED_SINCE'] = 1.hour.ago.httpdate
|
151
|
+
get :show
|
152
|
+
assert_headers('200 OK', 'no-cache')
|
153
|
+
|
154
|
+
set_parent_controller!
|
155
|
+
@request.env['HTTP_IF_MODIFIED_SINCE'] = 3.hours.ago.httpdate
|
156
|
+
get :show
|
157
|
+
assert_headers('200 OK', 'no-cache')
|
158
|
+
end
|
159
|
+
|
160
|
+
private
|
161
|
+
def reset!
|
162
|
+
@request = ActionController::TestRequest.new
|
163
|
+
@response = ActionController::TestResponse.new
|
164
|
+
@controller = HttpCacheTestController.new
|
165
|
+
end
|
166
|
+
|
167
|
+
def set_parent_controller!
|
168
|
+
get :index
|
169
|
+
old_controller = @controller.dup
|
170
|
+
reset!
|
171
|
+
|
172
|
+
@controller.instance_variable_set('@parent_controller', old_controller)
|
173
|
+
end
|
174
|
+
|
175
|
+
def etag_for(etag)
|
176
|
+
%("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(etag))}")
|
177
|
+
end
|
178
|
+
|
179
|
+
def assert_headers(status, control, cache_header=nil, value=nil)
|
180
|
+
assert_equal status, @response.headers['Status']
|
181
|
+
assert_equal control, @response.headers['Cache-Control']
|
182
|
+
|
183
|
+
if cache_header
|
184
|
+
if value
|
185
|
+
assert_equal value, @response.headers[cache_header]
|
186
|
+
else
|
187
|
+
assert @response.headers[cache_header]
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
# Goes through a http cache process:
|
193
|
+
#
|
194
|
+
# 1. Request an action
|
195
|
+
# 2. Get a '200 OK' status
|
196
|
+
# 3. Request the same action with a not expired HTTP_IF_MODIFIED_SINCE
|
197
|
+
# 4. Get a '304 Not Modified' status
|
198
|
+
# 5. Request the same action with an expired HTTP_IF_MODIFIED_SINCE
|
199
|
+
# 6. Get a '200 OK' status
|
200
|
+
#
|
201
|
+
def last_modified_http_cache(action, not_expired_time, expired_time)
|
202
|
+
get action
|
203
|
+
assert_headers('200 OK', 'private, max-age=0, must-revalidate', 'Last-Modified')
|
204
|
+
reset!
|
205
|
+
|
206
|
+
@request.env['HTTP_IF_MODIFIED_SINCE'] = not_expired_time.httpdate
|
207
|
+
get action
|
208
|
+
assert_headers('304 Not Modified', 'private, max-age=0, must-revalidate', 'Last-Modified')
|
209
|
+
reset!
|
210
|
+
|
211
|
+
@request.env['HTTP_IF_MODIFIED_SINCE'] = expired_time.httpdate
|
212
|
+
get action
|
213
|
+
assert_headers('200 OK', 'private, max-age=0, must-revalidate', 'Last-Modified')
|
214
|
+
end
|
215
|
+
|
216
|
+
# Goes through a http cache process:
|
217
|
+
#
|
218
|
+
# 1. Request an action
|
219
|
+
# 2. Get a '200 OK' status
|
220
|
+
# 3. Request the same action with a valid ETAG
|
221
|
+
# 4. Get a '304 Not Modified' status
|
222
|
+
# 5. Request the same action with an invalid IF_NONE_MATCH
|
223
|
+
# 6. Get a '200 OK' status
|
224
|
+
#
|
225
|
+
def etag_http_cache(action, variable)
|
226
|
+
get action
|
227
|
+
assert_headers('200 OK', 'private, max-age=0, must-revalidate', 'ETag', etag_for(variable))
|
228
|
+
reset!
|
229
|
+
|
230
|
+
@request.env['HTTP_IF_NONE_MATCH'] = etag_for(variable)
|
231
|
+
get action
|
232
|
+
assert_headers('304 Not Modified', 'private, max-age=0, must-revalidate', 'ETag', etag_for(variable))
|
233
|
+
reset!
|
234
|
+
|
235
|
+
@request.env['HTTP_IF_NONE_MATCH'] = 'INVALID'
|
236
|
+
get action
|
237
|
+
assert_headers('200 OK', 'private, max-age=0, must-revalidate', 'ETag', etag_for(variable))
|
238
|
+
end
|
239
|
+
end
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: josevalim-easy_http_cache
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "2.0"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- "Jos\xC3\xA9 Valim"
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-11-29 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Allows Rails applications to use HTTP cache specifications easily.
|
17
|
+
email: jose.valim@gmail.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README
|
24
|
+
files:
|
25
|
+
- MIT-LICENSE
|
26
|
+
- README
|
27
|
+
- Rakefile
|
28
|
+
- init.rb
|
29
|
+
- lib/easy_http_cache.rb
|
30
|
+
- test/easy_http_cache_test.rb
|
31
|
+
has_rdoc: true
|
32
|
+
homepage: http://github.com/josevalim/easy_http_cache
|
33
|
+
post_install_message:
|
34
|
+
rdoc_options:
|
35
|
+
- --main
|
36
|
+
- README
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
46
|
+
requirements:
|
47
|
+
- - ">="
|
48
|
+
- !ruby/object:Gem::Version
|
49
|
+
version: "0"
|
50
|
+
version:
|
51
|
+
requirements: []
|
52
|
+
|
53
|
+
rubyforge_project:
|
54
|
+
rubygems_version: 1.2.0
|
55
|
+
signing_key:
|
56
|
+
specification_version: 2
|
57
|
+
summary: Allows Rails applications to use HTTP cache specifications easily.
|
58
|
+
test_files:
|
59
|
+
- test/easy_http_cache_test.rb
|