Skip to content

Commit

Permalink
Rename *WithCacheBehavior to *::Cached
Browse files Browse the repository at this point in the history
  • Loading branch information
ixti committed Feb 4, 2015
1 parent bcf947e commit 69dfa0a
Show file tree
Hide file tree
Showing 11 changed files with 99 additions and 117 deletions.
22 changes: 8 additions & 14 deletions lib/http/cache.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
require "time"
require "http/cache/cache_control"
require "http/cache/response_with_cache_behavior"
require "http/cache/request_with_cache_behavior"

module HTTP
class Cache
Expand All @@ -20,7 +18,7 @@ def perform(request, options)
# @yield [request, options] on cache miss so that an actual
# request can be made
def perform(request, options, &request_performer)
req = RequestWithCacheBehavior.coerce(request)
req = request.cached

invalidate_cache(req) if req.invalidates_cache?

Expand Down Expand Up @@ -64,26 +62,22 @@ def handle_response(cached_resp, actual_resp, req)
end
end

# @return [ResponseWithCacheBehavior] the actual response returned
# @return [HTTP::Response::Cached] the actual response returned
# by request_performer
def make_request(req, options, request_performer)
req.sent_at = Time.now
ResponseWithCacheBehavior.coerce(request_performer.call(req, options)).tap do |resp|
resp.received_at = Time.now
resp.requested_at = req.sent_at

request_performer.call(req, options).cached.tap do |res|
res.received_at = Time.now
res.requested_at = req.sent_at
end
end

# @return [ResponseWithCacheBehavior] the cached response for the
# request or nil if there isn't one
# @return [HTTP::Response::Cached, nil] the cached response for the request
def cache_lookup(request)
return nil if request.skips_cache?
c = @cache_adapter.lookup(request)
if c
ResponseWithCacheBehavior.coerce(c)
else
nil
end
c && c.cached
end

# Store response in cache
Expand Down
6 changes: 6 additions & 0 deletions lib/http/request.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
require "http/errors"
require "http/headers"
require "http/request/cached"
require "http/request/writer"
require "http/version"
require "base64"
Expand Down Expand Up @@ -130,6 +131,11 @@ def socket_port
using_proxy? ? proxy[:proxy_port] : uri.port
end

# @return [HTTP::Request::Cached]
def cached
Cached.new self
end

private

def path_for_request_header
Expand Down
Original file line number Diff line number Diff line change
@@ -1,35 +1,31 @@
require "http/request"
require "http/cache/cache_control"

module HTTP
class Cache
class Request
# Decorator class for requests to provide convenience methods
# related to caching. Instantiate using the `.coerce` method.
class RequestWithCacheBehavior < DelegateClass(HTTP::Request)
# related to caching.
class Cached < DelegateClass(HTTP::Request)
INVALIDATING_METHODS = [:post, :put, :delete, :patch].freeze
CACHEABLE_METHODS = [:get, :head].freeze

class << self
protected :new

# @return [RequestWithCacheBehavior]] a instance of self by
# wrapping `another` a new instance of self or by just
# returning it
#
# @api public
def coerce(another)
if another.respond_to? :cacheable?
another
else
new(another)
end
end
end

# When was this request sent to the server
#
# @api public
attr_accessor :sent_at

# Inits a new instance
# @api private
def initialize(obj)
super
@requested_at = nil
@received_at = nil
end

# @return [HTTP::Request::Cached]
def cached
self
end

# @return [Boolean] true iff request demands the resources cache entry be invalidated
#
# @api public
Expand All @@ -38,7 +34,7 @@ def invalidates_cache?
cache_control.no_store?
end

# @return [Boolean] true iff request is cacheable
# @return [Boolean] true if request is cacheable
#
# @api public
def cacheable?
Expand All @@ -57,26 +53,24 @@ def skips_cache?
cache_control.no_cache?
end

# @return [RequestWithCacheBehavior] new request based on this
# @return [HTTP::Request::Cached] new request based on this
# one but conditional on the resource having changed since
# `cached_response`
#
# @api public
def conditional_on_changes_to(cached_response)
raw_cond_req = HTTP::Request.new(verb, uri,
headers.merge(conditional_headers_for(cached_response)),
proxy, body, version)

self.class.coerce(raw_cond_req)
self.class.new HTTP::Request.new(
verb, uri, headers.merge(conditional_headers_for(cached_response)),
proxy, body, version)
end

# @return [CacheControl] cache control helper for this request
# @return [HTTP::Cache::CacheControl] cache control helper for this request
# @api public
def cache_control
@cache_control ||= CacheControl.new(self)
@cache_control ||= HTTP::Cache::CacheControl.new(self)
end

protected
private

# @return [Headers] conditional request headers
# @api private
Expand All @@ -93,14 +87,6 @@ def conditional_headers_for(cached_response)

headers
end

# Inits a new instance
# @api private
def initialize(obj)
super
@requested_at = nil
@received_at = nil
end
end
end
end
6 changes: 6 additions & 0 deletions lib/http/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
require "http/headers"
require "http/content_type"
require "http/mime_type"
require "http/response/cached"
require "http/response/status"
require "time"

Expand Down Expand Up @@ -106,5 +107,10 @@ def parse(as = nil)
def inspect
"#<#{self.class}/#{@version} #{code} #{reason} #{headers.to_h.inspect}>"
end

# @return [HTTP::Response::Cached]
def cached
Cached.new self
end
end
end
Original file line number Diff line number Diff line change
@@ -1,24 +1,21 @@
require "http/response"
require "http/cache/cache_control"

module HTTP
class Cache
class Response
# Decorator class for responses to provide convenience methods
# related to caching. Instantiate using the `.coerce` method.
class ResponseWithCacheBehavior < DelegateClass(HTTP::Response)
# related to caching.
class Cached < DelegateClass(HTTP::Response)
CACHEABLE_RESPONSE_CODES = [200, 203, 300, 301, 410].freeze

class << self
protected :new
def initialize(obj)
super
@requested_at = nil
@received_at = nil
end

# @return [Boolean] a instance of self by wrapping `another` a new
# instance of self or by just returning it
def coerce(another)
if another.respond_to? :cacheable?
another
else
new(another)
end
end
# @return [HTTP::Response::Cached]
def cached
self
end

# @return [Boolean] true iff this response is stale
Expand All @@ -40,10 +37,10 @@ def expired?
def cacheable?
@cacheable ||=
begin
CACHEABLE_RESPONSE_CODES.include?(code) &&
!(cache_control.vary_star? ||
cache_control.no_store? ||
cache_control.no_cache?)
CACHEABLE_RESPONSE_CODES.include?(code) \
&& !(cache_control.vary_star? ||
cache_control.no_store? ||
cache_control.no_cache?)
end
end

Expand Down Expand Up @@ -84,9 +81,9 @@ def validated!(validating_response)
self.authoritative = true
end

# @return [CacheControl] cache control helper object.
# @return [HTTP::Cache::CacheControl] cache control helper object.
def cache_control
@cache_control ||= CacheControl.new(self)
@cache_control ||= HTTP::Cache::CacheControl.new(self)
end

protected
Expand All @@ -107,12 +104,6 @@ def to_time_or_epoch(t_str)
rescue ArgumentError
Time.at(0)
end

def initialize(obj)
super
@requested_at = nil
@received_at = nil
end
end
end
end
12 changes: 5 additions & 7 deletions spec/lib/http/cache_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,10 @@

context "cache hit" do
let(:cached_response) do
HTTP::Cache::ResponseWithCacheBehavior.coerce(
HTTP::Response.new(200,
"http/1.1",
{"Cache-Control" => "private", "test" => "foo"},
"")
).tap { |r| r.requested_at = r.received_at = Time.now }
headers = {"Cache-Control" => "private", "test" => "foo"}
HTTP::Response.new(200, "http/1.1", headers, "").cached.tap do |r|
r.requested_at = r.received_at = Time.now
end
end

it "does not call request_performer block" do
Expand Down Expand Up @@ -244,7 +242,7 @@
let(:cached_response) { nil } # cold cache by default

def build_cached_response(*args)
r = HTTP::Cache::ResponseWithCacheBehavior.coerce(HTTP::Response.new(*args))
r = HTTP::Response.new(*args).cached
r.requested_at = r.received_at = Time.now

yield r if block_given?
Expand Down
2 changes: 1 addition & 1 deletion spec/lib/http/client_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def simple_response(body, status = 200)

describe "caching" do
it "returns cached responses if they exist" do
cached_response = HTTP::Cache::ResponseWithCacheBehavior.coerce(simple_response("OK"))
cached_response = simple_response("OK").cached
adapter = double("persistance_adapter", :lookup => cached_response)
client = StubbedClient.new(:cache => HTTP::Cache.new(adapter)).stub(
"http://example.com/" => simple_response("OK")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
RSpec.describe HTTP::Cache::RequestWithCacheBehavior do
describe ".coerce" do
it "should accept a base request" do
expect(described_class.coerce(request)).to be_kind_of described_class
end

it "should accept an already decorated request" do
decorated_req = described_class.coerce(request)
expect(decorated_req).to be_kind_of described_class
end
end

subject { described_class.coerce(request) }
RSpec.describe HTTP::Request::Cached do
subject { described_class.new request }

it "provides access to it's cache control object" do
expect(subject.cache_control).to be_kind_of HTTP::Cache::CacheControl
Expand All @@ -30,11 +19,11 @@
end

it "can construct a new conditional version of itself based on a cached response" do
mod_date = Time.now.httpdate
mod_date = Time.now.httpdate
cached_resp = HTTP::Response.new(200, "http/1.1",
{"Etag" => "foo",
"Last-Modified" => mod_date},
"")
"")
cond_req = subject.conditional_on_changes_to(cached_resp)

expect(cond_req.headers["If-None-Match"]).to eq "foo"
Expand Down Expand Up @@ -66,7 +55,7 @@
cached_resp = HTTP::Response.new(200, "http/1.1",
{"Etag" => "foo",
"Last-Modified" => mod_date},
"")
"")
cond_req = subject.conditional_on_changes_to(cached_resp)
expect(cond_req.headers["If-None-Match"]).to eq "foo"
expect(cond_req.headers["If-Modified-Since"]).to eq mod_date
Expand Down Expand Up @@ -140,4 +129,9 @@

# Background
let(:request) { HTTP::Request.new(:get, "http://example.com/") }

describe "#cached" do
subject(:cached_request) { request.cached }
it { is_expected.to be cached_request }
end
end
5 changes: 5 additions & 0 deletions spec/lib/http/request_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -138,4 +138,9 @@
its(:verb) { is_expected.to be :get }
end
end

describe "#cached" do
subject { request.cached }
it { is_expected.to be_a HTTP::Request::Cached }
end
end

0 comments on commit 69dfa0a

Please sign in to comment.