riakrest 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +4 -0
- data/Manifest.txt +41 -0
- data/PostInstall.txt +2 -0
- data/README.rdoc +51 -0
- data/Rakefile +24 -0
- data/examples/auto_update_data.rb +50 -0
- data/examples/auto_update_links.rb +48 -0
- data/examples/basic_client.rb +33 -0
- data/examples/basic_resource.rb +34 -0
- data/examples/json_data_resource.rb +32 -0
- data/examples/linked_resource.rb +113 -0
- data/examples/multiple_resources.rb +43 -0
- data/lib/riakrest/core/exceptions.rb +73 -0
- data/lib/riakrest/core/jiak_bucket.rb +146 -0
- data/lib/riakrest/core/jiak_client.rb +316 -0
- data/lib/riakrest/core/jiak_data.rb +265 -0
- data/lib/riakrest/core/jiak_link.rb +131 -0
- data/lib/riakrest/core/jiak_object.rb +233 -0
- data/lib/riakrest/core/jiak_schema.rb +242 -0
- data/lib/riakrest/core/query_link.rb +156 -0
- data/lib/riakrest/data/jiak_data_hash.rb +182 -0
- data/lib/riakrest/resource/jiak_resource.rb +628 -0
- data/lib/riakrest/version.rb +7 -0
- data/lib/riakrest.rb +164 -0
- data/riakrest.gemspec +38 -0
- data/script/console +10 -0
- data/script/destroy +14 -0
- data/script/generate +14 -0
- data/spec/core/exceptions_spec.rb +18 -0
- data/spec/core/jiak_bucket_spec.rb +103 -0
- data/spec/core/jiak_client_spec.rb +358 -0
- data/spec/core/jiak_link_spec.rb +77 -0
- data/spec/core/jiak_object_spec.rb +210 -0
- data/spec/core/jiak_schema_spec.rb +184 -0
- data/spec/core/query_link_spec.rb +128 -0
- data/spec/data/jiak_data_hash_spec.rb +14 -0
- data/spec/resource/jiak_resource_spec.rb +128 -0
- data/spec/riakrest_spec.rb +17 -0
- data/spec/spec.opts +5 -0
- data/spec/spec_helper.rb +12 -0
- data/tasks/rspec.rake +21 -0
- metadata +113 -0
@@ -0,0 +1,146 @@
|
|
1
|
+
module RiakRest
|
2
|
+
|
3
|
+
# Data is stored on the Jiak server by key under a bucket. During Jiak
|
4
|
+
# interaction, the bucket on the server has an associated schema which
|
5
|
+
# determines permissible data interaction. See JiakSchema for a discussion of
|
6
|
+
# schemas in Jiak. Since the bucket schema can be changed dynamically,
|
7
|
+
# schemas can be viewed more as a loose type system rather than an onerous
|
8
|
+
# restriction.
|
9
|
+
#
|
10
|
+
# In RiakRest buckets have an associated JiakData class, and each JiakData
|
11
|
+
# class has an associated JiakSchema. These associations facility setting and
|
12
|
+
# maintaining the current schema in use for a Jiak bucket. Dynamically
|
13
|
+
# changing the bucket schema means you can have either homogeneous (simplest)
|
14
|
+
# or heterogenous data in a single Jiak server bucket. It also means you can
|
15
|
+
# define multiple JiakData classes that effectively present different "views"
|
16
|
+
# (via schemas) into the same data stored on the Jiak server. These classes
|
17
|
+
# act like types that can determine which fields are accessible for reading
|
18
|
+
# and writing data. The JiakData class associated with a bucket is also used
|
19
|
+
# to marshal user-defined data going to and from the Jiak server.
|
20
|
+
#
|
21
|
+
# JiakResource greatly eases the bookkeeping necessary for heterogenous, as
|
22
|
+
# well as homogenous, data interaction with the Jiak server.
|
23
|
+
class JiakBucket
|
24
|
+
|
25
|
+
attr_reader :schema
|
26
|
+
attr_accessor :name, :data_class, :params
|
27
|
+
|
28
|
+
# :call-seq:
|
29
|
+
# JiakBucket.new(name,data_class,params={}) -> JiakBucket
|
30
|
+
#
|
31
|
+
# Create a bucket for use in Jiak interaction.
|
32
|
+
#
|
33
|
+
# Valid optional parameters are <code>params</code> hash are <code>:reads,
|
34
|
+
# :writes, :durable_writes, :waits</code>. See JiakClient#store,
|
35
|
+
# JiakClient#get, and JiakClient#delete for discriptions of these
|
36
|
+
# parameters.
|
37
|
+
#
|
38
|
+
# Raise JiakBucketException if the bucket name is not a non-empty string or
|
39
|
+
# the data class has not included JiakData.
|
40
|
+
def initialize(name,data_class,params={})
|
41
|
+
@name = transform_name(name)
|
42
|
+
@data_class = check_data_class(data_class)
|
43
|
+
@params = check_params(params)
|
44
|
+
end
|
45
|
+
|
46
|
+
# :call-seq:
|
47
|
+
# name = gname
|
48
|
+
#
|
49
|
+
# Set the name of the Jiak bucket.
|
50
|
+
#
|
51
|
+
# Raise JiakBucketException if not a non-empty string.
|
52
|
+
def name=(gname)
|
53
|
+
@name = transform_name(gname)
|
54
|
+
end
|
55
|
+
|
56
|
+
# :call-seq:
|
57
|
+
# data_class = klass
|
58
|
+
#
|
59
|
+
# Set the class for the data to be stored or retrieved from the bucket.
|
60
|
+
#
|
61
|
+
# Raise JiakBucketException if the data class has not included JiakData.
|
62
|
+
def data_class=(data_class)
|
63
|
+
@data_class = check_data_class(data_class)
|
64
|
+
end
|
65
|
+
|
66
|
+
# :call-seq:
|
67
|
+
# bucket.params = params
|
68
|
+
#
|
69
|
+
# Set default params for Jiak client requests. See JiakBucket#new for
|
70
|
+
# valid parameters.
|
71
|
+
#
|
72
|
+
def params=(params)
|
73
|
+
@params = check_params(params)
|
74
|
+
end
|
75
|
+
|
76
|
+
# :call-seq:
|
77
|
+
# bucket.schema -> JiakSchema
|
78
|
+
#
|
79
|
+
# Gets the data schema for this bucket. This call does not access the
|
80
|
+
# server, but rather returns the schema of the current data class
|
81
|
+
# associated with the bucket. This association is required to establish the
|
82
|
+
# Jiak server schema in the first place, so as long as the Jiak server
|
83
|
+
# schema has not be altered by another call to JiakClient#set_schema the
|
84
|
+
# information returned via this call will be current.
|
85
|
+
def schema
|
86
|
+
data_class.schema
|
87
|
+
end
|
88
|
+
|
89
|
+
# :call-seq:
|
90
|
+
# bucket == other -> true or false
|
91
|
+
#
|
92
|
+
# Equality -- JiakBuckets are equal if they contain the same attribute
|
93
|
+
# values.
|
94
|
+
def ==(other)
|
95
|
+
(@name == other.name &&
|
96
|
+
@data_class == other.data_class &&
|
97
|
+
@params == other.params) rescue false
|
98
|
+
end
|
99
|
+
|
100
|
+
# :call-seq:
|
101
|
+
# jiak_bucket.eql?(other) -> true or false
|
102
|
+
#
|
103
|
+
# Returns <code>true</code> if <i>jiak_bucket</i> and <i>other</i> contain
|
104
|
+
# the same attribute values.
|
105
|
+
def eql?(other)
|
106
|
+
(@name.eql?(other.name) &&
|
107
|
+
@data_class.eql?(other.data_class) &&
|
108
|
+
@params.eql?(other.params)) rescue false
|
109
|
+
end
|
110
|
+
|
111
|
+
def hash # :nodoc:
|
112
|
+
@name.hash + @data_class.hash + @params.hash
|
113
|
+
end
|
114
|
+
|
115
|
+
def transform_name(name)
|
116
|
+
unless name.is_a?(String)
|
117
|
+
raise JiakBucketException, "Name must be a string"
|
118
|
+
end
|
119
|
+
b_name = name.dup
|
120
|
+
b_name.strip!
|
121
|
+
raise JiakBucketException, "Name cannot be empty" if b_name.empty?
|
122
|
+
b_name
|
123
|
+
end
|
124
|
+
private :transform_name
|
125
|
+
|
126
|
+
def check_data_class(data_class)
|
127
|
+
unless data_class.include?(JiakData)
|
128
|
+
raise JiakBucketException, "Data class must be type of JiakData."
|
129
|
+
end
|
130
|
+
data_class
|
131
|
+
end
|
132
|
+
private :check_data_class
|
133
|
+
|
134
|
+
def check_params(params)
|
135
|
+
valid = [:reads,:writes,:durable_writes,:waits]
|
136
|
+
err = params.select {|k,v| !valid.include?(k)}
|
137
|
+
unless err.empty?
|
138
|
+
raise JiakBucketException, "unrecognized request params: #{err.keys}"
|
139
|
+
end
|
140
|
+
params
|
141
|
+
end
|
142
|
+
private :check_params
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
@@ -0,0 +1,316 @@
|
|
1
|
+
module RiakRest
|
2
|
+
|
3
|
+
# Restful client interaction with a Riak document store via a JSON
|
4
|
+
# interface. See RiakRest for a basic example usage.
|
5
|
+
class JiakClient
|
6
|
+
|
7
|
+
# :stopdoc:
|
8
|
+
APP_JSON = 'application/json'
|
9
|
+
JSON_DATA = 'json'
|
10
|
+
|
11
|
+
RETURN_BODY = 'returnbody'
|
12
|
+
READS = 'r'
|
13
|
+
WRITES = 'w'
|
14
|
+
DURABLE_WRITES = 'dw'
|
15
|
+
RESPONSE_WAITS = 'rw'
|
16
|
+
|
17
|
+
KEYS='keys'
|
18
|
+
SCHEMA='schema'
|
19
|
+
# :startdoc:
|
20
|
+
|
21
|
+
# :call-seq:
|
22
|
+
# JiakClient.new(uri) -> uri
|
23
|
+
#
|
24
|
+
# Create a new client for Riak RESTful (Jiak) interaction with the server at
|
25
|
+
# the specified URI.
|
26
|
+
#
|
27
|
+
# Raise JiakClientException if the server URI is not a string.
|
28
|
+
#
|
29
|
+
def initialize(uri='http://127.0.0.1:8002/jiak/')
|
30
|
+
unless uri.is_a?(String)
|
31
|
+
raise JiakClientException, "Jiak server URI shoud be a String."
|
32
|
+
end
|
33
|
+
@uri = uri
|
34
|
+
@uri += '/' unless @uri.end_with?('/')
|
35
|
+
@uri
|
36
|
+
end
|
37
|
+
|
38
|
+
# :call-seq:
|
39
|
+
# set_schema(bucket) -> nil
|
40
|
+
#
|
41
|
+
# Set the Jiak server schema for a bucket. The schema is determined by the
|
42
|
+
# JiakData associated with the JiakBucket.
|
43
|
+
#
|
44
|
+
# Raise JiakClientException if the bucket is not a JiakBucket.
|
45
|
+
#
|
46
|
+
def set_schema(bucket)
|
47
|
+
unless bucket.is_a?(JiakBucket)
|
48
|
+
raise JiakClientException, "Bucket must be a JiakBucket."
|
49
|
+
end
|
50
|
+
resp = RestClient.put(jiak_uri(bucket),
|
51
|
+
bucket.schema.to_jiak,
|
52
|
+
:content_type => APP_JSON,
|
53
|
+
:data_type => JSON_DATA,
|
54
|
+
:accept => APP_JSON)
|
55
|
+
end
|
56
|
+
|
57
|
+
# :call-seq:
|
58
|
+
# schema(bucket) -> JiakSchema
|
59
|
+
#
|
60
|
+
# Get the data schema for a bucket on a Jiak server. This involves a call
|
61
|
+
# to the Jiak server. See JiakBucket#schema for a way to get this
|
62
|
+
# information without server access.
|
63
|
+
def schema(bucket)
|
64
|
+
JiakSchema.from_jiak(bucket_info(bucket,SCHEMA))
|
65
|
+
end
|
66
|
+
|
67
|
+
# :call-seq:
|
68
|
+
# client.keys(bucket) -> array
|
69
|
+
#
|
70
|
+
# Get an Array of all known keys for the specified bucket. Since key lists
|
71
|
+
# are updated asynchronously the returned array can be out of date
|
72
|
+
# immediately after a put or delete.
|
73
|
+
def keys(bucket)
|
74
|
+
bucket_info(bucket,KEYS)
|
75
|
+
end
|
76
|
+
|
77
|
+
# :call-seq:
|
78
|
+
# store(object,opts={}) -> JiakObject or key
|
79
|
+
#
|
80
|
+
# Stores user-defined data (wrapped in a JiakObject) on the Jiak
|
81
|
+
# server. JiakData#to_jiak is used to prepare user-defined data for JSON
|
82
|
+
# transport to Jiak. That call is expected to return a Ruby hash
|
83
|
+
# representation of the writable JiakData fields that are JSONized for HTTP
|
84
|
+
# transport. Successful server writes return either the storage key or the
|
85
|
+
# stored JiakObject depending on the option <code>key</code>. The object
|
86
|
+
# for storage must be JiakObject. Valid options are:
|
87
|
+
#
|
88
|
+
# <code>:object</code> :: If <code>true</code>, on success return the stored JiakObject (which includes Jiak metadata); otherwise return just the key. Default is <code>false</code>, which returns the key.
|
89
|
+
# <code>:writes</code> :: The number of Riak nodes that must successfully store the data.
|
90
|
+
# <code>:durable_writes</code> :: The number of Riak nodes (<code>< writes</code>) that must successfully store the data in a durable manner.
|
91
|
+
# <code>:reads</code> :: The number of Riak nodes that must successfully read data if a JiakObject is being returned.
|
92
|
+
#
|
93
|
+
# If any of the request parameters <code>:writes, :durable_writes,
|
94
|
+
# :reads</code> are not set, each first defaults to the value set for the
|
95
|
+
# JiakBucket in the JiakObject, then to the value set on the Riak
|
96
|
+
# cluster. In general the values set on the Riak cluster should suffice.
|
97
|
+
#
|
98
|
+
# Raise JiakClientException if object not a JiakObject or illegal options
|
99
|
+
# are passed.<br/>
|
100
|
+
# Raise JiakResourceException on RESTful HTTP errors.
|
101
|
+
#
|
102
|
+
def store(jobj,opts={})
|
103
|
+
params = jobj.bucket.params
|
104
|
+
req_params = {
|
105
|
+
WRITES => opts[:writes] || params[:writes],
|
106
|
+
DURABLE_WRITES => opts[:durable_writes] || params[:durable_writes],
|
107
|
+
READS => opts[:reads] || params[:reads]
|
108
|
+
}
|
109
|
+
req_params[RETURN_BODY] = opts[:object] if opts[:object]
|
110
|
+
|
111
|
+
begin
|
112
|
+
uri = jiak_uri(jobj.bucket,jobj.key,req_params)
|
113
|
+
payload = jobj.to_jiak
|
114
|
+
headers = {
|
115
|
+
:content_type => APP_JSON,
|
116
|
+
:data_type => JSON_DATA,
|
117
|
+
:accept => APP_JSON }
|
118
|
+
# Decision tree:
|
119
|
+
# If key empty POST
|
120
|
+
# Else PUT
|
121
|
+
# If object true, return JiakObject
|
122
|
+
# Else
|
123
|
+
# POST - parse key from location header
|
124
|
+
# PUT - return the given key
|
125
|
+
key_empty = jobj.key.empty?
|
126
|
+
if(key_empty)
|
127
|
+
resp = RestClient.post(uri,payload,headers)
|
128
|
+
else
|
129
|
+
resp = RestClient.put(uri,payload,headers)
|
130
|
+
end
|
131
|
+
|
132
|
+
if(req_params[RETURN_BODY])
|
133
|
+
JiakObject.from_jiak(JSON.parse(resp),jobj.bucket.data_class)
|
134
|
+
elsif(key_empty)
|
135
|
+
resp.headers[:location].split('/').last
|
136
|
+
else
|
137
|
+
jobj.key
|
138
|
+
end
|
139
|
+
rescue RestClient::ExceptionWithResponse => err
|
140
|
+
fail_with_response("put", err)
|
141
|
+
rescue RestClient::Exception => err
|
142
|
+
fail_with_message("put", err)
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
# :call-seq:
|
147
|
+
# get(bucket,key,opts={}) -> JiakObject
|
148
|
+
#
|
149
|
+
# Get data stored on a Jiak server at a bucket/key. The user-defined data
|
150
|
+
# stored on the Jiak server is inflated inside a JiakObject that also
|
151
|
+
# includes Riak storage information. Data inflation is controlled by the
|
152
|
+
# data class associated with the bucket of this call.
|
153
|
+
#
|
154
|
+
# Since the data schema validation that occurs on the Jiak server validates
|
155
|
+
# to the last schema set for that Jiak server bucket, it is imperative that
|
156
|
+
# schema be the same (or at least consistent) with the schema associated
|
157
|
+
# with this bucket at retrieval time. If you only store homogeneous objects
|
158
|
+
# in a bucket this will not be an issue.
|
159
|
+
#
|
160
|
+
# The bucket must be a JiakBucket and the key must be a non-empty
|
161
|
+
# String. Valid options are:
|
162
|
+
#
|
163
|
+
# <code>:reads</code> --- The number of Riak nodes that must successfully
|
164
|
+
# reply with the data. If not set, defaults first to the value set for the
|
165
|
+
# JiakBucket, then to the value set on the Riak cluster. In general the
|
166
|
+
# values set on the Riak cluster should suffice.
|
167
|
+
#
|
168
|
+
# Raise JiakClientException if bucket not a JiakBucket.<br/>
|
169
|
+
# Raise JiakResourceNotFound if resource not found on Jiak server.<br/>
|
170
|
+
# Raise JiakResourceException on other HTTP RESTful errors.
|
171
|
+
#
|
172
|
+
def get(bucket,key,opts={})
|
173
|
+
unless bucket.is_a?(JiakBucket)
|
174
|
+
raise JiakClientException, "Bucket must be a JiakBucket."
|
175
|
+
end
|
176
|
+
req_params = {READS => opts[:reads] || bucket.params[:reads]}
|
177
|
+
|
178
|
+
begin
|
179
|
+
uri = jiak_uri(bucket,key,req_params)
|
180
|
+
resp = RestClient.get(uri, :accept => APP_JSON)
|
181
|
+
JiakObject.from_jiak(JSON.parse(resp),bucket.data_class)
|
182
|
+
rescue RestClient::ResourceNotFound => err
|
183
|
+
raise JiakResourceNotFound, "failed get: #{err.message}"
|
184
|
+
rescue RestClient::ExceptionWithResponse => err
|
185
|
+
fail_with_response("get", err)
|
186
|
+
rescue RestClient::Exception => err
|
187
|
+
fail_with_message("get",err)
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
# :call-seq:
|
192
|
+
# delete(bucket,key,opts={}) -> true or false
|
193
|
+
#
|
194
|
+
# Delete the JiakObject stored at the bucket/key. Valid options are:
|
195
|
+
#
|
196
|
+
# <code>:waits</code> --- The number of Riak nodes that must reply the
|
197
|
+
# delete has occurred before success. If not set, defaults first to the
|
198
|
+
# value set for the JiakBucket, then to the value set on the Riak
|
199
|
+
# cluster. In general the values set on the Riak cluster should suffice.
|
200
|
+
#
|
201
|
+
# Raise JiakResourceException on RESTful HTTP errors.
|
202
|
+
#
|
203
|
+
def delete(bucket,key,opts={})
|
204
|
+
begin
|
205
|
+
req_params = {RESPONSE_WAITS => opts[:waits] || bucket.params[:waits]}
|
206
|
+
uri = jiak_uri(bucket,key,req_params)
|
207
|
+
RestClient.delete(uri, :accept => APP_JSON)
|
208
|
+
true
|
209
|
+
rescue RestClient::ExceptionWithResponse => err
|
210
|
+
fail_with_response("delete", err)
|
211
|
+
rescue RestClient::Exception => err
|
212
|
+
fail_with_message("delete", err)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
|
216
|
+
# :call-seq:
|
217
|
+
# walk(bucket,key,query,data_class) -> array
|
218
|
+
#
|
219
|
+
# Return an array of JiakObjects retrieved by walking a query links
|
220
|
+
# array starting with the links for the object at bucket/key. The
|
221
|
+
# data_class is used to inflate the data objects returned in the
|
222
|
+
# JiakObject array.
|
223
|
+
#
|
224
|
+
# See QueryLink for a description of the <code>query</code> structure.
|
225
|
+
def walk(bucket,key,query,data_class)
|
226
|
+
begin
|
227
|
+
start = jiak_uri(bucket,key)
|
228
|
+
case query
|
229
|
+
when QueryLink
|
230
|
+
uri = start+'/'+query.for_uri
|
231
|
+
when Array
|
232
|
+
uri = query.inject(start) {|build,link| build+'/'+link.for_uri}
|
233
|
+
else
|
234
|
+
raise QueryLinkException, 'failed: query must be '+
|
235
|
+
'a QueryLink or an Array of QueryLink objects'
|
236
|
+
end
|
237
|
+
resp = RestClient.get(uri, :accept => APP_JSON)
|
238
|
+
# JSON.parse(resp)['results'][0]
|
239
|
+
JSON.parse(resp)['results'][0].map do |jiak|
|
240
|
+
JiakObject.from_jiak(jiak,data_class)
|
241
|
+
end
|
242
|
+
rescue RestClient::ExceptionWithResponse => err
|
243
|
+
fail_with_response("put", err)
|
244
|
+
rescue RestClient::Exception => err
|
245
|
+
fail_with_message("put", err)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
|
249
|
+
# :call-seq:
|
250
|
+
# client.uri -> string
|
251
|
+
#
|
252
|
+
# String representation of the base URI of the Jiak server.
|
253
|
+
def uri
|
254
|
+
@uri
|
255
|
+
end
|
256
|
+
|
257
|
+
# :call-seq:
|
258
|
+
# client == other -> true or false
|
259
|
+
#
|
260
|
+
# Equality -- JiakClients are equal if they have the same URI
|
261
|
+
def ==(other)
|
262
|
+
(@uri == other.uri) rescue false
|
263
|
+
end
|
264
|
+
|
265
|
+
# :call-seq:
|
266
|
+
# eql?(other) -> true or false
|
267
|
+
#
|
268
|
+
# Returns <code>true</code> if <code>other</code> is a JiakClint with the
|
269
|
+
# same URI.
|
270
|
+
def eql?(other)
|
271
|
+
other.is_a?(JiakClient) &&
|
272
|
+
@uri.eql?(other.uri)
|
273
|
+
end
|
274
|
+
|
275
|
+
def hash # :nodoc:
|
276
|
+
@uri.hash
|
277
|
+
end
|
278
|
+
|
279
|
+
# Build the URI for accessing the Jiak server.
|
280
|
+
def jiak_uri(bucket,key="",params={})
|
281
|
+
uri = "#{@uri}#{URI.encode(bucket.name)}"
|
282
|
+
uri += "/#{URI.encode(key)}" unless key.empty?
|
283
|
+
qstring = params.reject {|k,v| v.nil?}.map{|k,v| "#{k}=#{v}"}.join('&')
|
284
|
+
uri += "?#{URI.encode(qstring)}" unless qstring.empty?
|
285
|
+
uri
|
286
|
+
end
|
287
|
+
private :jiak_uri
|
288
|
+
|
289
|
+
# Get either the schema or keys for the bucket.
|
290
|
+
def bucket_info(bucket,info)
|
291
|
+
ignore = (info == SCHEMA) ? KEYS : SCHEMA
|
292
|
+
begin
|
293
|
+
uri = jiak_uri(bucket,"",{ignore => false})
|
294
|
+
JSON.parse(RestClient.get(uri, :accept => APP_JSON))[info]
|
295
|
+
rescue RestClient::ExceptionWithResponse => err
|
296
|
+
fail_with_response("get", err)
|
297
|
+
rescue RestClient::Exception => err
|
298
|
+
fail_with_message("get", err)
|
299
|
+
end
|
300
|
+
end
|
301
|
+
private :bucket_info
|
302
|
+
|
303
|
+
def fail_with_response(action,err)
|
304
|
+
raise( JiakResourceException,
|
305
|
+
"failed #{action}: HTTP code #{err.http_code}: #{err.http_body}")
|
306
|
+
end
|
307
|
+
private :fail_with_response
|
308
|
+
|
309
|
+
def fail_with_message(action,err)
|
310
|
+
raise JiakResourceException, "failed #{action}: #{err.message}"
|
311
|
+
end
|
312
|
+
private :fail_with_message
|
313
|
+
|
314
|
+
end
|
315
|
+
|
316
|
+
end
|