ohm 0.0.3 → 0.0.4

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.
data/lib/ohm/redis.rb ADDED
@@ -0,0 +1,245 @@
1
+ require 'socket'
2
+
3
+ begin
4
+ if (RUBY_VERSION >= '1.9')
5
+ require 'timeout'
6
+ RedisTimer = Timeout
7
+ else
8
+ require 'system_timer'
9
+ RedisTimer = SystemTimer
10
+ end
11
+ rescue LoadError
12
+ RedisTimer = nil
13
+ end
14
+
15
+ module Ohm
16
+ class Redis
17
+ class ProtocolError < RuntimeError
18
+ def initialize(reply_type)
19
+ super("Protocol error, got '#{reply_type}' as initial reply byte")
20
+ end
21
+ end
22
+
23
+ BulkCommands = {
24
+ :set => true,
25
+ :setnx => true,
26
+ :rpush => true,
27
+ :lpush => true,
28
+ :lset => true,
29
+ :lrem => true,
30
+ :sadd => true,
31
+ :srem => true,
32
+ :sismember => true,
33
+ :echo => true,
34
+ :getset => true,
35
+ :smove => true
36
+ }
37
+
38
+ ProcessorIdentity = lambda { |reply| reply }
39
+ ProcessorConvertToBool = lambda { |reply| reply == 0 ? false : reply }
40
+ ProcessorSplitKeys = lambda { |reply| reply.split(" ") }
41
+ ProcessorInfo = lambda do |reply|
42
+ info = Hash.new
43
+ reply.each_line do |line|
44
+ key, value = line.split(":", 2).map { |part| part.chomp }
45
+ info[key.to_sym] = value
46
+ end
47
+ info
48
+ end
49
+
50
+ ReplyProcessor = {
51
+ :exists => ProcessorConvertToBool,
52
+ :sismember=> ProcessorConvertToBool,
53
+ :sadd=> ProcessorConvertToBool,
54
+ :srem=> ProcessorConvertToBool,
55
+ :smove=> ProcessorConvertToBool,
56
+ :move=> ProcessorConvertToBool,
57
+ :setnx=> ProcessorConvertToBool,
58
+ :del=> ProcessorConvertToBool,
59
+ :renamenx=> ProcessorConvertToBool,
60
+ :expire=> ProcessorConvertToBool,
61
+ :keys => ProcessorSplitKeys,
62
+ :info => ProcessorInfo
63
+ }
64
+
65
+ ReplyProcessor.send(:initialize) do |hash, key|
66
+ hash[key] = ProcessorIdentity
67
+ end
68
+
69
+ def initialize(opts={})
70
+ @host = opts[:host] || '127.0.0.1'
71
+ @port = opts[:port] || 6379
72
+ @db = opts[:db] || 0
73
+ @timeout = opts[:timeout] || 0
74
+ connect
75
+ end
76
+
77
+ def to_s
78
+ "Redis Client connected to #{@host}:#{@port} against DB #{@db}"
79
+ end
80
+
81
+ # Shorthand for getting all the elements in a list.
82
+ def list(key)
83
+ call_command([:lrange, key, 0, -1])
84
+ end
85
+
86
+ # We need to define type because otherwise it will escape method_missing.
87
+ def type(key)
88
+ call_command([:type, key])
89
+ end
90
+
91
+ def sort(key, opts = {})
92
+ cmd = []
93
+ cmd << "SORT #{key}"
94
+ cmd << "BY #{opts[:by]}" if opts[:by]
95
+ cmd << "GET #{[opts[:get]].flatten * ' GET '}" if opts[:get]
96
+ cmd << "#{opts[:order]}" if opts[:order]
97
+ cmd << "LIMIT #{opts[:limit].join(' ')}" if opts[:limit]
98
+ call_command(cmd)
99
+ end
100
+
101
+ private
102
+
103
+ def connect
104
+ connect_to(@host, @port, @timeout == 0 ? nil : @timeout)
105
+ call_command([:select, @db]) if @db != 0
106
+ end
107
+
108
+ def connect_to(host, port, timeout = nil)
109
+
110
+ # We support connect() timeout only if system_timer is availabe or
111
+ # if we are running against Ruby >= 1.9. Timeout reading from the
112
+ # socket instead will be supported anyway.
113
+ if @timeout != 0 and RedisTimer
114
+ begin
115
+ @sock = TCPSocket.new(host, port, 0)
116
+ rescue Timeout::Error
117
+ @sock = nil
118
+ raise Timeout::Error, "Timeout connecting to the server"
119
+ end
120
+ else
121
+ @sock = TCPSocket.new(host, port, 0)
122
+ end
123
+
124
+ @sock.setsockopt Socket::IPPROTO_TCP, Socket::TCP_NODELAY, 1
125
+
126
+ # If the timeout is set we configure the low level socket options in
127
+ # order to make sure a blocking read will return after the specified
128
+ # number of seconds. This hack is from the Memcached Ruby client.
129
+ if timeout
130
+ secs = Integer(timeout)
131
+ usecs = Integer((timeout - secs) * 1_000_000)
132
+ optval = [secs, usecs].pack("l_2")
133
+ @sock.setsockopt Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, optval
134
+ @sock.setsockopt Socket::SOL_SOCKET, Socket::SO_SNDTIMEO, optval
135
+ end
136
+ end
137
+
138
+ def connected?
139
+ !! @sock
140
+ end
141
+
142
+ def disconnect
143
+ @sock.close
144
+ end
145
+
146
+ def reconnect
147
+ disconnect and connect
148
+ end
149
+
150
+ def method_missing(*argv)
151
+ call_command(argv)
152
+ end
153
+
154
+
155
+ # Wrap raw_call_command to handle reconnection on socket error. We
156
+ # try to reconnect just one time, otherwise let the error araise.
157
+ def call_command(argv)
158
+ connect unless connected?
159
+ raw_call_command(argv)
160
+ rescue Errno::ECONNRESET
161
+ reconnect
162
+ raw_call_command(argv)
163
+ end
164
+
165
+ def raw_call_command(argv)
166
+ bulk = extract_bulk_argument(argv)
167
+ @sock.write(argv.join(" ") + "\r\n")
168
+ @sock.write(bulk + "\r\n") if bulk
169
+ process_reply(argv[0])
170
+ end
171
+
172
+ def bulk_command?(argv)
173
+ BulkCommands[argv[0]] and argv.length > 1
174
+ end
175
+
176
+ def extract_bulk_argument(argv)
177
+ if bulk_command?(argv)
178
+ bulk = argv[-1].to_s
179
+ argv[-1] = bulk.length
180
+ bulk
181
+ end
182
+ end
183
+
184
+ def process_reply(command)
185
+ ReplyProcessor[command][read_reply]
186
+ end
187
+
188
+ def read_reply
189
+
190
+ # We read the first byte using read() mainly because gets() is
191
+ # immune to raw socket timeouts.
192
+ begin
193
+ reply_type = @sock.read(1)
194
+ rescue Errno::EAGAIN
195
+
196
+ # We want to make sure it reconnects on the next command after the
197
+ # timeout. Otherwise the server may reply in the meantime leaving
198
+ # the protocol in a desync status.
199
+ @sock = nil
200
+ raise Errno::EAGAIN, "Timeout reading from the socket"
201
+ end
202
+
203
+ raise Errno::ECONNRESET, "Connection lost" unless reply_type
204
+
205
+ format_reply(reply_type, @sock.gets)
206
+ end
207
+
208
+ def format_reply(reply_type, line)
209
+ case reply_type
210
+ when "-" then format_error_reply(line)
211
+ when "+" then format_status_reply(line)
212
+ when ":" then format_integer_reply(line)
213
+ when "$" then format_bulk_reply(line)
214
+ when "*" then format_multi_bulk_reply(line)
215
+ else raise ProtocolError.new(reply_type)
216
+ end
217
+ end
218
+
219
+ def format_error_reply(line)
220
+ raise "-" + line.strip
221
+ end
222
+
223
+ def format_status_reply(line)
224
+ line.strip
225
+ end
226
+
227
+ def format_integer_reply(line)
228
+ line.to_i
229
+ end
230
+
231
+ def format_bulk_reply(line)
232
+ bulklen = line.to_i
233
+ return nil if bulklen == -1
234
+ reply = @sock.read(bulklen)
235
+ @sock.read(2) # Discard CRLF.
236
+ reply
237
+ end
238
+
239
+ def format_multi_bulk_reply(line)
240
+ reply = []
241
+ line.to_i.times { reply << read_reply }
242
+ reply
243
+ end
244
+ end
245
+ end
@@ -23,7 +23,7 @@ module Ohm
23
23
 
