dagger 1.6.1 → 1.9.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7d9231125c099e4e5e391e00719bede1da167f044242b6c2604d9690dee0d842
4
- data.tar.gz: e997c56d4dc24be6cb747e5f1c350cffe2e804dd852a712ca3979381e4c6df03
3
+ metadata.gz: 692b25152c7fd3229ad3dc13bd25250b613e55a211e489b8835258b18405db19
4
+ data.tar.gz: 5560f64e580e4f72f9e243eaf1c5eed1b389e439074607dc24bdd8fbe9c532f0
5
5
  SHA512:
6
- metadata.gz: 3e8c581c55ac7eb75af99f1ec50e2d85324d30f45faae37b45bf920f0bc6a63b5c38ba2ae99f5b98913698208f158dc8f0775e9b2833958a92a4a88dce392547
7
- data.tar.gz: f870dc8b936d2e64e4d4dc0b06a94cd22527c0675f3317b5cb69b1c6c6e6263fd5a58a6fe35e37e8b87e2f74b535713127c92c525afbdcdb9e56ca9e64def6b8
6
+ metadata.gz: 85d160f0130c12fde8b2723cd54bcaf4a75c4f48319cb040c4725a93e6cc09d4f8a68a7977135f33c38922583cc8de481bc078ea6d3ff5636c42c3154a5dca74
7
+ data.tar.gz: 9bd29716a9e84b75e898f71b00e8fc145550fcccd15b2c0006172e56e3e5454b62668bc4536dacde371a33b429f437a942354368de4cce0394ff73296cefa8e8
@@ -19,7 +19,6 @@ Gem::Specification.new do |s|
19
19
  s.add_development_dependency "rspec-mocks"
20
20
  s.add_development_dependency "rspec-expectations"
21
21
 
22
- s.add_runtime_dependency "net-http-persistent", "~> 3.0"
23
22
  s.add_runtime_dependency "oj", "~> 2.1"
24
23
  s.add_runtime_dependency "ox", "~> 2.4"
25
24
 
@@ -1,10 +1,10 @@
1
1
  require 'dagger/version'
2
2
  require 'dagger/response'
3
3
  require 'dagger/parsers'
4
-
5
- require 'net/http/persistent'
4
+ require 'dagger/connection_manager'
6
5
  require 'net/https'
7
6
  require 'base64'
7
+ require 'erb'
8
8
 
9
9
  class URI::HTTP
10
10
  def scheme_and_host
@@ -14,13 +14,19 @@ end
14
14
 
15
15
  module Dagger
16
16
 
17
- DAGGER_NAME = "Dagger/#{VERSION}"
17
+ DAGGER_NAME = "Dagger/#{VERSION}".freeze
18
18
  REDIRECT_CODES = [301, 302, 303].freeze
19
19
  DEFAULT_RETRY_WAIT = 5.freeze # seconds
20
20
  DEFAULT_HEADERS = {
21
21
  'Accept' => '*/*',
22
- 'User-Agent' => "#{DAGGER_NAME} (Ruby Net::HTTP Wrapper, like curl)"
23
- }
22
+ 'User-Agent' => "#{DAGGER_NAME} (Ruby Net::HTTP wrapper, like curl)"
23
+ }.freeze
24
+
25
+ DEFAULTS = {
26
+ open_timeout: 10,
27
+ read_timeout: 10,
28
+ keep_alive_timeout: 10
29
+ }.freeze
24
30
 
25
31
  module Utils
26
32
 
@@ -34,7 +40,7 @@ module Dagger
34
40
  end
35
41
 
36
42
  def self.resolve_uri(uri, host = nil, query = nil)
37
- uri = host + uri if uri.to_s['//'].nil? && host
43
+ uri = host + uri if uri[0] == '/' && host
38
44
  uri = parse_uri(uri.to_s)
39
45
  uri.path.sub!(/\?.*|$/, '?' + Utils.encode(query)) if query and query.any?
40
46
  uri
@@ -50,7 +56,7 @@ module Dagger
50
56
  when Array then obj.map { |v| encode(v, "#{key}[]") }.join('&')
51
57
  when nil then ''
52
58
  else
53
- "#{key}=#{URI.escape(obj.to_s)}"
59
+ "#{key}=#{ERB::Util.url_encode(obj.to_s)}"
54
60
  end
55
61
  end
56
62
 
@@ -62,25 +68,40 @@ module Dagger
62
68
 
63
69
  class Client
64
70
 
