tickethub 0.3.51 → 0.3.52

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
  SHA1:
3
- metadata.gz: c3b1862803f43e2f70410a2eb6108b1d13ed51a4
4
- data.tar.gz: 7a630302ee8cd1745aae8107d4635818494daf27
3
+ metadata.gz: 0c3d2fbee50f8b1649c00ce062145feba07d6ad1
4
+ data.tar.gz: e5ab1ddb6e9503d8d3a7b77c834f04223765f96b
5
5
  SHA512:
6
- metadata.gz: 7aed3e243ea8f61ecdb5729b916ef64837051eee842b182617cd4e4e24e93332b8fa1c103d9af2d6fe3ad153ae2f781f126a701600253bac42b7efc8e723a390
7
- data.tar.gz: d8c8e3ea78ac78453813dbadb3a51f0c76274f1bc75cc44dee55301bfeb64a4f8ecaa2e53e0d77e087a0af0321824aa95399cc662b7a0db6e5cba1a5f753e0fa
6
+ metadata.gz: e148ff6eede8074c3b76be0f36f69f79c5143fcf20bba3f6ef79d2784c7c5a90dceebacc38543ff7b8bab5b141be45877f2d0d243fa197442606d5fda4ed639f
7
+ data.tar.gz: 67e18750bd49ba334f9092d31caa2642e412b9a8d67f0d654a27698608fcbd42deb867d6f7bf4f8b356d09ffa8545b1b5eb96ba258280f58f6e01ce5b5a0c455
@@ -40,6 +40,10 @@ module Tickethub
40
40
  request(:head, path, headers, &block)
41
41
  end
42
42
 
43
+ def put(path, body = '', headers = {}, &block)
44
+ request(:put, path, body, headers, &block)
45
+ end
46
+
43
47
  def patch(path, body = '', headers = {}, &block)
44
48
  request(:patch, path, body, headers, &block)
45
49
  end
@@ -50,48 +54,72 @@ module Tickethub
50
54
 
51
55
  protected
52
56
 
57
+ def request=(request)
58
+ @request = request
59
+ end
60
+
53
61
  # Makes a request to the remote service.
54
62
  def request(method, path, *arguments)
55
63
  response = http.send(method, path, *arguments)
64
+ response.uri = URI.join(endpoint, path)
65
+
56
66
  handle_response(response)
57
67
 
58
- rescue Timeout::Error => e
59
- raise TimeoutError.new(e.message)
68
+ rescue Timeout::Error, Net::OpenTimeout => e
69
+ raise TimeoutError.new(@request, e.message)
60
70
  rescue OpenSSL::SSL::SSLError => e
61
- raise SSLError.new(e.message)
71
+ raise SSLError.new(@request, e.message)
72
+ rescue SocketError,
73
+ EOFError,
74
+ Net::HTTPBadResponse,
75
+ Net::HTTPHeaderSyntaxError,
76
+ Net::HTTPServerException,
77
+ Net::ProtocolError,
78
+ Errno::ECONNABORTED,
79
+ Errno::ECONNREFUSED,
80
+ Errno::ECONNRESET,
81
+ Errno::ETIMEDOUT,
82
+ Errno::ENETUNREACH,
83
+ Errno::EHOSTUNREACH,
84
+ Errno::EINVAL,
85
+ Errno::ENOPROTOOPT => e
86
+ raise ErrnoError.new(@request, e.message)
87
+ rescue Zlib::DataError,
88
+ Zlib::BufError => e
89
+ raise ZlibError.new(@request, e.message)
62
90
  end
63
91
 
64
92
  # Handles response and error codes from the remote service.
65
93
  def handle_response(response)
66
94
  case response.code.to_i