24
24
  def assert_present(att)
25
25
  if assert_not_nil(att)
26
- assert send(att).any?, [att, :empty]
26
+ assert !send(att).empty?, [att, :empty]
27
27
  end
28
28
  end
29
29
 
data/lib/ohm.rb CHANGED
@@ -1,13 +1,24 @@
1
- require "rubygems"
2
- require "redis"
1
+ require File.join(File.dirname(__FILE__), "ohm", "redis")
3
2
  require File.join(File.dirname(__FILE__), "ohm", "validations")
4
3
 
5
4
  module Ohm
5
+ def redis
6
+ @redis
7
+ end
8
+
9
+ def connect(*attrs)
10
+ @redis = Ohm::Redis.new(*attrs)
11
+ end
12
+
13
+ def flush
14
+ @redis.flushdb
15
+ end
16
+
6
17
  def key(*args)
7
18
  args.join(":")
8
19
  end
9
20
 
10
- module_function :key
21
+ module_function :key, :connect, :flush, :redis
11
22
 
12
23
  module Attributes
13
24
  class Collection < Array
@@ -22,29 +33,29 @@ module Ohm
22
33
 
23
34
  class List < Collection
24
35
  def retrieve
25
- db.list_range(key, 0, -1)
36
+ db.list(key)
26
37
  end
