activeredis 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp/*
18
+ ~
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in alc-cmd.gemspec
4
+ gemspec
data/README.markdown ADDED
@@ -0,0 +1,68 @@
1
+ # ActiveRedis
2
+
3
+ ActiveModel based object persisting library for [Redis](http://code.google.com/p/redis) key-value database.
4
+
5
+ ## Features
6
+
7
+ * ActiveModel compatibility
8
+ * Race condition free operations
9
+
10
+ ## Missing features
11
+
12
+ A lot. ActiveRedis is currently designed to handle concurrency issues. Use [OHM](http://ohm.keyvalue.org/) or [remodel](http://github.com/tlossen/remodel) if you need advanced features and can accept some potential concurrency issues.
13
+
14
+ * Indexes
15
+ * Relations
16
+ * Other cool stuff
17
+
18
+ ## How to start
19
+
20
+ 1. Clone the repository
21
+ 3. Install gems
22
+
23
+ bundle install
24
+
25
+ 4. Run spec
26
+
27
+ rspec spec/core_spec.rb
28
+
29
+ ## Sample Code
30
+
31
+ require 'active_redis'
32
+ require 'pp'
33
+
34
+ class User < ActiveRedis::Base
35
+ fields :name, :age, :country
36
+ end
37
+
38
+ create user(1)
39
+ user = User.new :age => 22, :name => "Joe"
40
+ user.save
41
+
42
+ # create user(2)
43
+ user = User.new
44
+ user.age = 12
45
+ user.name = "Tom"
46
+ user.country = "japan"
47
+ user.save
48
+
49
+ puts "count: #{User.count}" #=> 2
50
+ pp joe = User.find_by_name("Joe") #=>#<User:0x0000010193e930 @attributes={"age"=>"22", "name"=>"Joe"}, @id="1">
51
+ pp tom = User.find_by_age(12) #=>#<User:0x00000101910c60 @attributes={"age"=>"12", "name"=>"Tom", "country"=>"japan"}, @id="2">
52
+
53
+ joe.destroy
54
+ puts "count: #{User.count}" #=> 1
55
+
56
+ tom.destroy
57
+ puts "count: #{User.count}" #=> 0
58
+
59
+ pp User.find_by_name("Tom") #=> nil
60
+
61
+ User.delete_all
62
+
63
+
64
+ ## Contributing
65
+
66
+ Pull requests are welcome!
67
+
68
+ For any questions contact [yamadakazu45@gmail.com](mailto:yamadakazu45@gmail.com)
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,4 @@
1
+ class Cat < ActiveRedis::Base
2
+
3
+ end
4
+
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'activeredis/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "activeredis"
8
+ gem.version = ActiveRedis::VERSION
9
+ gem.authors = ["Kazuhiro Yamada", "Yuta Hirakawa"]
10
+ gem.email = ["sonixlabs@sonix.asia", "kyamada@sonix.asia"]
11
+ gem.description = %q{ActiveModel based object persistance library for Redis}
12
+ gem.summary = %q{ActiveModel based object persisting library for Redis key-value database.}
13
+ gem.homepage = "https://github.com/sonixlabs/activeredis"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_dependency 'redis'
21
+ gem.add_dependency 'activemodel'
22
+ gem.add_development_dependency 'rspec'
23
+ end
data/benchmark.rb ADDED
@@ -0,0 +1,61 @@
1
+ $LOAD_PATH << File.dirname(__FILE__)
2
+ require 'benchmark'
3
+ require 'lib/active_redis'
4
+
5
+ class Post < ActiveRedis::Base
6
+
7
+ end
8
+
9
+
10
+ LOREM = "Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
11
+
12
+ # Returns 0 if first round, otherwise return offset which is the sum
13
+ # of previous rounds.
14
+ def calculate_offset(index, rounds)
15
+ return (index == 0) ? 0 : rounds[0..index-1].inject{|sum,x| sum +x}
16
+ end
17
+
18
+ Benchmark.bm(26) do |x|
19
+
20
+ rounds = [1000,5000,10000]
21
+
22
+ rounds.each_with_index do |howmany, index|
23
+
24
+ x.report("Create #{howmany} posts: ") do
25
+ howmany.times do |t|
26
+ p = Post.new({ :topic => "a"*(8+rand(55)),
27
+ :author => "a"*(8+rand(55)),
28
+ :content => LOREM*(8+rand(55))})
29
+ p.save
30
+ end
31
+ end
32
+
33
+ x.report("Count posts: ") do
34
+ Post.count
35
+ end
36
+
37
+ x.report("Find posts: ") do
38
+ howmany.times do |t|
39
+ p = Post.find(calculate_offset(index, rounds)+t+1)
40
+ end
41
+ end
42
+
43
+ x.report("Update (find+save) posts: ") do
44
+ howmany.times do |t|
45
+ p = Post.find(calculate_offset(index, rounds)+t+1)
46
+ p.save
47
+ end
48
+ end
49
+
50
+ x.report("Delete posts: ") do
51
+ howmany.times do |t|
52
+ p = Post.find(calculate_offset(index, rounds)+t+1)
53
+ p.destroy
54
+ end
55
+ end
56
+
57
+ puts
58
+ end
59
+
60
+ end
61
+
@@ -0,0 +1,19 @@
1
+ user system total real
2
+ Create 1000 posts: 0.350000 0.110000 0.460000 ( 0.708575)
3
+ Count posts: 0.000000 0.000000 0.000000 ( 0.000095)
4
+ Find posts: 0.180000 0.040000 0.220000 ( 0.301364)
5
+ Update (find+save) posts: 0.420000 0.140000 0.560000 ( 0.845421)
6
+ Delete posts: 0.310000 0.080000 0.390000 ( 0.479831)
7
+
8
+ Create 5000 posts: 1.760000 0.560000 2.320000 ( 3.553861)
9
+ Count posts: 0.000000 0.000000 0.000000 ( 0.000091)
10
+ Find posts: 0.900000 0.210000 1.110000 ( 1.509994)
11
+ Update (find+save) posts: 2.140000 0.680000 2.820000 ( 4.263500)
12
+ Delete posts: 1.490000 0.390000 1.880000 ( 2.368327)
13
+
14
+ Create 10000 posts: 3.500000 1.100000 4.600000 ( 7.094810)
15
+ Count posts: 0.000000 0.000000 0.000000 ( 0.000092)
16
+ Find posts: 1.840000 0.420000 2.260000 ( 3.062314)
17
+ Update (find+save) posts: 4.250000 1.350000 5.600000 ( 8.498068)
18
+ Delete posts: 3.040000 0.790000 3.830000 ( 4.838310)
19
+
@@ -0,0 +1,3 @@
1
+ module ActiveRedis
2
+ VERSION = "0.0.3"
3
+ end
@@ -0,0 +1,325 @@
1
+ require 'redis'
2
+ require 'time'
3
+ require 'active_model'
4
+
5
+ require 'active_model/version'
6
+
7
+ class Module
8
+ def module_attr_reader(*syms)
9
+ syms.each do |sym|
10
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
11
+ @@#{sym} = nil unless defined?(@@#{sym})
12
+ def self.#{sym}()
13
+ return @@#{sym}
14
+ end
15
+ EOS
16
+ end
17
+ end
18
+ def module_attr_writer(*syms)
19
+ syms.each do |sym|
20
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
21
+ def self.#{sym}=(obj)
22
+ @@#{sym} = obj
23
+ end
24
+ EOS
25
+ end
26
+ end
27
+ def module_attr_accessor(*syms)
28
+ module_attr_reader(*syms)
29
+ module_attr_writer(*syms)
30
+ end
31
+ end
32
+
33
+ module ActiveRedis
34
+ module_attr_accessor :host, :port
35
+ @@host = "localhost"
36
+ @@port = "6379"
37
+
38
+ class ActiveRedisError < StandardError
39
+ end
40
+
41
+ # Raised when Active Redis cannot find record by given id or set of ids.
42
+ class RecordNotFound < ActiveRedisError
43
+ end
44
+
45
+ class Base
46
+ include ActiveModel::Validations
47
+ include ActiveModel::Dirty
48
+ include ActiveModel::Serialization
49
+ include ActiveModel::Serializers::JSON
50
+ include ActiveModel::Naming
51
+
52
+ QUEUED = "QUEUED"
53
+
54
+ # RAILSISM
55
+ # Returns a hash of all the attributes with their names as keys and the values of the attributes as values
56
+ # --> means: Strings as keys!
57
+ # --> initialize stringifies
58
+ #
59
+ # called by to_json for_example
60
+ attr_reader :attributes
61
+ attr_reader :id
62
+ attr :frozen
63
+
64
+ class << self; attr_accessor :_fields; end
65
+
66
+ # INSTANCE METHODS
67
+
68
+ def initialize(attributes = {}, id = nil)
69
+ @id = id if id
70
+ @attributes = {}
71
+ initialize_attributes(attributes)
72
+ frozen = false
73
+ end
74
+
75
+ # Object's attributes' keys are converted to strings because of railsisms.
76
+ # Because of activeredisism, also values are converted to strings for consistency.
77
+ def initialize_attributes(attributes)
78
+ fields = Hash[[self.class._fields, Array.new(self.class._fields.size)].transpose]
79
+ fields.stringify_keys! # NEEDS to be strings for railsisms
80
+ attributes.stringify_keys! # NEEDS to be strings for railsisms
81
+ attributes.each_pair { |key, value| attributes[key] = value.to_s }
82
+ fields.merge!(attributes)
83
+ @attributes.merge!(fields)
84
+ @attributes.each_pair do |key, value|
85
+ self.class.define_field key
86
+ end
87
+ end
88
+
89
+ def save
90
+ creation = new_record?
91
+ @id = self.class.fetch_new_identifier if creation
92
+ while true
93
+ begin
94
+ connection.multi
95
+ rescue
96
+ sleep(0.1)
97
+ redo
98
+ end
99
+ break
100
+ end
101
+ if @attributes.size > 0
102
+ @attributes.each_pair { |key, value|
103
+ if key.to_sym == :updated_at
104
+ value = Time.now.to_s
105
+ end
106
+ connection.hset("#{key_namespace}:attributes", key, value)
107
+ }
108
+ end
109
+ connection.zadd("#{class_namespace}:all", @id, @id)
110
+ connection.exec
111
+ return true
112
+ end
113
+
114
+ def update_attributes(attributes)
115
+ attributes.stringify_keys! # NEEDS to be strings for railsisms
116
+ attributes.each_pair { |key, value| attributes[key] = value.to_s }
117
+ @attributes.merge!(attributes)
118
+ attributes.each_pair do |key, value|
119
+ self.class.define_field key
120
+ end
121
+ save
122
+ end
123
+
124
+ def reload
125
+ @attributes = connection.hgetall "#{key_namespace}:attributes"
126
+ end
127
+
128
+ def new_record?
129
+ @id == nil
130
+ end
131
+
132
+ def key_namespace
133
+ "#{self.class.key_namespace}:#{self.id}"
134
+ end
135
+
136
+ def class_namespace
137
+ "#{self.class.key_namespace}"
138
+ end
139
+
140
+ def connection
141
+ self.class.connection
142
+ end
143
+
144
+ def destroy
145
+ connection.multi do
146
+ connection.del "#{key_namespace}:attributes"
147
+ connection.zrem "#{class_namespace}:all", @id
148
+ @frozen = true
149
+ end
150
+ return true
151
+ end
152
+
153
+ def frozen?
154
+ @frozen
155
+ end
156
+
157
+ def add_attribute(name, value=nil)
158
+ initialize_attributes({name => value})
159
+ end
160
+
161
+ def [](field)
162
+ send(field)
163
+ end
164
+
165
+ def []=(field, value)
166
+ send(field+"=", value)
167
+ end
168
+
169
+ #
170
+ # CLASS METHODS
171
+ #
172
+
173
+ def self.create(attributes)
174
+ self.new(attributes).save
175
+ end
176
+
177
+ def self.define_field(field)
178
+ define_method field.to_sym do
179
+ if field.to_sym == :updated_at
180
+ Time.parse(@attributes["#{field}"])
181
+ else
182
+ @attributes["#{field}"]
183
+ end
184
+ end
185
+
186
+ define_method "#{field}=".to_sym do |new_value|
187
+ @attributes["#{field}"] = new_value.to_s
188
+ end
189
+ end
190
+
191
+ # Run this method to declare the fields of your model.
192
+ def self.fields(*fields)
193
+ self._fields ||= []
194
+ self._fields = fields
195
+ end
196
+
197
+ def self.get_fields
198
+ self._fields
199
+ end
200
+
201
+ def self.key_namespace
202
+ "#{self}"
203
+ end
204
+
205
+ def self.fetch_new_identifier
206
+ self.connection.incr self.identifier_sequencer
207
+ end
208
+
209
+ def self.identifier_sequencer
210
+ "#{key_namespace}:sequence"
211
+ end
212
+
213
+ def self.inherited(child)
214
+ #puts "Redis.new(:host => #{ActiveRedis.host}, :port => #{ActiveRedis.port})"
215
+ @@redis = Redis.new(:host => ActiveRedis.host, :port => ActiveRedis.port)
216
+ @@class = child
217
+ end
218
+
219
+ def self.redis_information
220
+ connection.info # call_command [:info]
221
+ end
222
+
223
+ def self.connection
224
+ @@redis
225
+ end
226
+
227
+ def self.count
228
+ begin
229
+ size = connection.zcard "#{key_namespace}:all"
230
+ while size == QUEUED
231
+ sleep(0.1)
232
+ size = connection.zcard "#{key_namespace}:all"
233
+ end
234
+ return size
235
+ rescue RuntimeError => e
236
+ return 0
237
+ end
238
+ end
239
+
240
+ def self.find_all()
241
+ record = []
242
+ # TODO Interim fix, "QUEUED" is find(id) rescue
243
+ while true
244
+ ids = connection.zrange "#{key_namespace}:all", 0, count
245
+ while ids == QUEUED
246
+ sleep(0.1)
247
+ ids = connection.zrange "#{key_namespace}:all", 0, count
248
+ end
249
+ begin
250
+ ids.each do |id|
251
+ record << find(id)
252
+ end
253
+ rescue
254
+ redo
255
+ end
256
+ break
257
+ end
258
+ record
259
+ end
260
+
261
+ def self.all
262
+ find_all
263
+ end
264
+
265
+ def self.delete_all
266
+ records = find_all
267
+ records.each do |record|
268
+ record.destroy
269
+ end
270
+ end
271
+
272
+ def self.find(id)
273
+ return find_all if id == :all
274
+ exists = connection.zscore "#{key_namespace}:all", id
275
+ raise RecordNotFound.new("Couldn't find #{self.name} with ID=#{id}") unless exists
276
+ attributes = connection.hgetall "#{key_namespace}:#{id}:attributes"
277
+ obj = self.new attributes, id
278
+ return obj
279
+ end
280
+
281
+ def self.find_all_by_param(field, value)
282
+ finded = []
283
+ records = find_all
284
+ records.each do |record|
285
+ if record.attributes[field.to_s] == value.to_s
286
+ finded << record
287
+ end
288
+ end
289
+ return finded
290
+ end
291
+
292
+ def self.find_by_param(field, value)
293
+ records = find_all_by_param(field, value)
294
+ if records.size > 0
295
+ return records[0]
296
+ else
297
+ nil
298
+ end
299
+ end
300
+
301
+ def self.method_missing(name, *args)
302
+ return find_by_param($1.to_sym, args[0]) if name.to_s =~ /^find_by_(.*)/
303
+ return find_all_by_param($1.to_sym, args[0]) if name.to_s =~ /^find_all_by_(.*)/
304
+ super
305
+ end
306
+
307
+ def self.delete_unused_field
308
+ ids = connection.zrange "#{key_namespace}:all", 0, count
309
+ if ids.size > 0
310
+ attributes = connection.hgetall "#{key_namespace}:#{ids[0]}:attributes"
311
+ now_keys = self.get_fields
312
+ array = []
313
+ now_keys.each do |key|
314
+ array << key.to_s
315
+ end
316
+ attributes.reject! {|key| array.include? key }
317
+ attributes.keys.each do |delete_key|
318
+ ids.each do |id|
319
+ connection.hdel "#{key_namespace}:#{id}:attributes", delete_key
320
+ end
321
+ end
322
+ end
323
+ end
324
+ end
325
+ end
data/sample/user.rb ADDED
@@ -0,0 +1,38 @@
1
+ require 'active_redis'
2
+ require 'pp'
3
+
4
+ # ActiveRedis.host = "localhost"
5
+ # ActiveRedis.port = "6379"
6
+ class User < ActiveRedis::Base
7
+ fields "name", "age", "country", "updated_at"
8
+ end
9
+
10
+ # create user(1)
11
+ user = User.new :age => 22, :name => "Joe"
12
+ user.save
13
+
14
+ # create user(2)
15
+ user = User.new
16
+ user.age = 12
17
+ user.name = "Tom"
18
+ user.country = "japan"
19
+ user.save
20
+
21
+ puts "count: #{User.count}" #=> 2
22
+ pp joe = User.find_by_name("Joe") #=>#<User:0x0000010193e930 @attributes={"age"=>"22", "name"=>"Joe"}, @id="1">
23
+ pp tom = User.find_by_name("Tom") #=>#<User:0x00000101910c60 @attributes={"age"=>"12", "name"=>"Tom", "country"=>"japan"}, @id="2">
24
+
25
+ tom.update_attributes({:age => 20})
26
+ #tom = User.find_by_age(20)
27
+ tom.reload
28
+ pp tom
29
+
30
+ joe.destroy
31
+ puts "count: #{User.count}" #=> 1
32
+
33
+ User.delete_all
34
+ puts "count: #{User.count}" #=> 0
35
+
36
+ pp User.find_by_name("Tom") #=> nil
37
+
38
+
@@ -0,0 +1,264 @@
1
+ require 'spec_helper'
2
+
3
+ class Cat < ActiveRedis::Base
4
+ fields :name, :age
5
+ end
6
+
7
+ class Dog < ActiveRedis::Base
8
+ fields :category, :name, :color
9
+ end
10
+
11
+ describe Cat do
12
+
13
+ before(:each) do
14
+ ActiveRedis::Base.connection.flushall
15
+ @cat = Cat.new(:name => "lol")
16
+ @dog = Dog.new(:category => "Dachshund")
17
+ end
18
+
19
+ it "should be a rektive model" do
20
+ Cat.ancestors.should include(ActiveRedis::Base)
21
+ end
22
+
23
+ it "should give out info on the redis server" do
24
+ Cat.redis_information.should be_an_instance_of(Hash)
25
+ end
26
+
27
+ describe "counting" do
28
+ it "should know count of its objects when empty" do
29
+ Cat.count.should == 0
30
+ end
31
+
32
+ it "should know count of its objects" do
33
+ @cat.save
34
+ Cat.count.should == 1
35
+ end
36
+ end
37
+
38
+ describe "attributes" do
39
+
40
+ it "should have attributes" do
41
+ @cat.attributes.should include("name")
42
+ end
43
+
44
+ it "should have accessors for all attributes" do
45
+ @cat.name.should == "lol"
46
+ end
47
+
48
+ it "should get fields" do
49
+ Cat.get_fields.should == [:name, :age]
50
+ end
51
+
52
+ it "should stringify all values" do
53
+ numeric_cat = Cat.new(:age => 25, :length => 5.4)
54
+ numeric_cat.age.should == "25"
55
+ numeric_cat.length.should == "5.4"
56
+ end
57
+
58
+ it "should add a new attribute to a persisted object" do
59
+ @cat.save
60
+ @cat.add_attribute(:color)
61
+ @cat.color.should == ""
62
+ end
63
+
64
+ it "should add a new attribute with empty value and persist changes" do
65
+ @cat.save
66
+ @cat.add_attribute(:color)
67
+ @cat.color.should == ""
68
+ @cat.save
69
+ Cat.find(@cat.id).color.should == ""
70
+ end
71
+
72
+ it "should add a new attribute with non-empty value and persist changes" do
73
+ @cat.save
74
+ @cat.add_attribute(:color, "black")
75
+ @cat.color.should == "black"
76
+ @cat.save
77
+ Cat.find(@cat.id).color.should == "black"
78
+ end
79
+
80
+ it "should add a new attribute with a value" do
81
+ @cat.add_attribute(:color, "black")
82
+ @cat.color.should == "black"
83
+ end
84
+
85
+ it "should add a new attribute to a non-persisted object" do
86
+ cat = Cat.new(:name => "long")
87
+ cat.add_attribute(:length, 2.5)
88
+ cat.length.should == "2.5"
89
+ end
90
+
91
+ it "should update a attribute" do
92
+ cat = Cat.new(:name => "mike", :color => "black")
93
+ cat.update_attributes({:name => "tama"})
94
+ cat.reload
95
+ cat.name.should == "tama"
96
+ cat.color.should == "black"
97
+ end
98
+
99
+ it "should convert attribute values to string when creating new" do
100
+ cat = Cat.new(:age => 2.5, :long => true, :length => 100)
101
+ cat.attributes["age"].should == "2.5"
102
+ cat.attributes["long"].should == "true"
103
+ cat.attributes["length"].should == "100"
104
+ end
105
+
106
+ it "should create persisted object" do
107
+ Cat.create(:age => 100, :long => true, :length => 120)
108
+ Cat.find_by_age(100).age.should == "100"
109
+ end
110
+
111
+ end
112
+
113
+ describe "persisting simple objects" do
114
+ before(:each) do
115
+ @cat = Cat.new(:name => "lol",
116
+ :color => "white")
117
+ end
118
+
119
+ it "should persist a simple object successfully" do
120
+ @cat.save.should be_true
121
+ end
122
+
123
+ it "should have a key to fetch the next free identifier from" do
124
+ Cat.identifier_sequencer.should == "Cat:sequence"
125
+ end
126
+
127
+ it "should reserve a unique identifier for the object" do
128
+ Cat.connection.should_receive(:incr).with(Cat.identifier_sequencer).and_return(1)
129
+
130
+ Cat.fetch_new_identifier
131
+ end
132
+
133
+ it "should save the identifier to the object" do
134
+ @cat.save
135
+
136
+ @cat.id.should_not be_nil
137
+ end
138
+
139
+ # it "should persist the attributes of the object" do
140
+ # Cat.stub!(:fetch_new_identifier).and_return(1)
141
+ # Cat.connection.stub!(:multi).and_yield
142
+ #
143
+ # Cat.connection.should_receive(:call_command).with([ "hmset",
144
+ # "#{Cat.key_namespace}:1:attributes",
145
+ # "name", "lol",
146
+ # "color", "white"]).and_return(true)
147
+ #
148
+ # Cat.connection.should_receive(:zadd).with("#{Cat.key_namespace}:all", 1, 1).and_return(true)
149
+ #
150
+ # @cat.save
151
+ # end
152
+
153
+ # it "should have atomic save operations" do
154
+ # Cat.stub!(:fetch_new_identifier)
155
+ # Cat.connection.should_receive(:multi).and_yield
156
+ # Cat.connection.should_receive(:call_command)
157
+ # Cat.connection.should_receive(:zadd)
158
+ # @cat.save
159
+ # end
160
+
161
+ it "should persist attributeless cat" do
162
+ attributeless = Cat.new
163
+ attributeless.save
164
+
165
+ attributeless.id.should_not be_nil
166
+ end
167
+
168
+
169
+
170
+ end
171
+
172
+ describe "destroy" do
173
+ it "object should remove itself" do
174
+ @cat.save
175
+
176
+ @cat.destroy.should == true
177
+ lambda {
178
+ Cat.find(@cat.id)
179
+ }.should raise_error(ActiveRedis::RecordNotFound)
180
+
181
+ end
182
+
183
+ it "should destroy as an atomic operation" do
184
+ dead_cat = Cat.new({}, 1)
185
+ Cat.connection.should_receive(:multi).and_yield
186
+ Cat.connection.should_receive(:del).with("#{Cat.key_namespace}:1:attributes").and_return(10)
187
+ Cat.connection.should_receive(:zrem).with("#{Cat.key_namespace}:all", 1).and_return(1)
188
+ dead_cat.destroy.should == true
189
+ end
190
+
191
+ end
192
+
193
+ describe "find" do
194
+
195
+ it "should raise error if record was not found" do
196
+ no_such_cat_id = 99
197
+ lambda {
198
+ Cat.find(no_such_cat_id)
199
+ }.should raise_error(ActiveRedis::RecordNotFound)
200
+
201
+ end
202
+
203
+ it "should check that object exists in all" do
204
+ @cat.save
205
+ Cat.find(1)
206
+ end
207
+
208
+ it "should find existing cat" do
209
+ attributes_to_persist = {"name" => "", "age" => "", "fur" => "long", "eyes" => "blue"}
210
+ c = Cat.new attributes_to_persist
211
+ c.save
212
+
213
+ same_cat = Cat.find(c.id)
214
+ same_cat.attributes.should == attributes_to_persist
215
+ end
216
+
217
+ it "should have id set" do
218
+ @cat.save
219
+
220
+ existing_cat = Cat.find(1)
221
+ existing_cat.id.should == 1
222
+ end
223
+
224
+ it "should find attributeless cat" do
225
+ attributeless = Cat.new
226
+ attributeless.save
227
+ Cat.find(attributeless.id).should_not be_nil
228
+ end
229
+
230
+ end
231
+
232
+ describe "update" do
233
+
234
+ it "should not create new objects when updating" do
235
+ same_cat = Cat.new
236
+ same_cat.save
237
+ id_before_save = same_cat.id
238
+
239
+ same_cat.save
240
+ same_cat.id.should == id_before_save
241
+ end
242
+
243
+ it "should update attribute with new value" do
244
+
245
+ @cat.save
246
+
247
+ new_name = "updated lolcat"
248
+ @cat.name = new_name
249
+ @cat.save
250
+ Cat.find(@cat.id).name.should == new_name
251
+
252
+ end
253
+
254
+ end
255
+
256
+
257
+ describe "ActiveModel compatibility" do
258
+
259
+ it "should respond to new_record?" do
260
+ Cat.new.new_record?.should == true
261
+ end
262
+ end
263
+
264
+ end
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'bundler/setup'
3
+
4
+ require 'activeredis' # and any other gems you need
5
+
6
+ RSpec.configure do |config|
7
+ config.before(:each) do
8
+ ActiveRedis::Base.connection.flushall
9
+ end
10
+ end
metadata ADDED
@@ -0,0 +1,97 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: activeredis
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.3
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Kazuhiro Yamada
9
+ - Yuta Hirakawa
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2012-10-09 00:00:00.000000000Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: redis
17
+ requirement: &70240177908780 !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: *70240177908780
26
+ - !ruby/object:Gem::Dependency
27
+ name: activemodel
28
+ requirement: &70240177908360 !ruby/object:Gem::Requirement
29
+ none: false
30
+ requirements:
31
+ - - ! '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: *70240177908360
37
+ - !ruby/object:Gem::Dependency
38
+ name: rspec
39
+ requirement: &70240177907940 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ! '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ type: :development
46
+ prerelease: false
47
+ version_requirements: *70240177907940
48
+ description: ActiveModel based object persistance library for Redis
49
+ email:
50
+ - sonixlabs@sonix.asia
51
+ - kyamada@sonix.asia
52
+ executables: []
53
+ extensions: []
54
+ extra_rdoc_files: []
55
+ files:
56
+ - .gitignore
57
+ - .rspec
58
+ - Gemfile
59
+ - README.markdown
60
+ - Rakefile
61
+ - activeredis-console.rb
62
+ - activeredis.gemspec
63
+ - benchmark.rb
64
+ - benchmark_results.txt
65
+ - lib/activeredis.rb
66
+ - lib/activeredis/version.rb
67
+ - sample/user.rb
68
+ - spec/active_redis_spec.rb
69
+ - spec/spec_helper.rb
70
+ homepage: https://github.com/sonixlabs/activeredis
71
+ licenses: []
72
+ post_install_message:
73
+ rdoc_options: []
74
+ require_paths:
75
+ - lib
76
+ required_ruby_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ required_rubygems_version: !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ requirements: []
89
+ rubyforge_project:
90
+ rubygems_version: 1.8.6
91
+ signing_key:
92
+ specification_version: 3
93
+ summary: ActiveModel based object persisting library for Redis key-value database.
94
+ test_files:
95
+ - spec/active_redis_spec.rb
96
+ - spec/spec_helper.rb
97
+ has_rdoc: