hurley-http-cache 0.1.0.beta → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: fb71f71333adf5e4e068bb81c474c092cc16a5f4
4
- data.tar.gz: 55b436e2743064185ac7383b3e9ec71389dc0950
3
+ metadata.gz: b8f6cd27a44789b425f61cb2f6290640ec1895e6
4
+ data.tar.gz: a42afa9bf4a1308195da0d408c1c44ba7adb1bb2
5
5
  SHA512:
6
- metadata.gz: ee330c420cb6080c33e2ecb673a1de0efc99e1498c413cac9861d4555e6c30c12fb056fa1a7c67dca2b49047d872860599c14c07a32a951467187b952c88940a
7
- data.tar.gz: 19de7bdbe98a40d5fbc70f56c3e61c721f1a0cbcdf6c9c49ef9b3d040c6592492f80fe40b297c172211c6aba4453291030882912fe9ab62115590dae0930599b
6
+ metadata.gz: f6b829f62715e840786ba9c4583145992adf98ef16f682b322b376b30ea5e3ed622f5a0a8952cac8709666af39fccf498f06de74b14e05d9c146df836eafef26
7
+ data.tar.gz: fa0d454ada4b5413dd64a366d2928ebfb123ac487c992d5dc1c377be02a2867547696bf1b632a76ce9933806cae8e9f4ea9515af042e7a1a25e25e51abb2dfb9
@@ -21,7 +21,7 @@ module Hurley
21
21
  # (default: nil).
22
22
  # logger - A Logger object (default: nil).
23
23
  #
24
- # Examples:
24
+ # Examples
25
25
  #
26
26
  # # Initialize the connection with the Rails logger.
27
27
  # client = Hurley.new
@@ -97,7 +97,8 @@ module Hurley
97
97
  # and the exceeding will be treated as the value. If only the key is
98
98
  # present then the assigned value will default to true.
99
99
  #
100
- # Examples:
100
+ # Examples
101
+ #
101
102
  # parse('max-age=600')
102
103
  # # => { 'max-age' => '600'}
103
104
  #
@@ -17,6 +17,17 @@ module Hurley
17
17
  true
18
18
  end
19
19
 
20
+ # Internal: Check if a pair of existing request and response matches the
21
+ # current request object.
22
+ #
23
+ # request - The Hash of the request that was cached.
24
+ # response - The Hash of the response that was cached.
25
+ #
26
+ # Returns true or false.
27
+ def matches?(request, response)
28
+ verb.to_s == request['verb'] && vary_matches?(request, response)
29
+ end
30
+
20
31
  # Internal: Check if the request can't be cached, accordingly to the
21
32
  # 'Cache-Control' header.
22
33
  #
@@ -39,6 +50,25 @@ module Hurley
39
50
 
40
51
  private
41
52
 
53
+ # Internal: Check if the header fields described in a response 'Vary'
54
+ # header matches between the current request and an existing request
55
+ # Hash. The 'Vary' header can be empty (meaning that the requests match),
56
+ # a '*' String (meaning that the request never match) or be a comma
57
+ # separated list of the headers that should be compared.
58
+ #
59
+ # request - The Hash of the request that was cached.
60
+ # response - The Hash of the response that was cached.
61
+ #
62
+ # Returns true or false.
63
+ def vary_matches?(request, response)
64
+ response_headers = Hurley::Header.new(response['header'])
65
+ vary = response_headers['Vary'].to_s
66
+
67
+ vary.empty? || (vary != '*' && vary.split(/[\s,]+/).all? do |key|
68
+ header[key] == request['header'][key]
69
+ end)
70
+ end
71
+
42
72
  # Internal: Get a CacheControl object to inspect the directives in the
43
73
  # 'Cache-Control' header.
44
74
  #
@@ -156,7 +156,7 @@ module Hurley
156
156
  # Internal: The logic behind cacheable_in_private_cache? and
157
157
  # cacheable_in_shared_cache? The logic is the same except for the
158
158
  # treatment of the private Cache-Control directive.
159
- def cacheable?(shared:)
159
+ def cacheable?(shared: false)
160
160
  return false if (cache_control.private? && shared) || cache_control.no_store?
161
161
 
162
162
  cacheable_status_code? && (validateable? || fresh?)
@@ -32,7 +32,9 @@ module Hurley
32
32
  assert_valid_store!
33
33
  end
34
34
 