27
38
 
28
39
  def << value
29
- super(value) if db.push_tail(key, value)
40
+ super(value) if db.rpush(key, value)
30
41
  end
31
42
  end
32
43
 
33
44
  class Set < Collection
34
45
  def retrieve
35
- db.set_members(key).sort
46
+ db.smembers(key).sort
36
47
  end
37
48
 
38
49
  def << value
39
- super(value) if db.set_add(key, value)
50
+ super(value) if db.sadd(key, value)
40
51
  end
41
52
 
42
53
  def delete(value)
43
- super(value) if db.set_delete(key, value)
54
+ super(value) if db.srem(key, value)
44
55
  end
45
56
 
46
57
  def include?(value)
47
- db.set_member?(key, value)
58
+ db.sismember(key, value)
48
59
  end
49
60
  end
50
61
  end
@@ -55,7 +66,7 @@ module Ohm
55
66
 
56
67
  def assert_unique(attrs)
57
68
  index_key = index_key_for(attrs, read_locals(attrs))
58
- assert(db.set_count(index_key).zero? || db.set_member?(index_key, id), [attrs, :not_unique])
69
+ assert(db.scard(index_key).zero? || db.sismember(index_key, id), [attrs, :not_unique])
59
70
  end
60
71
  end
61
72
 
@@ -135,10 +146,7 @@ module Ohm
135
146
  new(*args).create
136
147
  end
137
148
 
138
- # TODO Add a method that receives several arguments and returns a
139
- # string with the values separated by colons.
140
149
  def self.find(attribute, value)
141
- # filter("#{attribute}:#{value}")
142
150
  filter(Ohm.key(attribute, value))
143
151
  end
144
152
 
@@ -200,7 +208,7 @@ module Ohm
200
208
  private
201
209
 
202
210
  def self.db
203
- $redis
211
+ Ohm.redis
204
212
  end
205
213
 
206
214
  def self.key(*args)
@@ -208,13 +216,13 @@ module Ohm
208
216
  end
209
217
 
210
218
  def self.filter(name)
211
- db.set_members(key(name)).map do |id|
219
+ db.smembers(key(name)).map do |id|
212
220
  new(:id => id)
213
221
  end
214
222
  end
215
223
 
216
224
  def self.exists?(id)
217
- db.set_member?(key(:all), id)
225
+ db.sismember(key(:all), id)
218
226
  end
219
227
 
220
228
  def initialize_id
@@ -222,21 +230,21 @@ module Ohm
222
230
  end
223
231
 
224
232
  def db
225
- self.class.db
233
+ Ohm.redis
226
234
  end
227
235
 
228
236
  def delete_attributes(atts)
229
237
  atts.each do |att|
230
- db.delete(key(att))
238
+ db.del(key(att))
231
239
  end
232
240
  end
233
241
 
234
242
  def create_model_membership
235
- db.set_add(self.class.key(:all), id)
243
+ db.sadd(self.class.key(:all), id)
236
244
  end
237
245
 
238
246
  def delete_model_membership
239
- db.set_delete(self.class.key(:all), id)
247
+ db.srem(self.class.key(:all), id)
240
248
  end
241
249
 
242
250
  def save!
@@ -251,13 +259,13 @@ module Ohm
251
259
 
252
260
  def add_to_indices
253
261
  indices.each do |attrs|
254
- db.set_add(index_key_for(attrs, read_locals(attrs)), id)
262
+ db.sadd(index_key_for(attrs, read_locals(attrs)), id)
255
263
  end