65
- def self.init(uri, opts)
66
- uri = Utils.parse_uri(uri)
67
- http = if opts.delete(:persistent)
68
- pool_size = opts[:pool_size] || Net::HTTP::Persistent::DEFAULT_POOL_SIZE
69
- Net::HTTP::Persistent.new(name: DAGGER_NAME, pool_size: pool_size)
70
- else
71
- Net::HTTP.new(opts[:ip] || uri.host, uri.port)
71
+ def self.init_persistent(opts = {})
72
+ # this line below forces one connection manager between multiple threads
73
+ # @persistent ||= Dagger::ConnectionManager.new(opts)
74
+
75
+ # here we initialize a connection manager for each thread
76
+ Thread.current[:dagger_persistent] ||= begin
77
+ Dagger::ConnectionManager.new(opts)
72
78
  end
79
+ end
80
+
81
+ def self.init_connection(uri, opts = {})
82
+ http = Net::HTTP.new(opts[:ip] || uri.host, uri.port)
73
83
 
74
84
  if uri.port == 443
75
85
  http.use_ssl = true if http.respond_to?(:use_ssl=) # persistent does it automatically
76
86
  http.verify_mode = opts[:verify_ssl] === false ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
77
87
  end
78
88
 
79
- [:open_timeout, :read_timeout, :ssl_version, :ciphers].each do |key|
80
- http.send("#{key}=", opts[key]) if opts.has_key?(key)
89
+ [:keep_alive_timeout, :open_timeout, :read_timeout, :ssl_version, :ciphers].each do |key|
90
+ http.send("#{key}=", opts[key] || DEFAULTS[key]) if (opts.has_key?(key) || DEFAULTS.has_key?(key))
91
+ end
92
+
93
+ http
94
+ end
95
+
96
+ def self.init(uri, opts)
97
+ uri = Utils.parse_uri(uri)
98
+
99
+ http = if opts.delete(:persistent)
100
+ init_persistent(opts)
101
+ else
102
+ init_connection(uri, opts)
81
103
  end
82
104
 
83
- # new(http, [uri.scheme, uri.host].join('://'))
84
105
  new(http, uri.scheme_and_host)
85
106
  end
86
107
 
@@ -91,7 +112,9 @@ module Dagger
91
112
  def get(uri, opts = {})
92
113
  uri = Utils.resolve_uri(uri, @host, opts[:query])
93
114
 
94
- raise ArgumentError.new("#{uri.scheme_and_host} does not match #{@host}") if @host != uri.scheme_and_host
115
+ if @host != uri.scheme_and_host
116
+ raise ArgumentError.new("#{uri.scheme_and_host} does not match #{@host}")
117
+ end
95
118
 
96
119
  opts[:follow] = 10 if opts[:follow] == true
97
120
  headers = opts[:headers] || {}
@@ -109,23 +132,23 @@ module Dagger
109
132
  @http.start unless @http.started?
110
133
  resp, data = @http.request(request)
111
134
  else # persistent
112
- puts @http.pool.inspect
113
- resp, data = @http.request(uri, request)
135
+ resp, data = @http.send_request(uri, request)
114
136
  end
115
137
 
116
138
  if REDIRECT_CODES.include?(resp.code.to_i) && resp['Location'] && (opts[:follow] && opts[:follow] > 0)
117
139
  opts[:follow] -= 1
118
- puts "Following redirect to #{resp['Location']}"
140
+ debug "Following redirect to #{resp['Location']}"
119
141
  return get(resp['Location'], opts)
120
142
  end
121
143
 
122
144
  @response = build_response(resp, data || resp.body)
123
145
 
124
146
  rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EINVAL, Timeout::Error, \
125
- SocketError, EOFError, Net::ReadTimeout, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError => e
147
+ Net::OpenTimeout, Net::ReadTimeout, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, \
148
+ SocketError, EOFError, OpenSSL::SSL::SSLError => e
126
149
 
127
150
  if retries = opts[:retries] and retries.to_i > 0
128
- puts "Got #{e.class}! Retrying in a sec (#{retries} retries left)"
151
+ debug "Got #{e.class}! Retrying in a sec (#{retries} retries left)"
129
152
  sleep (opts[:retry_wait] || DEFAULT_RETRY_WAIT)
130
153
  get(uri, opts.merge(retries: retries - 1))
131
154
  else
@@ -156,7 +179,10 @@ module Dagger
156
179
  end
157
180
 
158
181
  uri = Utils.resolve_uri(uri, @host)
