ohm 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
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