dagger 1.7.0 → 2.0.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: 823740e0f504d009f41e1b81a4bb863fbfc3be957490b3b7f3830b8475f48fb7
4
- data.tar.gz: 9b701943b6e3e96a9485d9256cf8acb4b1be00ce04175d14a3dfc48c1b93c0d1
3
+ metadata.gz: 447d77e66ec69445f5040231d19c038279d4ec7de624461deda078c544423ca9
4
+ data.tar.gz: c5eb814e71fd6a37b3b4272f6f747654eeb157655dec65ae6ec6f0cf86d4e5ff
5
5
  SHA512:
6
- metadata.gz: ee5a19d43f8464211880231c8e4057cc801a4ded07cc75a144abf9d40bcf4ad50fdb5de245653308fca8d832499c2f5808e37797644783caf61db18fab248dcf
7
- data.tar.gz: '09266e416582be9747dd48b69edb0d9b392ed923f581458336bfaa7ab9af4a5d1141d9d805a3cd2d9d20d4aa15bfe57191e19175fc53f6157f2337b324d2a7d1'
6
+ metadata.gz: 31108057fbc3cd1d2e0c319d4c64244c596cbbb90d8ae6067945caedb2f8f7ff5c0fd144b16be55ac24cd36bbd466f08659b995b47ecd67425d26224bd505ede
7
+ data.tar.gz: d996eaefa87c30682eb127d9f2f28cbb28363fcf2a2f758e32610d553937c05a35750316f043a6f4d2a9eb6727aad8f0351ca761ee62b326313b46e902764fd2
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
 
@@ -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,6 +1,6 @@
1
1
  module Dagger
2
- MAJOR = 1
3
- MINOR = 7
2
+ MAJOR = 2
3
+ MINOR = 0
4
4
  PATCH = 0
5
5
 
6
6
  VERSION = [MAJOR, MINOR, PATCH].join('.')
@@ -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/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 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,20 +40,30 @@ 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
- uri.path.sub!(/\?.*|$/, '?' + Utils.encode(query)) if query and query.any?
45
+ uri.path.sub!(/\?.*|$/, '?' + to_query_string(query)) if query and query.any?
40
46
  uri
41
47
  end
42
48
 
43
- def self.encode(obj, key = nil)
49
+ def self.encode_body(obj, opts = {})
50
+ if obj.is_a?(String)
51
+ obj
52
+ elsif opts[:json]
53
+ Oj.dump(obj, mode: :compat) # compat ensures symbols are converted to strings
54
+ else
55
+ to_query_string(obj)
56
+ end
57
+ end
58
+
59
+ def self.to_query_string(obj, key = nil)
44
60
  if key.nil? && obj.is_a?(String) # && obj['=']
45
61
  return obj
46
62
  end
47
63
 
48
64
  case obj
49
- when Hash then obj.map { |k, v| encode(v, append_key(key,k)) }.join('&')
50
- when Array then obj.map { |v| encode(v, "#{key}[]") }.join('&')
65
+ when Hash then obj.map { |k, v| to_query_string(v, append_key(key, k)) }.join('&')
66
+ when Array then obj.map { |v| to_query_string(v, "#{key}[]") }.join('&')
51
67
  when nil then ''
52
68
  else
53
69
  "#{key}=#{ERB::Util.url_encode(obj.to_s)}"
@@ -62,25 +78,40 @@ module Dagger
62
78
 
63
79
  class Client
64
80
 
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)
81
+ def self.init_persistent(opts = {})
82
+ # this line below forces one connection manager between multiple threads
83
+ # @persistent ||= Dagger::ConnectionManager.new(opts)
84
+
85
+ # here we initialize a connection manager for each thread
86
+ Thread.current[:dagger_persistent] ||= begin
87
+ Dagger::ConnectionManager.new(opts)
72
88
  end
89
+ end
90
+
91
+ def self.init_connection(uri, opts = {})
92
+ http = Net::HTTP.new(opts[:ip] || uri.host, uri.port)
73
93
 
74
94
  if uri.port == 443
75
95
  http.use_ssl = true if http.respond_to?(:use_ssl=) # persistent does it automatically
76
96
  http.verify_mode = opts[:verify_ssl] === false ? OpenSSL::SSL::VERIFY_NONE : OpenSSL::SSL::VERIFY_PEER
77
97
  end
78
98
 
