web_fetch 0.4.0 → 0.5.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/.gitignore +2 -0
- data/.strong_versions.yml +6 -0
- data/Makefile +11 -2
- data/README.md +25 -6
- data/Rakefile +5 -3
- data/bin/strong_versions +29 -0
- data/config/locales/en.yml +0 -3
- data/docker/Dockerfile +2 -2
- data/lib/web_fetch.rb +4 -1
- data/lib/web_fetch/client.rb +28 -29
- data/lib/web_fetch/concerns/http_helpers.rb +8 -35
- data/lib/web_fetch/gatherer.rb +65 -3
- data/lib/web_fetch/logger.rb +1 -2
- data/lib/web_fetch/promise.rb +3 -3
- data/lib/web_fetch/request.rb +8 -15
- data/lib/web_fetch/resources.rb +12 -22
- data/lib/web_fetch/response.rb +15 -10
- data/lib/web_fetch/retriever.rb +6 -25
- data/lib/web_fetch/server.rb +15 -28
- data/lib/web_fetch/storage.rb +17 -13
- data/lib/web_fetch/storage/memcached.rb +45 -0
- data/lib/web_fetch/storage/memory.rb +35 -0
- data/lib/web_fetch/storage/redis.rb +45 -0
- data/lib/web_fetch/version.rb +1 -1
- data/manifest +64 -0
- data/spec/client_spec.rb +3 -6
- data/spec/gatherer_spec.rb +38 -21
- data/spec/promise_spec.rb +49 -11
- data/spec/resources_spec.rb +14 -17
- data/spec/response_spec.rb +13 -9
- data/spec/retriever_spec.rb +16 -17
- data/spec/router_spec.rb +6 -4
- data/spec/spec_helper.rb +15 -7
- data/spec/storage/memcached_spec.rb +27 -0
- data/spec/storage/memory_spec.rb +5 -0
- data/spec/storage/redis_spec.rb +27 -0
- data/spec/storage/shared_examples.rb +27 -0
- data/web_fetch.gemspec +9 -5
- metadata +75 -11
- data/lib/web_fetch/concerns/event_machine_helpers.rb +0 -57
- data/spec/storage_spec.rb +0 -27
data/spec/router_spec.rb
CHANGED
@@ -10,7 +10,9 @@ describe WebFetch::Router do
|
|
10
10
|
describe '#route' do
|
11
11
|
it 'provides a route to GET /' do
|
12
12
|
expect(router.route('/'))
|
13
|
-
.to eql(
|
13
|
+
.to eql(
|
14
|
+
status: 200, command: 'root', payload: { application: 'WebFetch' }
|
15
|
+
)
|
14
16
|
end
|
15
17
|
|
16
18
|
it 'provides a route to POST /gather' do
|
@@ -39,9 +41,9 @@ describe WebFetch::Router do
|
|
39
41
|
|
40
42
|
it 'returns appropriate response when invaid json provided' do
|
41
43
|
response = router.route('/gather',
|
42
|
-
|
43
|
-
|
44
|
-
|
44
|
+
method: 'POST',
|
45
|
+
query_string: 'json=uh oh :(',
|
46
|
+
server: nil)
|
45
47
|
expect(response).to eql(status: 400, payload: I18n.t(:bad_json))
|
46
48
|
end
|
47
49
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
4
|
-
require 'pp'
|
3
|
+
require 'betterp'
|
5
4
|
require 'byebug'
|
6
|
-
require '
|
5
|
+
require 'pp'
|
7
6
|
require 'rspec/its'
|
7
|
+
require 'web_fetch'
|
8
|
+
require 'webmock/rspec'
|
9
|
+
|
10
|
+
require File.join(__dir__, 'storage', 'shared_examples')
|
8
11
|
|
9
12
|
WebMock.disable_net_connect!(allow_localhost: true)
|
10
13
|
|
@@ -33,8 +36,12 @@ module WebFetch
|
|
33
36
|
class MockServer
|
34
37
|
def gather(requests); end
|
35
38
|
|
39
|
+
def self.storage
|
40
|
+
@storage ||= Storage::Memory.new
|
41
|
+
end
|
42
|
+
|
36
43
|
def storage
|
37
|
-
|
44
|
+
self.class.storage
|
38
45
|
end
|
39
46
|
end
|
40
47
|
end
|
@@ -44,13 +51,14 @@ RSpec.configure do |config|
|
|
44
51
|
expectations.include_chain_clauses_in_custom_matcher_descriptions = true
|
45
52
|
end
|
46
53
|
|
54
|
+
config.before(:each) { WebFetch::MockServer.storage.clear }
|
47
55
|
config.mock_with :rspec do |mocks|
|
48
56
|
mocks.verify_partial_doubles = true
|
49
57
|
end
|
50
58
|
|
51
59
|
config.shared_context_metadata_behavior = :apply_to_host_groups
|
52
60
|
|
53
|
-
config.order = :random
|
54
|
-
|
55
|
-
Kernel.srand config.seed
|
61
|
+
# config.order = :random
|
62
|
+
#
|
63
|
+
# Kernel.srand config.seed
|
56
64
|
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe WebFetch::Storage::Memcached do
|
4
|
+
before(:all) do
|
5
|
+
class MockDalliClient
|
6
|
+
def initialize(*_args)
|
7
|
+
@state = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def set(key, value)
|
11
|
+
@state[key] = value
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(key)
|
15
|
+
@state[key]
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete(key)
|
19
|
+
@state.delete(key)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
subject { described_class.new(MockDalliClient) }
|
25
|
+
|
26
|
+
it_behaves_like 'a storage adapter'
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
describe WebFetch::Storage::Redis do
|
4
|
+
before(:all) do
|
5
|
+
class MockRedisClient
|
6
|
+
def initialize(*_args)
|
7
|
+
@state = {}
|
8
|
+
end
|
9
|
+
|
10
|
+
def set(key, value, _options = {})
|
11
|
+
@state[key] = value
|
12
|
+
end
|
13
|
+
|
14
|
+
def get(key)
|
15
|
+
@state[key]
|
16
|
+
end
|
17
|
+
|
18
|
+
def del(key)
|
19
|
+
@state.delete(key)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
subject { described_class.new(MockRedisClient) }
|
25
|
+
|
26
|
+
it_behaves_like 'a storage adapter'
|
27
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
RSpec.shared_examples 'a storage adapter' do
|
4
|
+
describe '.store' do
|
5
|
+
it 'accepts a key and value to store' do
|
6
|
+
expect do
|
7
|
+
subject.store(:key, 'value')
|
8
|
+
end.to_not raise_error
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '.fetch' do
|
13
|
+
it 'fetches stored values' do
|
14
|
+
subject.store(:key, 'value')
|
15
|
+
expect(subject.fetch(:key)).to eql 'value'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe '.delete' do
|
20
|
+
it 'deletes stored values' do
|
21
|
+
subject.store(:key, 'value')
|
22
|
+
expect(subject.fetch(:key)).to eql 'value'
|
23
|
+
subject.delete(:key)
|
24
|
+
expect(subject.fetch(:key)).to eql nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
data/web_fetch.gemspec
CHANGED
@@ -13,7 +13,7 @@ Gem::Specification.new do |s|
|
|
13
13
|
s.description = 'Fetches HTTP responses as batch requests concurrently'
|
14
14
|
s.authors = ['Bob Farrell']
|
15
15
|
s.email = 'robertanthonyfarrell@gmail.com'
|
16
|
-
s.files =
|
16
|
+
s.files = File.read(File.join(__dir__, 'manifest')).split
|
17
17
|
s.homepage = 'https://github.com/bobf/web_fetch'
|
18
18
|
s.licenses = ['MIT']
|
19
19
|
s.require_paths = ['lib']
|
@@ -22,22 +22,26 @@ Gem::Specification.new do |s|
|
|
22
22
|
|
23
23
|
s.add_dependency 'activesupport', '>= 4.0'
|
24
24
|
s.add_dependency 'daemons', '~> 1.2'
|
25
|
+
s.add_dependency 'dalli', '~> 2.7'
|
25
26
|
s.add_dependency 'em-http-request', '~> 1.1'
|
26
|
-
s.add_dependency 'em-logger', '~> 0.1'
|
27
|
+
s.add_dependency 'em-logger', '~> 0.1.0'
|
27
28
|
s.add_dependency 'eventmachine', '~> 1.0'
|
28
|
-
s.add_dependency 'eventmachine_httpserver', '~> 0.2'
|
29
|
-
s.add_dependency 'faraday', '~> 0.
|
29
|
+
s.add_dependency 'eventmachine_httpserver', '~> 0.2.1'
|
30
|
+
s.add_dependency 'faraday', '~> 0.15.4'
|
30
31
|
s.add_dependency 'hanami-router', '~> 1.0'
|
31
32
|
s.add_dependency 'hanami-utils', '~> 1.0'
|
32
33
|
s.add_dependency 'i18n', '>= 0.7'
|
33
34
|
s.add_dependency 'rack', '>= 1.6'
|
35
|
+
s.add_dependency 'redis', '~> 4.1'
|
34
36
|
s.add_dependency 'subprocess', '~> 1.3'
|
35
37
|
|
38
|
+
s.add_development_dependency 'betterp', '~> 0.1.2'
|
36
39
|
s.add_development_dependency 'byebug', '~> 9.0'
|
37
40
|
s.add_development_dependency 'rake', '~> 12.3'
|
38
41
|
s.add_development_dependency 'rspec', '~> 3.5'
|
39
42
|
s.add_development_dependency 'rspec-its', '~> 1.2'
|
40
|
-
s.add_development_dependency 'rubocop', '~> 0.
|
43
|
+
s.add_development_dependency 'rubocop', '~> 0.60.0'
|
44
|
+
s.add_development_dependency 'strong_versions', '~> 0.3.2'
|
41
45
|
s.add_development_dependency 'webmock', '~> 3.4'
|
42
46
|
end
|
43
47
|
# rubocop:enable Metrics/BlockLength
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: web_fetch
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Bob Farrell
|
@@ -38,6 +38,20 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '1.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: dalli
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '2.7'
|
48
|
+
type: :runtime
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '2.7'
|
41
55
|
- !ruby/object:Gem::Dependency
|
42
56
|
name: em-http-request
|
43
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -58,14 +72,14 @@ dependencies:
|
|
58
72
|
requirements:
|
59
73
|
- - "~>"
|
60
74
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
75
|
+
version: 0.1.0
|
62
76
|
type: :runtime
|
63
77
|
prerelease: false
|
64
78
|
version_requirements: !ruby/object:Gem::Requirement
|
65
79
|
requirements:
|
66
80
|
- - "~>"
|
67
81
|
- !ruby/object:Gem::Version
|
68
|
-
version:
|
82
|
+
version: 0.1.0
|
69
83
|
- !ruby/object:Gem::Dependency
|
70
84
|
name: eventmachine
|
71
85
|
requirement: !ruby/object:Gem::Requirement
|
@@ -86,28 +100,28 @@ dependencies:
|
|
86
100
|
requirements:
|
87
101
|
- - "~>"
|
88
102
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
103
|
+
version: 0.2.1
|
90
104
|
type: :runtime
|
91
105
|
prerelease: false
|
92
106
|
version_requirements: !ruby/object:Gem::Requirement
|
93
107
|
requirements:
|
94
108
|
- - "~>"
|
95
109
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
110
|
+
version: 0.2.1
|
97
111
|
- !ruby/object:Gem::Dependency
|
98
112
|
name: faraday
|
99
113
|
requirement: !ruby/object:Gem::Requirement
|
100
114
|
requirements:
|
101
115
|
- - "~>"
|
102
116
|
- !ruby/object:Gem::Version
|
103
|
-
version:
|
117
|
+
version: 0.15.4
|
104
118
|
type: :runtime
|
105
119
|
prerelease: false
|
106
120
|
version_requirements: !ruby/object:Gem::Requirement
|
107
121
|
requirements:
|
108
122
|
- - "~>"
|
109
123
|
- !ruby/object:Gem::Version
|
110
|
-
version:
|
124
|
+
version: 0.15.4
|
111
125
|
- !ruby/object:Gem::Dependency
|
112
126
|
name: hanami-router
|
113
127
|
requirement: !ruby/object:Gem::Requirement
|
@@ -164,6 +178,20 @@ dependencies:
|
|
164
178
|
- - ">="
|
165
179
|
- !ruby/object:Gem::Version
|
166
180
|
version: '1.6'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: redis
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - "~>"
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '4.1'
|
188
|
+
type: :runtime
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - "~>"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '4.1'
|
167
195
|
- !ruby/object:Gem::Dependency
|
168
196
|
name: subprocess
|
169
197
|
requirement: !ruby/object:Gem::Requirement
|
@@ -178,6 +206,20 @@ dependencies:
|
|
178
206
|
- - "~>"
|
179
207
|
- !ruby/object:Gem::Version
|
180
208
|
version: '1.3'
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: betterp
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - "~>"
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: 0.1.2
|
216
|
+
type: :development
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - "~>"
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: 0.1.2
|
181
223
|
- !ruby/object:Gem::Dependency
|
182
224
|
name: byebug
|
183
225
|
requirement: !ruby/object:Gem::Requirement
|
@@ -240,14 +282,28 @@ dependencies:
|
|
240
282
|
requirements:
|
241
283
|
- - "~>"
|
242
284
|
- !ruby/object:Gem::Version
|
243
|
-
version: 0.
|
285
|
+
version: 0.60.0
|
286
|
+
type: :development
|
287
|
+
prerelease: false
|
288
|
+
version_requirements: !ruby/object:Gem::Requirement
|
289
|
+
requirements:
|
290
|
+
- - "~>"
|
291
|
+
- !ruby/object:Gem::Version
|
292
|
+
version: 0.60.0
|
293
|
+
- !ruby/object:Gem::Dependency
|
294
|
+
name: strong_versions
|
295
|
+
requirement: !ruby/object:Gem::Requirement
|
296
|
+
requirements:
|
297
|
+
- - "~>"
|
298
|
+
- !ruby/object:Gem::Version
|
299
|
+
version: 0.3.2
|
244
300
|
type: :development
|
245
301
|
prerelease: false
|
246
302
|
version_requirements: !ruby/object:Gem::Requirement
|
247
303
|
requirements:
|
248
304
|
- - "~>"
|
249
305
|
- !ruby/object:Gem::Version
|
250
|
-
version: 0.
|
306
|
+
version: 0.3.2
|
251
307
|
- !ruby/object:Gem::Dependency
|
252
308
|
name: webmock
|
253
309
|
requirement: !ruby/object:Gem::Requirement
|
@@ -274,6 +330,7 @@ files:
|
|
274
330
|
- ".rspec"
|
275
331
|
- ".rubocop.yml"
|
276
332
|
- ".ruby-version"
|
333
|
+
- ".strong_versions.yml"
|
277
334
|
- Gemfile
|
278
335
|
- LICENSE
|
279
336
|
- Makefile
|
@@ -282,6 +339,7 @@ files:
|
|
282
339
|
- TODO
|
283
340
|
- bin/rspec
|
284
341
|
- bin/rubocop
|
342
|
+
- bin/strong_versions
|
285
343
|
- bin/web_fetch_control
|
286
344
|
- bin/web_fetch_server
|
287
345
|
- config/locales/en.yml
|
@@ -293,7 +351,6 @@ files:
|
|
293
351
|
- lib/web_fetch.rb
|
294
352
|
- lib/web_fetch/client.rb
|
295
353
|
- lib/web_fetch/concerns/client_http.rb
|
296
|
-
- lib/web_fetch/concerns/event_machine_helpers.rb
|
297
354
|
- lib/web_fetch/concerns/http_helpers.rb
|
298
355
|
- lib/web_fetch/concerns/validatable.rb
|
299
356
|
- lib/web_fetch/errors.rb
|
@@ -308,7 +365,11 @@ files:
|
|
308
365
|
- lib/web_fetch/router.rb
|
309
366
|
- lib/web_fetch/server.rb
|
310
367
|
- lib/web_fetch/storage.rb
|
368
|
+
- lib/web_fetch/storage/memcached.rb
|
369
|
+
- lib/web_fetch/storage/memory.rb
|
370
|
+
- lib/web_fetch/storage/redis.rb
|
311
371
|
- lib/web_fetch/version.rb
|
372
|
+
- manifest
|
312
373
|
- spec/client_spec.rb
|
313
374
|
- spec/concerns/validatable_spec.rb
|
314
375
|
- spec/features/http_fetching_spec.rb
|
@@ -323,7 +384,10 @@ files:
|
|
323
384
|
- spec/router_spec.rb
|
324
385
|
- spec/server_spec.rb
|
325
386
|
- spec/spec_helper.rb
|
326
|
-
- spec/
|
387
|
+
- spec/storage/memcached_spec.rb
|
388
|
+
- spec/storage/memory_spec.rb
|
389
|
+
- spec/storage/redis_spec.rb
|
390
|
+
- spec/storage/shared_examples.rb
|
327
391
|
- swagger.yaml
|
328
392
|
- web_fetch.gemspec
|
329
393
|
homepage: https://github.com/bobf/web_fetch
|
@@ -1,57 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module WebFetch
|
4
|
-
# EventMachine layer-specific helpers
|
5
|
-
module EventMachineHelpers
|
6
|
-
def request_async(target)
|
7
|
-
request = target[:request]
|
8
|
-
target[:start_time] = Time.now.utc
|
9
|
-
async_request = EM::HttpRequest.new(request[:url])
|
10
|
-
method = request.fetch(:method, 'GET').downcase.to_sym
|
11
|
-
async_request.public_send(
|
12
|
-
method,
|
13
|
-
head: request[:headers],
|
14
|
-
query: request.fetch(:query, {}),
|
15
|
-
body: request.fetch(:body, nil)
|
16
|
-
)
|
17
|
-
end
|
18
|
-
|
19
|
-
def apply_callbacks(request)
|
20
|
-
request[:deferred].callback do
|
21
|
-
Logger.debug("HTTP fetch complete for uid: #{request[:uid]}")
|
22
|
-
save_response_time(request)
|
23
|
-
request[:succeeded] = true
|
24
|
-
end
|
25
|
-
|
26
|
-
request[:deferred].errback do
|
27
|
-
Logger.debug("HTTP fetch failed for uid: #{request[:uid]}")
|
28
|
-
save_response_time(request)
|
29
|
-
request[:failed] = true
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def wait_for_response(request, response)
|
34
|
-
tick_loop(request, response)
|
35
|
-
end
|
36
|
-
|
37
|
-
def tick_loop(request, response)
|
38
|
-
# XXX There may be a much nicer way to wait for an async task to complete
|
39
|
-
# before returning a response but I couldn't figure it out, so I used
|
40
|
-
# EM.tick_loop which effectively does the same as a Twisted deferred
|
41
|
-
# callback chain, just much more explicitly.
|
42
|
-
EM.tick_loop do
|
43
|
-
if request[:succeeded]
|
44
|
-
succeed(request, response)
|
45
|
-
:stop
|
46
|
-
elsif request[:failed]
|
47
|
-
fail_(request, response)
|
48
|
-
:stop
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def save_response_time(request)
|
54
|
-
request[:response_time] = Time.now.utc - request[:start_time]
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|