scalaroid 0.0.1
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 +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
|