redis-model 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
+ *~
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