web_fetch 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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(status: 200, payload: { application: 'WebFetch' })
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
- method: 'POST',
43
- query_string: 'json=uh oh :(',
44
- server: nil)
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
@@ -1,10 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'web_fetch'
4
- require 'pp'
3
+ require 'betterp'
5
4
  require 'byebug'
6
- require 'webmock/rspec'
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
- Storage
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,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe WebFetch::Storage::Memory do
4
+ it_behaves_like 'a storage adapter'
5
+ 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
@@ -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 = `git ls-files`.split($RS)
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.9'
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.59.2'
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.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: '0.1'
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: '0.1'
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: '0.2'
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: '0.2'
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: '0.9'
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: '0.9'
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.59.2
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.59.2
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/storage_spec.rb
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