256
264
  end
257
265
 
258
266
  def delete_from_indices
259
267
  indices.each do |attrs|
260
- db.set_delete(index_key_for(attrs, read_remotes(attrs)), id)
268
+ db.srem(index_key_for(attrs, read_remotes(attrs)), id)
261
269
  end
262
270
  end
263
271
 
@@ -270,11 +278,11 @@ module Ohm
270
278
  end
271
279
 
272
280
  def read_remote(att)
273
- id && db[key(att)]
281
+ id && db.get(key(att))
274
282
  end
275
283
 
276
284
  def write_remote(att, value)
277
- db[key(att)] = value
285
+ db.set(key(att), value)
278
286
  end
279
287
 
280
288
  def read_locals(attrs)
data/test/benchmarks.rb CHANGED
@@ -1,9 +1,12 @@
1
1
  require "rubygems"
2
2
  require "bench"
3
3
  require File.dirname(__FILE__) + "/../lib/ohm"
4
+ require "redis"
4
5
 
5
- $redis = Redis.new(:port => 6381)
6
- $redis.flush_db
6
+ Ohm.connect(:port => 6381)
7
+ Ohm.flush
8
+
9
+ $r = Redis.new(:port => 6381)
7
10
 
8
11
  class Event < Ohm::Model
9
12
  attribute :name
@@ -17,36 +20,59 @@ end
17
20
  event = Event.create(:name => "Ruby Tuesday")
18
21
  array = []
19
22
 
20
- benchmark "redis add to set" do
21
- $redis.set_add("foo", 1)
23
+ benchmark "add to set with ohm redis" do
24
+ Ohm.redis.sadd("foo", 1)
25
+ end
26
+
27
+ benchmark "add to set with redis" do
28
+ $r.set_add("foo", 1)
22
29
  end
23
30
 
24
- benchmark "ohm add to set" do
31
+ benchmark "add to set with ohm" do
25
32
  event.attendees << 1
26
33
  end
27
34
 
28
- benchmark "ruby array push" do
29
- array.push(1)
35
+ Ohm.redis.sadd("bar", 1)
36
+ Ohm.redis.sadd("bar", 2)
37
+
38
+ benchmark "retrieve a set of two members with ohm redis" do
39
+ Ohm.redis.sadd("bar", 3)
40
+ Ohm.redis.srem("bar", 3)
41
+ Ohm.redis.smembers("bar")
42
+ end
43
+
44
+ $r.set_add("bar", 1)
45
+ $r.set_add("bar", 2)
46
+
47
+ benchmark "retrieve a set of two members with redis" do
48
+ $r.set_add("bar", 3)
49
+ $r.set_delete("bar", 3)
50
+ $r.set_members("bar")
30
51
  end
31
52
 
32
- $redis.set_add("bar", 1)
33
- $redis.set_add("bar", 2)
53
+ Ohm.redis.del("Event:#{event.id}:attendees")
54
+ $r.delete("Event:#{event.id}:attendees")
55
+
56
+ event.attendees << 1
57
+ event.attendees << 2
34
58
 
35
- benchmark "retrieve a set of two members" do
36
- $redis.set_members("bar")
59
+ benchmark "retrieve a set of two members with ohm" do
60
+ event.attendees << 3
61
+ event.attendees.delete(3)
62
+ event.attendees
37
63
  end
38
64
 
39
65
  benchmark "retrieve membership status and set count" do
40
- $redis.set_count("bar")
41
- $redis.set_member?("bar", "1")
66
+ Ohm.redis.scard("bar")
67
+ Ohm.redis.sismember("bar", "1")
42
68
  end
43
69
 
44
70
  benchmark "retrieve set count" do
45
- $redis.set_count("bar").zero?
71
+ Ohm.redis.scard("bar").zero?
46
72
  end
47
73
 
48
74
  benchmark "retrieve membership status" do
49
- $redis.set_member?("bar", "1")
75
+ Ohm.redis.sismember("bar", "1")
50
76
  end
51
77
 
52
- run 20_000
78
+ run 10_000
data/test/db/dump.rdb CHANGED
Binary file
data/test/indices_test.rb CHANGED
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/test_helper'
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
2
 
3
3
  class IndicesTest < Test::Unit::TestCase
4
4
  class User < Ohm::Model
@@ -9,14 +9,14 @@ class IndicesTest < Test::Unit::TestCase
9
9
 
10
10
  context "A model with an indexed attribute" do
11
11
  setup do
12
- $redis.flush_db
12
+ Ohm.redis.flushdb
13
13
 
