scalaroid 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.document +4 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +30 -0
- data/LICENSE.txt +202 -0
- data/Rakefile +8 -0
- data/bin/scalaroid +117 -0
- data/lib/scalaroid/json_connection.rb +370 -0
- data/lib/scalaroid/version.rb +3 -0
- data/lib/scalaroid.rb +108 -0
- data/scalaroid.gemspec +48 -0
- data/test/replicated_dht_test.rb +143 -0
- data/test/test_helper.rb +28 -0
- data/test/test_pub_sub.rb +299 -0
- data/test/transaction_single_op_test.rb +375 -0
- data/test/transaction_test.rb +267 -0
- metadata +130 -0
@@ -0,0 +1,370 @@
|
|
1
|
+
module Scalaroid
|
2
|
+
# Abstracts connections to Scalaris using JSON
|
3
|
+
class JSONConnection
|
4
|
+
# Creates a JSON connection to the given URL using the given TCP timeout (or default)
|
5
|
+
def initialize(url = DEFAULT_URL, timeout = nil)
|
6
|
+
begin
|
7
|
+
@uri = URI.parse(url)
|
8
|
+
@timeout = timeout
|
9
|
+
start
|
10
|
+
rescue Exception => error
|
11
|
+
raise ConnectionError.new(error)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def start
|
16
|
+
if @conn == nil or not @conn.started?
|
17
|
+
@conn = Net::HTTP.start(@uri.host, @uri.port)
|
18
|
+
unless @timeout.nil?
|
19
|
+
@conn.read_timeout = @timeout
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
private :start
|
24
|
+
|
25
|
+
# Calls the given function with the given parameters via the JSON
|
26
|
+
# interface of Scalaris.
|
27
|
+
def call(function, params)
|
28
|
+
start
|
29
|
+
req = Net::HTTP::Post.new(DEFAULT_PATH)
|
30
|
+
req.add_field('Content-Type', 'application/json; charset=utf-8')
|
31
|
+
req.body = URI::encode({
|
32
|
+
:jsonrpc => :'2.0',
|
33
|
+
:method => function,
|
34
|
+
:params => params,
|
35
|
+
:id => 0 }.to_json({:ascii_only => true}))
|
36
|
+
begin
|
37
|
+
res = @conn.request(req)
|
38
|
+
if res.is_a?(Net::HTTPSuccess)
|
39
|
+
data = res.body
|
40
|
+
return JSON.parse(data)['result']
|
41
|
+
else
|
42
|
+
raise ConnectionError.new(res)
|
43
|
+
end
|
44
|
+
rescue ConnectionError => error
|
45
|
+
raise error
|
46
|
+
rescue Exception => error
|
47
|
+
raise ConnectionError.new(error)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Encodes the value to the form required by the Scalaris JSON API
|
52
|
+
def self.encode_value(value, binary = false)
|
53
|
+
if binary
|
54
|
+
return { :type => :as_bin, :value => Base64.encode64(value) }
|
55
|
+
else
|
56
|
+
return { :type => :as_is, :value => value }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Decodes the value from the Scalaris JSON API form to a native type
|
61
|
+
def self.decode_value(value)
|
62
|
+
if not (value.has_key?('type') and value.has_key?('value'))
|
63
|
+
raise ConnectionError.new(value)
|
64
|
+
end
|
65
|
+
if value['type'] == 'as_bin'
|
66
|
+
return Base64.decode64(value['value'])
|
67
|
+
else
|
68
|
+
return value['value']
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Processes the result of some Scalaris operation and raises a
|
73
|
+
# TimeoutError if found.
|
74
|
+
#
|
75
|
+
# result: {'status': 'ok'} or
|
76
|
+
# {'status': 'fail', 'reason': 'timeout'}
|
77
|
+
def self.check_fail_abort(result)
|
78
|
+
if result == {:status => 'fail', :reason => 'timeout'}
|
79
|
+
raise TimeoutError.new(result)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Processes the result of a read operation.
|
84
|
+
# Returns the read value on success.
|
85
|
+
# Raises the appropriate exception if the operation failed.
|
86
|
+
#
|
87
|
+
# result: {'status' => 'ok', 'value': xxx} or
|
88
|
+
# {'status' => 'fail', 'reason' => 'timeout' or 'not_found'}
|
89
|
+
def self.process_result_read(result)
|
90
|
+
if result.is_a?(Hash) and result.has_key?('status') and result.length == 2
|
91
|
+
if result['status'] == 'ok' and result.has_key?('value')
|
92
|
+
return decode_value(result['value'])
|
93
|
+
elsif result['status'] == 'fail' and result.has_key?('reason')
|
94
|
+
if result['reason'] == 'timeout'
|
95
|
+
raise TimeoutError.new(result)
|
96
|
+
elsif result['reason'] == 'not_found'
|
97
|
+
raise NotFoundError.new(result)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
raise UnknownError.new(result)
|
102
|
+
end
|
103
|
+
|
104
|
+
# Processes the result of a write operation.
|
105
|
+
# Raises the appropriate exception if the operation failed.
|
106
|
+
#
|
107
|
+
# result: {'status' => 'ok'} or
|
108
|
+
# {'status' => 'fail', 'reason' => 'timeout'}
|
109
|
+
def self.process_result_write(result)
|
110
|
+
if result.is_a?(Hash)
|
111
|
+
if result == {'status' => 'ok'}
|
112
|
+
return true
|
113
|
+
elsif result == {'status' => 'fail', 'reason' => 'timeout'}
|
114
|
+
raise TimeoutError.new(result)
|
115
|
+
end
|
116
|
+
end
|
117
|
+
raise UnknownError.new(result)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Processes the result of a commit operation.
|
121
|
+
# Raises the appropriate exception if the operation failed.
|
122
|
+
#
|
123
|
+
# result: {'status' => 'ok'} or
|
124
|
+
# {'status' => 'fail', 'reason' => 'abort', 'keys' => <list>} or
|
125
|
+
# {'status' => 'fail', 'reason' => 'timeout'}
|
126
|
+
def self.process_result_commit(result)
|
127
|
+
if result.is_a?(Hash) and result.has_key?('status')
|
128
|
+
if result == {'status' => 'ok'}
|
129
|
+
return true
|
130
|
+
elsif result['status'] == 'fail' and result.has_key?('reason')
|
131
|
+
if result.length == 2 and result['reason'] == 'timeout'
|
132
|
+
raise TimeoutError.new(result)
|
133
|
+
elsif result.length == 3 and result['reason'] == 'abort' and result.has_key?('keys')
|
134
|
+
raise AbortError.new(result, result['keys'])
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
raise UnknownError.new(result)
|
139
|
+
end
|
140
|
+
|
141
|
+
# Processes the result of a add_del_on_list operation.
|
142
|
+
# Raises the appropriate exception if the operation failed.
|
143
|
+
#
|
144
|
+
# results: {'status': 'ok'} or
|
145
|
+
# {'status': 'fail', 'reason': 'timeout' or 'not_a_list'}
|
146
|
+
def self.process_result_add_del_on_list(result)
|
147
|
+
if result.is_a?(Hash) and result.has_key?('status')
|
148
|
+
if result == {'status' => 'ok'}
|
149
|
+
return nil
|
150
|
+
elsif result['status'] == 'fail' and result.has_key?('reason')
|
151
|
+
if result.length == 2
|
152
|
+
if result['reason'] == 'timeout'
|
153
|
+
raise TimeoutError.new(result)
|
154
|
+
elsif result['reason'] == 'not_a_list'
|
155
|
+
raise NotAListError.new(result)
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
160
|
+
raise UnknownError.new(result)
|
161
|
+
end
|
162
|
+
|
163
|
+
# Processes the result of a add_on_nr operation.
|
164
|
+
# Raises the appropriate exception if the operation failed.
|
165
|
+
#
|
166
|
+
# results: {'status': 'ok'} or
|
167
|
+
# {'status': 'fail', 'reason': 'timeout' or 'not_a_number'}
|
168
|
+
def self.process_result_add_on_nr(result)
|
169
|
+
if result.is_a?(Hash) and result.has_key?('status')
|
170
|
+
if result == {'status' => 'ok'}
|
171
|
+
return nil
|
172
|
+
elsif result['status'] == 'fail' and result.has_key?('reason')
|
173
|
+
if result.length == 2
|
174
|
+
if result['reason'] == 'timeout'
|
175
|
+
raise TimeoutError.new(result)
|
176
|
+
elsif result['reason'] == 'not_a_number'
|
177
|
+
raise NotANumberError.new(result)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
raise UnknownError.new(result)
|
183
|
+
end
|
184
|
+
|
185
|
+
# Processes the result of a test_and_set operation.
|
186
|
+
# Raises the appropriate exception if the operation failed.
|
187
|
+
#
|
188
|
+
# results: {'status' => 'ok'} or
|
189
|
+
# {'status' => 'fail', 'reason' => 'timeout' or 'not_found'} or
|
190
|
+
# {'status' => 'fail', 'reason' => 'key_changed', 'value': xxx}
|
191
|
+
def self.process_result_test_and_set(result)
|
192
|
+
if result.is_a?(Hash) and result.has_key?('status')
|
193
|
+
if result == {'status' => 'ok'}
|
194
|
+
return nil
|
195
|
+
elsif result['status'] == 'fail' and result.has_key?('reason')
|
196
|
+
if result.length == 2
|
197
|
+
if result['reason'] == 'timeout'
|
198
|
+
raise TimeoutError.new(result)
|
199
|
+
elsif result['reason'] == 'not_found'
|
200
|
+
raise NotFoundError.new(result)
|
201
|
+
end
|
202
|
+
elsif result['reason'] == 'key_changed' and result.has_key?('value') and result.length == 3
|
203
|
+
raise KeyChangedError.new(result, decode_value(result['value']))
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
raise UnknownError.new(result)
|
208
|
+
end
|
209
|
+
|
210
|
+
# Processes the result of a publish operation.
|
211
|
+
# Raises the appropriate exception if the operation failed.
|
212
|
+
#
|
213
|
+
# results: {'status': 'ok'}
|
214
|
+
def self.process_result_publish(result)
|
215
|
+
if result == {'status' => 'ok'}
|
216
|
+
return nil
|
217
|
+
end
|
218
|
+
raise UnknownError.new(result)
|
219
|
+
end
|
220
|
+
|
221
|
+
# Processes the result of a subscribe operation.
|
222
|
+
# Raises the appropriate exception if the operation failed.
|
223
|
+
#
|
224
|
+
# results: {'status': 'ok'} or
|
225
|
+
# {'status': 'fail', 'reason': 'timeout' or 'abort'}
|
226
|
+
def self.process_result_subscribe(result)
|
227
|
+
process_result_commit(result)
|
228
|
+
end
|
229
|
+
|
230
|
+
# Processes the result of a unsubscribe operation.
|
231
|
+
# Raises the appropriate exception if the operation failed.
|
232
|
+
#
|
233
|
+
# results: {'status': 'ok'} or
|
234
|
+
# {'status': 'fail', 'reason': 'timeout' or 'abort' or 'not_found'}
|
235
|
+
def self.process_result_unsubscribe(result)
|
236
|
+
if result == {'status' => 'ok'}
|
237
|
+
return nil
|
238
|
+
elsif result.is_a?(Hash) and result.has_key?('status')
|
239
|
+
if result['status'] == 'fail' and result.has_key?('reason')
|
240
|
+
if result.length == 2
|
241
|
+
if result['reason'] == 'timeout'
|
242
|
+
raise TimeoutError.new(result)
|
243
|
+
elsif result['reason'] == 'not_found'
|
244
|
+
raise NotFoundError.new(result)
|
245
|
+
end
|
246
|
+
elsif result.length == 3 and result['reason'] == 'abort' and result.has_key?('keys')
|
247
|
+
raise AbortError.new(result, result['keys'])
|
248
|
+
end
|
249
|
+
end
|
250
|
+
end
|
251
|
+
raise UnknownError.new(result)
|
252
|
+
end
|
253
|
+
|
254
|
+
# Processes the result of a get_subscribers operation.
|
255
|
+
# Returns the list of subscribers on success.
|
256
|
+
# Raises the appropriate exception if the operation failed.
|
257
|
+
#
|
258
|
+
# results: [urls=str()]
|
259
|
+
def self.process_result_get_subscribers(result)
|
260
|
+
if result.is_a?(Array)
|
261
|
+
return result
|
262
|
+
end
|
263
|
+
raise UnknownError.new(result)
|
264
|
+
end
|
265
|
+
|
266
|
+
# Processes the result of a delete operation.
|
267
|
+
# Returns an Array of
|
268
|
+
# {:success => true | :timeout, :ok => <number of deleted items>, :results => <detailed results>}
|
269
|
+
# on success.
|
270
|
+
# Does not raise an exception if the operation failed unless the result
|
271
|
+
# is invalid!
|
272
|
+
#
|
273
|
+
# results: {'ok': xxx, 'results': ['ok' or 'locks_set' or 'undef']} or
|
274
|
+
# {'failure': 'timeout', 'ok': xxx, 'results': ['ok' or 'locks_set' or 'undef']}
|
275
|
+
def self.process_result_delete(result)
|
276
|
+
if result.is_a?(Hash) and result.has_key?('ok') and result.has_key?('results')
|
277
|
+
if not result.has_key?('failure')
|
278
|
+
return {:success => true,
|
279
|
+
:ok => result['ok'],
|
280
|
+
:results => result['results']}
|
281
|
+
elsif result['failure'] == 'timeout'
|
282
|
+
return {:success => :timeout,
|
283
|
+
:ok => result['ok'],
|
284
|
+
:results => result['results']}
|
285
|
+
end
|
286
|
+
end
|
287
|
+
raise UnknownError.new(result)
|
288
|
+
end
|
289
|
+
|
290
|
+
# Creates a new DeleteResult from the given result list.
|
291
|
+
#
|
292
|
+
# result: ['ok' or 'locks_set' or 'undef']
|
293
|
+
def self.create_delete_result(result)
|
294
|
+
ok = 0
|
295
|
+
locks_set = 0
|
296
|
+
undefined = 0
|
297
|
+
if result.is_a?(Array)
|
298
|
+
for element in result
|
299
|
+
if element == 'ok'
|
300
|
+
ok += 1
|
301
|
+
elsif element == 'locks_set'
|
302
|
+
locks_set += 1
|
303
|
+
elsif element == 'undef'
|
304
|
+
undefined += 1
|
305
|
+
else
|
306
|
+
raise UnknownError.new(:'Unknown reason ' + element + :'in ' + result)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
return DeleteResult.new(ok, locks_set, undefined)
|
310
|
+
end
|
311
|
+
raise UnknownError.new(:'Unknown result ' + result)
|
312
|
+
end
|
313
|
+
|
314
|
+
# Processes the result of a req_list operation of the Transaction class.
|
315
|
+
# Returns the Array (:tlog => <tlog>, :result => <result>) on success.
|
316
|
+
# Raises the appropriate exception if the operation failed.
|
317
|
+
#
|
318
|
+
# results: {'tlog': xxx,
|
319
|
+
# 'results': [{'status': 'ok'} or {'status': 'ok', 'value': xxx} or
|
320
|
+
# {'status': 'fail', 'reason': 'timeout' or 'abort' or 'not_found'}]}
|
321
|
+
def self.process_result_req_list_t(result)
|
322
|
+
if (not result.has_key?('tlog')) or (not result.has_key?('results')) or
|
323
|
+
(not result['results'].is_a?(Array))
|
324
|
+
raise UnknownError.new(result)
|
325
|
+
end
|
326
|
+
{:tlog => result['tlog'], :result => result['results']}
|
327
|
+
end
|
328
|
+
|
329
|
+
# Processes the result of a req_list operation of the TransactionSingleOp class.
|
330
|
+
# Returns <result> on success.
|
331
|
+
# Raises the appropriate exception if the operation failed.
|
332
|
+
#
|
333
|
+
# results: [{'status': 'ok'} or {'status': 'ok', 'value': xxx} or
|
334
|
+
# {'status': 'fail', 'reason': 'timeout' or 'abort' or 'not_found'}]
|
335
|
+
def self.process_result_req_list_tso(result)
|
336
|
+
if not result.is_a?(Array)
|
337
|
+
raise UnknownError.new(result)
|
338
|
+
end
|
339
|
+
result
|
340
|
+
end
|
341
|
+
|
342
|
+
# Processes the result of a nop operation.
|
343
|
+
# Raises the appropriate exception if the operation failed.
|
344
|
+
#
|
345
|
+
# result: 'ok'
|
346
|
+
def self.process_result_nop(result)
|
347
|
+
if result != 'ok'
|
348
|
+
raise UnknownError.new(result)
|
349
|
+
end
|
350
|
+
end
|
351
|
+
|
352
|
+
# Returns a new ReqList object allowing multiple parallel requests for
|
353
|
+
# the Transaction class.
|
354
|
+
def self.new_req_list_t(other = nil)
|
355
|
+
JSONReqListTransaction.new(other)
|
356
|
+
end
|
357
|
+
|
358
|
+
# Returns a new ReqList object allowing multiple parallel requests for
|
359
|
+
# the TransactionSingleOp class.
|
360
|
+
def self.new_req_list_tso(other = nil)
|
361
|
+
JSONReqListTransactionSingleOp.new(other)
|
362
|
+
end
|
363
|
+
|
364
|
+
def close
|
365
|
+
if @conn.started?
|
366
|
+
@conn.finish()
|
367
|
+
end
|
368
|
+
end
|
369
|
+
end
|
370
|
+
end
|
data/lib/scalaroid.rb
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
#!/usr/bin/ruby -KU
|
2
|
+
# Copyright 2008-2011 Zuse Institute Berlin
|
3
|
+
#
|
4
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
5
|
+
# you may not use this file except in compliance with the License.
|
6
|
+
# You may obtain a copy of the License at
|
7
|
+
#
|
8
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
9
|
+
#
|
10
|
+
# Unless required by applicable law or agreed to in writing, software
|
11
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
12
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
13
|
+
# See the License for the specific language governing permissions and
|
14
|
+
# limitations under the License.
|
15
|
+
|
16
|
+
require 'rubygems'
|
17
|
+
gem 'json', '>=1.4.1'
|
18
|
+
require 'json'
|
19
|
+
require 'net/http'
|
20
|
+
require 'base64'
|
21
|
+
require 'open-uri'
|
22
|
+
|
23
|
+
module InternalScalarisSimpleError
|
24
|
+
attr_reader :raw_result
|
25
|
+
def initialize(raw_result)
|
26
|
+
@raw_result = raw_result
|
27
|
+
end
|
28
|
+
|
29
|
+
def to_s
|
30
|
+
@raw_result
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
module InternalScalarisNopClose
|
35
|
+
# No operation (may be used for measuring the JSON overhead).
|
36
|
+
def nop(value)
|
37
|
+
value = @conn.class.encode_value(value)
|
38
|
+
result = @conn.call(:nop, [value])
|
39
|
+
@conn.class.process_result_nop(result)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Close the connection to Scalaris
|
43
|
+
# (it will automatically be re-opened on the next request).
|
44
|
+
def close_connection
|
45
|
+
@conn.close()
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# work around floating point numbers not being printed precisely enough
|
50
|
+
class Float
|
51
|
+
# note: can not override to_json (this is not done recursively, e.g. in a Hash, before ruby 1.9)
|
52
|
+
alias_method :orig_t_s, :to_s
|
53
|
+
def to_s
|
54
|
+
if not finite?
|
55
|
+
orig_to_json(*a)
|
56
|
+
else
|
57
|
+
sprintf("%#.17g", self)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
module Scalaroid
|
63
|
+
autoload :ScalarisError, "scalaroid/errors"
|
64
|
+
autoload :AbortError, "scalaroid/errors"
|
65
|
+
autoload :ConnectionError, "scalaroid/errors"
|
66
|
+
autoload :KeyChangedError, "scalaroid/errors"
|
67
|
+
autoload :NodeNotFoundError, "scalaroid/errors"
|
68
|
+
autoload :NotFoundError, "scalaroid/errors"
|
69
|
+
autoload :NotAListError, "scalaroid/errors"
|
70
|
+
autoload :NotANumberError, "scalaroid/errors"
|
71
|
+
autoload :TimeoutError, "scalaroid/errors"
|
72
|
+
autoload :UnknownError, "scalaroid/errors"
|
73
|
+
|
74
|
+
autoload :DeleteResult, "scalaroid/delete_result"
|
75
|
+
autoload :JSONConnection, "scalaroid/json_connection"
|
76
|
+
autoload :JSONReqList, "scalaroid/json_req_list"
|
77
|
+
autoload :JSONReqListTransaction, "scalaroid/json_req_list_transaction"
|
78
|
+
autoload :JSONReqListTransactionSingleOp, "scalaroid/json_req_list_transaction_single_op"
|
79
|
+
autoload :PubSub, "scalaroid/pub_sub"
|
80
|
+
autoload :ReplicatedDHT, "scalaroid/replicated_dht"
|
81
|
+
autoload :Transaction, "scalaroid/transaction"
|
82
|
+
autoload :TransactionSingleOp, "scalaroid/transaction_single_op"
|
83
|
+
autoload :VERSION, "scalaroid/version"
|
84
|
+
|
85
|
+
# default URL and port to a scalaris node
|
86
|
+
if ENV.has_key?('SCALARIS_JSON_URL') and not ENV['SCALARIS_JSON_URL'].empty?
|
87
|
+
DEFAULT_URL = ENV['SCALARIS_JSON_URL']
|
88
|
+
else
|
89
|
+
DEFAULT_URL = 'http://localhost:8000'
|
90
|
+
end
|
91
|
+
|
92
|
+
# path to the json rpc page
|
93
|
+
DEFAULT_PATH = '/jsonrpc.yaws'
|
94
|
+
|
95
|
+
|
96
|
+
# Converts a string to a list of integers.
|
97
|
+
# If the expected value of a read operation is a list, the returned value
|
98
|
+
# could be (mistakenly) a string if it is a list of integers.
|
99
|
+
def str_to_list(value)
|
100
|
+
if value.is_a?(String)
|
101
|
+
return value.unpack("U*")
|
102
|
+
else
|
103
|
+
return value
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
module_function :str_to_list
|
108
|
+
end
|
data/scalaroid.gemspec
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
# stub: scalaroid 0.0.1 ruby lib
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "scalaroid"
|
9
|
+
s.version = "0.0.1"
|
10
|
+
|
11
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
12
|
+
s.require_paths = ["lib"]
|
13
|
+
s.authors = ["Teodor Pripoae"]
|
14
|
+
s.date = "2014-07-07"
|
15
|
+
s.description = "Ruby bindings for Scalaris forked from official Scalaris bindings"
|
16
|
+
s.email = "teodor.pripoae@gmail.com"
|
17
|
+
s.executables = ["scalaroid"]
|
18
|
+
s.extra_rdoc_files = [
|
19
|
+
"LICENSE.txt"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".document",
|
23
|
+
"Gemfile",
|
24
|
+
"Gemfile.lock",
|
25
|
+
"LICENSE.txt",
|
26
|
+
"Rakefile",
|
27
|
+
"bin/scalaroid",
|
28
|
+
"lib/scalaroid.rb",
|
29
|
+
"lib/scalaroid/json_connection.rb",
|
30
|
+
"lib/scalaroid/version.rb",
|
31
|
+
"scalaroid.gemspec",
|
32
|
+
"test/replicated_dht_test.rb",
|
33
|
+
"test/test_helper.rb",
|
34
|
+
"test/test_pub_sub.rb",
|
35
|
+
"test/transaction_single_op_test.rb",
|
36
|
+
"test/transaction_test.rb"
|
37
|
+
]
|
38
|
+
s.homepage = "http://github.com/teodor-pripoae/scalaroid"
|
39
|
+
s.licenses = ["Apache-2.0"]
|
40
|
+
s.rubygems_version = "2.2.2"
|
41
|
+
s.summary = "Ruby bindings for Scalaris"
|
42
|
+
|
43
|
+
s.add_runtime_dependency(%q<json>, ["~> 1.7"])
|
44
|
+
s.add_development_dependency(%q<rake>, ["~> 10.0"])
|
45
|
+
s.add_development_dependency(%q<minitest>, ["~> 5.3"])
|
46
|
+
s.add_development_dependency(%q<pry>, ["= 0.9.12.6"])
|
47
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0"])
|
48
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
require_relative "test_helper"
|
2
|
+
|
3
|
+
class TestReplicatedDHT < Minitest::Test
|
4
|
+
def setup
|
5
|
+
@testTime = (Time.now.to_f * 1000).to_i
|
6
|
+
end
|
7
|
+
|
8
|
+
# Test method for ReplicatedDHT()
|
9
|
+
def test_replicated_dht1()
|
10
|
+
rdht = Scalaroid::ReplicatedDHT.new()
|
11
|
+
rdht.close_connection()
|
12
|
+
end
|
13
|
+
|
14
|
+
# Test method for ReplicatedDHT(conn)
|
15
|
+
def test_replicated_dht2()
|
16
|
+
rdht = Scalaroid::ReplicatedDHT.new(conn = Scalaroid::JSONConnection.new(url = Scalaroid::DEFAULT_URL))
|
17
|
+
rdht.close_connection()
|
18
|
+
end
|
19
|
+
|
20
|
+
# Test method for ReplicatedDHT.close_connection() trying to close the connection twice.
|
21
|
+
def test_double_close()
|
22
|
+
rdht = Scalaroid::ReplicatedDHT.new()
|
23
|
+
rdht.close_connection()
|
24
|
+
rdht.close_connection()
|
25
|
+
end
|
26
|
+
|
27
|
+
# Tries to read the value at the given key and fails if this does
|
28
|
+
# not fail with a NotFoundError.
|
29
|
+
def _checkKeyDoesNotExist(key)
|
30
|
+
conn = Scalaroid::TransactionSingleOp.new()
|
31
|
+
begin
|
32
|
+
conn.read(key)
|
33
|
+
assert(false, 'the value at ' + key + ' should not exist anymore')
|
34
|
+
rescue Scalaroid::NotFoundError
|
35
|
+
# nothing to do here
|
36
|
+
end
|
37
|
+
conn.close_connection()
|
38
|
+
end
|
39
|
+
|
40
|
+
# Test method for ReplicatedDHT.delete(key).
|
41
|
+
# Tries to delete some not existing keys.
|
42
|
+
def test_delete_not_existing_key()
|
43
|
+
key = "_Delete_NotExistingKey"
|
44
|
+
rdht = Scalaroid::ReplicatedDHT.new()
|
45
|
+
|
46
|
+
(0..($_TEST_DATA.length - 1)).each do |i|
|
47
|
+
ok = rdht.delete(@testTime.to_s + key + i.to_s)
|
48
|
+
assert_equal(0, ok)
|
49
|
+
results = rdht.get_last_delete_result()
|
50
|
+
assert_equal(0, results.ok)
|
51
|
+
assert_equal(0, results.locks_set)
|
52
|
+
assert_equal(4, results.undefined)
|
53
|
+
_checkKeyDoesNotExist(@testTime.to_s + key + i.to_s)
|
54
|
+
end
|
55
|
+
|
56
|
+
rdht.close_connection()
|
57
|
+
end
|
58
|
+
|
59
|
+
# Test method for ReplicatedDHT.delete(key) and TransactionSingleOp#write(key, value=str()).
|
60
|
+
# Inserts some values, tries to delete them afterwards and tries the delete again.
|
61
|
+
def test_delete1()
|
62
|
+
key = "_Delete1"
|
63
|
+
c = Scalaroid::JSONConnection.new(url = Scalaroid::DEFAULT_URL)
|
64
|
+
rdht = Scalaroid::ReplicatedDHT.new(conn = c)
|
65
|
+
sc = Scalaroid::TransactionSingleOp.new(conn = c)
|
66
|
+
|
67
|
+
(0..($_TEST_DATA.length - 1)).each do |i|
|
68
|
+
sc.write(@testTime.to_s + key + i.to_s, $_TEST_DATA[i])
|
69
|
+
end
|
70
|
+
|
71
|
+
# now try to delete the data:
|
72
|
+
(0..($_TEST_DATA.length - 1)).each do |i|
|
73
|
+
ok = rdht.delete(@testTime.to_s + key + i.to_s)
|
74
|
+
assert_equal(4, ok)
|
75
|
+
results = rdht.get_last_delete_result()
|
76
|
+
assert_equal(4, results.ok)
|
77
|
+
assert_equal(0, results.locks_set)
|
78
|
+
assert_equal(0, results.undefined)
|
79
|
+
_checkKeyDoesNotExist(@testTime.to_s + key + i.to_s)
|
80
|
+
|
81
|
+
# try again (should be successful with 0 deletes)
|
82
|
+
ok = rdht.delete(@testTime.to_s + key + i.to_s)
|
83
|
+
assert_equal(0, ok)
|
84
|
+
results = rdht.get_last_delete_result()
|
85
|
+
assert_equal(0, results.ok)
|
86
|
+
assert_equal(0, results.locks_set)
|
87
|
+
assert_equal(4, results.undefined)
|
88
|
+
_checkKeyDoesNotExist(@testTime.to_s + key + i.to_s)
|
89
|
+
end
|
90
|
+
|
91
|
+
c.close()
|
92
|
+
end
|
93
|
+
|
94
|
+
# Test method for ReplicatedDHT.delete(key) and TransactionSingleOp#write(key, value=str()).
|
95
|
+
# Inserts some values, tries to delete them afterwards, inserts them again and tries to delete them again (twice).
|
96
|
+
def test_delete2()
|
97
|
+
key = "_Delete2"
|
98
|
+
c = Scalaroid::JSONConnection.new(url = Scalaroid::DEFAULT_URL)
|
99
|
+
rdht = Scalaroid::ReplicatedDHT.new(conn = c)
|
100
|
+
sc = Scalaroid::TransactionSingleOp.new(conn = c)
|
101
|
+
|
102
|
+
(0..($_TEST_DATA.length - 1)).each do |i|
|
103
|
+
sc.write(@testTime.to_s + key + i.to_s, $_TEST_DATA[i])
|
104
|
+
end
|
105
|
+
|
106
|
+
# now try to delete the data:
|
107
|
+
(0..($_TEST_DATA.length - 1)).each do |i|
|
108
|
+
ok = rdht.delete(@testTime.to_s + key + i.to_s)
|
109
|
+
assert_equal(4, ok)
|
110
|
+
results = rdht.get_last_delete_result()
|
111
|
+
assert_equal(4, results.ok)
|
112
|
+
assert_equal(0, results.locks_set)
|
113
|
+
assert_equal(0, results.undefined)
|
114
|
+
_checkKeyDoesNotExist(@testTime.to_s + key + i.to_s)
|
115
|
+
end
|
116
|
+
|
117
|
+
(0..($_TEST_DATA.length - 1)).each do |i|
|
118
|
+
sc.write(@testTime.to_s + key + i.to_s, $_TEST_DATA[i])
|
119
|
+
end
|
120
|
+
|
121
|
+
# now try to delete the data:
|
122
|
+
(0..($_TEST_DATA.length - 1)).each do |i|
|
123
|
+
ok = rdht.delete(@testTime.to_s + key + i.to_s)
|
124
|
+
assert_equal(4, ok)
|
125
|
+
results = rdht.get_last_delete_result()
|
126
|
+
assert_equal(4, results.ok)
|
127
|
+
assert_equal(0, results.locks_set)
|
128
|
+
assert_equal(0, results.undefined)
|
129
|
+
_checkKeyDoesNotExist(@testTime.to_s + key + i.to_s)
|
130
|
+
|
131
|
+
# try again (should be successful with 0 deletes)
|
132
|
+
ok = rdht.delete(@testTime.to_s + key + i.to_s)
|
133
|
+
assert_equal(0, ok)
|
134
|
+
results = rdht.get_last_delete_result()
|
135
|
+
assert_equal(0, results.ok)
|
136
|
+
assert_equal(0, results.locks_set)
|
137
|
+
assert_equal(4, results.undefined)
|
138
|
+
_checkKeyDoesNotExist(@testTime.to_s + key + i.to_s)
|
139
|
+
end
|
140
|
+
|
141
|
+
c.close()
|
142
|
+
end
|
143
|
+
end
|