159
- raise ArgumentError.new("#{uri.scheme_and_host} does not match #{@host}") if @host != uri.scheme_and_host
182
+ if @host != uri.scheme_and_host
183
+ raise ArgumentError.new("#{uri.scheme_and_host} does not match #{@host}")
184
+ end
185
+
160
186
  headers = DEFAULT_HEADERS.merge(opts[:headers] || {})
161
187
 
162
188
  query = if data.is_a?(String)
@@ -184,16 +210,17 @@ module Dagger
184
210
  req = Kernel.const_get("Net::HTTP::#{method.capitalize}").new(uri.path, headers)
185
211
  # req.set_form_data(query)
186
212
  req.body = query
187
- resp, data = @http.request(uri, req)
213
+ resp, data = @http.send_request(uri, req)
188
214
  end
189
215
 
190
216
  @response = build_response(resp, data || resp.body)
191
217
 
192
218
  rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EINVAL, Timeout::Error, \
193
- SocketError, EOFError, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError => e
219
+ Net::OpenTimeout, Net::ReadTimeout, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, \
220
+ SocketError, EOFError, OpenSSL::SSL::SSLError => e
194
221
 
195
222
  if method.to_s.downcase != 'get' && retries = opts[:retries] and retries.to_i > 0
196
- puts "[#{DAGGER_NAME}] Got #{e.class}! Retrying in a sec (#{retries} retries left)"
223
+ debug "[#{DAGGER_NAME}] Got #{e.class}! Retrying in a sec (#{retries} retries left)"
197
224
  sleep (opts[:retry_wait] || DEFAULT_RETRY_WAIT)
198
225
  request(method, uri, data, opts.merge(retries: retries - 1))
199
226
  else
@@ -206,7 +233,7 @@ module Dagger
206
233
  end
207
234
 
208
235
  def open(&block)
209
- if @http.is_a?(Net::HTTP::Persistent)
236
+ if @http.is_a?(Dagger::ConnectionManager)
210
237
  instance_eval(&block)
211
238
  else
212
239
  @http.start do
@@ -216,7 +243,7 @@ module Dagger
216
243
  end
217
244
 
218
245
  def close
219
- if @http.is_a?(Net::HTTP::Persistent)
246
+ if @http.is_a?(Dagger::ConnectionManager)
220
247
  @http.shutdown # calls finish on pool connections
221
248
  else
222
249
  @http.finish if @http.started?
@@ -225,6 +252,10 @@ module Dagger
225
252
 
226
253
  private
227
254
 
255
+ def debug(str)
256
+ puts str if ENV['DEBUGGING']
257
+ end
258
+
228
259
  def build_response(resp, body)
229
260
  resp.extend(Response)
230
261
  resp.set_body(body) unless resp.body
@@ -270,4 +301,4 @@ module Dagger
270
301
 
271
302
  end
272
303
 
273
- end
304
+ end
@@ -0,0 +1,59 @@
1
+ module Dagger
2
+
3
+ class ConnectionManager
4
+
5
+ def initialize(opts = {})
6
+ @opts = {}
7
+ @active_connections = {}
8
+ @mutex = Mutex.new
9
+ end
10
+
11
+ def shutdown
12
+ @mutex.synchronize do
13
+ # puts "Shutting down connections: #{@active_connections.count}"
14
+ @active_connections.each do |_, connection|
15
+ connection.finish
16
+ end
17
+ @active_connections = {}
18
+ end
19
+ end
20
+
21
+ # Gets a connection for a given URI. This is for internal use only as it's
22
+ # subject to change (we've moved between HTTP client schemes in the past
23
+ # and may do it again).
24
+ #
25
+ # `uri` is expected to be a string.
26
+ def connection_for(uri)
27
+ @mutex.synchronize do
28
+ connection = @active_connections[[uri.host, uri.port]]
29
+
30
+ if connection.nil?
31
+ connection = Dagger::Client.init_connection(uri, @opts)
32
+ connection.start
33
+
34
+ @active_connections[[uri.host, uri.port]] = connection
35
+ # puts "#{@active_connections.count} connections"
36
+ end
37
+
38
+ connection
39
+ end
40
+ end
41
+
42
+ # Executes an HTTP request to the given URI with the given method. Also
43
+ # allows a request body, headers, and query string to be specified.
44
+ def send_request(uri, request)
45
+ connection = connection_for(uri)
46
+ @mutex.synchronize do
47
+ begin
48
+ connection.request(request)
49
+ rescue StandardError => err
50
+ err
51
+ end
52
+ end.tap do |result|
53
+ raise(result) if result.is_a?(StandardError)
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ end
@@ -5,12 +5,20 @@ XMLNode = Struct.new(:name, :text, :attributes, :children) do
5
5
  alias_method :to_s, :text
