hurley-http-cache 0.1.0.beta → 0.1.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.
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