14
14
  @user1 = User.create(:email => "foo")
15
15
  @user2 = User.create(:email => "bar")
16
16
  end
17
17
 
18
18
  should "be able to find by the given attribute" do
19
- assert_equal [@user1], User.find(:email, "foo").to_a
19
+ assert_equal [@user1], User.find(:email, "foo")
20
20
  end
21
21
 
22
22
  should "update indices when changing attribute values" do
@@ -24,13 +24,13 @@ class IndicesTest < Test::Unit::TestCase
24
24
  @user1.save
25
25
 
26
26
  assert_equal [], User.find(:email, "foo").to_a
27
- assert_equal [@user1], User.find(:email, "baz").to_a
27
+ assert_equal [@user1], User.find(:email, "baz")
28
28
  end
29
29
 
30
30
  should "remove from the index after deleting" do
31
31
  @user2.delete
32
32
 
33
- assert_equal [], User.find(:email, "bar").to_a
33
+ assert_equal [], User.find(:email, "bar")
34
34
  end
35
35
  end
36
36
  end
data/test/model_test.rb CHANGED
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/test_helper'
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
2
 
3
3
  class Event < Ohm::Model
4
4
  attribute :name
@@ -32,8 +32,8 @@ class TestRedis < Test::Unit::TestCase
32
32
 
33
33
  context "Finding an event" do
34
34
  setup do
35
- $redis.set_add("Event", 1)
36
- $redis["Event:1:name"] = "Concert"
35
+ Ohm.redis.sadd("Event", 1)
36
+ Ohm.redis.set("Event:1:name", "Concert")
37
37
  end
38
38
 
39
39
  should "return an instance of Event" do
@@ -45,8 +45,8 @@ class TestRedis < Test::Unit::TestCase
45
45
 
46
46
  context "Finding a user" do
47
47
  setup do
48
- $redis.set_add("User:all", 1)
49
- $redis["User:1:email"] = "albert@example.com"
48
+ Ohm.redis.sadd("User:all", 1)
49
+ Ohm.redis.set("User:1:email", "albert@example.com")
50
50
  end
51
51
 
52
52
  should "return an instance of User" do
@@ -58,8 +58,8 @@ class TestRedis < Test::Unit::TestCase
58
58
 
59
59
  context "Updating a user" do
60
60
  setup do
61
- $redis.set_add("User:all", 1)
62
- $redis["User:1:email"] = "albert@example.com"
61
+ Ohm.redis.sadd("User:all", 1)
62
+ Ohm.redis.set("User:1:email", "albert@example.com")
63
63
 
64
64
  @user = User[1]
65
65
  end
@@ -131,10 +131,10 @@ class TestRedis < Test::Unit::TestCase
131
131
 
132
132
  @model.delete
133
133
 
134
- assert_nil $redis[ModelToBeDeleted.key(id)]
135
- assert_nil $redis[ModelToBeDeleted.key(id, :name)]
136
- assert_equal Set.new, $redis.set_members(ModelToBeDeleted.key(id, :foos))
137
- assert_equal Array.new, $redis.list_range(ModelToBeDeleted.key(id, :bars), 0, -1)
134
+ assert_nil Ohm.redis.get(ModelToBeDeleted.key(id))
135
+ assert_nil Ohm.redis.get(ModelToBeDeleted.key(id, :name))
136
+ assert_equal Array.new, Ohm.redis.smembers(ModelToBeDeleted.key(id, :foos))
137
+ assert_equal Array.new, Ohm.redis.list(ModelToBeDeleted.key(id, :bars))
138
138
 
139
139
  assert ModelToBeDeleted.all.empty?
140
140
  end