6
6
  alias_method :value, :text
7
7
 
8
+ def to_node
9
+ self
10
+ end
11
+
8
12
  def count
9
13
  raise "Please call #children.count"
10
14
  end
11
15
 
12
- def to_hash
13
- self # for backwards compat
16
+ def keys
17
+ @keys ||= children.collect(&:name)
18
+ end
19
+
20
+ def is_array?
21
+ keys.count != keys.uniq.count
14
22
  end
15
23
 
16
24
  # this lets us traverse an parsed object like this:
@@ -20,30 +28,33 @@ XMLNode = Struct.new(:name, :text, :attributes, :children) do
20
28
  found.empty? ? nil : found.size == 1 ? found.first : found
21
29
  end
22
30
 
31
+ # returns list of XMLNodes with matching names
23
32
  def slice(*arr)
24
33
  Array(arr).flatten.map { |key| self[key] }
25
34
  end
26
35
 
27
- def values(arr = nil, include_empty: false)
28
- if arr
29
- Array(arr).flatten.each_with_object({}) do |key, memo|
36
+ def values(keys_arr = nil, include_empty: false)
37
+ if keys_arr
38
+ Array(keys_arr).flatten.each_with_object({}) do |key, memo|
30
39
  if found = self[key] and (found.to_s || include_empty)
31
40
  memo[key] = found.to_s
32
41
  end
33
42
  end
43
+ elsif is_array?
44
+ children.map(&:values)
34
45
  else
35
- children.each_with_object({}) do |child, memo|
36
- if child.to_s || include_empty
37
- memo[child.name] = child.to_s
38
- end
46
+ children.each_with_object({}) do |child, memo|
47
+ memo[child.name] = child.children.any? ? child.values : child.text
39
48
  end
40
49
  end
41
50
  end
42
51
 
52
+ alias_method :to_hash, :values
53
+ alias_method :to_h, :values
54
+
43
55
  def dig(*paths)
44
56
  list = Array(paths).flatten
45
57
  res = list.reduce([self]) do |parents, key|
46
-
47
58
  if parents
48
59
  found = parents.map do |parent|
49
60
  parent.children.select { |node| node.name.to_s == key.to_s }
@@ -79,14 +90,14 @@ XMLNode = Struct.new(:name, :text, :attributes, :children) do
79
90
  end
80
91
 
81
92
  class Ox::Document
82
- def to_hash
83
- nodes.first.to_hash
93
+ def to_node
94
+ nodes.first.to_node
84
95
  end
85
96
  end
86
97
 
87
98
  class Ox::Element
88
- def to_hash
89
- children = nodes.map { |n| n.class == self.class ? n.to_hash : nil }.compact
99
+ def to_node
100
+ children = nodes.map { |n| n.class == self.class ? n.to_node : nil }.compact
90
101
  XMLNode.new(value, text, attributes, children)
91
102
  end
92
103
  end
@@ -26,7 +26,7 @@ class Parsers
26
26
 
27
27
  def text_xml(body)
28
28
  if res = Ox.parse(body)
29
- res.to_hash
29
+ res.to_node
30
30
  end
31
31
  rescue Ox::ParseError
32
32
  nil
@@ -1,7 +1,7 @@
1
1
  module Dagger
2
2
  MAJOR = 1
3
- MINOR = 6
4
- PATCH = 1
3
+ MINOR = 9
4
+ PATCH = 0
5
5
 
6
6
  VERSION = [MAJOR, MINOR, PATCH].join('.')
7
7
  end