67
- when 301,302
68
- raise Redirection.new(response)
69
- when 200...400
70
- response
71
- when 400
72
- raise BadRequest.new(response)
73
- when 401
74
- raise UnauthorizedAccess.new(response)
75
- when 403
76
- raise ForbiddenAccess.new(response)
77
- when 404
78
- raise ResourceNotFound.new(response)
79
- when 405
80
- raise MethodNotAllowed.new(response)
81
- when 409
82
- raise ResourceConflict.new(response)
83
- when 410
84
- raise ResourceGone.new(response)
85
- when 422
86
- raise ResourceInvalid.new(response)
87
- when 401...500
88
- raise ClientError.new(response)
89
- when 500...600
90
- raise ServerError.new(response)
91
- else
92
- raise ConnectionError.new(
93
- response, "Unknown response code: #{response.code}"
94
- )
95
+ when 200...299
96
+ response
97
+ when 300..399
98
+ raise Redirection.new(@request, response)
99
+ when 400
100
+ raise BadRequest.new(@request, response)
101
+ when 401
102
+ raise UnauthorizedAccess.new(@request, response)
103
+ when 403
104
+ raise ForbiddenAccess.new(@request, response)
105
+ when 404
106
+ raise ResourceNotFound.new(@request, response)
107
+ when 405
108
+ raise MethodNotAllowed.new(@request, response)
109
+ when 409
110
+ raise ResourceConflict.new(@request, response)
111
+ when 410
112
+ raise ResourceGone.new(@request, response)
113
+ when 422
114
+ raise ResourceInvalid.new(@request, response)
115
+ when 401...500
116
+ raise ClientError.new(@request, response)
117
+ when 500...600
118
+ raise ServerError.new(@request, response)
119
+ else
120
+ raise ResponseError.new(
121
+ @request, response, "Unknown response code: #{response.code}"
122
+ )
95
123
  end
96
124
  end
97
125
 
@@ -113,7 +141,13 @@ module Tickethub
113
141
 
114
142
  def configure_http(http)
115
143
  http = apply_ssl_options(http)
116
- http.read_timeout = timeout if timeout
144
+
145
+ # Net::HTTP timeouts default to 60 seconds.
146
+ if timeout
147
+ http.open_timeout = timeout
148
+ http.read_timeout = timeout
149
+ end
150
+
117
151
  http
118
152
  end
119
153
 
@@ -1,44 +1,65 @@
1
1
  module Tickethub
2
- class ConnectionError < StandardError # :nodoc:
2
+ class Error < StandardError
3
+ attr_reader :request
4
+
5
+ def initialize(request = nil, message = nil)
6
+ super(message)
7
+ @request = request
8
+ end
9
+ end
10
+
11
+ ConnectionError = Error
12
+ RequestError = Error
13
+
14
+ class ResponseError < Error
3
15
  attr_reader :response
4
16
 
5
- def initialize(response, message = nil)
17
+ def initialize(request, response, message = nil)
18
+ super(request, message)
6
19
  @response = response
7
- @message = message
8
20
  end
9
21
 
10
22
  def to_s
11
23
  message = "Failed."
12
24
  message << " Response code = #{response.code}." if response.respond_to?(:code)
13
25
  message << " Response message = #{response.message}." if response.respond_to?(:message)
14
- message << " Response Body = #{response.body}." if response.respond_to?(:body)
26
+
27
+ if response.respond_to?(:body)
28
+ # Error messages need to be in UTF-8
29
+ body = response.body.dup.to_s
30
+ body = body.encode('UTF-8', :invalid => :replace, :undef => :replace, :replace => '?')
31
+ message << " Response Body = #{body}."
32
+ end
33
+
15
34
  message
16
35
  end
17
36
  end
18
37
 
19
38
  # Raised when a Timeout::Error occurs.
20
- class TimeoutError < ConnectionError
21
- def initialize(message)
22
- @message = message
23
- end
24
- def to_s; @message ;end
39
+ class TimeoutError < RequestError
25
40
  end
26
41
 
27
42
  # Raised when a OpenSSL::SSL::SSLError occurs.
28
- class SSLError < ConnectionError
29
- def initialize(message)
30
- @message = message
31
- end
32
- def to_s; @message ;end
43
+ class SSLError < RequestError
44
+ end
45
+
46
+ class ErrnoError < RequestError
47
+ end
48
+
49
+ class ZlibError < RequestError
33
50
  end
34
51
 
35
52
  # 3xx Redirection
36
- class Redirection < ConnectionError # :nodoc:
53
+ class Redirection < ResponseError # :nodoc:
54
+ def to_s; response['Location'] ? "#{super} => #{response['Location']}" : super; end
55
+ end
56
+
57
+ class RedirectionLoop < ResponseError # :nodoc:
37
58
  def to_s; response['Location'] ? "#{super} => #{response['Location']}" : super; end
38
59
  end
39
60
 
40
61
  # 4xx Client Error
