redis-model 0.1.0

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.
@@ -0,0 +1,3 @@
1
+ *~
2
+ /coverage
3
+ /pkg
@@ -0,0 +1,46 @@
1
+ ## redis-model
2
+
3
+ Minimal model support for [redis-rb](http://github.com/ezmobius/redis-rb).
4
+ Directly maps ruby properties to `model_name:id:field_name` keys in redis.
5
+ Scalar, list and set properties are supported.
6
+
7
+ Values can be marshaled to/from `Integer`, `Float`, `DateTime`, `JSON`. See `Redis::Model::Marshal` for more info.
8
+
9
+ ### Define
10
+
11
+ require 'redis/model'
12
+
13
+ class User < Redis::Model
14
+ value :name, :string
15
+ value :created, :datetime
16
+ value :profile, :json
17
+
18
+ list :posts, :json
19
+
20
+ set :followers, :int
21
+ end
22
+
23
+ ### Write
24
+
25
+ u = User.with_key(1)
26
+ u.name = 'Joe' # set user:1:name Joe
27
+ u.created = DateTime.now # set user:1:created 2009-10-05T12:09:56+0400
28
+ u.profile = { # set user:1:profile {"sex":"M","about":"Lorem","age":23}
29
+ :age => 23,
30
+ :sex => 'M',
31
+ :about => 'Lorem'
32
+ }
33
+ u.posts << { # rpush user:1:posts {"title":"Hello world!","text":"lorem"}
34
+ :title => "Hello world!",
35
+ :text => "lorem"
36
+ }
37
+ u.followers << 2 # sadd user:1:followers 2
38
+
39
+ ### Read
40
+
41
+ u = User.with_key(1)
42
+ p u.name # get user:1:name
43
+ p u.created.strftime('%m/%d/%Y') # get user:1:created
44
+ p u.posts[0,20] # lrange user:1:posts 0 20
45
+ p u.posts[0] # lindex user:1:posts 0
46
+ p u.followers.has_key?(2) # sismember user:1:followers 2
@@ -0,0 +1,30 @@
1
+ require 'rubygems'
2
+ require 'spec/rake/spectask'
3
+
4
+ task :default => :spec
5
+
6
+ desc "Run specs"
7
+ Spec::Rake::SpecTask.new do |t|
8
+ t.spec_files = FileList['spec/**/*_spec.rb']
9
+ t.spec_opts = %w(-fs --color)
10
+ end
11
+
12
+ desc "Run all examples with RCov"
13
+ Spec::Rake::SpecTask.new(:rcov) do |t|
14
+ t.spec_files = FileList['spec/**/*_spec.rb']
15
+ t.rcov = true
16
+ end
17
+
18
+ begin
19
+ require 'jeweler'
20
+ Jeweler::Tasks.new do |gemspec|
21
+ gemspec.name = "redis-model"
22
+ gemspec.summary = "Minimal models for Redis"
23
+ gemspec.description = "Minimal model support for redis-rb. Directly maps ruby properties to model_name:id:field_name keys in redis. Scalar, list and set properties are supported."
24
+ gemspec.email = "voloko@gmail.com"
25
+ gemspec.homepage = "http://github.com/voloko/redis-model"
26
+ gemspec.authors = ["Vladimir Kolesnikov"]
27
+ end
28
+ rescue LoadError
29
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
30
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,36 @@
1
+ require 'rubygems'
2
+ require 'benchmark'
3
+ $:.push File.join(File.dirname(__FILE__), 'lib')
4
+ require 'redis/model'
5
+
6
+ n = 20000
7
+
8
+ class TestModel < Redis::Model
9
+ value :foo
10
+ list :bar
11
+ end
12
+
13
+ @r = Redis.new#(:debug => true)
14
+ @r['foo'] = "The first line we sent to the server is some text"
15
+
16
+ Benchmark.bmbm do |x|
17
+ x.report("set (model)") do
18
+ n.times do |i|
19
+ m = TestModel.with_key(i)
20
+ m.foo = "The first line we sent to the server is some text";
21
+ m.foo
22
+ end
23
+ end
24
+
25
+ x.report("push+trim (model)") do
26
+ n.times do |i|
27
+ m = TestModel.with_key(i)
28
+ m.bar << i
29
+ m.bar.trim 0, 30
30
+ end
31
+ end
32
+ end
33
+
34
+ @r.keys('*').each do |k|
35
+ @r.delete k
36
+ end
@@ -0,0 +1,40 @@
1
+ require 'rubygems'
2
+ $LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
3
+ require 'redis/model'
4
+
5
+
6
+ class User < Redis::Model
7
+ value :name, :string
8
+ value :created, :datetime
9
+ value :profile, :json
10
+
11
+ list :posts, :json
12
+
13
+ set :followers, :int
14
+ end
15
+
16
+
17
+
18
+ u = User.with_key(1)
19
+ u.delete
20
+ u.name = 'Joe'
21
+ u.created = DateTime.now
22
+ u.profile = {
23
+ :age => 23,
24
+ :sex => 'M',
25
+ :about => 'Lorem'
26
+ }
27
+ u.posts << {
28
+ :title => "Hello world!",
29
+ :text => "lorem"
30
+ }
31
+ u.followers << 2
32
+
33
+
34
+
35
+ u = User.with_key(1)
36
+ p u.name
37
+ p u.created.strftime('%m/%d/%Y')
38
+ p u.posts[0,20]
39
+ p u.posts[0]
40
+ p u.followers.has_key?(2)
@@ -0,0 +1,364 @@
1
+ require "redis"
2
+ require "json"
3
+
4
+ # Simple models for redis-rb.
5
+ # It maps ruby properties to <tt>model_name:id:field_name</tt> keys in redis.
6
+ # It also adds marshaling for string fields and more OOP style access for sets and lists
7
+ #
8
+ # == Define
9
+ #
10
+ # require 'redis/model'
11
+ # class User < Redis::Model
12
+ # value :name, :string
13
+ # value :created, :datetime
14
+ # value :profile, :json
15
+ # list :posts
16
+ # set :followers
17
+ # end
18
+ #
19
+ # See Redis::Marshal for more types
20
+ #
21
+ #
22
+ # == Write
23
+ #
24
+ # u = User.with_key(1)
25
+ # u.name = 'Joe' # set user:1:name Joe
26
+ # u.created = DateTime.now # set user:1:created 2009-10-05T12:09:56+0400
27
+ # u.profile = { # set user:1:profile {"sex":"M","about":"Lorem","age":23}
28
+ # :age => 23,
29
+ # :sex => 'M',
30
+ # :about => 'Lorem'
31
+ # }
32
+ # u.posts << "Hello world!" # rpush user:1:posts 'Hello world!'
33
+ # u.followers << 2 # sadd user:1:followers 2
34
+ #
35
+ # == Read
36
+ #
37
+ # u = User.with_key(1)
38
+ # p u.name # get user:1:name
39
+ # p u.created.strftime('%m/%d/%Y') # get user:1:created
40
+ # p u.posts[0,20] # lrange user:1:posts 0 20
41
+ # p u.followers.has_key?(2) # sismember user:1:followers 2
42
+ #
43
+
44
+
45
+ class Redis::Model
46
+ attr_accessor :id
47
+
48
+ def initialize(id)
49
+ self.id = id
50
+ end
51
+
52
+ def redis #:nodoc:
53
+ self.class.redis
54
+ end
55
+
56
+ # Issues delete commands for all defined fields
57
+ def delete
58
+ self.class.fields.each do |field|
59
+ redis.delete field_key(field[:name])
60
+ end
61
+ end
62
+
63
+ protected
64
+ def prefix #:nodoc:
65
+ @prefix ||= self.class.prefix || self.class.to_s.
66
+ sub(%r{(.*::)}, '').
67
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
68
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
69
+ downcase
70
+ end
71
+
72
+ def field_key(name) #:nodoc:
73
+ "#{prefix}:#{id}:#{name}"
74
+ end
75
+
76
+ class << self
77
+ # Defaults to model_name.dasherize
78
+ attr_accessor :prefix
79
+
80
+
81
+ # Creates new model instance with new uniqid
82
+ # NOTE: "sequence:model_name:id" key is used
83
+ def create(values = {})
84
+ populate_model(self.new(next_id))
85
+ end
86
+
87
+ # Creates new model instance with given id
88
+ alias_method :with_key, :new
89
+ alias_method :with_next_key, :create
90
+
91
+ # Defines marshaled rw accessor for redis string value
92
+ def field(name, type = :string)
93
+ class_name = marshal_class_name(name, type)
94
+
95
+ fields << {:name => name.to_s, :type => :type}
96
+ if type == :string
97
+ class_eval "def #{name}; @#{name} ||= redis[field_key('#{name}')]; end"
98
+ class_eval "def #{name}=(value); @#{name} = redis[field_key('#{name}')] = value; end"
99
+ else
100
+ class_eval "def #{name}; @#{name} ||= Marshal::#{class_name}.load(redis[field_key('#{name}')]); end"
101
+ class_eval "def #{name}=(value); @#{name} = value; redis[field_key('#{name}')] = Marshal::#{class_name}.dump(value) end"
102
+ end
103
+ end
104
+ alias_method :value, :field
105
+
106
+ # Defines accessor for redis list
107
+ def list(name, type = :string)
108
+ class_name = marshal_class_name(name, type)
109
+
110
+ fields << {:name => name.to_s, :type => :list}
111
+ class_eval "def #{name}; @#{name} ||= ListProxy.new(self.redis, field_key('#{name}'), Marshal::#{class_name}); end"
112
+ eval_writer(name)
113
+ end
114
+
115
+ # Defines accessor for redis set
116
+ def set(name, type = :string)
117
+ class_name = marshal_class_name(name, type)
118
+
119
+ fields << {:name => name.to_s, :type => :set}
120
+ class_eval "def #{name}; @#{name} ||= SetProxy.new(self.redis, field_key('#{name}'), Marshal::#{class_name}); end"
121
+ eval_writer(name)
122
+ end
123
+
124
+ def marshal_class_name(name, type)
125
+ Marshal::TYPES[type] or raise ArgumentError.new("Unknown type #{type} for field #{name}")
126
+ end
127
+
128
+ # Redefine this to change connection options
129
+ def redis
130
+ @@redis ||= Redis.new
131
+ end
132
+
133
+ def fields #:nodoc:
134
+ @fields ||= []
135
+ end
136
+
137
+ protected
138
+ def eval_writer(name) #:nodoc:
139
+ class_eval <<-END
140
+ def #{name}=(value)
141
+ field = self.#{name};
142
+ if value.respond_to?(:each)
143
+ value.each {|v| field << v}
144
+ else
145
+ field << v
146
+ end
147
+ end
148
+ END
149
+ end
150
+
151
+ def next_id #:nodoc:
152
+ redis.incr "sequence:#{self.new.prefix}:id"
153
+ end
154
+
155
+ def populate_model(model, values) #:nodoc:
156
+ return model if values.empty?
157
+ @@fields.each do |field|
158
+ model.send(:"#{field[:name]}=", value) if values.has_key?(field[:name])
159
+ end
160
+ model
161
+ end
162
+ end
163
+
164
+ module Marshal
165
+ TYPES = {
166
+ :string => 'String',
167
+ :int => 'Integer',
168
+ :integer => 'Integer',
169
+ :float => 'Float',
170
+ :datetime => 'DateTime',
171
+ :json => 'JSON',
172
+ }
173
+
174
+ class String
175
+ def self.dump(v)
176
+ v
177
+ end
178
+
179
+ def self.load(v)
180
+ v
181
+ end
182
+ end
183
+
184
+ class Integer
185
+ def self.dump(v)
186
+ v.to_s
187
+ end
188
+
189
+ def self.load(v)
190
+ v && v.to_i
191
+ end
192
+ end
193
+
194
+ class Float
195
+ def self.dump(v)
196
+ v.to_s
197
+ end
198
+
199
+ def self.load(v)
200
+ v && v.to_f
201
+ end
202
+ end
203
+
204
+ class DateTime
205
+ def self.dump(v)
206
+ v.strftime('%FT%T%z')
207
+ end
208
+
209
+ def self.load(v)
210
+ v && ::DateTime.strptime(v, '%FT%T%z')
211
+ end
212
+ end
213
+
214
+ class JSON
215
+ def self.dump(v)
216
+ ::JSON.dump(v)
217
+ end
218
+
219
+ def self.load(v)
220
+ v && ::JSON.load(v)
221
+ end
222
+ end
223
+ end
224
+
225
+
226
+
227
+ class FieldProxy #:nodoc
228
+ def initialize(redis, name, marshal)
229
+ @redis = redis
230
+ @name = name
231
+ @marshal = marshal
232
+ end
233
+
234
+ def method_missing(method, *argv)
235
+ translated_method = translate_method_name(method)
236
+ raise NoMethodError.new("Method '#{method}' is not defined") unless translated_method
237
+ @redis.send translated_method, @name, *argv
238
+ end
239
+
240
+ protected
241
+ def translate_method_name(m)
242
+ m
243
+ end
244
+ end
245
+
246
+
247
+
248
+ class ListProxy < FieldProxy #:nodoc:
249
+ def <<(v)
250
+ @redis.rpush @name, @marshal.dump(v)
251
+ end
252
+ alias_method :push_tail, :<<
253
+
254
+ def push_head(v)
255
+ @redis.lpush @name, @marshal.dump(v)
256
+ end
257
+
258
+ def pop_tail
259
+ @marshal.load(@redis.rpop(@name))
260
+ end
261
+
262
+ def pop_head
263
+ @marshal.load(@redis.lpop(@name))
264
+ end
265
+
266
+ def [](from, to = nil)
267
+ if to.nil?
268
+ @marshal.load(@redis.lindex(@name, from))
269
+ else
270
+ @redis.lrange(@name, from, to).map! { |v| @marshal.load(v) }
271
+ end
272
+ end
273
+ alias_method :range, :[]
274
+
275
+ def []=(index, v)
276
+ @redis.lset(@name, index, @marshal.dump(v))
277
+ end
278
+ alias_method :set, :[]=
279
+
280
+ def include?(v)
281
+ @redis.exists(@name, @marshal.dump(v))
282
+ end
283
+
284
+ def remove(count, v)
285
+ @redis.lrem(@name, count, @marshal.dump(v))
286
+ end
287
+
288
+ def length
289
+ @redis.llen(@name)
290
+ end
291
+
292
+ def trim(from, to)
293
+ @redis.ltrim(@name, from, to)
294
+ end
295
+
296
+ def to_s
297
+ range(0, 100).join(', ')
298
+ end
299
+
300
+ protected
301
+ def translate_method_name(m)
302
+ COMMANDS[m]
303
+ end
304
+ end
305
+
306
+
307
+
308
+ class SetProxy < FieldProxy #:nodoc:
309
+ COMMANDS = {
310
+ :intersect_store => "sinterstore",
311
+ :union_store => "sunionstore",
312
+ :diff_store => "sdiffstore",
313
+ :move => "smove",
314
+ }
315
+
316
+ def <<(v)
317
+ @redis.sadd @name, @marshal.dump(v)
318
+ end
319
+ alias_method :add, :<<
320
+
321
+ def delete(v)
322
+ @redis.srem @name, @marshal.dump(v)
323
+ end
324
+ alias_method :remove, :delete
325
+
326
+ def include?(v)
327
+ @redis.sismember @name, @marshal.dump(v)
328
+ end
329
+ alias_method :has_key?, :include?
330
+ alias_method :member?, :include?
331
+
332
+ def members
333
+ @redis.smembers(@name).map { |v| @marshal.load(v) }
334
+ end
335
+
336
+ def intersect(*keys)
337
+ @redis.sinter(@name, *keys).map { |v| @marshal.load(v) }
338
+ end
339
+
340
+ def union(*keys)
341
+ @redis.sunion(@name, *keys).map { |v| @marshal.load(v) }
342
+ end
343
+
344
+ def diff(*keys)
345
+ @redis.sdiff(@name, *keys).map { |v| @marshal.load(v) }
346
+ end
347
+
348
+ def length
349
+ @redis.llen(@name)
350
+ end
351
+
352
+ def to_s
353
+ members.join(', ')
354
+ end
355
+
356
+ protected
357
+ def translate_method_name(m)
358
+ COMMANDS[m]
359
+ end
360
+ end
361
+
362
+
363
+ end
364
+
@@ -0,0 +1,188 @@
1
+ require File.dirname(__FILE__) + '/../spec_helper.rb'
2
+ require 'redis/model'
3
+ require 'json'
4
+
5
+ describe Redis::Model do
6
+
7
+ context "DSL" do
8
+ class TestDSL < Redis::Model
9
+ field :foo
10
+ list :bar
11
+ set :sloppy
12
+ end
13
+
14
+ before(:each) do
15
+ @x = TestDSL.with_key(1)
16
+ end
17
+
18
+ it "should define rw accessors for field" do
19
+ @x.should respond_to(:foo)
20
+ @x.should respond_to(:foo=)
21
+ end
22
+
23
+ it "should define r accessor for list" do
24
+ @x.should respond_to(:bar)
25
+ end
26
+
27
+ it "should define r accessor for set" do
28
+ @x.should respond_to(:sloppy)
29
+ end
30
+
31
+ it "should raise error on invalid type" do
32
+ lambda do
33
+ class TestInvalidType < Redis::Model
34
+ field :invalid, :invalid_type
35
+ end
36
+ end.should raise_error(ArgumentError, 'Unknown type invalid_type for field invalid')
37
+ end
38
+ end
39
+
40
+ context "field type cast" do
41
+ class TestType < Redis::Model
42
+ field :foo_string, :string
43
+ field :foo_json, :json
44
+ field :foo_date, :datetime
45
+ field :foo_int, :int
46
+ field :foo_float, :float
47
+
48
+ list :list_date, :datetime
49
+ set :set_date, :datetime
50
+ end
51
+
52
+ before(:each) do
53
+ @xRedisMock = Spec::Mocks::Mock.new
54
+ @yRedisMock = Spec::Mocks::Mock.new
55
+ @x = TestType.with_key(1)
56
+ @y = TestType.with_key(1)
57
+ @x.stub!(:redis).and_return(@xRedisMock)
58
+ @y.stub!(:redis).and_return(@yRedisMock)
59
+ end
60
+
61
+ it "should save string as is" do
62
+ @xRedisMock.should_receive(:[]=).with('test_type:1:foo_string', 'xxx')
63
+ @yRedisMock.should_receive(:[]).with('test_type:1:foo_string').and_return('xxx')
64
+ @x.foo_string = 'xxx'
65
+ @y.foo_string.should be_instance_of(String)
66
+ end
67
+
68
+ it "should marshal integer fields" do
69
+ @xRedisMock.should_receive(:[]=).with('test_type:1:foo_int', '12')
70
+ @yRedisMock.should_receive(:[]).with('test_type:1:foo_int').and_return('12')
71
+ @x.foo_int = 12
72
+ @y.foo_int.should be_kind_of(Integer)
73
+ @y.foo_int.should == 12
74
+ end
75
+
76
+ it "should marshal float fields" do
77
+ @xRedisMock.should_receive(:[]=).with('test_type:1:foo_float', '12.1')
78
+ @yRedisMock.should_receive(:[]).with('test_type:1:foo_float').and_return('12.1')
79
+ @x.foo_float = 12.1
80
+ @y.foo_float.should be_kind_of(Float)
81
+ @y.foo_float.should == 12.1
82
+ end
83
+
84
+ it "should marshal datetime fields" do
85
+ time = DateTime.now
86
+ str = time.strftime('%FT%T%z')
87
+ @xRedisMock.should_receive(:[]=).with('test_type:1:foo_date', str)
88
+ @yRedisMock.should_receive(:[]).with('test_type:1:foo_date').and_return(str)
89
+ @x.foo_date = time
90
+ @y.foo_date.should be_kind_of(DateTime)
91
+ @y.foo_date.should.to_s == time.to_s
92
+ end
93
+
94
+ it "should marshal json structs" do
95
+ data = {'foo' => 'bar', 'x' => 2}
96
+ str = JSON.dump(data)
97
+ @xRedisMock.should_receive(:[]=).with('test_type:1:foo_json', str)
98
+ @yRedisMock.should_receive(:[]).with('test_type:1:foo_json').and_return(str)
99
+ @x.foo_json = data
100
+ @y.foo_json.should be_kind_of(Hash)
101
+ @y.foo_json.should.inspect == data.inspect
102
+ end
103
+
104
+ it "should return nil for empty fields" do
105
+ @xRedisMock.should_receive(:[]).with('test_type:1:foo_date').and_return(nil)
106
+ @x.foo_date.should be_nil
107
+ end
108
+
109
+ it "should marshal list values" do
110
+ data = DateTime.now
111
+ str = data.strftime('%FT%T%z')
112
+
113
+ @xRedisMock.should_receive('rpush').with('test_type:1:list_date', str)
114
+ @xRedisMock.should_receive('lset').with('test_type:1:list_date', 1, str)
115
+ @xRedisMock.should_receive('exists').with('test_type:1:list_date', str)
116
+ @xRedisMock.should_receive('lrem').with('test_type:1:list_date', 0, str)
117
+ @xRedisMock.should_receive('lpush').with('test_type:1:list_date', str)
118
+ @xRedisMock.should_receive('lrange').with('test_type:1:list_date', 0, 1).and_return([str])
119
+ @xRedisMock.should_receive('rpop').with('test_type:1:list_date').and_return(str)
120
+ @xRedisMock.should_receive('lpop').with('test_type:1:list_date').and_return(str)
121
+ @xRedisMock.should_receive('lindex').with('test_type:1:list_date', 0).and_return(str)
122
+ @x.list_date << data
123
+ @x.list_date[1] = data
124
+ @x.list_date.include?(data)
125
+ @x.list_date.remove(0, data)
126
+ @x.list_date.push_head(data)
127
+ @x.list_date[0].should be_kind_of(DateTime)
128
+ @x.list_date[0, 1][0].should be_kind_of(DateTime)
129
+ @x.list_date.pop_tail.should be_kind_of(DateTime)
130
+ @x.list_date.pop_head.should be_kind_of(DateTime)
131
+ end
132
+
133
+ it "should marshal set values" do
134
+ data = DateTime.now
135
+ str = data.strftime('%FT%T%z')
136
+
137
+ @xRedisMock.should_receive('sadd').with('test_type:1:set_date', str)
138
+ @xRedisMock.should_receive('srem').with('test_type:1:set_date', str)
139
+ @xRedisMock.should_receive('sismember').with('test_type:1:set_date', str)
140
+ @xRedisMock.should_receive('smembers').with('test_type:1:set_date').and_return([str])
141
+ @xRedisMock.should_receive('sinter').with('test_type:1:set_date', 'x').and_return([str])
142
+ @xRedisMock.should_receive('sunion').with('test_type:1:set_date', 'x').and_return([str])
143
+ @xRedisMock.should_receive('sdiff').with('test_type:1:set_date', 'x', 'y').and_return([str])
144
+ @x.set_date << data
145
+ @x.set_date.delete(data)
146
+ @x.set_date.include?(data)
147
+ @x.set_date.members[0].should be_kind_of(DateTime)
148
+ @x.set_date.intersect('x')[0].should be_kind_of(DateTime)
149
+ @x.set_date.union('x')[0].should be_kind_of(DateTime)
150
+ @x.set_date.diff('x', 'y')[0].should be_kind_of(DateTime)
151
+ end
152
+
153
+ end
154
+
155
+ context "redis commands" do
156
+ class TestCommands < Redis::Model
157
+ field :foo
158
+ list :bar
159
+ set :sloppy
160
+ end
161
+
162
+ before(:each) do
163
+ @redisMock = Spec::Mocks::Mock.new
164
+ @x = TestCommands.with_key(1)
165
+ @x.stub!(:redis).and_return(@redisMock)
166
+ end
167
+
168
+ it "should send GET on field read" do
169
+ @redisMock.should_receive(:[]).with('test_commands:1:foo')
170
+ @x.foo
171
+ end
172
+
173
+ it "should send SET on field write" do
174
+ @redisMock.should_receive(:[]=).with('test_commands:1:foo', 'bar')
175
+ @x.foo = 'bar'
176
+ end
177
+
178
+ it "should send RPUSH on list <<" do
179
+ @redisMock.should_receive(:rpush).with('test_commands:1:bar', 'bar')
180
+ @x.bar << 'bar'
181
+ end
182
+
183
+ it "should send SADD on set <<" do
184
+ @redisMock.should_receive(:sadd).with('test_commands:1:sloppy', 'bar')
185
+ @x.sloppy << 'bar'
186
+ end
187
+ end
188
+ end
@@ -0,0 +1,4 @@
1
+ require 'rubygems'
2
+ $TESTING=true
3
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
4
+ require 'redis'
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redis-model
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Vladimir Kolesnikov
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-10-14 00:00:00 +04:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Minimal model support for redis-rb. Directly maps ruby properties to model_name:id:field_name keys in redis. Scalar, list and set properties are supported.
17
+ email: voloko@gmail.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.markdown
24
+ files:
25
+ - .gitignore
26
+ - README.markdown
27
+ - Rakefile
28
+ - VERSION
29
+ - bench.rb
30
+ - examples/model.rb
31
+ - lib/redis/model.rb
32
+ - spec/redis/model_spec.rb
33
+ - spec/spec_helper.rb
34
+ has_rdoc: true
35
+ homepage: http://github.com/voloko/redis-model
36
+ licenses: []
37
+
38
+ post_install_message:
39
+ rdoc_options:
40
+ - --charset=UTF-8
41
+ require_paths:
42
+ - lib
43
+ required_ruby_version: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: "0"
48
+ version:
49
+ required_rubygems_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: "0"
54
+ version:
55
+ requirements: []
56
+
57
+ rubyforge_project:
58
+ rubygems_version: 1.3.4
59
+ signing_key:
60
+ specification_version: 3
61
+ summary: Minimal models for Redis
62
+ test_files:
63
+ - spec/redis/model_spec.rb
64
+ - spec/spec_helper.rb
65
+ - examples/model.rb