@@ -0,0 +1,151 @@
1
+ require_relative '../dagger'
2
+
3
+ module Dagger
4
+
5
+ module Wrapper
6
+
7
+ def self.included(base)
8
+ base.extend(ClassMethods)
9
+ end
10
+
11
+ module ClassMethods
12
+ def base_url(str = nil)
13
+ if str # set
14
+ @base_url = str
15
+ else
16
+ @base_url or raise "base_url unset!" # get
17
+ end
18
+ end
19
+
20
+ def base_options(opts = nil)
21
+ if opts # set
22
+ @base_options = opts
23
+ else
24
+ @base_options or raise "base_url unset!" # get
25
+ end
26
+ end
27
+ end
28
+
29
+ def initialize(opts = {})
30
+ @logger = opts.delete(:logger)
31
+ @options = opts
32
+ end
33
+
34
+ def get(path, params = {}, opts = {})
35
+ request(:get, path, params, opts)
36
+ end
37
+
38
+ def post(path, params = {}, opts = {})
39
+ request(:post, path, params, opts)
40
+ end
41
+
42
+ def put(path, params = {}, opts = {})
43
+ request(:put, path, params, opts)
44
+ end
45
+
46
+ def patch(path, params = {}, opts = {})
47
+ request(:patch, path, params, opts)
48
+ end
49
+
50
+ def delete(path, params = {}, opts = {})
51
+ request(:delete, path, params, opts)
52
+ end
53
+
54
+ def request(method, path, params = {}, opts = nil)
55
+ url = self.class.base_url + path
56
+ resp = benchmark("#{method} #{path}") do
57
+ http.request(method, url, params, base_options.merge(opts))
58
+ end
59
+
60
+ handle_response(resp, method, path, params)
61
+ end
62
+
63
+ def connect(&block)
64
+ open_http
65
+ if block_given?
66
+ yield
67
+ close_http
68
+ else
69
+ at_exit { close_http }
70
+ end
71
+ end
72
+
73
+ def disconnect
74
+ close_http
75
+ end
76
+
77
+ private
78
+ attr_reader :options
79
+
80
+ def handle_response(resp, method, path, params)
81
+ resp
82
+ end
83
+
84
+ def base_options
85
+ {}
86
+ end
87
+
88
+ def request_options
89
+ self.class.base_options.merge(base_options)
90
+ end
91
+
92
+ def benchmark(message, &block)
93
+ log(message)
94
+ start = Time.now
95
+ resp = yield
96
+ time = Time.now - start
97
+ log("Got response in #{time.round(2)} secs")
98
+ resp
99
+ end
100
+
101
+ def log(str)
102
+ logger.info(str)
103
+ end
104
+
105
+ def logger
106
+ @logger ||= begin
107
+ require 'logger'
108
+ Logger.new(@options[:logfile])
109
+ end
110
+ end
111
+
112
+ def http
113
+ @http || Dagger
114
+ end
115
+
116
+ def open_http
117
+ raise "Already open!" if @http
118
+ @http = Dagger.open(self.class.base_url)
119
+ end
120
+
121
+ def close_http
122
+ @http.close if @http
123
+ @http = nil
124
+ end
125
+
126
+ # def wrap(hash)
127
+ # Entity.new(hash)
128
+ # end
129
+
130
+ # class Entity
131
+ # def initialize(props)
132
+ # @props = props
133
+ # end
134
+
135
+ # def get(prop)
136
+ # val = @props[name.to_s]
137
+ # end
138
+
139
+ # def method_missing(name, args, &block)
140
+ # if @props.key?(name.to_s)
141
+ # get(name)
142
+ # else
143
+ # # raise NoMethodError, "undefined method #{name}"
144
+ # super
145
+ # end
146
+ # end
147
+ # end
148
+
149
+ end
150
+
151
+ end
@@ -23,7 +23,7 @@ describe 'arguments' do
23
23
  describe 'invalid URL' do
24
24
 
25
25
  it 'raises error' do
26
- expect { send_request('asd123.rewqw') }.to raise_error(ArgumentError)
26
+ expect { send_request('asd123.rewqw') }.to raise_error(SocketError)
27
27
  end
28
28
 
29
29
  end
@@ -36,6 +36,15 @@ describe 'arguments' do
36
36
 
37
37
  end
38
38
 
39
+ describe 'host without protocol' do
40
+
41
+ it 'works' do
42
+ expect(send_request('www.google.com')).to be_a(Net::HTTPResponse)
43
+ end
44
+
45
+ end
46
+
47
+
39
48
  describe 'valid host' do
40
49
 
41
50
  it 'works' do
@@ -14,6 +14,9 @@ describe 'Parsers' do
14
14
 
15
15
  before do
16
16
  allow(Net::HTTP).to receive(:new).and_return(fake_http)
17
+ allow(fake_http).to receive(:keep_alive_timeout=).and_return(true)
18
+ allow(fake_http).to receive(:read_timeout=).and_return(true)
19
+ allow(fake_http).to receive(:open_timeout=).and_return(true)
17
20
  allow(fake_http).to receive(:verify_mode=).and_return(true)
18
21
  allow(fake_http).to receive(:post).and_return(fake_resp)
19
22
  end
