net-http-not_modified_cache 0.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.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +12 -0
- data/lib/net-http-not_modified_cache.rb +127 -0
- data/spec/net-http-not_modified_cache_spec.rb +253 -0
- data/spec/spec_helper.rb +19 -0
- metadata +85 -0
data/MIT-LICENSE
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
Copyright (c) 2011 Einstein Industries
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
|
+
a copy of this software and associated documentation files (the
|
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
9
|
+
the following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice shall be
|
|
12
|
+
included in all copies or substantial portions of the Software.
|
|
13
|
+
|
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
= net-http-not_modified_cache - {<img src="https://secure.travis-ci.org/einstein/net-http-not_modified_cache.png" />}[http://travis-ci.org/einstein/net-http-not_modified_cache]
|
|
2
|
+
|
|
3
|
+
Caches responses for requests that respond with "304 - Not Modified" and adds "if-modified-since" or "if-none-match" headers to future requests
|
|
4
|
+
|
|
5
|
+
== Installation
|
|
6
|
+
|
|
7
|
+
gem install net-http-not_modified_cache
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
== Usage
|
|
11
|
+
|
|
12
|
+
Instructions
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
require 'active_support/cache'
|
|
2
|
+
require 'net/http'
|
|
3
|
+
require 'rack/utils'
|
|
4
|
+
require 'time'
|
|
5
|
+
|
|
6
|
+
module Net
|
|
7
|
+
class HTTP
|
|
8
|
+
module NotModifiedCache
|
|
9
|
+
def cache_entry(response)
|
|
10
|
+
last_modified_at = Time.parse(response['last-modified'] || response['date']) rescue Time.now
|
|
11
|
+
Entry.new(response.body, response['etag'], last_modified_at)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def cache_key(request)
|
|
15
|
+
[address, request.path].join
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def cache_request(request)
|
|
19
|
+
cache_request!(request) if cacheable_request?(request)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def cache_request!(request)
|
|
23
|
+
key = cache_key(request)
|
|
24
|
+
unless request['if-modified-since'] || request['if-none-match']
|
|
25
|
+
if entry = NotModifiedCache.store.read(key)
|
|
26
|
+
request['if-modified-since'] = entry.last_modified_at.httpdate
|
|
27
|
+
request['if-none-match'] = entry.etag
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
key
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def cacheable_request?(request)
|
|
34
|
+
NotModifiedCache.enabled? && request.is_a?(Get)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def cache_response(response, key)
|
|
38
|
+
cache_response!(response, key) if cacheable_response?(response)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def cache_response!(response, key)
|
|
42
|
+
if response.code == '200'
|
|
43
|
+
NotModifiedCache.store.write(key, cache_entry(response))
|
|
44
|
+
elsif response.code == '304' && entry = NotModifiedCache.store.read(key)
|
|
45
|
+
response.instance_variable_set('@body', entry.body)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def cacheable_response?(response)
|
|
50
|
+
NotModifiedCache.enabled? && %w(200 304).include?(response.code)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def request_with_not_modified_cache(request, body = nil, &block)
|
|
54
|
+
key = cache_request(request)
|
|
55
|
+
response = request_without_not_modified_cache(request, body, &block)
|
|
56
|
+
cache_response(response, key) if key
|
|
57
|
+
response
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
class << self
|
|
61
|
+
attr_writer :root, :store
|
|
62
|
+
|
|
63
|
+
def disable!
|
|
64
|
+
@enabled = false
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def enable!
|
|
68
|
+
@enabled = true
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def enabled?
|
|
72
|
+
@enabled
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def included(base)
|
|
76
|
+
base.class_eval do
|
|
77
|
+
alias_method :request_without_not_modified_cache, :request
|
|
78
|
+
alias_method :request, :request_with_not_modified_cache
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def root
|
|
83
|
+
@root ||= '/tmp/net-http-not_modified_cache'
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def store
|
|
87
|
+
@store ||= ActiveSupport::Cache.lookup_store(:file_store, root, :compress => true)
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def version
|
|
91
|
+
@version ||= '0.0.0'
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
def while_disabled(&block)
|
|
95
|
+
while_enabled_is(false, &block)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def while_enabled(&block)
|
|
99
|
+
while_enabled_is(true, &block)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
def while_enabled_is(boolean)
|
|
103
|
+
old_enabled = @enabled
|
|
104
|
+
@enabled = boolean
|
|
105
|
+
yield
|
|
106
|
+
ensure
|
|
107
|
+
@enabled = old_enabled
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def with_store(store)
|
|
111
|
+
old_store = self.store
|
|
112
|
+
self.store = store
|
|
113
|
+
yield
|
|
114
|
+
ensure
|
|
115
|
+
self.store = old_store
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
enable!
|
|
120
|
+
|
|
121
|
+
class Entry < Struct.new(:body, :etag, :last_modified_at)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
Net::HTTP.send(:include, Net::HTTP::NotModifiedCache)
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
require File.expand_path('../spec_helper', __FILE__)
|
|
2
|
+
|
|
3
|
+
describe Net::HTTP::NotModifiedCache do
|
|
4
|
+
let(:nmc) { Net::HTTP::NotModifiedCache }
|
|
5
|
+
|
|
6
|
+
context 'when included in Net::HTTP' do
|
|
7
|
+
subject { Net::HTTP.new(url.host) }
|
|
8
|
+
|
|
9
|
+
let(:found) do
|
|
10
|
+
instance = response.dup
|
|
11
|
+
instance.instance_variable_set('@body', 'test')
|
|
12
|
+
instance.stub!(:code).and_return('200')
|
|
13
|
+
instance
|
|
14
|
+
end
|
|
15
|
+
let(:not_modified) do
|
|
16
|
+
instance = response.dup
|
|
17
|
+
instance.stub!(:code).and_return('304')
|
|
18
|
+
instance
|
|
19
|
+
end
|
|
20
|
+
let(:response) do
|
|
21
|
+
instance = Net::HTTPResponse.allocate
|
|
22
|
+
instance.instance_variable_set('@body', '')
|
|
23
|
+
instance.instance_variable_set('@header', {})
|
|
24
|
+
instance.instance_variable_set('@read', true)
|
|
25
|
+
instance
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
let(:get) { Net::HTTP::Get.allocate }
|
|
29
|
+
let(:post) { Net::HTTP::Post.allocate }
|
|
30
|
+
let(:request) { Net::HTTP::Get.new(url.path) }
|
|
31
|
+
|
|
32
|
+
let(:key) { subject.cache_key(get) }
|
|
33
|
+
let(:url) { URI.parse('http://fakeweb.test/index.html') }
|
|
34
|
+
|
|
35
|
+
context '#cache_entry' do
|
|
36
|
+
it 'should return an Entry instance' do
|
|
37
|
+
subject.cache_entry(found).should be_an_instance_of(nmc::Entry)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
it 'should set body to the response body' do
|
|
41
|
+
subject.cache_entry(found).body.should == found.body
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
it 'should set etag header if it exists' do
|
|
45
|
+
subject.cache_entry(found).etag.should be_nil
|
|
46
|
+
|
|
47
|
+
found['etag'] = 'test'
|
|
48
|
+
subject.cache_entry(found).etag.should == 'test'
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it 'should set last_modified_at to last-modified header if it exists' do
|
|
52
|
+
time = Time.now - 100
|
|
53
|
+
found['last-modified'] = time.httpdate
|
|
54
|
+
found['date'] = (time - 100).httpdate
|
|
55
|
+
subject.cache_entry(found).last_modified_at.httpdate.should == time.httpdate
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
it 'should set last_modified_at to date header if it exists and last-modified header is not specified' do
|
|
59
|
+
time = Time.now - 200
|
|
60
|
+
found['date'] = time.httpdate
|
|
61
|
+
subject.cache_entry(found).last_modified_at.httpdate.should == time.httpdate
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'should set last_modified_at to Time.now if both last-modified and date headers are not specified' do
|
|
65
|
+
Timecop.freeze(Time.now - 500) { subject.cache_entry(found).last_modified_at.httpdate.should == Time.now.httpdate }
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
context '#cache_key' do
|
|
70
|
+
it 'should join the address and request path' do
|
|
71
|
+
subject.cache_key(request).should == [subject.address, request.path].join
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
context '#cache_request' do
|
|
76
|
+
it 'should only call #cache_request! if request is cacheable' do
|
|
77
|
+
subject.should_receive(:cache_request!)
|
|
78
|
+
subject.cache_request(get)
|
|
79
|
+
|
|
80
|
+
subject.should_not_receive(:cache_request!)
|
|
81
|
+
subject.cache_request(post)
|
|
82
|
+
end
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
context '#cache_request!' do
|
|
86
|
+
it 'should not add if-modified-since or if-none-match header if either already exists' do
|
|
87
|
+
nmc.store.should_not_receive(:read)
|
|
88
|
+
|
|
89
|
+
request['if-none-match'] = 'etag'
|
|
90
|
+
subject.cache_request!(request)
|
|
91
|
+
|
|
92
|
+
request['if-none-match'] = nil
|
|
93
|
+
request['if-modified-since'] = Time.now.httpdate
|
|
94
|
+
subject.cache_request!(request)
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
it 'should search for cached entry' do
|
|
98
|
+
nmc.store.should_receive(:read)
|
|
99
|
+
subject.cache_request!(request)
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
it 'should set etag and if-modified-since headers' do
|
|
103
|
+
entry = nmc::Entry.new('testing', 'test', Time.now)
|
|
104
|
+
nmc.store.should_receive(:read).with(subject.cache_key(request)).and_return(entry)
|
|
105
|
+
subject.cache_request!(request)
|
|
106
|
+
|
|
107
|
+
request['if-none-match'].should == entry.etag
|
|
108
|
+
request['if-modified-since'].should == entry.last_modified_at.httpdate
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
context '#cacheable_request?' do
|
|
113
|
+
it 'should only return true if enabled' do
|
|
114
|
+
nmc.disable!
|
|
115
|
+
subject.cacheable_request?(get).should be_false
|
|
116
|
+
nmc.enable!
|
|
117
|
+
subject.cacheable_request?(get).should be_true
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
it 'should only return true if request is a Net::HTTP::Get' do
|
|
121
|
+
subject.cacheable_request?(get).should be_true
|
|
122
|
+
subject.cacheable_request?(post).should be_false
|
|
123
|
+
end
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
context '#cache_response' do
|
|
127
|
+
it 'should only be called if request is cacheable' do
|
|
128
|
+
subject.stub(:request_without_not_modified_cache)
|
|
129
|
+
subject.should_not_receive(:cache_response)
|
|
130
|
+
subject.request(post)
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
it 'should only call #cache_response! if response is cacheable' do
|
|
134
|
+
subject.should_receive(:cache_response!)
|
|
135
|
+
subject.cache_response(found, 'test')
|
|
136
|
+
|
|
137
|
+
subject.should_not_receive(:cache_response!)
|
|
138
|
+
subject.cache_response(response, 'test')
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
context '#cache_response!' do
|
|
143
|
+
it 'should cache entry if response is a 200' do
|
|
144
|
+
nmc.store.should_receive(:write)
|
|
145
|
+
subject.cache_response!(found, key)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it 'should set cached body if response is a 304' do
|
|
149
|
+
entry = nmc::Entry.new('testing', nil, Time.now)
|
|
150
|
+
nmc.store.should_receive(:read).with(key).and_return(entry)
|
|
151
|
+
subject.cache_response!(not_modified, key)
|
|
152
|
+
not_modified.body.should == entry.body
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
context '#cacheable_response?' do
|
|
157
|
+
it 'should only return true if enabled' do
|
|
158
|
+
nmc.disable!
|
|
159
|
+
subject.cacheable_response?(found).should be_false
|
|
160
|
+
nmc.enable!
|
|
161
|
+
subject.cacheable_response?(found).should be_true
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
it 'should only return true if response code is a 200 or 304' do
|
|
165
|
+
subject.cacheable_response?(response).should be_false
|
|
166
|
+
subject.cacheable_response?(found).should be_true
|
|
167
|
+
subject.cacheable_response?(not_modified).should be_true
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
context '#request_with_not_modified_cache' do
|
|
172
|
+
it 'should run fakeweb tests'
|
|
173
|
+
end
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
context '.enabled?' do
|
|
177
|
+
it 'should be toggleable and true by default' do
|
|
178
|
+
subject.enabled?.should be_true
|
|
179
|
+
subject.disable!
|
|
180
|
+
subject.enabled?.should be_false
|
|
181
|
+
subject.enable!
|
|
182
|
+
subject.enabled?.should be_true
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
context '.root' do
|
|
187
|
+
it 'should be /tmp/net-http-not_modified_cache by default' do
|
|
188
|
+
subject.root.should == '/tmp/net-http-not_modified_cache'
|
|
189
|
+
end
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
context '.store' do
|
|
193
|
+
it 'should be an ActiveSupport::Cache::FileStore by default' do
|
|
194
|
+
subject.store.should be_an_instance_of(ActiveSupport::Cache::FileStore)
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
it 'should use root as cache root' do
|
|
198
|
+
subject.store.cache_path.should == subject.root
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
context '.version' do
|
|
203
|
+
it 'should return a version string' do
|
|
204
|
+
subject.version.should match(/^\d+\.\d+\.\d+(\.[^\.]+)?$/)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
context '.while_disabled' do
|
|
209
|
+
it 'should set enabled? to false for the duration of the block' do
|
|
210
|
+
subject.while_disabled { subject.enabled?.should be_false }
|
|
211
|
+
subject.enabled?.should be_true
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
context '.while_enabled' do
|
|
216
|
+
it 'should set enabled? to true for the duration of the block' do
|
|
217
|
+
subject.disable!
|
|
218
|
+
subject.while_enabled { subject.enabled?.should be_true }
|
|
219
|
+
subject.enabled?.should be_false
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
context '.while_enabled_is' do
|
|
224
|
+
it 'should set enabled? and return it back to its previous value after evaluating the block' do
|
|
225
|
+
subject.while_enabled_is(false) { subject.enabled?.should be_false }
|
|
226
|
+
subject.enabled?.should be_true
|
|
227
|
+
|
|
228
|
+
subject.disable!
|
|
229
|
+
subject.while_enabled_is(true) { subject.enabled?.should be_true }
|
|
230
|
+
subject.enabled?.should be_false
|
|
231
|
+
end
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
context '.with_store' do
|
|
235
|
+
let(:store) { ActiveSupport::Cache.lookup_store(:file_store, '/tmp/test') }
|
|
236
|
+
|
|
237
|
+
it 'should switch lookup store when yielding' do
|
|
238
|
+
current_store = subject.store
|
|
239
|
+
subject.with_store(store) { subject.store.should_not == current_store }
|
|
240
|
+
subject.store.should == current_store
|
|
241
|
+
end
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
context '::Entry' do
|
|
245
|
+
subject { nmc::Entry.new }
|
|
246
|
+
|
|
247
|
+
it 'should respond to :body, :etag, and :last_modified_at' do
|
|
248
|
+
subject.should respond_to(:body)
|
|
249
|
+
subject.should respond_to(:etag)
|
|
250
|
+
subject.should respond_to(:last_modified_at)
|
|
251
|
+
end
|
|
252
|
+
end
|
|
253
|
+
end
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'bundler/setup'
|
|
3
|
+
|
|
4
|
+
# [TODO] why do specs fail without this?
|
|
5
|
+
# [TODO] why doesn't "gem 'timecop', :require => 'timecop/timecop'" work?
|
|
6
|
+
require 'timecop/timecop'
|
|
7
|
+
|
|
8
|
+
spec_root = File.dirname(__FILE__)
|
|
9
|
+
$:.unshift(File.join(spec_root, '..', 'lib'))
|
|
10
|
+
$:.unshift(spec_root)
|
|
11
|
+
require 'net-http-not_modified_cache'
|
|
12
|
+
|
|
13
|
+
RSpec.configure do |config|
|
|
14
|
+
config.filter_run :focus => true
|
|
15
|
+
config.run_all_when_everything_filtered = true
|
|
16
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
|
17
|
+
|
|
18
|
+
config.before { Net::HTTP::NotModifiedCache.enable! }
|
|
19
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: net-http-not_modified_cache
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.0
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Sean Huber
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2011-10-17 00:00:00.000000000Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: activesupport
|
|
16
|
+
requirement: &70308578689380 !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ! '>='
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '0'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: *70308578689380
|
|
25
|
+
- !ruby/object:Gem::Dependency
|
|
26
|
+
name: i18n
|
|
27
|
+
requirement: &70308578688740 !ruby/object:Gem::Requirement
|
|
28
|
+
none: false
|
|
29
|
+
requirements:
|
|
30
|
+
- - ! '>='
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '0'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: *70308578688740
|
|
36
|
+
- !ruby/object:Gem::Dependency
|
|
37
|
+
name: rack
|
|
38
|
+
requirement: &70308578688120 !ruby/object:Gem::Requirement
|
|
39
|
+
none: false
|
|
40
|
+
requirements:
|
|
41
|
+
- - ! '>='
|
|
42
|
+
- !ruby/object:Gem::Version
|
|
43
|
+
version: '0'
|
|
44
|
+
type: :runtime
|
|
45
|
+
prerelease: false
|
|
46
|
+
version_requirements: *70308578688120
|
|
47
|
+
description: Caches responses for requests that respond with "304 - Not Modified"
|
|
48
|
+
and adds "if-modified-since" or "if-none-match" headers to future requests
|
|
49
|
+
email: shuber@einsteinindustries.com
|
|
50
|
+
executables: []
|
|
51
|
+
extensions: []
|
|
52
|
+
extra_rdoc_files: []
|
|
53
|
+
files:
|
|
54
|
+
- lib/net-http-not_modified_cache.rb
|
|
55
|
+
- MIT-LICENSE
|
|
56
|
+
- README.rdoc
|
|
57
|
+
- spec/net-http-not_modified_cache_spec.rb
|
|
58
|
+
- spec/spec_helper.rb
|
|
59
|
+
homepage: http://github.com/einstein/net-http-not_modified_cache
|
|
60
|
+
licenses: []
|
|
61
|
+
post_install_message:
|
|
62
|
+
rdoc_options: []
|
|
63
|
+
require_paths:
|
|
64
|
+
- lib
|
|
65
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
66
|
+
none: false
|
|
67
|
+
requirements:
|
|
68
|
+
- - ! '>='
|
|
69
|
+
- !ruby/object:Gem::Version
|
|
70
|
+
version: '0'
|
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
72
|
+
none: false
|
|
73
|
+
requirements:
|
|
74
|
+
- - ! '>='
|
|
75
|
+
- !ruby/object:Gem::Version
|
|
76
|
+
version: '0'
|
|
77
|
+
requirements: []
|
|
78
|
+
rubyforge_project:
|
|
79
|
+
rubygems_version: 1.8.10
|
|
80
|
+
signing_key:
|
|
81
|
+
specification_version: 3
|
|
82
|
+
summary: Caches responses for requests that respond with "304 - Not Modified"
|
|
83
|
+
test_files:
|
|
84
|
+
- spec/net-http-not_modified_cache_spec.rb
|
|
85
|
+
- spec/spec_helper.rb
|