httparty-cache 0.0.1 → 0.0.2

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
  SHA256:
3
- metadata.gz: ff16750de74919a4428091908be24f05775deded7885af69960c38265ee1679b
4
- data.tar.gz: 12ccabffc3f4d24dfeef6327a3ac5649560b275051840826f31c3482e2075b2d
3
+ metadata.gz: 6b5ee7949c1ff90bad003ea68a65b11de85f2aba908c3aa3b733e53976d15b33
4
+ data.tar.gz: d7f636874b72c75534ec6e060fc44c1fd3eb3191f80986b0a69bdc6c41e29279
5
5
  SHA512:
6
- metadata.gz: f2d9b8a53ed1ef26b0ab22dc64108ea69cecfc76c13505b4d184e2df0823ddb78857ccf815152bc1adf202a94a0b1a3010af4c7714012842f675ee3428c6109e
7
- data.tar.gz: 5b33148ca9f3d650218f66ca59964c04c91e87dfa730f8cd997aaad87fe36d19decdf1833463512ecc3f457cbe1bb8b5fbe7c5f4527325fe686cde1718b335e7
6
+ metadata.gz: 9af68ad0b61042517beea619be96074c5e0f8c87c99e8f1094a6d6efdc63da87a5dcb31349435d17ae61755ac34ba7a9c50709f71b4b22ed87ff93db309bca60
7
+ data.tar.gz: 59fcbe7267b2eb0670605770bdf450784d06290089cbe8c0e66cd18919283564c77b798ba7556c4c24010e6c058e0c013b4018ca723fb1ddb60862732a7ab1c5
data/README.md CHANGED
@@ -33,8 +33,51 @@ class Client
33
33
  end