79
- [:open_timeout, :read_timeout, :ssl_version, :ciphers].each do |key|
80
- http.send("#{key}=", opts[key]) if opts.has_key?(key)
99
+ [:keep_alive_timeout, :open_timeout, :read_timeout, :ssl_version, :ciphers].each do |key|
100
+ http.send("#{key}=", opts[key] || DEFAULTS[key]) if (opts.has_key?(key) || DEFAULTS.has_key?(key))
101
+ end
102
+
103
+ http
104
+ end
105
+
106
+ def self.init(uri, opts)
107
+ uri = Utils.parse_uri(uri)
108
+
109
+ http = if opts.delete(:persistent)
110
+ init_persistent(opts)
111
+ else
112
+ init_connection(uri, opts)
81
113
  end
82
114
 
83
- # new(http, [uri.scheme, uri.host].join('://'))
84
115
  new(http, uri.scheme_and_host)
85
116
  end
86
117
 
@@ -91,11 +122,14 @@ module Dagger
91
122
  def get(uri, opts = {})
92
123
  uri = Utils.resolve_uri(uri, @host, opts[:query])
93
124
 
94
- raise ArgumentError.new("#{uri.scheme_and_host} does not match #{@host}") if @host != uri.scheme_and_host
125
+ if @host != uri.scheme_and_host
126
+ raise ArgumentError.new("#{uri.scheme_and_host} does not match #{@host}")
127
+ end
95
128
 
96
129
  opts[:follow] = 10 if opts[:follow] == true
97
130
  headers = opts[:headers] || {}
98
131
  headers['Accept'] = 'application/json' if opts[:json] && headers['Accept'].nil?
132
+ headers['Content-Type'] = 'application/json' if opts[:json] && opts[:body]
99
133
 
100
134
  if opts[:ip]
101
135
  headers['Host'] = uri.host
@@ -104,27 +138,29 @@ module Dagger
104
138
 
105
139
  request = Net::HTTP::Get.new(uri, DEFAULT_HEADERS.merge(headers))
106
140
  request.basic_auth(opts.delete(:username), opts.delete(:password)) if opts[:username]
141
+ request.body = Utils.encode_body(opts[:body], opts) if opts[:body]
107
142
 
108
143
  if @http.respond_to?(:started?) # regular Net::HTTP
109
144
  @http.start unless @http.started?
110
145
  resp, data = @http.request(request)
111
146
  else # persistent
112
- resp, data = @http.request(uri, request)
147
+ resp, data = @http.send_request(uri, request)
113
148
  end
114
149
 
115
150
  if REDIRECT_CODES.include?(resp.code.to_i) && resp['Location'] && (opts[:follow] && opts[:follow] > 0)
116
151
  opts[:follow] -= 1
117
- debug "Following redirect to #{resp['Location']}"
152
+ debug { "Following redirect to #{resp['Location']}" }
118
153
  return get(resp['Location'], opts)
119
154
  end
120
155
 
121
156
  @response = build_response(resp, data || resp.body)
122
157
 
123
158
  rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EINVAL, Timeout::Error, \
124
- SocketError, EOFError, Net::ReadTimeout, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, OpenSSL::SSL::SSLError => e
159
+ Net::OpenTimeout, Net::ReadTimeout, Net::HTTPBadResponse, Net::HTTPHeaderSyntaxError, Net::ProtocolError, \
160
+ SocketError, EOFError, OpenSSL::SSL::SSLError => e
125
161
 
126
162
  if retries = opts[:retries] and retries.to_i > 0
127
- debug "Got #{e.class}! Retrying in a sec (#{retries} retries left)"
163
+ debug { "Got #{e.class}! Retrying in a sec (#{retries} retries left)" }
128
164
  sleep (opts[:retry_wait] || DEFAULT_RETRY_WAIT)
129
165
  get(uri, opts.merge(retries: retries - 1))
130
166
  else
@@ -150,42 +186,44 @@ module Dagger
150
186
 
151
187
  def request(method, uri, data, opts = {})
152
188
  if method.to_s.downcase == 'get'
153
- query = (opts[:query] || {}).merge(data || {})
154
- return get(uri, opts.merge(query: query))
189
+ data ||= opts[:body]
190
+ return get(uri, opts.merge(body: data))
155
191
  end
156
192
 