35
- # Internal: Store a response inside the cache.
35
+ # Internal: Store a response inside the cache. Existing entries for the
36
+ # exact same request will be removed from the cache and replaced by the
37
+ # given response object.
36
38
  #
37
39
  # request - A Hurley::HttpCache::::Request instance of the executed HTTP
38
40
  # request.
@@ -41,16 +43,12 @@ module Hurley
41
43
  # Returns nothing.
42
44
  def write(request, response)
43
45
  key = cache_key_for(request.url)
44
- entry = serialize_entry(request.serializable_hash, response.serializable_hash)
46
+ entry = read_entry(key) || []
45
47
 
46
- entries = cache.read(key) || []
48
+ entry.reject! { |(req, res)| request.matches?(req, res) }
49
+ entry << [request.serializable_hash, response.serializable_hash]
47
50
 
48
- entries.reject! do |(cached_request, cached_response)|
49
- response_matches?(request, cached_request, cached_response)
50
- end
51
-
52
- entries << entry
53
- cache.write(key, entries)
51
+ write_entry(key, entry)
54
52
  rescue Encoding::UndefinedConversionError => e
55
53
  warn { "Response could not be serialized: #{e.message}. Try using Marshal to serialize." }
56
54
  raise e
@@ -65,8 +63,8 @@ module Hurley
65
63
  # Returns a Hash.
66
64
  def read(request)
67
65
  cache_key = cache_key_for(request.url)
68
- entries = cache.read(cache_key)
69
- lookup_response(request, entries)
66
+ entry = read_entry(cache_key)
67
+ lookup_response(request, entry)
70
68
  end
71
69
 
72
70
  def delete(url)
@@ -76,61 +74,30 @@ module Hurley
76
74
 
77
75
  private
78
76
 
77
+ def read_entry(key)
78
+ entry = cache.read(key)
79
+ @serializer.load(entry) if entry
80
+ end
81
+
82
+ def write_entry(key, entry)
83
+ cache.write(key, @serializer.dump(entry))
84
+ end
85
+
79
86
  # Internal: Retrieve a response Hash from the list of entries that match
80
87
  # the given request.
81
88
  #
82
89
  # request - A Hurley::HttpCache::::Request instance of the incoming HTTP
83
90
  # request.
84
- # entries - An Array of pairs of Hashes (request, response).
91
+ # entry - An Array of pairs of Hashes (request, response).
85
92
  #
86
93
  # Returns a Hash or nil.
87
- def lookup_response(request, entries)
88
- if entries
89
- entries = entries.map { |entry| deserialize_entry(*entry) }
90
- _, response = entries.find { |req, res| response_matches?(request, req, res) }
94
+ def lookup_response(request, entry)
95
+ if entry
96
+ _, response = entry.find { |req, res| request.matches?(req, res) }
91
97
  response
92
98
  end
93
99
  end
94
100
 
95
- # Internal: Check if a cached response and request matches the given
96
- # request.
97
- #
98
- # request - A Hurley::HttpCache::::Request instance of the
99
- # current HTTP request.
100
- # cached_request - The Hash of the request that was cached.
101
- # cached_response - The Hash of the response that was cached.
102
- #
103
- # Returns true or false.
104
- def response_matches?(request, cached_request, cached_response)
105
- request.verb.to_s == cached_request['verb'] &&
106
- vary_matches?(cached_response, request, cached_request)
107
- end
108
-
109
- def vary_matches?(cached_response, request, cached_request)
110
- headers = Hurley::Header.new(cached_response['header'])
111
- vary = headers['Vary'].to_s
112
-
113
- vary.empty? || (vary != '*' && vary.split(/[\s,]+/).all? do |header|
114
- request.header[header] == cached_request['header'][header]
115
- end)
116
- end
117
-
118
- def serialize_entry(*objects)
119
- objects.map { |object| serialize_object(object) }
120
- end
121
-
122
- def serialize_object(object)
123
- @serializer.dump(object)
124
- end
125
-
126
- def deserialize_entry(*objects)
127
- objects.map { |object| deserialize_object(object) }
128
- end
129
-
130
- def deserialize_object(object)
131
- @serializer.load(object)
132
- end
133
-
134
101
  # Internal: Computes the cache key for a specific request, taking in
135
102
  # account the current serializer to avoid cross serialization issues.
136
103
  #
@@ -55,6 +55,60 @@ class RequestTest < MiniTest::Test
55
55
  assert_predicate request, :no_cache?