@@ -0,0 +1,314 @@
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
+
3
+ class Foo
4
+ attr_accessor :bar
5
+ def initialize(bar)
6
+ @bar = bar
7
+ end
8
+
9
+ def ==(other)
10
+ @bar == other.bar
11
+ end
12
+ end
13
+
14
+ class RedisTest < Test::Unit::TestCase
15
+ describe "redis" do
16
+ setup do
17
+ @r ||= Ohm.redis
18
+ @r.set("foo", "bar")
19
+ end
20
+
21
+ teardown do
22
+ @r.flushdb
23
+ end
24
+
25
+ should "should be able to GET a key" do
26
+ assert_equal "bar", @r.get("foo")
27
+ end
28
+
29
+ should "should be able to SET a key" do
30
+ @r.set("foo", "nik")
31
+ assert_equal "nik", @r.get("foo")
32
+ end
33
+
34
+ should "should be able to SETNX(setnx)" do
35
+ @r.set("foo", "nik")
36
+ assert_equal "nik", @r.get("foo")
37
+ @r.setnx("foo", "bar")
38
+ assert_equal "nik", @r.get("foo")
39
+ end
40
+
41
+ should "should be able to INCR(increment) a key" do
42
+ @r.del("counter")
43
+ assert_equal 1, @r.incr("counter")
44
+ assert_equal 2, @r.incr("counter")
45
+ assert_equal 3, @r.incr("counter")
46
+ end
47
+
48
+ should "should be able to DECR(decrement) a key" do
49
+ @r.del("counter")
50
+ assert_equal 1, @r.incr("counter")
51
+ assert_equal 2, @r.incr("counter")
52
+ assert_equal 3, @r.incr("counter")
53
+ assert_equal 2, @r.decr("counter")
54
+ assert_equal 0, @r.decrby("counter", 2)
55
+ end
56
+
57
+ should "should be able to RANDKEY(return a random key)" do
58
+ assert_not_nil @r.randomkey
59
+ end
60
+
61
+ should "should be able to RENAME a key" do
62
+ @r.del "foo"
63
+ @r.del "bar"
64
+ @r.set("foo", "hi")
65
+ @r.rename "foo", "bar"
66
+ assert_equal "hi", @r.get("bar")
67
+ end
68
+
69
+ should "should be able to RENAMENX(rename unless the new key already exists) a key" do
70
+ @r.del "foo"
71
+ @r.del "bar"
72
+ @r.set("foo", "hi")
73
+ @r.set("bar", "ohai")
74
+
75
+ @r.renamenx "foo", "bar"
76
+
77
+ assert_equal "ohai", @r.get("bar")
78
+ end
79
+
80
+ should "should be able to EXISTS(check if key exists)" do
81
+ @r.set("foo", "nik")
82
+ assert @r.exists("foo")
83
+ @r.del "foo"
84
+ assert_equal false, @r.exists("foo")
85
+ end
86
+
87
+ should "should be able to KEYS(glob for keys)" do
88
+ @r.keys("f*").each do |key|
89
+ @r.del key
90
+ end
91
+ @r.set("f", "nik")
92
+ @r.set("fo", "nak")
93
+ @r.set("foo", "qux")
94
+ assert_equal ["f","fo", "foo"], @r.keys("f*").sort
95
+ end
96
+
97
+ should "should be able to check the TYPE of a key" do
98
+ @r.set("foo", "nik")
99
+ assert_equal "string", @r.type("foo")
100
+ @r.del "foo"
101
+ assert_equal "none", @r.type("foo")
102
+ end
103
+
104
+ should "should be able to push to the head of a list" do
105
+ @r.lpush "list", "hello"
106
+ @r.lpush "list", 42
107
+ assert_equal "list", @r.type("list")
108
+ assert_equal 2, @r.llen("list")
109
+ assert_equal "42", @r.lpop("list")
110
+ @r.del("list")
111
+ end
112
+
113
+ should "should be able to push to the tail of a list" do
114
+ @r.rpush "list", "hello"
115
+ assert_equal "list", @r.type("list")
116
+ assert_equal 1, @r.llen("list")
117
+ @r.del("list")
118
+ end
119
+
120
+ should "should be able to pop the tail of a list" do
121
+ @r.rpush "list", "hello"
122
+ @r.rpush "list", "goodbye"
123
+ assert_equal "list", @r.type("list")
124
+ assert_equal 2, @r.llen("list")
125
+ assert_equal "goodbye", @r.rpop("list")
126
+ @r.del("list")
127
+ end
128
+
129
+ should "should be able to pop the head of a list" do
130
+ @r.rpush "list", "hello"
131
+ @r.rpush "list", "goodbye"
132
+ assert_equal "list", @r.type("list")
133
+ assert_equal 2, @r.llen("list")
134
+ assert_equal "hello", @r.lpop("list")
135
+ @r.del("list")
136
+ end
137
+
138
+ should "should be able to get the length of a list" do
139
+ @r.rpush "list", "hello"
140
+ @r.rpush "list", "goodbye"
141
+ assert_equal "list", @r.type("list")
142
+ assert_equal 2, @r.llen("list")
143
+ @r.del("list")
144
+ end
145
+
146
+ should "should be able to get a range of values from a list" do
147
+ @r.rpush "list", "hello"
148
+ @r.rpush "list", "goodbye"
149
+ @r.rpush "list", "1"
150
+ @r.rpush "list", "2"
151
+ @r.rpush "list", "3"
152
+ assert_equal "list", @r.type("list")
153
+ assert_equal 5, @r.llen("list")
154
+ assert_equal ["1", "2", "3"], @r.lrange("list", 2, -1)
155
+ @r.del("list")
156
+ end
157
+
158
+ should "should be able to get all the values from a list" do
159
+ @r.rpush "list", "1"
160
+ @r.rpush "list", "2"
161
+ @r.rpush "list", "3"
162
+ assert_equal "list", @r.type("list")
163
+ assert_equal 3, @r.llen("list")
164
+ assert_equal ["1", "2", "3"], @r.list("list")
165
+ @r.del("list")
166
+ end
167
+
168
+ should "should be able to trim a list" do
169
+ @r.rpush "list", "hello"
170
+ @r.rpush "list", "goodbye"
171
+ @r.rpush "list", "1"
172
+ @r.rpush "list", "2"
173
+ @r.rpush "list", "3"
174
+ assert_equal "list", @r.type("list")
175
+ assert_equal 5, @r.llen("list")
176
+ @r.ltrim "list", 0, 1
177
+ assert_equal 2, @r.llen("list")
178
+ assert_equal ["hello", "goodbye"], @r.lrange("list", 0, -1)
179
+ @r.del("list")
180
+ end
181
+
182
+ should "should be able to get a value by indexing into a list" do
183
+ @r.rpush "list", "hello"
184
+ @r.rpush "list", "goodbye"
185
+ assert_equal "list", @r.type("list")
186
+ assert_equal 2, @r.llen("list")
187
+ assert_equal "goodbye", @r.lindex("list", 1)
188
+ @r.del("list")
189
+ end
190
+
191
+ should "should be able to set a value by indexing into a list" do
192
+ @r.rpush "list", "hello"
193
+ @r.rpush "list", "hello"
194
+ assert_equal "list", @r.type("list")
195
+ assert_equal 2, @r.llen("list")
196
+ assert @r.lset("list", 1, "goodbye")
197
+ assert_equal "goodbye", @r.lindex("list", 1)
198
+ @r.del("list")
199
+ end
200
+
201
+ should "should be able to remove values from a list LREM" do
202
+ @r.rpush "list", "hello"
203
+ @r.rpush "list", "goodbye"
204
+ assert_equal "list", @r.type("list")
205
+ assert_equal 2, @r.llen("list")
206
+ assert_equal 1, @r.lrem("list", 1, "hello")
207
+ assert_equal ["goodbye"], @r.lrange("list", 0, -1)
208
+ @r.del("list")
209
+ end
210
+
211
+ should "should be able add members to a set" do
212
+ @r.sadd "set", "key1"
213
+ @r.sadd "set", "key2"
214
+ assert_equal "set", @r.type("set")
215
+ assert_equal 2, @r.scard("set")
216
+ assert_equal ["key1", "key2"], @r.smembers("set").sort
217
+ @r.del("set")
218
+ end
219
+
220
+ should "should be able delete members to a set" do
221
+ @r.sadd "set", "key1"
222
+ @r.sadd "set", "key2"
223
+ assert_equal "set", @r.type("set")
224
+ assert_equal 2, @r.scard("set")
225
+ assert_equal ["key1", "key2"], @r.smembers("set").sort
226
+ @r.srem("set", "key1")
227
+ assert_equal 1, @r.scard("set")
228
+ assert_equal ["key2"], @r.smembers("set")
229
+ @r.del("set")
230
+ end
231
+
232
+ should "should be able count the members of a set" do
233
+ @r.sadd "set", "key1"
234
+ @r.sadd "set", "key2"
235
+ assert_equal "set", @r.type("set")
236
+ assert_equal 2, @r.scard("set")
237
+ @r.del("set")
238
+ end
239
+
240
+ should "should be able test for set membership" do
241
+ @r.sadd "set", "key1"
242
+ @r.sadd "set", "key2"
243
+ assert_equal "set", @r.type("set")
244
+ assert_equal 2, @r.scard("set")
245
+ assert @r.sismember("set", "key1")
246
+ assert @r.sismember("set", "key2")
247
+ assert_equal false, @r.sismember("set", "notthere")
248
+ @r.del("set")
249
+ end
250
+
251
+ should "should be able to do set intersection" do
252
+ @r.sadd "set", "key1"
253
+ @r.sadd "set", "key2"
254
+ @r.sadd "set2", "key2"
255
+ assert_equal ["key2"], @r.sinter("set", "set2")
256
+ @r.del("set")
257
+ end
258
+
259
+ should "should be able to do set intersection and store the results in a key" do
260
+ @r.sadd "set", "key1"
261
+ @r.sadd "set", "key2"
262
+ @r.sadd "set2", "key2"
263
+ @r.sinterstore("newone", "set", "set2")
264
+ assert_equal ["key2"], @r.smembers("newone")
265
+ @r.del("set")
266
+ end
267
+
268
+ should "should be able to do crazy SORT queries" do
269
+ @r.set("dog_1", "louie")
270
+ @r.rpush "dogs", 1
271
+ @r.set("dog_2", "lucy")
272
+ @r.rpush "dogs", 2
273
+ @r.set("dog_3", "max")
274
+ @r.rpush "dogs", 3
275
+ @r.set("dog_4", "taj")
276
+ @r.rpush "dogs", 4
277
+ assert_equal ["louie"], @r.sort("dogs", :get => "dog_*", :limit => [0,1])
278
+ assert_equal ["taj"], @r.sort("dogs", :get => "dog_*", :limit => [0,1], :order => "desc alpha")
279
+ end
280
+
281
+ should "should provide info" do
282
+ [:last_save_time, :redis_version, :total_connections_received, :connected_clients, :total_commands_processed, :connected_slaves, :uptime_in_seconds, :used_memory, :uptime_in_days, :changes_since_last_save].each do |x|
283
+ assert @r.info.keys.include?(x)
284
+ end
285
+ end
286
+
287
+ should "should be able to flush the database" do
288
+ @r.set("key1", "keyone")
289
+ @r.set("key2", "keytwo")
290
+ assert_equal ["foo", "key1", "key2"], @r.keys("*").sort #foo from before
291
+ @r.flushdb
292
+ assert_equal [], @r.keys("*")
293
+ end
294
+
295
+ should "should be able to provide the last save time" do
296
+ savetime = @r.lastsave
297
+ assert_equal Time, Time.at(savetime).class
298
+ assert Time.at(savetime) <= Time.now
299
+ end
300
+
301
+ should "should be able to MGET keys" do
302
+ @r.set("foo", 1000)
303
+ @r.set("bar", 2000)
304
+ assert_equal ["1000", "2000"], @r.mget("foo", "bar")
305
+ assert_equal ["1000", "2000", nil], @r.mget("foo", "bar", "baz")
306
+ end
307
+
308
+ should "should bgsave" do
309
+ assert_nothing_raised do
310
+ @r.bgsave
311
+ end
312
+ end
313
+ end
314
+ end
data/test/test_helper.rb CHANGED
@@ -1,7 +1,12 @@
1
1
  require "rubygems"
