easy_http_cache 2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +21 -0
- data/README.rdoc +155 -0
- data/Rakefile +37 -0
- data/init.rb +1 -0
- data/lib/easy_http_cache.rb +126 -0
- data/test/easy_http_cache_test.rb +212 -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.rdoc
ADDED
@@ -0,0 +1,155 @@
|
|
1
|
+
== Easy HTTP Cache
|
2
|
+
|
3
|
+
Allows Rails applications to do conditional cache easily and in a DRY way
|
4
|
+
(without messing up your actions):
|
5
|
+
|
6
|
+
class ListsController < ApplicationController
|
7
|
+
http_cache :index, :show, :last_modified => :list
|
8
|
+
|
9
|
+
protected
|
10
|
+
def list
|
11
|
+
@list ||= List.find(params[:id])
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
It uses :last_modified and :etag keys, that besides Time, String or resources
|
16
|
+
accepts Proc, Method and Symbol that are evaluated within the current controller.
|
17
|
+
|
18
|
+
Read more about each option (more examples at the end of this page):
|
19
|
+
|
20
|
+
:last_modified
|
21
|
+
Used to manipulate Last-Modified header. You can pass any object that responds
|
22
|
+
to :updated_at, :updated_on or :to_time. If you pass a Proc or Method or Symbol,
|
23
|
+
they will be evaluated within the current controller first.
|
24
|
+
|
25
|
+
Finally, if you pass an array, it will get the most recent time to be used.
|
26
|
+
|
27
|
+
:etag
|
28
|
+
Used to manipulate Etag header. The Etag is generated as memcached keys are
|
29
|
+
generated, i.e. calling to_param in the object and then MD5 is applied.
|
30
|
+
|
31
|
+
If you pass a Proc or Method or Symbols, they will be also evaluated within the
|
32
|
+
current controller first.
|
33
|
+
|
34
|
+
:if
|
35
|
+
Only perform http cache if it returns true.
|
36
|
+
|
37
|
+
:unless
|
38
|
+
Only perform http cache if it returns false.
|
39
|
+
|
40
|
+
:method
|
41
|
+
If in :last_modified you want to pass a object that doesn't respond to updated_at,
|
42
|
+
updated_on or to_time, you can specify the method that will be called in this object.
|
43
|
+
|
44
|
+
== Install
|
45
|
+
|
46
|
+
Install Easy HTTP Cache is available on gemcutter, so just execute:
|
47
|
+
|
48
|
+
sudo gem install easy_http_cache
|
49
|
+
|
50
|
+
And add it to your environment.
|
51
|
+
|
52
|
+
== Environment Variables
|
53
|
+
|
54
|
+
As in memcached, you can set ENV['RAILS_CACHE_ID'] or ENV['RAILS_APP_VERSION'] variables
|
55
|
+
to change the Etag that will be generated. This means you can control the cache by setting
|
56
|
+
a timestamp or a version number in ENV['RAILS_APP_VERSION'] everytime you deploy.
|
57
|
+
|
58
|
+
== Examples
|
59
|
+
|
60
|
+
The example below will cache your actions and it will never expire:
|
61
|
+
|
62
|
+
class ListsController < ApplicationController
|
63
|
+
http_cache :index, :show
|
64
|
+
end
|
65
|
+
|
66
|
+
If you do not want to cache when you are showing a flash message (and you
|
67
|
+
usually want that), you can simply do:
|
68
|
+
|
69
|
+
class ListsController < ApplicationController
|
70
|
+
http_cache :index, :show, :if => Proc.new { |c| c.send(:flash).empty? }
|
71
|
+
end
|
72
|
+
|
73
|
+
And if you do not want to cache JSON requests:
|
74
|
+
|
75
|
+
class ListsController < ApplicationController
|
76
|
+
http_cache :index, :show, :unless => Proc.new { |c| c.request.format.json? }
|
77
|
+
end
|
78
|
+
|
79
|
+
Or if you want to expire all http cache before 2008, just do:
|
80
|
+
|
81
|
+
class ListsController < ApplicationController
|
82
|
+
http_cache :index, :show, :last_modified => Time.utc(2008)
|
83
|
+
end
|
84
|
+
|
85
|
+
If you want to cache a list and automatically expire the cache when it changes,
|
86
|
+
just do (it will check updated_at and updated_on on the @list object):
|
87
|
+
|
88
|
+
class ListsController < ApplicationController
|
89
|
+
http_cache :index, :show, :last_modified => :list
|
90
|
+
|
91
|
+
protected
|
92
|
+
def list
|
93
|
+
@list ||= List.find(params[:id])
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
You can also set :etag header (it will generate an etag calling to_param
|
98
|
+
in the object and applying MD5):
|
99
|
+
|
100
|
+
class ListsController < ApplicationController
|
101
|
+
http_cache :index, :show, :etag => :list
|
102
|
+
|
103
|
+
protected
|
104
|
+
def list
|
105
|
+
@list ||= List.find(params[:id])
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
If you are using a resource that doesn't respond to updated_at or updated_on,
|
110
|
+
you can pass a method as parameter that will be called in your resources:
|
111
|
+
|
112
|
+
class ListsController < ApplicationController
|
113
|
+
http_cache :index, :show, :last_modified => :list, :method => :cached_at
|
114
|
+
|
115
|
+
protected
|
116
|
+
def list
|
117
|
+
@list ||= List.find(params[:id])
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
The sample below will call @list.cached_at to generate Last-Modified header.
|
122
|
+
Finally, you can also pass an array at :last_modified as below:
|
123
|
+
|
124
|
+
class ItemsController < ApplicationController
|
125
|
+
http_cache :index, :show,
|
126
|
+
:last_modified => [ :list, :item ]
|
127
|
+
|
128
|
+
protected
|
129
|
+
def list
|
130
|
+
@list ||= List.find(params[:list_id])
|
131
|
+
end
|
132
|
+
|
133
|
+
def item
|
134
|
+
@item ||= list.items.find(params[:id])
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
This will check which one is the most recent to compare with the
|
139
|
+
"Last-Modified" field sent by the client.
|
140
|
+
|
141
|
+
== What if?
|
142
|
+
|
143
|
+
At this point (or at some point), you will ask what happens if you use :etag
|
144
|
+
and :last_modified at the same time.
|
145
|
+
|
146
|
+
Well, the specification says that if both are sent by the client, both have
|
147
|
+
to be valid for the cache not be considered stale. This subject was already brought
|
148
|
+
to Rails Core group and this is also how Rails' current implementation behaves.
|
149
|
+
|
150
|
+
== Bugs and Feedback
|
151
|
+
|
152
|
+
If you find any issues, please use Github issues tracker.
|
153
|
+
|
154
|
+
Copyright (c) 2009 José Valim
|
155
|
+
http://blog.plataformatec.com.br/
|
data/Rakefile
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Run tests for Easy HTTP Cache.'
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
7
|
+
t.pattern = 'test/**/*_test.rb'
|
8
|
+
t.verbose = true
|
9
|
+
end
|
10
|
+
|
11
|
+
desc 'Generate documentation for Easy HTTP Cache.'
|
12
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
13
|
+
rdoc.rdoc_dir = 'rdoc'
|
14
|
+
rdoc.title = 'Easy HTTP Cache'
|
15
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
16
|
+
rdoc.rdoc_files.include('README')
|
17
|
+
rdoc.rdoc_files.include('MIT-LICENSE')
|
18
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
19
|
+
end
|
20
|
+
|
21
|
+
begin
|
22
|
+
require 'jeweler'
|
23
|
+
Jeweler::Tasks.new do |s|
|
24
|
+
s.name = "easy_http_cache"
|
25
|
+
s.version = "2.2"
|
26
|
+
s.summary = "Allows Rails applications to use HTTP cache specifications easily."
|
27
|
+
s.email = "contact@plataformatec.com.br"
|
28
|
+
s.homepage = "http://github.com/josevalim/easy_http_cache"
|
29
|
+
s.description = "Allows Rails applications to use HTTP cache specifications easily."
|
30
|
+
s.authors = ['José Valim']
|
31
|
+
s.files = FileList["[A-Z]*", "lib/**/*", "init.rb"]
|
32
|
+
end
|
33
|
+
|
34
|
+
Jeweler::GemcutterTasks.new
|
35
|
+
rescue LoadError
|
36
|
+
puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install jeweler"
|
37
|
+
end
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'easy_http_cache'
|
@@ -0,0 +1,126 @@
|
|
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
|
+
return unless flash.empty? && controller.request.get?
|
37
|
+
|
38
|
+
last_modified = get_last_modified(controller)
|
39
|
+
controller.response.last_modified = last_modified if last_modified
|
40
|
+
|
41
|
+
processed_etags = get_processed_etags(controller)
|
42
|
+
controller.response.etag = processed_etags if processed_etags
|
43
|
+
|
44
|
+
if controller.request.fresh?(controller.response)
|
45
|
+
controller.send(:head, :not_modified)
|
46
|
+
return false
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
protected
|
51
|
+
# If :etag is an array, it processes all Methods, Procs and Symbols
|
52
|
+
# and return them as array. If it's an object, we only evaluate it.
|
53
|
+
#
|
54
|
+
def get_processed_etags(controller)
|
55
|
+
if @options[:etag].is_a?(Array)
|
56
|
+
@options[:etag].collect do |item|
|
57
|
+
evaluate_method(item, controller)
|
58
|
+
end
|
59
|
+
elsif @options[:etag]
|
60
|
+
evaluate_method(@options[:etag], controller)
|
61
|
+
else
|
62
|
+
nil
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# We perform Last-Modified HTTP Cache when the option :last_modified is sent
|
67
|
+
# or no other cache mechanism is set (then we set a very old timestamp).
|
68
|
+
#
|
69
|
+
def get_last_modified(controller)
|
70
|
+
# Then, if @options[:last_modified] is not empty, we run through the array
|
71
|
+
# processing all objects (if needed) and return the latest one to be used.
|
72
|
+
#
|
73
|
+
if !@options[:last_modified].empty?
|
74
|
+
@options[:last_modified].collect do |item|
|
75
|
+
evaluate_time(item, controller)
|
76
|
+
end.compact.sort.last
|
77
|
+
elsif @options[:etag].blank?
|
78
|
+
Time.utc(0)
|
79
|
+
else
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def evaluate_method(method, controller)
|
85
|
+
case method
|
86
|
+
when Symbol
|
87
|
+
controller.__send__(method)
|
88
|
+
when Proc, Method
|
89
|
+
method.call(controller)
|
90
|
+
else
|
91
|
+
method
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# Evaluate the objects sent and return time objects
|
96
|
+
#
|
97
|
+
# It process Symbols, String, Proc and Methods, get its results and then
|
98
|
+
# call :to_time, :updated_at, :updated_on on it.
|
99
|
+
#
|
100
|
+
# If the parameter :method is sent, it will try to call it on the object before
|
101
|
+
# calling :to_time, :updated_at, :updated_on.
|
102
|
+
#
|
103
|
+
def evaluate_time(method, controller)
|
104
|
+
return nil unless method
|
105
|
+
time = evaluate_method(method, controller)
|
106
|
+
|
107
|
+
time = time.__send__(@options[:method]) if @options[:method].is_a?(Symbol) && time.respond_to?(@options[:method])
|
108
|
+
|
109
|
+
if time.respond_to?(:to_time)
|
110
|
+
time.to_time.utc
|
111
|
+
elsif time.respond_to?(:updated_at)
|
112
|
+
time.updated_at.utc
|
113
|
+
elsif time.respond_to?(:updated_on)
|
114
|
+
time.updated_on.utc
|
115
|
+
else
|
116
|
+
nil
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
end
|
121
|
+
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
ActionController::Base.send :include, ActionController::Caching::HttpCache
|
@@ -0,0 +1,212 @@
|
|
1
|
+
# Those lines are plugin test settings
|
2
|
+
require 'test/unit'
|
3
|
+
require 'rubygems'
|
4
|
+
require 'ostruct'
|
5
|
+
|
6
|
+
ENV["RAILS_ENV"] = "test"
|
7
|
+
|
8
|
+
require 'active_support'
|
9
|
+
require 'action_controller'
|
10
|
+
require 'action_controller/test_case'
|
11
|
+
require 'action_controller/test_process'
|
12
|
+
|
13
|
+
require File.dirname(__FILE__) + '/../lib/easy_http_cache.rb'
|
14
|
+
|
15
|
+
ActionController::Base.perform_caching = true
|
16
|
+
ActionController::Routing::Routes.draw do |map|
|
17
|
+
map.connect ':controller/:action/:id'
|
18
|
+
end
|
19
|
+
|
20
|
+
class HttpCacheTestController < ActionController::Base
|
21
|
+
http_cache :index
|
22
|
+
http_cache :show, :last_modified => 2.hours.ago, :if => Proc.new { |c| !c.request.format.json? }
|
23
|
+
http_cache :edit, :last_modified => Proc.new{ 30.minutes.ago }
|
24
|
+
http_cache :destroy, :last_modified => [2.hours.ago, Proc.new{|c| 30.minutes.ago }]
|
25
|
+
http_cache :invalid, :last_modified => [1.hours.ago, false]
|
26
|
+
|
27
|
+
http_cache :etag, :etag => 'ETAG_CACHE'
|
28
|
+
http_cache :etag_array, :etag => [ 'ETAG_CACHE', :resource ]
|
29
|
+
http_cache :resources, :last_modified => [:resource, :list, :object]
|
30
|
+
http_cache :resources_with_method, :last_modified => [:resource, :list, :object], :method => :cached_at
|
31
|
+
|
32
|
+
def index
|
33
|
+
render :text => '200 OK', :status => 200
|
34
|
+
end
|
35
|
+
|
36
|
+
alias_method :show, :index
|
37
|
+
alias_method :edit, :index
|
38
|
+
alias_method :destroy, :index
|
39
|
+
alias_method :invalid, :index
|
40
|
+
alias_method :etag, :index
|
41
|
+
alias_method :etag_array, :index
|
42
|
+
alias_method :resources, :index
|
43
|
+
alias_method :resources_with_method, :index
|
44
|
+
|
45
|
+
protected
|
46
|
+
|
47
|
+
def resource
|
48
|
+
resource = OpenStruct.new
|
49
|
+
resource.instance_eval do
|
50
|
+
def to_param
|
51
|
+
12345
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
resource.updated_at = 2.hours.ago
|
56
|
+
resource
|
57
|
+
end
|
58
|
+
|
59
|
+
def list
|
60
|
+
list = OpenStruct.new
|
61
|
+
list.updated_on = 30.minutes.ago
|
62
|
+
list
|
63
|
+
end
|
64
|
+
|
65
|
+
def object
|
66
|
+
object = OpenStruct.new
|
67
|
+
object.cached_at = 15.minutes.ago
|
68
|
+
object
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class HttpCacheTest < ActionController::TestCase
|
73
|
+
def setup
|
74
|
+
reset!
|
75
|
+
end
|
76
|
+
|
77
|
+
def test_last_modified_http_cache
|
78
|
+
last_modified_http_cache(:show, 1.hour.ago, 3.hours.ago)
|
79
|
+
end
|
80
|
+
|
81
|
+
def test_last_modified_http_cache_with_proc
|
82
|
+
last_modified_http_cache(:edit, 15.minutes.ago, 45.minutes.ago)
|
83
|
+
end
|
84
|
+
|
85
|
+
def test_last_modified_http_cache_with_array
|
86
|
+
last_modified_http_cache(:destroy, 15.minutes.ago, 45.minutes.ago)
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_last_modified_http_cache_with_resources
|
90
|
+
last_modified_http_cache(:resources, 15.minutes.ago, 45.minutes.ago)
|
91
|
+
end
|
92
|
+
|
93
|
+
def test_last_modified_http_cache_with_resources_with_method
|
94
|
+
last_modified_http_cache(:resources_with_method, 10.minutes.ago, 20.minutes.ago)
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_last_modified_http_cache_discards_invalid_input
|
98
|
+
last_modified_http_cache(:invalid, 30.minutes.ago, 90.minutes.ago)
|
99
|
+
end
|
100
|
+
|
101
|
+
def test_http_cache_without_input
|
102
|
+
get :index
|
103
|
+
assert_headers('200 OK', 'private, max-age=0, must-revalidate', 'Last-Modified', Time.utc(0).httpdate)
|
104
|
+
reset!
|
105
|
+
|
106
|
+
@request.env['HTTP_IF_MODIFIED_SINCE'] = 1.hour.ago.httpdate
|
107
|
+
get :index
|
108
|
+
assert_headers('304 Not Modified', 'private, max-age=0, must-revalidate', 'Last-Modified', Time.utc(0).httpdate)
|
109
|
+
reset!
|
110
|
+
|
111
|
+
@request.env['HTTP_IF_MODIFIED_SINCE'] = 3.hours.ago.httpdate
|
112
|
+
get :index
|
113
|
+
assert_headers('304 Not Modified', 'private, max-age=0, must-revalidate', 'Last-Modified', Time.utc(0).httpdate)
|
114
|
+
end
|
115
|
+
|
116
|
+
def test_http_cache_with_conditional_options
|
117
|
+
@request.env['HTTP_ACCEPT'] = 'application/json'
|
118
|
+
get :show
|
119
|
+
assert_nil @response.headers['Last-Modified']
|
120
|
+
reset!
|
121
|
+
|
122
|
+
@request.env['HTTP_ACCEPT'] = 'application/json'
|
123
|
+
@request.env['HTTP_IF_MODIFIED_SINCE'] = 1.hour.ago.httpdate
|
124
|
+
get :show
|
125
|
+
assert_equal '200 OK', @response.status
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_etag_http_cache
|
129
|
+
etag_http_cache(:etag, 'ETAG_CACHE')
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_etag_http_cache_with_array
|
133
|
+
etag_http_cache(:etag_array, ['ETAG_CACHE', 12345])
|
134
|
+
end
|
135
|
+
|
136
|
+
def test_etag_http_cache_with_env_variable
|
137
|
+
ENV['RAILS_APP_VERSION'] = '1.2.3'
|
138
|
+
etag_http_cache(:etag, 'ETAG_CACHE')
|
139
|
+
end
|
140
|
+
|
141
|
+
private
|
142
|
+
def reset!
|
143
|
+
@request = ActionController::TestRequest.new
|
144
|
+
@response = ActionController::TestResponse.new
|
145
|
+
@controller = HttpCacheTestController.new
|
146
|
+
end
|
147
|
+
|
148
|
+
def etag_for(etag)
|
149
|
+
%("#{Digest::MD5.hexdigest(ActiveSupport::Cache.expand_cache_key(etag))}")
|
150
|
+
end
|
151
|
+
|
152
|
+
def assert_headers(status, control, cache_header=nil, value=nil)
|
153
|
+
assert_equal status, @response.status
|
154
|
+
assert_equal control, @response.headers['Cache-Control']
|
155
|
+
|
156
|
+
if cache_header
|
157
|
+
if value
|
158
|
+
assert_equal value, @response.headers[cache_header]
|
159
|
+
else
|
160
|
+
assert @response.headers[cache_header]
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
# Goes through a http cache process:
|
166
|
+
#
|
167
|
+
# 1. Request an action
|
168
|
+
# 2. Get a '200 OK' status
|
169
|
+
# 3. Request the same action with a not expired HTTP_IF_MODIFIED_SINCE
|
170
|
+
# 4. Get a '304 Not Modified' status
|
171
|
+
# 5. Request the same action with an expired HTTP_IF_MODIFIED_SINCE
|
172
|
+
# 6. Get a '200 OK' status
|
173
|
+
#
|
174
|
+
def last_modified_http_cache(action, not_expired_time, expired_time)
|
175
|
+
get action
|
176
|
+
assert_headers('200 OK', 'private, max-age=0, must-revalidate', 'Last-Modified')
|
177
|
+
reset!
|
178
|
+
|
179
|
+
@request.env['HTTP_IF_MODIFIED_SINCE'] = not_expired_time.httpdate
|
180
|
+
get action
|
181
|
+
assert_headers('304 Not Modified', 'private, max-age=0, must-revalidate', 'Last-Modified')
|
182
|
+
reset!
|
183
|
+
|
184
|
+
@request.env['HTTP_IF_MODIFIED_SINCE'] = expired_time.httpdate
|
185
|
+
get action
|
186
|
+
assert_headers('200 OK', 'private, max-age=0, must-revalidate', 'Last-Modified')
|
187
|
+
end
|
188
|
+
|
189
|
+
# Goes through a http cache process:
|
190
|
+
#
|
191
|
+
# 1. Request an action
|
192
|
+
# 2. Get a '200 OK' status
|
193
|
+
# 3. Request the same action with a valid ETAG
|
194
|
+
# 4. Get a '304 Not Modified' status
|
195
|
+
# 5. Request the same action with an invalid IF_NONE_MATCH
|
196
|
+
# 6. Get a '200 OK' status
|
197
|
+
#
|
198
|
+
def etag_http_cache(action, variable)
|
199
|
+
get action
|
200
|
+
assert_headers('200 OK', 'private, max-age=0, must-revalidate', 'ETag', etag_for(variable))
|
201
|
+
reset!
|
202
|
+
|
203
|
+
@request.env['HTTP_IF_NONE_MATCH'] = etag_for(variable)
|
204
|
+
get action
|
205
|
+
assert_headers('304 Not Modified', 'private, max-age=0, must-revalidate', 'ETag', etag_for(variable))
|
206
|
+
reset!
|
207
|
+
|
208
|
+
@request.env['HTTP_IF_NONE_MATCH'] = 'INVALID'
|
209
|
+
get action
|
210
|
+
assert_headers('200 OK', 'private, max-age=0, must-revalidate', 'ETag', etag_for(variable))
|
211
|
+
end
|
212
|
+
end
|
metadata
ADDED
@@ -0,0 +1,59 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: easy_http_cache
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: "2.2"
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- "Jos\xC3\xA9 Valim"
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-12-23 00:00:00 +01:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Allows Rails applications to use HTTP cache specifications easily.
|
17
|
+
email: contact@plataformatec.com.br
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.rdoc
|
24
|
+
files:
|
25
|
+
- MIT-LICENSE
|
26
|
+
- README.rdoc
|
27
|
+
- Rakefile
|
28
|
+
- init.rb
|
29
|
+
- lib/easy_http_cache.rb
|
30
|
+
has_rdoc: true
|
31
|
+
homepage: http://github.com/josevalim/easy_http_cache
|
32
|
+
licenses: []
|
33
|
+
|
34
|
+
post_install_message:
|
35
|
+
rdoc_options:
|
36
|
+
- --charset=UTF-8
|
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.3.5
|
55
|
+
signing_key:
|
56
|
+
specification_version: 3
|
57
|
+
summary: Allows Rails applications to use HTTP cache specifications easily.
|
58
|
+
test_files:
|
59
|
+
- test/easy_http_cache_test.rb
|