better-riak-client 1.0.5

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.
Files changed (72) hide show
  1. data/LICENSE +16 -0
  2. data/README.markdown +198 -0
  3. data/RELEASE_NOTES.md +211 -0
  4. data/better-riak-client.gemspec +61 -0
  5. data/erl_src/riak_kv_test014_backend.beam +0 -0
  6. data/erl_src/riak_kv_test014_backend.erl +189 -0
  7. data/erl_src/riak_kv_test_backend.beam +0 -0
  8. data/erl_src/riak_kv_test_backend.erl +697 -0
  9. data/erl_src/riak_search_test_backend.beam +0 -0
  10. data/erl_src/riak_search_test_backend.erl +175 -0
  11. data/lib/riak/bucket.rb +221 -0
  12. data/lib/riak/client/beefcake/messages.rb +213 -0
  13. data/lib/riak/client/beefcake/object_methods.rb +111 -0
  14. data/lib/riak/client/beefcake_protobuffs_backend.rb +226 -0
  15. data/lib/riak/client/decaying.rb +36 -0
  16. data/lib/riak/client/excon_backend.rb +162 -0
  17. data/lib/riak/client/feature_detection.rb +88 -0
  18. data/lib/riak/client/http_backend/configuration.rb +211 -0
  19. data/lib/riak/client/http_backend/key_streamer.rb +43 -0
  20. data/lib/riak/client/http_backend/object_methods.rb +106 -0
  21. data/lib/riak/client/http_backend/request_headers.rb +34 -0
  22. data/lib/riak/client/http_backend/transport_methods.rb +201 -0
  23. data/lib/riak/client/http_backend.rb +340 -0
  24. data/lib/riak/client/net_http_backend.rb +82 -0
  25. data/lib/riak/client/node.rb +115 -0
  26. data/lib/riak/client/protobuffs_backend.rb +173 -0
  27. data/lib/riak/client/search.rb +91 -0
  28. data/lib/riak/client.rb +540 -0
  29. data/lib/riak/cluster.rb +151 -0
  30. data/lib/riak/core_ext/blank.rb +53 -0
  31. data/lib/riak/core_ext/deep_dup.rb +13 -0
  32. data/lib/riak/core_ext/extract_options.rb +7 -0
  33. data/lib/riak/core_ext/json.rb +15 -0
  34. data/lib/riak/core_ext/slice.rb +18 -0
  35. data/lib/riak/core_ext/stringify_keys.rb +10 -0
  36. data/lib/riak/core_ext/symbolize_keys.rb +10 -0
  37. data/lib/riak/core_ext/to_param.rb +31 -0
  38. data/lib/riak/core_ext.rb +7 -0
  39. data/lib/riak/encoding.rb +6 -0
  40. data/lib/riak/failed_request.rb +81 -0
  41. data/lib/riak/i18n.rb +5 -0
  42. data/lib/riak/json.rb +52 -0
  43. data/lib/riak/link.rb +94 -0
  44. data/lib/riak/locale/en.yml +53 -0
  45. data/lib/riak/locale/fr.yml +52 -0
  46. data/lib/riak/map_reduce/filter_builder.rb +103 -0
  47. data/lib/riak/map_reduce/phase.rb +98 -0
  48. data/lib/riak/map_reduce.rb +225 -0
  49. data/lib/riak/map_reduce_error.rb +7 -0
  50. data/lib/riak/node/configuration.rb +293 -0
  51. data/lib/riak/node/console.rb +133 -0
  52. data/lib/riak/node/control.rb +207 -0
  53. data/lib/riak/node/defaults.rb +83 -0
  54. data/lib/riak/node/generation.rb +106 -0
  55. data/lib/riak/node/log.rb +34 -0
  56. data/lib/riak/node/version.rb +43 -0
  57. data/lib/riak/node.rb +38 -0
  58. data/lib/riak/robject.rb +318 -0
  59. data/lib/riak/search.rb +3 -0
  60. data/lib/riak/serializers.rb +74 -0
  61. data/lib/riak/stamp.rb +77 -0
  62. data/lib/riak/test_server.rb +89 -0
  63. data/lib/riak/util/escape.rb +76 -0
  64. data/lib/riak/util/headers.rb +53 -0
  65. data/lib/riak/util/multipart/stream_parser.rb +62 -0
  66. data/lib/riak/util/multipart.rb +52 -0
  67. data/lib/riak/util/tcp_socket_extensions.rb +58 -0
  68. data/lib/riak/util/translation.rb +19 -0
  69. data/lib/riak/version.rb +3 -0
  70. data/lib/riak/walk_spec.rb +105 -0
  71. data/lib/riak.rb +21 -0
  72. metadata +348 -0
