Permalink
Browse files

Rack::Offline in uncached mode now changes the hash key only after an…

… interval (by default 10 seconds) to allow the browser to download the cache first.
  • Loading branch information...
1 parent 0b2f888 commit e057754a3aede50e6e779730144d29a2a9a17b08 @arsduo arsduo committed Jun 1, 2011
Showing with 61 additions and 7 deletions.
  1. +16 −1 lib/rack/offline.rb
  2. +22 −6 spec/base_offline_spec.rb
  3. +23 −0 spec/spec_helper.rb
View
@@ -10,6 +10,13 @@ class Offline
def self.configure(*args, &block)
new(*args, &block)
end
+
+ # interval in seconds used to compute the cache key when in uncached mode
+ # which can be set by passing in options[:cache_interval]
+ # note: setting it to 0 or a low value will change the cache key every request
+ # which means the manifest will never successfully download
+ # (since it gets downloaded again at the end)
+ UNCACHED_KEY_INTERVAL = 10
def initialize(options = {}, &block)
@cache = options[:cache]
@@ -29,11 +36,13 @@ def initialize(options = {}, &block)
"you need to supply a root so Rack::Offline can " \
"calculate a hash of the files." unless @root
precache_key!
+ else
+ @cache_interval = (options[:cache_interval] || UNCACHED_KEY_INTERVAL).to_i
end
end
def call(env)
- key = @key || Digest::SHA2.hexdigest(Time.now.to_s + Time.now.usec.to_s)
+ key = @key || uncached_key
body = ["CACHE MANIFEST"]
body << "# #{key}"
@@ -69,5 +78,11 @@ def precache_key!
@key = Digest::SHA2.hexdigest(hash.join)
end
+
+ def uncached_key
+ now = Time.now.to_i - Time.now.to_i % @cache_interval
+ Digest::SHA2.hexdigest(now.to_s)
+ end
+
end
end
View
@@ -9,17 +9,33 @@
it_should_behave_like "a cache manifest"
- it "returns a different cache-busting comment each time" do
- cache_buster = body[/^# .{64}$/]
- get "/"
- body[/^# .{64}$/].should_not == cache_buster
- end
-
it "doesn't contain a network section" do
body.should_not =~ %r{^NETWORK:}
end
it "doesn't contain a fallback section" do
body.should_not =~ %r{^FALLBACK:}
end
+
+ describe "cache-busting comment" do
+ context "if no interval is specified" do
+ self.app = Rack::Offline.configure do
+ cache "images/masthead.png"
+ end
+
+ it_should_behave_like "uncached cache manifests"
+ end
+
+ context "if an interval is specified" do
+ INTERVAL = 15
+ self.app = Rack::Offline.configure(:cache_interval => INTERVAL) do
+ cache "images/masthead.png"
+ end
+
+ before do
+ @interval = INTERVAL
+ end
+ it_should_behave_like "uncached cache manifests"
+ end
+ end
end
View
@@ -51,4 +51,27 @@ def app
it "includes a cache-busting comment" do
body.should =~ %r{^# .{64}$}
end
+end
+
+shared_examples_for "uncached cache manifests" do
+ before do
+ @interval ||= Rack::Offline::UNCACHED_KEY_INTERVAL
+ Time.stub(:now).and_return(Time.at(@interval))
+ get "/"
+ end
+
+ it "returns the same cache-busting comment within a given interval" do
+ cache_buster = body[/^# .{64}$/]
+ Time.stub(:now).and_return(Time.at(2 * @interval - 1))
+ get "/"
+ body[/^# .{64}$/].should == cache_buster
+ end
+
+ it "returns a different cache-busting comment after the interval" do
+ Time.stub(:now).and_return(Time.at(@interval))
+ cache_buster = body[/^# .{64}$/]
+ Time.stub(:now).and_return(Time.at(2 * @interval))
+ get "/"
+ body[/^# .{64}$/].should_not == cache_buster
+ end
end

0 comments on commit e057754

Please sign in to comment.