ssdb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ *.gem
2
+ .yardoc/
3
+ doc/
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source "https://rubygems.org"
2
+ gemspec
@@ -0,0 +1,31 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ ssdb (0.1.0)
5
+
6
+ GEM
7
+ remote: https://rubygems.org/
8
+ specs:
9
+ diff-lcs (1.2.3)
10
+ rake (10.0.4)
11
+ redcarpet (2.2.2)
12
+ rspec (2.13.0)
13
+ rspec-core (~> 2.13.0)
14
+ rspec-expectations (~> 2.13.0)
15
+ rspec-mocks (~> 2.13.0)
16
+ rspec-core (2.13.1)
17
+ rspec-expectations (2.13.0)
18
+ diff-lcs (>= 1.1.3, < 2.0)
19
+ rspec-mocks (2.13.1)
20
+ yard (0.8.6.1)
21
+
22
+ PLATFORMS
23
+ ruby
24
+
25
+ DEPENDENCIES
26
+ bundler
27
+ rake
28
+ redcarpet
29
+ rspec
30
+ ssdb!
31
+ yard
@@ -0,0 +1,117 @@
1
+ # ssdb-rb
2
+
3
+ A Ruby client library for [SSDB][ssdb-home], heaviliy inspired by the great
4
+ [redis-rb][redisrb-home] library. Requires SSDB version 1.4.2 or higher.
5
+
6
+ [ssdb-home]: https://github.com/ideawu/ssdb
7
+ [redisrb-home]: https://github.com/redis/redis-rb
8
+
9
+ ### Installation
10
+
11
+ Install via rubygems:
12
+
13
+ ```ruby
14
+ gem install ssdb
15
+ ```
16
+
17
+ Use it with bundler, by adding the following line to your Gemfile:
18
+
19
+ ```ruby
20
+ gem "ssdb"
21
+ ```
22
+
23
+ For more information please visit http://gembundler.com/.
24
+
25
+ ### Basic usage
26
+
27
+ Connect to SSDB, assuming it is listening on `localhost`, port 8888.
28
+
29
+ ```ruby
30
+ require "ssdb"
31
+
32
+ ssdb = SSDB.new
33
+ ```
34
+
35
+ To connect to a custom server, please provide a custom `:url` option:
36
+
37
+ ```ruby
38
+ ssdb = SSDB.new url: "ssdb://1.2.3.4:8889"
39
+ ```
40
+
41
+ To execute commands:
42
+
43
+ ```ruby
44
+ ssdb.set("mykey", "hello world")
45
+ # => true
46
+
47
+ ssdb.get("mykey")
48
+ # => "hello world"
49
+ ```
50
+
51
+ Full documentation of all commands is available on [rdoc.info][rdoc].
52
+
53
+ [rdoc]: http://rdoc.info/github/bsm/ssdb-rb/
54
+
55
+ ### Batching/pipelining
56
+
57
+ Multiple commands can be executed as a batch operations. Instead of sending
58
+ commands one-by-one the client is able to send a batch of commands and
59
+ retrieve all responses as a single socket message exchange cycle.
60
+
61
+ Example:
62
+
63
+ ```ruby
64
+ ssdb.batch do
65
+ ssdb.set "foo", "5"
66
+ ssdb.get "foo"
67
+ ssdb.incr "foo"
68
+ end
69
+ # => [true, "5", 6]
70
+ ```
71
+
72
+ ### Futures
73
+
74
+ Results of individual batch operations are stored as *futures*. Future values
75
+ can be retrieved via the `#value` method once the batch execution is complete.
76
+
77
+ ```ruby
78
+ ssdb.batch do
79
+ v = ssdb.set "foo", "bar"
80
+ w = ssdc.incr "baz"
81
+ end
82
+
83
+ v.value
84
+ # => true
85
+
86
+ w.value
87
+ # => 1
88
+ ```
89
+
90
+ ### TODO
91
+
92
+ * Implement HASH operations
93
+
94
+ ### Licence (MIT)
95
+
96
+ ```
97
+ Copyright (c) 2013 Black Square Media Ltd
98
+
99
+ Permission is hereby granted, free of charge, to any person obtaining
100
+ a copy of this software and associated documentation files (the
101
+ "Software"), to deal in the Software without restriction, including
102
+ without limitation the rights to use, copy, modify, merge, publish,
103
+ distribute, sublicense, and/or sell copies of the Software, and to
104
+ permit persons to whom the Software is furnished to do so, subject to
105
+ the following conditions:
106
+
107
+ The above copyright notice and this permission notice shall be
108
+ included in all copies or substantial portions of the Software.
109
+
110
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
111
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
112
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
113
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
114
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
115
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
116
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
117
+ ```
@@ -0,0 +1,10 @@
1
+ require 'bundler/setup'
2
+ require 'rspec/core/rake_task'
3
+ require 'yard'
4
+
5
+ RSpec::Core::RakeTask.new(:spec)
6
+
7
+ YARD::Rake::YardocTask.new
8
+
9
+ desc 'Default: run specs.'
10
+ task :default => :spec
@@ -0,0 +1,431 @@
1
+ require "monitor"
2
+
3
+ class SSDB
4
+ include MonitorMixin
5
+
6
+ Error = Class.new(RuntimeError)
7
+ ConnectionError = Class.new(Error)
8
+ TimeoutError = Class.new(Error)
9
+ CommandError = Class.new(Error)
10
+ FutureNotReady = Class.new(Error)
11
+
12
+ T_BOOL = ->r { r == "1" }
13
+ T_INT = ->r { r.to_i }
14
+ T_CINT = ->r { r.to_i if r }
15
+ T_VBOOL = ->r { r.each_slice(2).map {|_, v| v == "1" }}
16
+ T_VINT = ->r { r.each_slice(2).map {|_, v| v.to_i }}
17
+ T_STRSTR = ->r { r.each_slice(2).to_a }
18
+ T_STRINT = ->r { r.each_slice(2).map {|v, s| [v, s.to_i] } }
19
+ T_MAPINT = ->r,n { h = {}; r.each_slice(2) {|k, v| h[k] = v }; n.map {|k| h[k].to_i } }
20
+ T_MAPSTR = ->r,n { h = {}; r.each_slice(2) {|k, v| h[k] = v }; n.map {|k| h[k] } }
21
+ BLANK = "".freeze
22
+
23
+ # @attr_reader [SSDB::Client] the client
24
+ attr_reader :client
25
+
26
+ # @return [SSDB] the current/global SSDB connection
27
+ def self.current
28
+ @current ||= SSDB.new
29
+ end
30
+
31
+ # @param [SSDB] ssdb the current/global SSDB connection
32
+ def self.current=(ssdb)
33
+ @current = ssdb
34
+ end
35
+
36
+ # @see SSDB::Client#initialize
37
+ def initialize(*a)
38
+ @client = Client.new(*a)
39
+ super() # Monitor#initialize
40
+ end
41
+
42
+ # Execute a batch operation
43
+ #
44
+ # @example simple batch
45
+ #
46
+ # ssdb.batch do
47
+ # ssdb.set "foo", "5"
48
+ # ssdb.get "foo"
49
+ # ssdb.incr "foo"
50
+ # end
51
+ # # => [true, "5", 6]
52
+ #
53
+ # @example batch with futures
54
+ #
55
+ # ssdb.batch do
56
+ # v = ssdb.set "foo", "5"
57
+ # w = ssdb.incr "foo"
58
+ # end
59
+ #
60
+ # v.value
61
+ # # => true
62
+ # w.value
63
+ # # => 6
64
+ #
65
+ def batch
66
+ mon_synchronize do
67
+ begin
68
+ original, @client = @client, SSDB::Batch.new
69
+ yield(self)
70
+ @client.values = original.perform(@client)
71
+ ensure
72
+ @client = original
73
+ end
74
+ end
75
+ end
76
+
77
+ # Returns value at `key`.
78
+ #
79
+ # @param [String] key the key
80
+ # @return [String] the value
81
+ def get(key)
82
+ mon_synchronize do
83
+ perform ["get", key]
84
+ end
85
+ end
86
+
87
+ # Sets `value` at `key`.
88
+ #
89
+ # @param [String] key the key
90
+ # @param [String] value the value
91
+ def set(key, value)
92
+ mon_synchronize do
93
+ perform ["set", key, value], proc: T_BOOL
94
+ end
95
+ end
96
+
97
+ # Increments a `key` by value
98
+ #
99
+ # @param [String] key the key
100
+ # @param [Integer] value the increment
101
+ def incr(key, value = 1)
102
+ mon_synchronize do
103
+ perform ["incr", key, value], proc: T_INT
104
+ end
105
+ end
106
+
107
+ # Decrements a `key` by value
108
+ #
109
+ # @param [String] key the key
110
+ # @param [Integer] value the decrement
111
+ def decr(key, value = 1)
112
+ mon_synchronize do
113
+ perform ["decr", key, value], proc: T_INT
114
+ end
115
+ end
116
+
117
+ # Checks existence of `key`.
118
+ #
119
+ # @param [String] key the key
120
+ # @return [Boolean] true if exists
121
+ def exists(key)
122
+ mon_synchronize do
123
+ perform ["exists", key], proc: T_BOOL
124
+ end
125
+ end
126
+ alias_method :exists?, :exists
127
+
128
+ # Delete `key`.
129
+ #
130
+ # @param [String] key the key
131
+ def del(key)
132
+ mon_synchronize do
133
+ perform ["del", key]
134
+ end
135
+ end
136
+
137
+ # Scans keys between `start` and `stop`.
138
+ #
139
+ # @param [String] start start at this key
140
+ # @param [String] stop stop at this key
141
+ # @param [Hash] opts options
142
+ # @option opts [Integer] :limit limit results
143
+ # @return [Array<String>] matching keys
144
+ def keys(start, stop, opts = {})
145
+ limit = opts[:limit] || -1
146
+ mon_synchronize do
147
+ perform ["keys", start, stop, limit], multi: true
148
+ end
149
+ end
150
+
151
+ # Scans keys between `start` and `stop`.
152
+ #
153
+ # @param [String] start start at this key
154
+ # @param [String] stop stop at this key
155
+ # @param [Hash] opts options
156
+ # @option opts [Integer] :limit limit results
157
+ # @return [Array<Array<String,String>>] key/value pairs
158
+ def scan(start, stop, opts = {})
159
+ limit = opts[:limit] || -1
160
+ mon_synchronize do
161
+ perform ["scan", start, stop, limit], multi: true, proc: T_STRSTR
162
+ end
163
+ end
164
+
165
+ # Reverse-scans keys between `start` and `stop`.
166
+ #
167
+ # @param [String] start start at this key
168
+ # @param [String] stop stop at this key
169
+ # @param [Hash] opts options
170
+ # @option opts [Integer] :limit limit results
171
+ # @return [Array<Array<String,String>>] key/value pairs in reverse order
172
+ def rscan(start, stop, opts = {})
173
+ limit = opts[:limit] || -1
174
+ mon_synchronize do
175
+ perform ["rscan", start, stop, limit], multi: true, proc: T_STRSTR
176
+ end
177
+ end
178
+
179
+ # Sets multiple keys
180
+ #
181
+ # @param [Hash] pairs key/value pairs
182
+ def multi_set(pairs)
183
+ mon_synchronize do
184
+ perform ["multi_set", *pairs.to_a].flatten, proc: T_INT
185
+ end
186
+ end
187
+
188
+ # Retrieves multiple keys
189
+ #
190
+ # @param [Array<String>] keys
191
+ # @return [Array<String>] values
192
+ def multi_get(keys)
193
+ keys = Array(keys) unless keys.is_a?(Array)
194
+ mon_synchronize do
195
+ perform ["multi_get", *keys], multi: true, proc: T_MAPSTR, args: [keys]
196
+ end
197
+ end
198
+
199
+ # Deletes multiple keys
200
+ #
201
+ # @param [Array<String>] keys
202
+ def multi_del(keys)
203
+ keys = Array(keys) unless keys.is_a?(Array)
204
+ mon_synchronize do
205
+ perform ["multi_del", *keys], proc: T_INT
206
+ end
207
+ end
208
+
209
+ # Checks existence of multiple keys
210
+ #
211
+ # @param [Array<String>] keys
212
+ # @return [Array<Boolean>] results
213
+ def multi_exists(keys)
214
+ keys = Array(keys) unless keys.is_a?(Array)
215
+ mon_synchronize do
216
+ perform ["multi_exists", *keys], multi: true, proc: T_VBOOL
217
+ end
218
+ end
219
+
220
+ # Returns the score of `member` at `key`.
221
+ #
222
+ # @param [String] key the key
223
+ # @param [String] member the member
224
+ # @return [Float] the score
225
+ def zget(key, member)
226
+ mon_synchronize do
227
+ perform ["zget", key, member], proc: T_CINT
228
+ end
229
+ end
230
+
231
+ # Sets the `score` of `member` at `key`.
232
+ #
233
+ # @param [String] key the key
234
+ # @param [String] member the member
235
+ # @param [Numeric] score the score
236
+ def zset(key, member, score)
237
+ mon_synchronize do
238
+ perform ["zset", key, member, score], proc: T_BOOL
239
+ end
240
+ end
241
+
242
+ # Redis 'compatibility'.
243
+ #
244
+ # @param [String] key the key
245
+ # @param [Numeric] score the score
246
+ # @param [String] member the member
247
+ def zadd(key, score, member)
248
+ zset(key, member, score)
249
+ end
250
+
251
+ # Increments the `member` in `key` by `score`
252
+ #
253
+ # @param [String] key the key
254
+ # @param [String] member the member
255
+ # @param [Integer] score the increment
256
+ def zincr(key, member, score = 1)
257
+ mon_synchronize do
258
+ perform ["zincr", key, member, score], proc: T_INT
259
+ end
260
+ end
261
+
262
+ # Decrements the `member` in `key` by `score`
263
+ #
264
+ # @param [String] key the key
265
+ # @param [String] member the member
266
+ # @param [Integer] score the decrement
267
+ def zdecr(key, member, score = 1)
268
+ mon_synchronize do
269
+ perform ["zdecr", key, member, score], proc: T_INT
270
+ end
271
+ end
272
+
273
+ # Checks existence of a zset at `key`.
274
+ #
275
+ # @param [String] key the key
276
+ # @return [Boolean] true if exists
277
+ def zexists(key)
278
+ mon_synchronize do
279
+ perform ["zexists", key], proc: T_BOOL
280
+ end
281
+ end
282
+ alias_method :zexists?, :zexists
283
+
284
+ # Returns the cardinality of a set `key`.
285
+ #
286
+ # @param [String] key the key
287
+ def zsize(key)
288
+ mon_synchronize do
289
+ perform ["zsize", key], proc: T_INT
290
+ end
291
+ end
292
+
293
+ # Delete an `member` from a zset `key`.
294
+ #
295
+ # @param [String] key the key
296
+ # @param [String] member the member
297
+ def zdel(key, member)
298
+ mon_synchronize do
299
+ perform ["zdel", key, member], proc: T_BOOL
300
+ end
301
+ end
302
+
303
+ # List zset keys between `start` and `stop`.
304
+ #
305
+ # @param [String] start start at this key
306
+ # @param [String] stop stop at this key
307
+ # @param [Hash] opts options
308
+ # @option opts [Integer] :limit limit results
309
+ # @return [Array<String>] matching zset keys
310
+ def zlist(start, stop, opts = {})
311
+ limit = opts[:limit] || -1
312
+ mon_synchronize do
313
+ perform ["zlist", start, stop, limit], multi: true
314
+ end
315
+ end
316
+
317
+ # Lists members at `key` starting at `start_member`
318
+ # between `start` and `stop` scores.
319
+ #
320
+ # @param [String] key the zset
321
+ # @param [Float] start start at this score
322
+ # @param [Float] stop stop at this score
323
+ # @param [Hash] opts options
324
+ # @option opts [Integer] :limit limit results
325
+ # @return [Array<String>] matching members
326
+ def zkeys(key, start, stop, opts = {})
327
+ limit = opts[:limit] || -1
328
+ mon_synchronize do
329
+ perform ["zkeys", key, BLANK, start, stop, limit], multi: true
330
+ end
331
+ end
332
+
333
+ # Scans for members at `key` starting at `start_member`
334
+ # between `start` and `stop` scores.
335
+ #
336
+ # @param [String] key the zset
337
+ # @param [Float] start start at this score
338
+ # @param [Float] stop stop at this score
339
+ # @param [Hash] opts options
340
+ # @option opts [Integer] :limit limit results
341
+ # @return [Array<Array<String,Float>>] member/score pairs
342
+ def zscan(key, start, stop, opts = {})
343
+ limit = opts[:limit] || -1
344
+ mon_synchronize do
345
+ perform ["zscan", key, BLANK, start, stop, limit], multi: true, proc: T_STRINT
346
+ end
347
+ end
348
+
349
+ # Reverse scans for members at `key` starting at `start_member`
350
+ # between `start` and `stop` scores.
351
+ #
352
+ # @param [String] key the zset
353
+ # @param [Float] start start at this score
354
+ # @param [Float] stop stop at this score
355
+ # @param [Hash] opts options
356
+ # @option opts [Integer] :limit limit results
357
+ # @return [Array<Array<String,Float>>] member/score pairs
358
+ def zrscan(key, start, stop, opts = {})
359
+ limit = opts[:limit] || -1
360
+ mon_synchronize do
361
+ perform ["zrscan", key, BLANK, start, stop, limit], multi: true, proc: T_STRINT
362
+ end
363
+ end
364
+
365
+ # Checks existence of multiple sets
366
+ #
367
+ # @param [Array<String>] keys
368
+ # @return [Array<Boolean>] results
369
+ def multi_zexists(keys)
370
+ keys = Array(keys) unless keys.is_a?(Array)
371
+ mon_synchronize do
372
+ perform ["multi_zexists", *keys], multi: true, proc: T_VBOOL
373
+ end
374
+ end
375
+
376
+ # Returns cardinalities of multiple sets
377
+ #
378
+ # @param [Array<String>] keys
379
+ # @return [Array<Boolean>] results
380
+ def multi_zsize(keys)
381
+ keys = Array(keys) unless keys.is_a?(Array)
382
+ mon_synchronize do
383
+ perform ["multi_zsize", *keys], multi: true, proc: T_VINT
384
+ end
385
+ end
386
+
387
+ # Sets multiple members of `key`
388
+ #
389
+ # @param [String] key the zset
390
+ # @param [Hash] pairs key/value pairs
391
+ def multi_zset(key, pairs)
392
+ mon_synchronize do
393
+ perform ["multi_zset", key, *pairs.to_a].flatten, proc: T_INT
394
+ end
395
+ end
396
+
397
+ # Retrieves multiple scores from `key`
398
+ #
399
+ # @param [String] key the zset
400
+ # @param [Array<String>] members
401
+ # @return [Array<Float>] scores
402
+ def multi_zget(key, members)
403
+ members = Array(members) unless members.is_a?(Array)
404
+ mon_synchronize do
405
+ perform ["multi_zget", key, *members], multi: true, proc: T_MAPINT, args: [members]
406
+ end
407
+ end
408
+
409
+ # Deletes multiple members from `key`
410
+ #
411
+ # @param [String] key the zset
412
+ # @param [Array<String>] members
413
+ def multi_zdel(key, members)
414
+ members = Array(members) unless members.is_a?(Array)
415
+ mon_synchronize do
416
+ perform ["multi_zdel", key, *members], proc: T_INT
417
+ end
418
+ end
419
+
420
+ private
421
+
422
+ def perform(chain, opts = {})
423
+ opts[:cmd] = chain.map(&:to_s)
424
+ client.call(opts)
425
+ end
426
+
427
+ end
428
+
429
+ %w|version client batch future|.each do |name|
430
+ require "ssdb/#{name}"
431
+ end