2
- require "ruby-debug"
2
+
3
+ begin
4
+ require "ruby-debug"
5
+ rescue LoadError
6
+ end
7
+
3
8
  require "contest"
4
9
  require File.dirname(__FILE__) + "/../lib/ohm"
5
10
 
6
- $redis = Redis.new(:port => 6381)
7
- $redis.flush_db
11
+ Ohm.connect(:port => 6381)
12
+ Ohm.flush
@@ -1,4 +1,4 @@
1
- require File.dirname(__FILE__) + '/test_helper'
1
+ require File.join(File.dirname(__FILE__), "test_helper")
2
2
 
3
3
  class ValidationsTest < Test::Unit::TestCase
4
4
  class Event < Ohm::Model
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ohm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Michel Martens, Damian Janowski
@@ -11,17 +11,8 @@ cert_chain: []
11
11
 
12
12
  date: 2009-03-13 00:00:00 -02:00
13
13
  default_executable:
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
16
- name: ezmobius-redis-rb
17
- type: :runtime
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: 0.0.3
24
- version:
14
+ dependencies: []
15
+
25
16
  description:
26
17
  email: michel@soveran.com
27
18
  executables: []
@@ -31,6 +22,7 @@ extensions: []
31
22
  extra_rdoc_files: []
32
23
 
33
24
  files:
25
+ - lib/ohm/redis.rb
34
26
  - lib/ohm/validations.rb
35
27
  - lib/ohm.rb
36
28
  - README.markdown
@@ -42,6 +34,7 @@ files:
42
34
  - test/db/redis.pid
43
35
  - test/indices_test.rb
44
36
  - test/model_test.rb
37
+ - test/redis_test.rb
45
38
  - test/test.conf
46
39
  - test/test_helper.rb
47
40
  - test/validations_test.rb