dagger 1.7.0 → 2.0.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: 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: {}