56
56
  end
57
57
 
58
+ def test_request_verb_matches
59
+ request = new_request(verb: :get)
60
+
61
+ refute request.matches?({ 'verb' => 'post' }, {})
62
+ refute request.matches?({ 'verb' => 'delete' }, {})
63
+
64
+ assert request.matches?({ 'verb' => 'get' }, {})
65
+ end
66
+
67
+ def test_request_verb_and_Vary_matches
68
+ request = new_request(verb: :get, header: { 'User-Agent' => 'FooBar/1.0' })
69
+
70
+ req = new_request(verb: :get, header: { 'User-Agent' => 'FooBar/1.0' }).serializable_hash
71
+ res = { 'header' => { 'Vary' => 'User-Agent' } }
72
+
73
+ assert request.matches?(req, res)
74
+ end
75
+
76
+ def test_request_verb_and_multiple_Vary_matches
77
+ request = new_request(verb: :get, header: { 'User-Agent' => 'FooBar/1.0', 'Accept' => 'application/json' })
78
+
79
+ req = new_request(verb: :get, header: { 'User-Agent' => 'FooBar/1.0', 'Accept' => 'application/json' }).serializable_hash
80
+ res = { 'header' => { 'Vary' => 'User-Agent, Accept' } }
81
+
82
+ assert request.matches?(req, res)
83
+ end
84
+
85
+ def test_request_matches_wrong_Vary
86
+ request = new_request(verb: :get, header: { 'User-Agent' => 'FooBar/2.0' })
87
+
88
+ req = new_request(verb: :get, header: { 'User-Agent' => 'FooBar/1.0' }).serializable_hash
89
+ res = { 'header' => { 'Vary' => 'User-Agent' } }
90
+
91
+ refute request.matches?(req, res)
92
+ end
93
+
94
+ def test_request_verb_and_any_wrong_Vary
95
+ request = new_request(verb: :get, header: { 'User-Agent' => 'FooBar/1.0', 'Accept' => 'application/json' })
96
+
97
+ req = new_request(verb: :get, header: { 'User-Agent' => 'FooBar/1.0', 'Accept' => 'text/html' }).serializable_hash
98
+ res = { 'header' => { 'Vary' => 'User-Agent, Accept' } }
99
+
100
+ refute request.matches?(req, res)
101
+ end
102
+
103
+ def test_request_Vary_star_never_matches
104
+ request = new_request(verb: :get, header: { 'User-Agent' => 'FooBar/1.0' })
105
+
106
+ req = new_request(verb: :get, header: { 'User-Agent' => 'FooBar/1.0' }).serializable_hash
107
+ res = { 'header' => { 'Vary' => '*' } }
108
+
109
+ refute request.matches?(req, res)
110
+ end
111
+
58
112
  def test_serializable_hash
59
113
  request = new_request(header: { 'User-Agent' => 'FooBar' })
60
114
  hash = request.serializable_hash
@@ -26,7 +26,7 @@ module StorageTests
26
26
 
27
27
  def test_writes_the_response_object_to_the_underlying_cache
28
28
  @storage.write(@request, @response)
29
- assert_kind_of Array, @storage.cache.read(cache_key)
29
+ refute_nil @storage.cache.read(cache_key)
30
30
  end
31
31
 
32
32
  def test_retrieves_the_response_from_the_cache
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hurley-http-cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.beta
4
+ version: 0.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Lucas Mazza
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-18 00:00:00.000000000 Z
11
+ date: 2015-02-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: hurley
@@ -58,6 +58,34 @@ dependencies:
58
58
  - - "~>"
59
59
  - !ruby/object:Gem::Version
60
60
  version: '10.0'
61
+ - !ruby/object:Gem::Dependency
62
+ name: sinatra
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: '0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: minitest
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: 5.5.1
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: 5.5.1
61
89
  description: Hurley connection to handle HTTP caching
62
90
  email:
63
91
  - opensource@plataformatec.com.br
@@ -95,9 +123,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
95
123
  version: 2.0.0
96
124
  required_rubygems_version: !ruby/object:Gem::Requirement
97
125
  requirements:
98
- - - ">"
126
+ - - ">="
99
127
  - !ruby/object:Gem::Version
100
- version: 1.3.1
128
+ version: '0'
101
129
  requirements: []
102
130
  rubyforge_project:
103
131
  rubygems_version: 2.4.5