34
34
  ```
35
35
 
36
+ ### Caching requisites
37
+
38
+ * GET requests only.
39
+
40
+ * The server must send a `Cache-Control` header. Otherwise this gem
41
+ will always revalidate.
42
+
43
+ * A request is revalidated when:
44
+ * The server sends a `no-store` directive.
45
+ * The server sends a `no-cache` directive.
46
+ * The server sends a `max-age=0,must-revalidate` directive.
47
+
48
+ * A request is stale when its age is greater than `max-age`.
49
+
36
50
  ### Cache stores
37
51
 
52
+ Requests are stored by their URL and a cache key, and point at the
53
+ latest response.
54
+
55
+ The cache key is generated from requested URL and headers used. If the
56
+ headers change for every request or are sensitive (X-* headers used for
57
+ bespoke authentication), you can add the `cache_key: 'string'` option to
58
+ the request.
59
+
60
+ ```ruby
61
+ url = 'https://example.org'
62
+ sensitive_token = 'token'
63
+
64
+ HTTParty.get(
65
+ url,
66
+ headers: { 'X-Token': sensitive_token },
67
+ cache_key: url
68
+ )
69
+ ```
70
+
71
+ Otherwise you'll see many requests cached all pointing to the same
72
+ response.
73
+
74
+ `Date`, `Authorization`, `Signature` and `Cookie` headers are already
75
+ ignored.
76
+
77
+ Responses are stored by their URL and a cache key composed by its `Vary`
78
+ headers. This way if you send a request header that could change the
79
+ response, you'll get a new one.
80
+
38
81
  #### Redis
39
82
 
40
83
  If you want to use Redis as a cache store:
@@ -6,6 +6,7 @@ module HTTParty
6
6
  #
7
7
  # @see {https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching}
8
8
  module Request
9
+ SENSITIVE_HEADERS = %w[Cookie Authorization Date Signature].freeze
9
10
  CACHE_REVALIDATION_HEADERS = {
10
11
  'ETag' => 'If-None-Match',
11
12
  'Last-Modified' => 'If-Modified-Since'
@@ -15,7 +16,14 @@ module HTTParty
15
16
  #
16
17
  # @return [String]
17
18
  def cache_key
18
- @cache_key ||= "request:#{uri}##{URI.encode_www_form(options[:headers] || {})}"
19
+ @cache_key ||= "http-party-cache:request:#{uri}##{URI.encode_www_form(cache_params)}"
20
+ end
21
+
22
+ # @return [Hash]
23
+ def cache_params
24
+ return { 'X-Cache-Key' => options[:cache_key] } if options.key? :cache_key
25
+
26
+ options[:headers].to_h.except(*SENSITIVE_HEADERS) || {}
19
27
  end
20
28
 
21
29
  def validate
@@ -24,14 +32,21 @@ module HTTParty
24
32
  raise ArgumentError, 'Caching is enabled but store was not provided' if caching? && cache_store.nil?
25
33
  end
26
34
 
35
+ # A cacheable request
36
+ #
37
+ # @return [Boolean]
38
+ def cacheable?
39
+ caching? && http_method == Net::HTTP::Get
40
+ end
41
+
27
42
  # Patch HTTParty::Request#perform to provide caching for GET
28
43
  # requests
29
44
  #
30
45
  # @return [HTTParty::Response]
31
46
  def perform(&block)
32
- return super(&block) unless caching? && http_method == Net::HTTP::Get
47
+ return super(&block) unless cacheable?
33
48
 
34
- if cached?
49
+ if response_cached?
35
50
  handle_revalidation
36
51
  else
37
52
  super(&block).tap do |response|
@@ -46,24 +61,27 @@ module HTTParty
46
61
  # evicted from cache or is corrupted
47
62
  #
48
63
  # @return [Bool]
49
- def cached?
64
+ def response_cached?
50
65
  cache_store.key?(cache_key) &&
51
66
  cache_store.key?(cache_store.get(cache_key)) &&
52
- cache_store.get(cache_store.get(cache_key)).is_a?(HTTParty::Response)
67
+ cached_response.is_a?(HTTParty::Response)
53
68
  end
54
69
 
55
70
  def caching?
56
71
  !!options[:caching]
57
72
  end
58
73
 
74
+ # @return [HTTParty::Response]
75
+ def cached_response
76
+ @cached_response ||= cache_store.get(cache_store.get(cache_key))
77
+ end
78
+
59
79
  private
60
80
 
61
81
  # Revalidates request
62
82
  #
63
83
  # @return [HTTParty::Response]
64
84
  def handle_revalidation
65
- cached_response = cache_store.get(cache_store.get(cache_key))
66
-
67
85
  if cached_response.revalidate? || cached_response.stale?
68
86
  self.http_method = Net::HTTP::Head
69
87
  options[:cached_response] = cached_response
@@ -80,7 +98,7 @@ module HTTParty
80
98
  # Remove from cache so we can perform the request, otherwise
81
99
  # we produce an infinite loop
82
100
  cache_store.delete(cache_key)
83
- response = perform
101
+ @cached_response = response = perform
84
102
  end
85
103
 
86
104
  cache_store.set(response.cache_key, response)
@@ -6,11 +6,6 @@ module HTTParty
6
6
  #
7
7
  # @see {https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching}
8
8
  module Response
9
- # Detects if response is cached
10
- def cached?
11
- code == 304
12
- end
13
-
14
9
  # Parses Cache-Control headers
15
10
  #
16
11
  # @see {https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/cache-control}
@@ -54,7 +49,13 @@ module HTTParty
54
49
  # @see {https://developer.mozilla.org/en-US/docs/Web/HTTP/Caching#force_revalidation}
55
50
  # @return [Bool]
56
51
  def revalidate?
57
- !!(cache_control[:no_cache] || (cache_control[:max_age]&.zero? && cache_control[:must_revalidate]))
52
+ cache_control.empty? ||
53
+ cache_control[:no_store] ||
54
+ cache_control[:no_cache] ||
55
+ (
56
+ cache_control[:max_age]&.zero? &&
57
+ cache_control[:must_revalidate]
58
+ )
58
59
  end
59
60
 
60
61
  # Returns the request headers that vary responses
@@ -68,7 +69,7 @@ module HTTParty
68
69
  #
69
70
  # @return [String]
70
71
  def cache_key
71
- @cache_key ||= "response:#{request.uri}##{URI.encode_www_form vary}"
72
+ @cache_key ||= "httparty-cache:response:#{request.uri}##{URI.encode_www_form vary}"
72
73
  end
73
74
  end
74
75
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: httparty-cache
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - f
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-01-31 00:00:00.000000000 Z
11
+ date: 2024-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: httparty
@@ -129,35 +129,35 @@ dependencies:
129
129
  - !ruby/object:Gem::Version
130
130
  version: 0.14.0
131
131
  - !ruby/object:Gem::Dependency
132
- name: redis-client
132
+ name: jekyll
133
133
  requirement: !ruby/object:Gem::Requirement
134
134
  requirements:
135
- - - "~>"
135
+ - - ">="
136
136
  - !ruby/object:Gem::Version
137
- version: 0.14.0
137
+ version: '0'
138
138
  type: :development
139
139
  prerelease: false
140
140
  version_requirements: !ruby/object:Gem::Requirement
141
141
  requirements:
142
- - - "~>"
142
+ - - ">="
143
143
  - !ruby/object:Gem::Version
144
- version: 0.14.0
144
+ version: '0'
145
145
  - !ruby/object:Gem::Dependency
146
- name: rubocop
146
+ name: redis-client
147
147
  requirement: !ruby/object:Gem::Requirement
148
148
  requirements:
149
- - - ">="
149
+ - - "~>"
150
150
  - !ruby/object:Gem::Version
151
- version: '0'
151
+ version: 0.14.0
152
152
  type: :development
153
153
  prerelease: false
154
154
  version_requirements: !ruby/object:Gem::Requirement
155
155
  requirements:
156
- - - ">="
156
+ - - "~>"
157
157
  - !ruby/object:Gem::Version
158
- version: '0'
158
+ version: 0.14.0
159
159
  - !ruby/object:Gem::Dependency
160
- name: timecop
160
+ name: rubocop
161
161
  requirement: !ruby/object:Gem::Requirement
162
162
  requirements:
163
163
  - - ">="
@@ -171,7 +171,7 @@ dependencies:
171
171
  - !ruby/object:Gem::Version
172
172
  version: '0'
173
173
  - !ruby/object:Gem::Dependency
174
- name: jekyll
174
+ name: timecop
175
175
  requirement: !ruby/object:Gem::Requirement
176
176
  requirements:
177
177
  - - ">="