41
- class ClientError < ConnectionError; end # :nodoc:
62
+ class ClientError < ResponseError; end # :nodoc:
42
63
 
43
64
  # 400 Bad Request
44
65
  class BadRequest < ClientError; end # :nodoc
@@ -52,11 +73,17 @@ module Tickethub
52
73
  # 404 Not Found
53
74
  class ResourceNotFound < ClientError; end # :nodoc:
54
75
 
76
+ # 409 Conflict
77
+ class ResourceConflict < ClientError; end # :nodoc:
78
+
79
+ # 410 Gone
80
+ class ResourceGone < ClientError; end # :nodoc:
81
+
55
82
  # 422 Invalid
56
83
  class ResourceInvalid < ClientError; end # :nodoc:
57
84
 
58
85
  # 5xx Server Error
59
- class ServerError < ConnectionError; end # :nodoc:
86
+ class ServerError < ResponseError; end # :nodoc:
60
87
 
61
88
  # 405 Method Not Allowed
62
89
  class MethodNotAllowed < ClientError # :nodoc:
@@ -10,6 +10,14 @@ module Tickethub
10
10
  value.to_s.split('_').map {|w| w.capitalize }.join
11
11
  end
12
12
 
13
+ def deep_merge(hash, other_hash)
14
+ hash.merge(other_hash) do |key, oldval, newval|
15
+ oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
16
+ newval = newval.to_hash if newval.respond_to?(:to_hash)
17
+ oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? deep_merge(oldval, newval) : newval
18
+ end
19
+ end
20
+
13
21
  # Stolen from Rack:
14
22
 
15
23
  DEFAULT_SEP = /[&;] */n
@@ -30,6 +38,22 @@ module Tickethub
30
38
  end
31
39
  end
32
40
 
41
+ def to_url_param(value, prefix = nil)
42
+ case value
43
+ when Array
44
+ value.map { |v|
45
+ to_url_param(v, "#{prefix}[]")
46
+ }.join("&")
47
+ when Hash
48
+ value.map { |k, v|
49
+ to_url_param(v, prefix ? "#{prefix}[#{uri_escape(k)}]" : uri_escape(k))
50
+ }.join("&")
51
+ else
52
+ raise ArgumentError, "value must be a Hash" if prefix.nil?
53
+ "#{prefix}=#{uri_escape(value)}"
54
+ end
55
+ end
56
+
33
57
  def from_param(param)
34
58
  Rack::Utils.parse_nested_query(param)
35
59
  (value || '').split('&').each do |res|
@@ -51,7 +75,15 @@ module Tickethub
51
75
  end
52
76
 
53
77
  def escape(s)
54
- URI.encode_www_form_component(s)
78
+ URI.encode_www_form_component(s.to_s)
79
+ end
80
+
81
+ ESCAPE_RE = /[^a-zA-Z0-9 .~_-]/
82
+
83
+ def uri_escape(s)
84
+ s.to_s.gsub(ESCAPE_RE) {|match|
85
+ '%' + match.unpack('H2' * match.bytesize).join('%').upcase
86
+ }.tr(' ', '+')
55
87
  end
56
88
 
57
89
  if defined?(::Encoding)
@@ -2,12 +2,12 @@ require 'securerandom'
2
2
 
3
3
  module Tickethub
4
4
  class Request
5
-
6
5
  attr_reader :options, :format, :url
7
- attr_accessor :params, :body, :method, :headers
8
6
 
9
- # Connection options
10
- attr_accessor :proxy, :user, :password, :auth_type, :timeout, :retries, :ssl_options
7
+ attr_accessor :params, :body, :method, :headers,
8
+ :proxy, :user, :password, :retries,
9
+ :auth_type, :timeout, :ssl_options,
10
+ :max_attempts, :follow_redirection
11
11
 
12
12
  def initialize(url, options = {})
13
13
  @url = url.to_s
@@ -15,16 +15,19 @@ module Tickethub
15
15
  @retries = 3
16
16
  @timeout = 10
17
17
 
18
- @options = options
18
+ @options = {
19
+ :method => :get,
20
+ :params => {},
21
+ :headers => {},
22
+ :format => :form,
23
+ :max_attempts => 5,
24
+ :follow_redirection => true
25
+ }.merge(options)
26
+
19
27
  @options.each do |key, val|