@@ -100,7 +103,7 @@ describe 'Parsers' do
100
103
  it 'returns XMLNode obj' do
101
104
  res = send_request.data
102
105
  expect(res).to be_a(XMLNode)
103
- expect(res.to_hash).to eql(res)
106
+ expect(res.to_node).to eql(res)
104
107
  expect(res['foo']).to be_a(XMLNode)
105
108
  expect(res['foo'].text).to eql('123')
106
109
 
@@ -119,16 +122,30 @@ describe 'Parsers' do
119
122
  <nested>
120
123
  <item>
121
124
  <title attr="downcased">foobar</title>
125
+ <category>cat</category>
126
+ <published>true</published>
122
127
  </item>
123
128
  </nested>
124
129
  </xml>
125
130
  )
126
131
 
127
- it 'works' do
128
- doc = Ox.parse(xml)
129
- obj = doc.to_hash
132
+ describe '#to_node' do
133
+ it 'works as expected' do
134
+ doc = Ox.parse(xml)
135
+ obj = doc.to_node
136
+ expect(obj[:nested][:item][:title].text).to eql('foobar')
137
+ end
138
+ end
130
139
 
131
- expect(obj[:nested][:item][:title].text).to eql('foobar')
140
+ describe '#values' do
141
+ it 'works as expected' do
142
+ doc = Ox.parse(xml)
143
+ obj = doc.to_node.values
144
+ expect(obj).to eql({
145
+ "foo" => "test",
146
+ "nested" => {"item"=>{"category"=>"cat", "published"=>"true", "title"=>"foobar"}},
147
+ })
148
+ end
132
149
  end
133
150
 
134
151
  end
@@ -17,4 +17,50 @@ describe 'Persistent mode' do
17
17
  end
18
18
  end
19
19
 
20
+ end
21
+
22
+ describe 'using threads' do
23
+
24
+ def get(url)
25
+ @http.get(url)
26
+ end
27
+
28
+ def connect(host)
29
+ raise if @http
30
+ @http = Dagger.open(host)
31
+ end
32
+
33
+ def disconnect
34
+ raise if @http.nil?
35
+ @http.close
36
+ @http = nil
37
+ end
38
+
39
+ it 'works' do
40
+ thread_count = 10
41
+ urls_count = 100
42
+ host = 'https://postman-echo.com'
43
+ urls = urls_count.times.map { |i| "/get?page/#{i}" }
44
+ result = []
45
+
46
+ mutex = Mutex.new
47
+ thread_count.times.map do
48
+ Thread.new(urls, result) do |urls, result|
49
+ # mutex.synchronize { Dagger.open(host) }
50
+ http = Dagger.open(host)
51
+ while url = mutex.synchronize { urls.pop }
52
+ puts "Fetching #{url}"
53
+ resp = http.get(url)
54
+ mutex.synchronize do
55
+ result.push(resp.code)
56
+ end
57
+ end
58
+ # mutex.synchronize { http.close }
59
+ http.close
60
+ end
61
+ end.each(&:join)
62
+
63
+ expect(result.count).to eq(urls_count)
64
+ end
65
+
20
66
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dagger
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.1
4
+ version: 1.9.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tomás Pollak
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-03-31 00:00:00.000000000 Z
11
+ date: 2020-12-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -66,20 +66,6 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
- - !ruby/object:Gem::Dependency
70
- name: net-http-persistent
71
- requirement: !ruby/object:Gem::Requirement
72
- requirements:
73
- - - "~>"
74
- - !ruby/object:Gem::Version
75
- version: '3.0'
76
- type: :runtime
77
- prerelease: false
78
- version_requirements: !ruby/object:Gem::Requirement
79
- requirements:
80
- - - "~>"
81
- - !ruby/object:Gem::Version
82
- version: '3.0'
83
69
  - !ruby/object:Gem::Dependency
84
70
  name: oj
85
71
  requirement: !ruby/object:Gem::Requirement
@@ -124,10 +110,12 @@ files:
124
110
  - bin/dagger
125
111
  - dagger.gemspec
126
112
  - lib/dagger.rb
113
+ - lib/dagger/connection_manager.rb
127
114
  - lib/dagger/ox_extension.rb
128
115
  - lib/dagger/parsers.rb
129
116
  - lib/dagger/response.rb
130
117
  - lib/dagger/version.rb
118
+ - lib/dagger/wrapper.rb
131
119
  - spec/arguments_spec.rb
132
120
  - spec/ip_connect_spec.rb
133
121
  - spec/parsers_spec.rb