@@ -0,0 +1,111 @@
1
+ require 'riak/robject'
2
+ require 'riak/link'
3
+ require 'riak/client/beefcake/messages'
4
+
5
+ module Riak
6
+ class Client
7
+ class BeefcakeProtobuffsBackend
8
+ module ObjectMethods
9
+ ENCODING = "Riak".respond_to?(:encoding)
10
+
11
+ # Returns RpbPutReq
12
+ def dump_object(robject, options={})
13
+ pbuf = RpbPutReq.new(options.merge(:bucket => maybe_encode(robject.bucket.name)))
14
+ pbuf.key = maybe_encode(robject.key) if robject.key # Put w/o key supported!
15
+ pbuf.vclock = maybe_encode(Base64.decode64(robject.vclock)) if robject.vclock
16
+ pbuf.content = RpbContent.new(:value => maybe_encode(robject.raw_data),
17
+ :content_type => maybe_encode(robject.content_type),
18
+ :links => robject.links.map {|l| encode_link(l) }.compact,
19
+ :indexes => robject.indexes.map {|k,s| encode_index(k,s) }.flatten)
20
+
21
+ pbuf.content.usermeta = robject.meta.map {|k,v| encode_meta(k,v)} if robject.meta.any?
22
+ pbuf.content.vtag = maybe_encode(robject.etag) if robject.etag.present?
23
+ if ENCODING # 1.9 support
24
+ pbuf.content.charset = maybe_encode(robject.raw_data.encoding.name)
25
+ end
26
+ pbuf
27
+ end
28
+
29
+ # Returns RObject
30
+ def load_object(pbuf, robject)
31
+ return robject if pbuf.respond_to?(:unchanged) && pbuf.unchanged # Reloading
32
+ robject.vclock = Base64.encode64(pbuf.vclock).chomp if pbuf.vclock
33
+ robject.key = maybe_unescape(pbuf.key) if pbuf.respond_to?(:key) && pbuf.key # Put w/o key
34
+ if pbuf.content.size > 1
35
+ robject.conflict = true
36
+ robject.siblings = pbuf.content.map do |c|
37
+ sibling = RObject.new(robject.bucket, robject.key)
38
+ sibling.vclock = robject.vclock
39
+ load_content(c, sibling)
40
+ end
41
+
42
+ return robject.attempt_conflict_resolution
43
+ else
44
+ load_content(pbuf.content.first, robject)
45
+ end
46
+ robject
47
+ end
48
+
49
+ private
50
+ def load_content(pbuf, robject)
51
+ if ENCODING && pbuf.charset.present?
52
+ pbuf.value.force_encoding(pbuf.charset) if Encoding.find(pbuf.charset)
53
+ end
54
+ robject.raw_data = pbuf.value
55
+ robject.etag = pbuf.vtag if pbuf.vtag.present?
56
+ robject.content_type = pbuf.content_type if pbuf.content_type.present?
57
+ robject.links = pbuf.links.map(&method(:decode_link)) if pbuf.links.present?
58
+ pbuf.usermeta.each {|pair| decode_meta(pair, robject.meta) } if pbuf.usermeta.present?
59
+ if pbuf.indexes.present?
60
+ robject.indexes.clear
61
+ pbuf.indexes.each {|pair| decode_index(pair, robject.indexes) }
62
+ end
63
+ if pbuf.last_mod.present?
64
+ robject.last_modified = Time.at(pbuf.last_mod)
65
+ robject.last_modified += pbuf.last_mod_usecs / 1000000 if pbuf.last_mod_usecs.present?
66
+ end
67
+ robject
68
+ end
69
+
70
+ def decode_link(pbuf)
71
+ Riak::Link.new(pbuf.bucket, pbuf.key, pbuf.tag)
72
+ end
73
+
74
+ def encode_link(link)
75
+ return nil unless link.key.present?
76
+ RpbLink.new(:bucket => maybe_encode(link.bucket.to_s),
77
+ :key => maybe_encode(link.key.to_s),
78
+ :tag => maybe_encode(link.tag.to_s))
79
+ end
80
+
81
+ def decode_meta(pbuf, hash)
82
+ hash[pbuf.key] = pbuf.value
83
+ end
84
+
85
+ def encode_meta(key,value)
86
+ return nil unless value.present?
87
+ RpbPair.new(:key => maybe_encode(key.to_s),
88
+ :value => maybe_encode(value.to_s))
89
+ end
90
+
91
+ def decode_index(pbuf, hash)
92
+ value = pbuf.key =~ /int$/ ? pbuf.value.to_i : pbuf.value
93
+ hash[pbuf.key] << value
94
+ end
95
+
96
+ def encode_index(key, set)
97
+ set.map do |v|
98
+ RpbPair.new(:key => maybe_encode(key),
99
+ :value => maybe_encode(v.to_s))
100
+ end
101
+ end
102
+
103
+ def maybe_encode(string)
104
+ ENCODING ? string.dup.force_encoding('BINARY') : string
105
+ end
106
+ end
107
+
108
+ include ObjectMethods
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,226 @@
1
+ require 'base64'
2
+ require 'riak/json'
3
+ require 'riak/client'
4
+ require 'riak/failed_request'
5
+ require 'riak/client/protobuffs_backend'
6
+
7
+ module Riak
8
+ class Client
9
+ class BeefcakeProtobuffsBackend < ProtobuffsBackend
10
+ def self.configured?
11
+ begin
12
+ require 'beefcake'
13
+ require 'riak/client/beefcake/messages'
14
+ require 'riak/client/beefcake/object_methods'
15
+ true
16
+ rescue LoadError, NameError
17
+ false
18
+ end
19
+ end
20
+
21
+ def set_client_id(id)
22
+ value = case id
23
+ when Integer
24
+ [id].pack("N")
25
+ else
26
+ id.to_s
27
+ end
28
+ req = RpbSetClientIdReq.new(:client_id => value)
29
+ write_protobuff(:SetClientIdReq, req)
30
+ decode_response
31
+ end
32
+
33
+ def fetch_object(bucket, key, options={})
34
+ options = prune_unsupported_options(:GetReq, normalize_quorums(options))
35
+ bucket = Bucket === bucket ? bucket.name : bucket
36
+ req = RpbGetReq.new(options.merge(:bucket => maybe_encode(bucket), :key => maybe_encode(key)))
37
+ write_protobuff(:GetReq, req)
38
+ decode_response(RObject.new(client.bucket(bucket), key))
39
+ end
40
+
41
+ def reload_object(robject, options={})
42
+ options = normalize_quorums(options)
43
+ options[:bucket] = maybe_encode(robject.bucket.name)
44
+ options[:key] = maybe_encode(robject.key)
45
+ options[:if_modified] = maybe_encode Base64.decode64(robject.vclock) if robject.vclock
46
+ req = RpbGetReq.new(prune_unsupported_options(:GetReq, options))
47
+ write_protobuff(:GetReq, req)
48
+ decode_response(robject)
49
+ end
50
+
51
+ def store_object(robject, options={})
52
+ options = normalize_quorums(options)
53
+ if robject.prevent_stale_writes
54
+ unless pb_conditionals?
55
+ other = fetch_object(robject.bucket, robject.key)
56
+ raise Riak::ProtobuffsFailedRequest.new(:stale_object, t("stale_write_prevented")) unless other.vclock == robject.vclock
57
+ end
58
+ if robject.vclock
59
+ options[:if_not_modified] = true
60
+ else
61
+ options[:if_none_match] = true
62
+ end
63
+ end
64
+ req = dump_object(robject, prune_unsupported_options(:PutReq, options))
65
+ write_protobuff(:PutReq, req)
66
+ decode_response(robject)
67
+ end
68
+
69
+ def delete_object(bucket, key, options={})
70
+ bucket = Bucket === bucket ? bucket.name : bucket
71
+ options = normalize_quorums(options)
72
+ options[:bucket] = maybe_encode(bucket)
73
+ options[:key] = maybe_encode(key)
74
+ options[:vclock] = Base64.decode64(options[:vclock]) if options[:vclock]
75
+ req = RpbDelReq.new(prune_unsupported_options(:DelReq, options))
76
+ write_protobuff(:DelReq, req)
77
+ decode_response
78
+ end
79
+
80
+ def get_bucket_props(bucket)
81
+ bucket = bucket.name if Bucket === bucket
82
+ req = RpbGetBucketReq.new(:bucket => maybe_encode(bucket))
83
+ write_protobuff(:GetBucketReq, req)
84
+ decode_response
85
+ end
86
+
87
+ def set_bucket_props(bucket, props)
88
+ bucket = bucket.name if Bucket === bucket
89
+ props = props.slice('n_val', 'allow_mult')
90
+ req = RpbSetBucketReq.new(:bucket => maybe_encode(bucket), :props => RpbBucketProps.new(props))
91
+ write_protobuff(:SetBucketReq, req)
92
+ decode_response
93
+ end
94
+
95
+ def list_keys(bucket, &block)
96
+ bucket = bucket.name if Bucket === bucket
97
+ req = RpbListKeysReq.new(:bucket => maybe_encode(bucket))
98
+ write_protobuff(:ListKeysReq, req)
99
+ keys = []
100
+ while msg = decode_response
101
+ break if msg.done
102
+ if block_given?
103
+ yield msg.keys
104
+ else
105
+ keys += msg.keys
106
+ end
107
+ end
108
+ block_given? || keys
109
+ end
110
+
111
+ def mapred(mr, &block)
112
+ raise MapReduceError.new(t("empty_map_reduce_query")) if mr.query.empty? && !mapred_phaseless?
113
+ req = RpbMapRedReq.new(:request => mr.to_json, :content_type => "application/json")
114
+ write_protobuff(:MapRedReq, req)
115
+ results = []
116
+ while msg = decode_response
117
+ break if msg.done
118
+ if block_given?
119
+ yield msg.phase, JSON.parse(msg.response)
120
+ else
121
+ results[msg.phase] ||= []
122
+ results[msg.phase] += JSON.parse(msg.response)
123
+ end
124
+ end
125
+ block_given? || results.compact.size == 1 ? results.last : results
126
+ end
127
+
128
+ def get_index(bucket, index, query)
129
+ return super unless pb_indexes?
130
+ if Range === query
131
+ options = {
132
+ :qtype => RpbIndexReq::IndexQueryType::RANGE,
133
+ :range_min => query.begin.to_s,
134
+ :range_max => query.end.to_s
135
+ }
136
+ else
137
+ options = {
138
+ :qtype => RpbIndexReq::IndexQueryType::EQ,
139
+ :key => query.to_s
140
+ }
141
+ end
142
+ req = RpbIndexReq.new(options.merge(:bucket => bucket, :index => index))
143
+ write_protobuff(:IndexReq, req)
144
+ decode_response
145
+ end
146
+
147
+ def search(index, query, options={})
148
+ return super unless pb_search?
149
+ options = options.symbolize_keys
150
+ options[:op] = options.delete(:'q.op') if options[:'q.op']
151
+ req = RpbSearchQueryReq.new(options.merge(:index => index || 'search', :q => query))
152
+ write_protobuff(:SearchQueryReq, req)
153
+ decode_response
154
+ end
155
+
156
+ private
157
+ def write_protobuff(code, message)
158
+ encoded = message.encode
159
+ header = [encoded.length+1, MESSAGE_CODES.index(code)].pack("NC")
160
+ socket.write(header + encoded)
161
+ end
162
+
163
+ def decode_response(*args)
164
+ header = socket.read(5)
165
+ raise SocketError, "Unexpected EOF on PBC socket" if header.nil?
166
+ msglen, msgcode = header.unpack("NC")
167
+ if msglen == 1
168
+ case MESSAGE_CODES[msgcode]
169
+ when :PingResp, :SetClientIdResp, :PutResp, :DelResp, :SetBucketResp
170
+ true
171
+ when :ListBucketsResp, :ListKeysResp
172
+ []
173
+ when :GetResp
174
+ raise Riak::ProtobuffsFailedRequest.new(:not_found, t('not_found'))
175
+ else
176
+ false
177
+ end
178
+ else
179
+ message = socket.read(msglen-1)
180
+ case MESSAGE_CODES[msgcode]
181
+ when :ErrorResp
182
+ res = RpbErrorResp.decode(message)
183
+ raise Riak::ProtobuffsFailedRequest.new(res.errcode, res.errmsg)
184
+ when :GetClientIdResp
185
+ res = RpbGetClientIdResp.decode(message)
186
+ res.client_id
187
+ when :GetServerInfoResp
188
+ res = RpbGetServerInfoResp.decode(message)
189
+ {:node => res.node, :server_version => res.server_version}
190
+ when :GetResp
191
+ res = RpbGetResp.decode(message)
192
+ load_object(res, args.first)
193
+ when :PutResp
194
+ res = RpbPutResp.decode(message)
195
+ load_object(res, args.first)
196
+ when :ListBucketsResp
197
+ res = RpbListBucketsResp.decode(message)
198
+ res.buckets
199
+ when :ListKeysResp
200
+ RpbListKeysResp.decode(message)
201
+ when :GetBucketResp
202
+ res = RpbGetBucketResp.decode(message)
203
+ {'n_val' => res.props.n_val, 'allow_mult' => res.props.allow_mult}
204
+ when :MapRedResp
205
+ RpbMapRedResp.decode(message)
206
+ when :IndexResp
207
+ res = RpbIndexResp.decode(message)
208
+ res.keys
209
+ when :SearchQueryResp
210
+ res = RpbSearchQueryResp.decode(message)
211
+ { 'docs' => res.docs.map {|d| decode_doc(d) },
212
+ 'max_score' => res.max_score,
213
+ 'num_found' => res.num_found }
214
+ end
215
+ end
216
+ rescue SystemCallError, SocketError => e
217
+ reset_socket
218
+ raise
219
+ end
220
+
221
+ def decode_doc(doc)
222
+ Hash[doc.properties.map {|p| [ p.key, p.value ] }]
223
+ end
224
+ end
225
+ end
226
+ end
@@ -0,0 +1,36 @@
1
+ module Riak
2
+ class Client
3
+ # A float value which decays exponentially toward 0 over time.
4
+ # @private
5
+ class Decaying
6
+ attr_accessor :e
7
+ attr_accessor :p
8
+
9
+ # @param [Hash] opts options
10
+ # @option options [Float] :p (0.0) The initial value
11
+ # @option options [Float] :e (Math::E) Exponent base
12
+ # @option options [Float] :r (Math.log(0.5) / 10) Timescale
13
+ # factor - defaulting to decay 50% every 10 seconds
14
+ def initialize(opts = {})
15
+ @p = opts[:p] || 0.0
16
+ @e = opts[:e] || Math::E
17
+ @r = opts[:r] || Math.log(0.5) / 10
18
+ @t0 = Time.now
19
+ end
20
+
21
+ # Add to current value.
22
+ # @param [Float] d the value to add
23
+ def <<(d)
24
+ @p = value + d
25
+ end
26
+
27
+ # @return [Float] the current value (adjusted for the time decay)
28
+ def value
29
+ now = Time.now
30
+ dt = now - @t0
31
+ @t0 = now
32
+ @p = @p * (@e ** (@r * dt))
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,162 @@
1
+ require 'riak/failed_request'
2
+ require 'riak/client/http_backend'
3
+ require 'riak/client/http_backend/request_headers'
4
+
5
+ module Riak
6
+ class Client
7
+ # An HTTP backend for Riak::Client that uses Wesley Beary's Excon
8
+ # HTTP library. Conforms to the Riak::Client::HTTPBackend
9
+ # interface.
10
+ class ExconBackend < HTTPBackend
11
+ def self.configured?
12
+ begin
13
+ require 'excon'
14
+ minimum_version?("0.5.7") && register_exceptions && handle_deprecations && patch_sockets
15
+ rescue LoadError
16
+ false
17
+ end
18
+ end
19
+
20
+ # Adds Excon's relevant internal exceptions to the rescuable
21
+ # network-related errors.
22
+ def self.register_exceptions
23
+ unless Client::NETWORK_ERRORS.include?(Excon::Errors::SocketError)
24
+ Client::NETWORK_ERRORS << Excon::Errors::SocketError
25
+ Client::NETWORK_ERRORS << Excon::Errors::Timeout if defined? Excon::Errors::Timeout
26
+ end
27
+ true
28
+ end
29
+
30
+ # Adjusts Excon's connection collection to allow multiple
31
+ # connections to the same host from the same Thread. Instead we
32
+ # use the Riak::Client::Pool to segregate connections.
33
+ # @note This can be changed when Excon has a proper pool of its own.
34
+ def self.patch_sockets
35
+ unless defined? @@patched
36
+ ::Excon::Connection.class_eval do
37
+ def sockets
38
+ @sockets ||= {}
39
+ end
40
+ end
41
+ end
42
+ @@patched = true
43
+ end
44
+
45
+ # Defines instance methods that handle changes in the Excon API
46
+ # across different versions.
47
+ def self.handle_deprecations
48
+ # Define #make_request
49
+ unless method_defined?(:make_request)
50
+ if minimum_version?("0.10.2")
51
+ def make_request(params, block)
52
+ params[:response_block] = block if block
53
+ connection.request(params)
54
+ end
55
+ else
56
+ def make_request(params, block)
57
+ response = connection.request(params, &block)
58
+ end
59
+ end
60
+ private :make_request
61
+ end
62
+
63
+ # Define #configure_ssl
64
+ unless method_defined?(:configure_ssl)
65
+ if minimum_version?("0.9.6")
66
+ def configure_ssl
67
+ Excon.defaults[:ssl_verify_peer] = (@node.ssl_options[:verify_mode].to_s === "peer")
68
+ Excon.defaults[:ssl_ca_path] = @node.ssl_options[:ca_path] if @node.ssl_options[:ca_path]
69
+ end
70
+ else
71
+ def configure_ssl
72
+ Excon.ssl_verify_peer = (@node.ssl_options[:verify_mode].to_s === "peer")
73
+ Excon.ssl_ca_path = @node.ssl_options[:ca_path] if @node.ssl_options[:ca_path]
74
+ end
75
+ end
76
+ private :configure_ssl
77
+ end
78
+ true
79
+ end
80
+
81
+ # Returns true if the Excon library is at least the given
82
+ # version. This is used inside the backend to check how to
83
+ # provide certain request and configuration options.
84
+ def self.minimum_version?(version)
85
+ Gem::Version.new(Excon::VERSION) >= Gem::Version.new(version)
86
+ end
87
+
88
+ # Sets the connect timeout applied to the Excon connection
89
+ # Increase this if you have very long request times.
90
+ def self.connect_timeout=(timeout)
91
+ @connect_timeout = timeout
92
+ end
93
+
94
+ def self.connect_timeout
95
+ @connect_timeout ||= 4096
96
+ end
97
+
98
+ # Sets the read_timeout applied to the Excon connection
99
+ # Increase this if you have very long request times.
100
+ def self.read_timeout=(timeout)
101
+ @read_timeout = timeout
102
+ end
103
+
104
+ def self.read_timeout
105
+ @read_timeout ||= 4096
106
+ end
107
+
108
+ # Sets the write_timeout applied to the Excon connection
109
+ # Increase this if you have very long request times.
110
+ def self.write_timeout=(timeout)
111
+ @write_timeout = timeout
112
+ end
113
+
114
+ def self.write_timeout
115
+ @write_timeout ||= 4096
116
+ end
117
+
118
+ def teardown
119
+ connection.reset
120
+ end
121
+
122
+ private
123
+ def perform(method, uri, headers, expect, data=nil, &block)
124
+ configure_ssl if @node.ssl_enabled?
125
+
126
+ params = {
127
+ :method => method.to_s.upcase,
128
+ :headers => RequestHeaders.new(headers).to_hash,
129
+ :path => uri.path
130
+ }
131
+ params[:query] = uri.query if uri.query
132
+ params[:body] = data if [:put,:post].include?(method)
133
+ params[:idempotent] = (method != :post)
134
+
135
+ # Later versions of Excon pass multiple arguments to the block
136
+ block = lambda {|*args| yield args.first } if block_given?
137
+
138
+ response = make_request(params, block)
139
+ response_headers.initialize_http_header(response.headers)
140
+
141
+ if valid_response?(expect, response.status)
142
+ result = {:headers => response_headers.to_hash, :code => response.status}
143
+ if return_body?(method, response.status, block_given?)
144
+ result[:body] = response.body
145
+ end
146
+ result
147
+ else
148
+ raise HTTPFailedRequest.new(method, expect, response.status, response_headers.to_hash, response.body)
149
+ end
150
+ end
151
+
152
+ def connection
153
+ @connection ||= Excon::Connection.new(
154
+ root_uri.to_s,
155
+ :read_timeout => self.class.read_timeout,
156
+ :write_timeout => self.class.write_timeout,
157
+ :connect_timeout => self.class.connect_timeout
158
+ )
159
+ end
160
+ end
161
+ end
162
+ end