20
28
  method = "#{key}="
21
29
  send(method, val) if respond_to?(method)
22
30
  end
23
-
24
- self.method ||= :get
25
- self.params ||= {}
26
- self.headers ||= {}
27
- self.format ||= :form
28
31
  end
29
32
 
30
33
  def format=(mime_or_format)
@@ -46,50 +49,86 @@ module Tickethub
46
49
  @uri = URI.parse(url)
47
50
  @uri.path = '/' if @uri.path.empty?
48
51
 
49
- if @uri.query
50
- @params.merge!(Helpers.from_param(@uri.query))
51
- @uri.query = nil
52
- end
53
-
54
52
  @uri
55
53
  end
56
54
 
55
+ def uri_params
56
+ uri.query ? Helpers.from_param(uri.query) : {}
57
+ end
58
+
57
59
  def path
58
60
  uri.path
59
61
  end
60
62
 
61
- def execute
62
- if encoded?
63
- result = connection.send(method, path, encoded, build_headers)
64
- else
65
- result = connection.send(method, query_path, build_headers)
63
+ def query_path
64
+ query_path = path.dup
65
+ query_params = uri_params.dup
66
+ query_params.merge!(params) unless encoded?
67
+
68
+ if query_params.any?
69
+ query_path += '?' + Helpers.to_url_param(query_params)
66
70
  end
67
71
 
68
- Response.new(result)
72
+ query_path
73
+ end
69
74
 
70
- rescue TimeoutError, ServerError, SSLError => err
71
- raise err if (@retries -= 1) == 0
72
- execute
73
- rescue Redirection => error
74
- raise error unless error.response['Location']
75
- location = URI.parse(error.response['Location'])
75
+ def encoded?
76
+ [:post, :put].include?(method)
77
+ end
76
78
 
77
- # Path is relative
78
- unless location.host
79
- location = URI.join(uri, location)
80
- end
79
+ def encoded
80
+ params.any? ? format.encode(params) : body
81
+ end
81
82
 
82
- self.url = location.to_s
83
+ def execute
84
+ with_redirection do
85
+ if encoded?
86
+ result = connection.send(method, query_path, encoded, build_headers)
87
+ else
88
+ result = connection.send(method, query_path, build_headers)
89
+ end
90
+
91
+ Response.new(result, uri)
92
+ end
93
+ rescue ServerError, RequestError => err
94
+ raise err if (@retries -= 1) == 0
83
95
  execute
84
96
  end
85
97
 
86
98
  protected
87
99
 
100
+ def with_redirection(&block)
101
+ attempts = 1
102
+
103
+ begin
104
+ yield
105
+ rescue Redirection => error
106
+ raise error unless follow_redirection
107
+
108
+ attempts += 1
109
+
110
+ raise error unless error.response['Location']
111
+ raise RedirectionLoop.new(self, error.response) if attempts > max_attempts
112
+
113
+ location = error.response['Location'].scrub
114
+ location = URI.parse(location)
115
+
116
+ # Path is relative
117
+ unless location.host
118
+ location = URI.join(uri, location)
119
+ end
120
+
121
+ self.url = location.to_s
122
+ retry
123
+ end
124
+ end
125
+
88
126
  def connection
89
127
  Connection.new(uri,
90
128
  :proxy => proxy,
91
129
  :timeout => timeout,
92
- :ssl_options => ssl_options
130
+ :ssl_options => ssl_options,
131
+ :request => self
93
132
  )
94
133
  end
95
134
 
@@ -117,23 +156,5 @@ module Tickethub
117
156
  .merge(headers)
118
157
  .merge('X-Request-ID' => @id)
119
158
  end
120
-
121
- def query_path
122
- query_path = path
123
-
124
- if params.any?
125
- query_path += '?' + Helpers.to_param(params)
126
- end
127
-
128
- query_path
129
- end
130
-
131
- def encoded?
132
- [:post, :patch].include?(method)
133
- end
134
-
135
- def encoded
136
- params.any? ? format.encode(params) : body
137
- end
138
159
  end
139
160
  end
@@ -1,3 +1,3 @@
1
1
  module Tickethub
2
- VERSION = '0.3.51'
2
+ VERSION = '0.3.52'
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: tickethub
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.51
4
+ version: 0.3.52
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oliver Morgan