httpdisk 0.2.0 → 0.3.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 +4 -4
- data/.rubocop.yml +5 -0
- data/Gemfile.lock +2 -2
- data/README.md +9 -1
- data/Rakefile +10 -9
- data/lib/httpdisk.rb +1 -0
- data/lib/httpdisk/cache.rb +2 -17
- data/lib/httpdisk/cache_key.rb +13 -4
- data/lib/httpdisk/client.rb +21 -12
- data/lib/httpdisk/payload.rb +2 -2
- data/lib/httpdisk/sloptions.rb +105 -0
- data/lib/httpdisk/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0e3b6af20bf5a6b76a23a6d170328c040cf3f0715e012c392739b70ab6911bee
|
4
|
+
data.tar.gz: 46385301b89bc19f2e8862bcc9bf48b0f1b14fafbd2fa4fc8542b7bf4dcfe930
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c4b163648a7adf00ae2ae103ccef899f5a3b958d8e38eeb56fc0d609f0bae73a19eed4fb7d5921c583b4f6d2755bd70d5cf1da748a6d8e858f5195da1683d009
|
7
|
+
data.tar.gz: 3270ce507204023e25451048fd0ddecc713ce6c637abfe795c2f159d619328892393bb8fa73249f08199a30e9db895fb2e2b6c440bfae0dcf07abd277851d050
|
data/.rubocop.yml
CHANGED
@@ -2,14 +2,19 @@ AllCops:
|
|
2
2
|
NewCops: enable
|
3
3
|
SuggestExtensions: false
|
4
4
|
|
5
|
+
# this is buggy in 2.7.0
|
6
|
+
Style/HashTransformValues: { Enabled: false }
|
7
|
+
|
5
8
|
# minimal personal preference
|
6
9
|
Layout/CaseIndentation: { Enabled: false }
|
7
10
|
Layout/EndAlignment: { EnforcedStyleAlignWith: variable }
|
8
11
|
Lint/AssignmentInCondition: { Enabled: false }
|
12
|
+
Lint/NonLocalExitFromIterator: { Enabled: false }
|
9
13
|
Metrics: { Enabled: false }
|
10
14
|
Naming/MethodParameterName: { Enabled: false }
|
11
15
|
Naming/VariableNumber: { Enabled: false }
|
12
16
|
Style/Documentation: { Enabled: false }
|
17
|
+
Style/DoubleNegation: { Enabled: false }
|
13
18
|
Style/FrozenStringLiteralComment: { Enabled: false }
|
14
19
|
Style/IfUnlessModifier: { Enabled: false }
|
15
20
|
Style/NegatedIf: { Enabled: false }
|
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
httpdisk (0.
|
4
|
+
httpdisk (0.3.0)
|
5
5
|
faraday (~> 1.4)
|
6
6
|
faraday-cookie_jar (~> 0.0)
|
7
7
|
faraday_middleware (~> 1.0)
|
@@ -63,7 +63,7 @@ GEM
|
|
63
63
|
parser (>= 3.0.1.1)
|
64
64
|
ruby-progressbar (1.11.0)
|
65
65
|
ruby2_keywords (0.0.4)
|
66
|
-
slop (4.
|
66
|
+
slop (4.9.0)
|
67
67
|
unf (0.1.4)
|
68
68
|
unf_ext
|
69
69
|
unf_ext (0.0.7.7)
|
data/README.md
CHANGED
@@ -128,6 +128,7 @@ httpdisk supports a few options:
|
|
128
128
|
- `expires_in:` when to expire cached requests, default is nil (never expire)
|
129
129
|
- `force:` don't read anything from cache (but still write)
|
130
130
|
- `force_errors:` don't read errors from cache (but still write)
|
131
|
+
- `ignore_params:` array of query params to ignore when calculating cache_key
|
131
132
|
- `logger`: log requests to stderr, or pass your own logger
|
132
133
|
|
133
134
|
Pass these in when setting up Faraday:
|
@@ -176,10 +177,17 @@ Specific to httpdisk:
|
|
176
177
|
|
177
178
|
## Changelog
|
178
179
|
|
180
|
+
#### 0.3 (unreleased)
|
181
|
+
|
182
|
+
- added :ignore_params, for ignoring query params when generating cache keys
|
183
|
+
- HTTP 40x & 50x responses return :error status (and respond to `force_error`)
|
184
|
+
|
179
185
|
#### 0.2 - May 2020
|
186
|
+
|
180
187
|
- added `response.env[:httpdisk]`, which will be true if the response came from the cache
|
181
|
-
- `:logger` option
|
188
|
+
- added `:logger` option
|
182
189
|
- rake rubocop
|
183
190
|
|
184
191
|
#### 0.1 - April 2020
|
192
|
+
|
185
193
|
- Original release
|
data/Rakefile
CHANGED
@@ -13,9 +13,10 @@ spec = Gem::Specification.load('httpdisk.gemspec')
|
|
13
13
|
Rake::TestTask.new { _1.libs << 'test' }
|
14
14
|
task default: :test
|
15
15
|
|
16
|
-
# Watch files, run tests whenever something changes
|
16
|
+
# Watch rb files, run tests whenever something changes
|
17
17
|
task :watch do
|
18
|
-
|
18
|
+
# https://superuser.com/a/665208 / https://unix.stackexchange.com/a/42288
|
19
|
+
system("while true; do find . -name '*.rb' | entr -c -d rake; test $? -gt 128 && break; done")
|
19
20
|
end
|
20
21
|
|
21
22
|
#
|
@@ -31,7 +32,7 @@ end
|
|
31
32
|
#
|
32
33
|
|
33
34
|
task :rubocop do
|
34
|
-
|
35
|
+
sh 'bundle exec rubocop -A .'
|
35
36
|
end
|
36
37
|
|
37
38
|
#
|
@@ -39,17 +40,17 @@ end
|
|
39
40
|
#
|
40
41
|
|
41
42
|
task :build do
|
42
|
-
|
43
|
+
sh 'gem build --quiet httpdisk.gemspec'
|
43
44
|
end
|
44
45
|
|
45
46
|
task install: :build do
|
46
|
-
|
47
|
+
sh "gem install --quiet httpdisk-#{spec.version}.gem"
|
47
48
|
end
|
48
49
|
|
49
|
-
task release: %i[test build] do
|
50
|
+
task release: %i[rubocop test build] do
|
50
51
|
raise "looks like git isn't clean" unless `git status --porcelain`.empty?
|
51
52
|
|
52
|
-
|
53
|
-
|
54
|
-
|
53
|
+
sh "git tag -a #{spec.version} -m 'Tagging #{spec.version}'"
|
54
|
+
sh 'git push --tags'
|
55
|
+
sh "gem push httpdisk-#{spec.version}.gem"
|
55
56
|
end
|
data/lib/httpdisk.rb
CHANGED
data/lib/httpdisk/cache.rb
CHANGED
@@ -7,21 +7,6 @@ module HTTPDisk
|
|
7
7
|
|
8
8
|
def initialize(options)
|
9
9
|
@options = options
|
10
|
-
|
11
|
-
# heavy sanity checking on arguments here
|
12
|
-
if !dir.is_a?(String)
|
13
|
-
raise ArgumentError, "expected :dir to be a string, not #{dir.inspect}"
|
14
|
-
end
|
15
|
-
if expires_in && !expires_in.is_a?(Integer)
|
16
|
-
raise ArgumentError, "expected :expires_in to be an integer, not #{expires_in.inspect}"
|
17
|
-
end
|
18
|
-
|
19
|
-
%i[force force_errors].each do
|
20
|
-
value = send(_1)
|
21
|
-
if ![nil, true, false].include?(value)
|
22
|
-
raise ArgumentError, "expected #{_1} to be a boolean, not #{value.inspect}"
|
23
|
-
end
|
24
|
-
end
|
25
10
|
end
|
26
11
|
|
27
12
|
%i[dir expires_in force force_errors].each do |method|
|
@@ -43,7 +28,7 @@ module HTTPDisk
|
|
43
28
|
payload_or_status = read0(cache_key, peek: true)
|
44
29
|
return payload_or_status if payload_or_status.is_a?(Symbol)
|
45
30
|
|
46
|
-
payload_or_status.
|
31
|
+
payload_or_status.error? ? :error : :hit
|
47
32
|
end
|
48
33
|
|
49
34
|
# Write response to the disk cache
|
@@ -69,7 +54,7 @@ module HTTPDisk
|
|
69
54
|
return :force if force?
|
70
55
|
|
71
56
|
payload = Zlib::GzipReader.open(path) { Payload.read(_1, peek: peek) }
|
72
|
-
return :force if force_errors? && payload.
|
57
|
+
return :force if force_errors? && payload.error?
|
73
58
|
|
74
59
|
payload
|
75
60
|
end
|
data/lib/httpdisk/cache_key.rb
CHANGED
@@ -4,10 +4,10 @@ require 'uri'
|
|
4
4
|
|
5
5
|
module HTTPDisk
|
6
6
|
class CacheKey
|
7
|
-
attr_reader :env
|
7
|
+
attr_reader :env, :ignore_params
|
8
8
|
|
9
|
-
def initialize(env)
|
10
|
-
@env = env
|
9
|
+
def initialize(env, ignore_params: [])
|
10
|
+
@env, @ignore_params = env, ignore_params
|
11
11
|
|
12
12
|
# sanity checks
|
13
13
|
raise 'http/https required' if env.url.scheme !~ /^https?$/
|
@@ -79,7 +79,16 @@ module HTTPDisk
|
|
79
79
|
|
80
80
|
# Calculate canonical key for a query
|
81
81
|
def querykey(q)
|
82
|
-
q.split('&').sort
|
82
|
+
parts = q.split('&').sort
|
83
|
+
if !ignore_params.empty?
|
84
|
+
parts = parts.map do |part|
|
85
|
+
key, value = part.split('=', 2)
|
86
|
+
next if ignore_params.include?(key)
|
87
|
+
|
88
|
+
"#{key}=#{value}"
|
89
|
+
end.compact
|
90
|
+
end
|
91
|
+
parts.join('&')
|
83
92
|
end
|
84
93
|
|
85
94
|
def default_port?
|
data/lib/httpdisk/client.rb
CHANGED
@@ -2,25 +2,26 @@ require 'faraday'
|
|
2
2
|
require 'logger'
|
3
3
|
|
4
4
|
module HTTPDisk
|
5
|
-
OPTIONS = {
|
6
|
-
dir: File.join(ENV['HOME'], 'httpdisk'),
|
7
|
-
expires_in: nil,
|
8
|
-
force: false,
|
9
|
-
force_errors: false,
|
10
|
-
logger: false,
|
11
|
-
}.freeze
|
12
|
-
|
13
5
|
# Middleware and main entry point.
|
14
6
|
class Client < Faraday::Middleware
|
15
7
|
attr_reader :cache, :options
|
16
8
|
|
17
9
|
def initialize(app, options = {})
|
18
|
-
|
10
|
+
options = Sloptions.parse(options) do
|
11
|
+
_1.string :dir, default: File.join(ENV['HOME'], 'httpdisk')
|
12
|
+
_1.integer :expires_in
|
13
|
+
_1.boolean :force
|
14
|
+
_1.boolean :force_errors
|
15
|
+
_1.array :ignore_params, default: []
|
16
|
+
_1.on :logger, type: [:boolean, Logger]
|
17
|
+
end
|
18
|
+
|
19
|
+
super(app, options)
|
19
20
|
@cache = Cache.new(options)
|
20
21
|
end
|
21
22
|
|
22
23
|
def call(env)
|
23
|
-
cache_key = CacheKey.new(env)
|
24
|
+
cache_key = CacheKey.new(env, ignore_params: ignore_params)
|
24
25
|
logger&.info("#{env.method.upcase} #{env.url} (#{cache.status(cache_key)})")
|
25
26
|
|
26
27
|
if cached_response = read(cache_key, env)
|
@@ -100,10 +101,18 @@ module HTTPDisk
|
|
100
101
|
err.to_s =~ /#{proxy.host}.*#{proxy.port}/
|
101
102
|
end
|
102
103
|
|
104
|
+
#
|
105
|
+
# options
|
106
|
+
#
|
107
|
+
|
108
|
+
def ignore_params
|
109
|
+
@ignore_params ||= options[:ignore_params].map { CGI.escape(_1.to_s) }.to_set
|
110
|
+
end
|
111
|
+
|
103
112
|
def logger
|
104
|
-
return
|
113
|
+
return if !options[:logger]
|
105
114
|
|
106
|
-
@logger
|
115
|
+
@logger ||= case options[:logger]
|
107
116
|
when true then Logger.new($stderr)
|
108
117
|
when Logger then options[:logger]
|
109
118
|
end
|
data/lib/httpdisk/payload.rb
CHANGED
@@ -0,0 +1,105 @@
|
|
1
|
+
module HTTPDisk
|
2
|
+
# Like Slop, but for sanity checking method options. Useful for library entry
|
3
|
+
# points that want to be strict. Example usage:
|
4
|
+
#
|
5
|
+
# options = Sloptions.new(options) do
|
6
|
+
# _1.boolean :force
|
7
|
+
# _1.integer :retries, required: true
|
8
|
+
# _1.string :hello, default: 'world'
|
9
|
+
# ...
|
10
|
+
# end
|
11
|
+
class Sloptions
|
12
|
+
attr_reader :flags
|
13
|
+
|
14
|
+
def self.parse(options, &block)
|
15
|
+
Sloptions.new(&block).parse(options)
|
16
|
+
end
|
17
|
+
|
18
|
+
def initialize
|
19
|
+
@flags = {}
|
20
|
+
yield(self)
|
21
|
+
end
|
22
|
+
|
23
|
+
#
|
24
|
+
# _1.on and friends
|
25
|
+
#
|
26
|
+
|
27
|
+
def on(flag, foptions = {})
|
28
|
+
raise ":#{flag} already defined" if flags[flag]
|
29
|
+
|
30
|
+
flags[flag] = foptions
|
31
|
+
end
|
32
|
+
|
33
|
+
%i[array boolean float hash integer string symbol].each do |method|
|
34
|
+
define_method(method) do |flag, foptions = {}|
|
35
|
+
on(flag, { type: method }.merge(foptions))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
alias bool boolean
|
39
|
+
|
40
|
+
#
|
41
|
+
# return parsed options
|
42
|
+
#
|
43
|
+
|
44
|
+
def parse(options)
|
45
|
+
# defaults
|
46
|
+
options = defaults.merge(options.compact)
|
47
|
+
|
48
|
+
flags.each do |flag, foptions|
|
49
|
+
# nil check
|
50
|
+
value = options[flag]
|
51
|
+
if value.nil?
|
52
|
+
raise ArgumentError, ":#{flag} is required" if foptions[:required]
|
53
|
+
|
54
|
+
next
|
55
|
+
end
|
56
|
+
|
57
|
+
# type cast (for boolean)
|
58
|
+
if foptions[:type] == :boolean
|
59
|
+
value = options[flag] = !!options[flag]
|
60
|
+
end
|
61
|
+
|
62
|
+
# type check
|
63
|
+
types = Array(foptions[:type])
|
64
|
+
raise ArgumentError, error_message(flag, value, types) if !valid?(value, types)
|
65
|
+
end
|
66
|
+
|
67
|
+
# return
|
68
|
+
options
|
69
|
+
end
|
70
|
+
|
71
|
+
protected
|
72
|
+
|
73
|
+
def defaults
|
74
|
+
flags.map { |flag, foptions| [flag, foptions[:default]] }.to_h.compact
|
75
|
+
end
|
76
|
+
|
77
|
+
# does value match valid?
|
78
|
+
def valid?(value, types)
|
79
|
+
types.any? do
|
80
|
+
case _1
|
81
|
+
when :array then true if value.is_a?(Array)
|
82
|
+
when :boolean then true # in Ruby everything is a boolean
|
83
|
+
when :float then true if value.is_a?(Float) || value.is_a?(Integer)
|
84
|
+
when :hash then true if value.is_a?(Hash)
|
85
|
+
when :integer then true if value.is_a?(Integer)
|
86
|
+
when :string then true if value.is_a?(String)
|
87
|
+
when :symbol then true if value.is_a?(Symbol)
|
88
|
+
when Class then true if value.is_a?(_1) # for custom checks
|
89
|
+
else
|
90
|
+
raise "unknown flag type #{_1.inspect}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# nice error message for when value is invalid
|
96
|
+
def error_message(flag, value, valid)
|
97
|
+
classes = valid.compact.map do
|
98
|
+
s = _1.to_s
|
99
|
+
s = s.downcase if s =~ /\b(Array|Float|Hash|Integer|String|Symbol)\b/
|
100
|
+
s
|
101
|
+
end.join('/')
|
102
|
+
"expected :#{flag} to be #{classes}, not #{value.inspect}"
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/lib/httpdisk/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: httpdisk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Doppelt
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-05-
|
11
|
+
date: 2021-05-20 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: faraday
|
@@ -92,6 +92,7 @@ files:
|
|
92
92
|
- lib/httpdisk/client.rb
|
93
93
|
- lib/httpdisk/error.rb
|
94
94
|
- lib/httpdisk/payload.rb
|
95
|
+
- lib/httpdisk/sloptions.rb
|
95
96
|
- lib/httpdisk/version.rb
|
96
97
|
- logo.svg
|
97
98
|
homepage: http://github.com/gurgeous/httpdisk
|