httpdisk 0.2.0 → 0.3.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
  SHA256:
3
- metadata.gz: d311812fa4d8d034c9eda6b9b18df36e21eb14ebc9d632cd1fe071268ca77786
4
- data.tar.gz: 90e891451c1805d6d8ba5ad268bc2682528954a2a8acd37546f69ab43d493311
3
+ metadata.gz: 0e3b6af20bf5a6b76a23a6d170328c040cf3f0715e012c392739b70ab6911bee
4
+ data.tar.gz: 46385301b89bc19f2e8862bcc9bf48b0f1b14fafbd2fa4fc8542b7bf4dcfe930
5
5
  SHA512:
6
- metadata.gz: c61e162e26d8a7b86fe00165e095d72f99415e8e5a4245513758731a009186999d5cf171db45c8b83115dae8c376e8391aa0bfbd00d3a2fd086c0451f77269ad
7
- data.tar.gz: 1ad5bdb0a51f2b84822a6164379a724a7b03338b30f9c427b956bbe4263b0326720aea3bcb02b1fa37110a8aa5adc24dc8f3f257733b11667df913b863da0d52
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.2.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.8.2)
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
- system('find . | entr -c rake test')
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
- system('bundle exec rubocop -A .', exception: true)
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
- system('gem build --quiet httpdisk.gemspec', exception: true)
43
+ sh 'gem build --quiet httpdisk.gemspec'
43
44
  end
44
45
 
45
46
  task install: :build do
46
- system("gem install --quiet httpdisk-#{spec.version}.gem", exception: true)
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
- system("git tag -a #{spec.version} -m 'Tagging #{spec.version}'", exception: true)
53
- system('git push --tags', exception: true)
54
- system("gem push httpdisk-#{spec.version}.gem", exception: true)
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
@@ -5,6 +5,7 @@ require 'httpdisk/cli'
5
5
  require 'httpdisk/client'
6
6
  require 'httpdisk/error'
7
7
  require 'httpdisk/payload'
8
+ require 'httpdisk/sloptions'
8
9
  require 'httpdisk/version'
9
10
 
10
11
  module HTTPDisk
@@ -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.error_999? ? :error : :hit
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.error_999?
57
+ return :force if force_errors? && payload.error?
73
58
 
74
59
  payload
75
60
  end
@@ -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.join('&')
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?
@@ -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
- super(app, options = OPTIONS.merge(options.compact))
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 @logger if defined?(@logger)
113
+ return if !options[:logger]
105
114
 
106
- @logger = case options[:logger]
115
+ @logger ||= case options[:logger]
107
116
  when true then Logger.new($stderr)
108
117
  when Logger then options[:logger]
109
118
  end
@@ -39,8 +39,8 @@ module HTTPDisk
39
39
  @headers = Faraday::Utils::Headers.new
40
40
  end
41
41
 
42
- def error_999?
43
- status == HTTPDisk::ERROR_STATUS
42
+ def error?
43
+ status >= 400
44
44
  end
45
45
 
46
46
  def write(f)
@@ -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
@@ -1,3 +1,3 @@
1
1
  module HTTPDisk
2
- VERSION = '0.2.0'.freeze
2
+ VERSION = '0.3.0'.freeze
3
3
  end
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.2.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-03 00:00:00.000000000 Z
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