157
- uri = Utils.resolve_uri(uri, @host)
158
- raise ArgumentError.new("#{uri.scheme_and_host} does not match #{@host}") if @host != uri.scheme_and_host
159
- headers = DEFAULT_HEADERS.merge(opts[:headers] || {})
160
-
161
- query = if data.is_a?(String)
162
- data
163
- elsif opts[:json]
164
- headers['Content-Type'] = 'application/json'
165
- headers['Accept'] = 'application/json' if headers['Accept'].nil?
166
- Oj.dump(data, mode: :compat) # compat ensures symbols are converted to strings
167
- else # querystring, then
168
- Utils.encode(data)
193
+ uri = Utils.resolve_uri(uri, @host, opts[:query])
194
+ if @host != uri.scheme_and_host
195
+ raise ArgumentError.new("#{uri.scheme_and_host} does not match #{@host}")
169
196
  end
170
197
 
198
+ headers = DEFAULT_HEADERS.merge(opts[:headers] || {})
199
+ body = Utils.encode_body(data, opts)
200
+
171
201
  if opts[:username] # opts[:password] is optional
172
202
  str = [opts[:username], opts[:password]].compact.join(':')
173
203
  headers['Authorization'] = 'Basic ' + Base64.encode64(str)
174
204
  end
175
205
 
206
+ if opts[:json]
207
+ headers['Content-Type'] = 'application/json'
208
+ headers['Accept'] = 'application/json' if headers['Accept'].nil?
209
+ end
210
+
211
+ start = Time.now
212
+ debug { "Sending #{method} request to #{uri.request_uri} with headers #{headers.inspect} -- #{query}" }
213
+
176
214
  if @http.respond_to?(:started?) # regular Net::HTTP
177
- args = [method.to_s.downcase, uri.path, query, headers]
215
+ args = [method.to_s.downcase, uri.request_uri, body, headers]
178
216
  args.delete_at(2) if args[0] == 'delete' # Net::HTTP's delete does not accept data
179
217
 
180
218
  @http.start unless @http.started?
181
219
  resp, data = @http.send(*args)
182
220
  else # Net::HTTP::Persistent
183
- req = Kernel.const_get("Net::HTTP::#{method.capitalize}").new(uri.path, headers)
184
- # req.set_form_data(query)
185
- req.body = query
186
- resp, data = @http.request(uri, req)
221
+ req = Kernel.const_get("Net::HTTP::#{method.capitalize}").new(uri.request_uri, headers)
222
+ req.body = body
223
+ resp, data = @http.send_request(uri, req)
187
224
  end
188
225
 
226
+ debug { "Got response #{resp.code} in #{(Time.now - start).round(2)}s: #{data || resp.body}" }
189
227
  @response = build_response(resp, data || resp.body)
190
228
 
191
229
  rescue Errno::ECONNREFUSED, Errno::ECONNRESET, Errno::ETIMEDOUT, Errno::EINVAL, Timeout::Error, \
@@ -193,7 +231,7 @@ module Dagger
193
231
  SocketError, EOFError, OpenSSL::SSL::SSLError => e
194
232
 
195
233
  if method.to_s.downcase != 'get' && retries = opts[:retries] and retries.to_i > 0
196
- debug "[#{DAGGER_NAME}] Got #{e.class}! Retrying in a sec (#{retries} retries left)"
234
+ debug { "Got #{e.class}! Retrying in a sec (#{retries} retries left)" }
197
235
  sleep (opts[:retry_wait] || DEFAULT_RETRY_WAIT)
198
236
  request(method, uri, data, opts.merge(retries: retries - 1))
199
237
  else
@@ -206,7 +244,7 @@ module Dagger
206
244
  end
207
245
 
208
246
  def open(&block)
209
- if @http.is_a?(Net::HTTP::Persistent)
247
+ if @http.is_a?(Dagger::ConnectionManager)
210
248
  instance_eval(&block)
211
249
  else
212
250
  @http.start do
@@ -216,7 +254,7 @@ module Dagger
216
254
  end
217
255
 
218
256
  def close
219
- if @http.is_a?(Net::HTTP::Persistent)
257
+ if @http.is_a?(Dagger::ConnectionManager)
220
258
  @http.shutdown # calls finish on pool connections
221
259
  else
222
260
  @http.finish if @http.started?
@@ -225,8 +263,16 @@ module Dagger
225
263
 
226
264
  private
227
265
 
