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 +4 -4
- data/dagger.gemspec +0 -1
- data/lib/dagger.rb +61 -30
- data/lib/dagger/connection_manager.rb +59 -0
- data/lib/dagger/ox_extension.rb +25 -14
- data/lib/dagger/parsers.rb +1 -1
- data/lib/dagger/version.rb +2 -2
- data/lib/dagger/wrapper.rb +151 -0
- data/spec/arguments_spec.rb +10 -1
- data/spec/parsers_spec.rb +22 -5
- data/spec/persistent_spec.rb +46 -0
- metadata +4 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 692b25152c7fd3229ad3dc13bd25250b613e55a211e489b8835258b18405db19
|
4
|
+
data.tar.gz: 5560f64e580e4f72f9e243eaf1c5eed1b389e439074607dc24bdd8fbe9c532f0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 85d160f0130c12fde8b2723cd54bcaf4a75c4f48319cb040c4725a93e6cc09d4f8a68a7977135f33c38922583cc8de481bc078ea6d3ff5636c42c3154a5dca74
|
7
|
+
data.tar.gz: 9bd29716a9e84b75e898f71b00e8fc145550fcccd15b2c0006172e56e3e5454b62668bc4536dacde371a33b429f437a942354368de4cce0394ff73296cefa8e8
|
data/dagger.gemspec
CHANGED
@@ -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
|
|
data/lib/dagger.rb
CHANGED
@@ -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
|
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
|
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}=#{
|
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.
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
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
|
-
|
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
|
-
|
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?(
|
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?(
|
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
|
data/lib/dagger/ox_extension.rb
CHANGED
@@ -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
|
13
|
-
|
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(
|
28
|
-
if
|
29
|
-
Array(
|
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
|
-
|
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
|
83
|
-
nodes.first.
|
93
|
+
def to_node
|
94
|
+
nodes.first.to_node
|
84
95
|
end
|
85
96
|
end
|
86
97
|
|
87
98
|
class Ox::Element
|
88
|
-
def
|
89
|
-
children = nodes.map { |n| n.class == self.class ? n.
|
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
|
data/lib/dagger/parsers.rb
CHANGED
data/lib/dagger/version.rb
CHANGED
@@ -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
|
data/spec/arguments_spec.rb
CHANGED
@@ -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(
|
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
|
data/spec/parsers_spec.rb
CHANGED
@@ -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.
|
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
|
-
|
128
|
-
|
129
|
-
|
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
|
-
|
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
|
data/spec/persistent_spec.rb
CHANGED
@@ -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.
|
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-
|
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
|