228
- def debug(str)
229
- puts str if ENV['DEBUGGING']
266
+ def debug(&block)
267
+ if ENV['DEBUGGING'] || ENV['DEBUG']
268
+ str = yield
269
+ logger.info "[#{DAGGER_NAME}] #{str}"
270
+ end
271
+ end
272
+
273
+ def logger
274
+ require 'logger'
275
+ @logger ||= Logger.new(@logfile || STDOUT)
230
276
  end
231
277
 
232
278
  def build_response(resp, body)
@@ -274,4 +320,4 @@ module Dagger
274
320
 
275
321
  end
276
322
 
277
- end
323
+ 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
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.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
@@ -6,15 +6,64 @@ require 'rspec/expectations'
6
6
  describe 'Persistent mode' do
7
7
 
8
8
  it 'works' do
9
- fake_client = double('Client')
10
- expect(Dagger::Client).to receive(:new).once.and_return(fake_client)
11
- expect(fake_client).to receive(:open).once #.and_return(fake_resp)
12
- expect(fake_client).to receive(:close).once #.and_return(fake_resp)
9
+ # fake_client = double('Client')
10
+ # expect(Dagger::Client).to receive(:new).once.and_return(fake_client)
11
+ # expect(fake_client).to receive(:open).once #.and_return(fake_resp)
12
+ # expect(fake_client).to receive(:close).once #.and_return(fake_resp)
13
13
 
14
+ res1, res2 = nil, nil
14
15
  obj = Dagger.open('https://www.google.com') do
15
- get('/search?q=dagger+http+client')
16
- get('google.com/search?q=thank+you+ruby')
16
+ res1 = get('/search?q=dagger+http+client', { body: 'foo' })
17
+ res2 = get('https://www.google.com/search?q=thank+you+ruby')
18
+ res3 = post('https://www.google.com/search?q=foobar', { foo: 'bar' })
17
19
  end
20
+
21
+ expect(res1.code).to eq(400)
22
+ expect(res2.code).to eq(200)
23
+ expect(res2.code).to eq(200)
24
+ expect(obj).to be_a(Dagger::Client)
25
+ end
26
+
27
+ end
28
+
29
+ describe 'using threads' do
30
+
31
+ def connect(host)
32
+ raise if @http
33
+ @http = Dagger.open(host)
34
+ end
35
+
36
+ def disconnect
37
+ raise if @http.nil?
38
+ @http.close
39
+ @http = nil
40
+ end
41
+
42
+ it 'works' do
43
+ thread_count = 10
44
+ urls_count = 100
45
+ host = 'https://postman-echo.com'
46
+ urls = urls_count.times.map { |i| "/get?page/#{i}" }
47
+ result = []
48
+
49
+ mutex = Mutex.new
50
+ thread_count.times.map do
51
+ Thread.new(urls, result) do |urls, result|
52
+ # mutex.synchronize { Dagger.open(host) }
53
+ http = Dagger.open(host)
54
+ while url = mutex.synchronize { urls.pop }
55
+ # puts "Fetching #{url}"
56
+ resp = http.get(url)
57
+ mutex.synchronize do
58
+ result.push(resp.code)
59
+ end
60
+ end
61
+ # mutex.synchronize { http.close }
62
+ http.close
63
+ end
64
+ end.each(&:join)
65
+
66
+ expect(result.count).to eq(urls_count)
18
67
  end
19
68
 
20
69
  end
@@ -0,0 +1,19 @@
1
+ require './lib/dagger'
2
+
3
+ require 'rspec/mocks'
4
+ require 'rspec/expectations'
5
+
6
+ describe 'sending data' do
7
+
8
+ it 'works with get if using .request' do
9
+ resp = Dagger.request('get', 'https://httpbingo.org/get?x=123', { foo: 'bar', testing: 1 }, { json: true })
10
+ expect(resp.ok?).to eq(true)
11
+ end
12
+
13
+ it 'works with get if passing body as option' do
14
+ resp = Dagger.get('https://httpbingo.org/get?x=123', { body: { foo: 'bar', testing: 1 }, json: true })
15
+ expect(resp.ok?).to eq(true)
16
+ end
17
+
18
+
19
+ 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.7.0
4
+ version: 2.0.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-05-08 00:00:00.000000000 Z
11
+ date: 2021-09-24 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,14 +110,17 @@ 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
134
122
  - spec/persistent_spec.rb
123
+ - spec/sending_data_spec.rb
135
124
  homepage: https://github.com/tomas/dagger
136
125
  licenses: []
137
126
  metadata: {}