redis-objects 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,9 @@
1
+ # This is the class loader, for use as "include Redis::Objects::Sets"
2
+ # For the object itself, see "Redis::Set"
3
+ require 'redis/set'
4
+ class Redis
5
+ module Objects
6
+ module Sets
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,37 @@
1
+ # This is the class loader, for use as "include Redis::Objects::Values"
2
+ # For the object itself, see "Redis::Value"
3
+ require 'redis/value'
4
+ class Redis
5
+ module Objects
6
+ module Values
7
+ def self.included(klass)
8
+ klass.instance_variable_set('@values', {})
9
+ klass.send :include, InstanceMethods
10
+ klass.extend ClassMethods
11
+ end
12
+
13
+ # Class methods that appear in your class when you include Redis::Objects.
14
+ module ClassMethods
15
+ attr_reader :values
16
+
17
+ # Define a new simple value. It will function like a regular instance
18
+ # method, so it can be used alongside ActiveRecord, DataMapper, etc.
19
+ def value(name, options={})
20
+ @values[name] = options
21
+ class_eval <<-EndMethods
22
+ def #{name}
23
+ @#{name} ||= Redis::Value.new(field_key(:#{name}), self.class.values[:#{name}].merge(:redis => redis))
24
+ end
25
+ def #{name}=(value)
26
+ #{name}.value = value
27
+ end
28
+ EndMethods
29
+ end
30
+ end
31
+
32
+ # Instance methods that appear in your class when you include Redis::Objects.
33
+ module InstanceMethods
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,7 @@
1
+ class Redis
2
+ #
3
+ # Class representing a set.
4
+ #
5
+ class Set
6
+ end
7
+ end
@@ -0,0 +1,39 @@
1
+ class Redis
2
+ #
3
+ # Class representing a simple value. You can use standard Ruby operations on it.
4
+ #
5
+ class Value
6
+ attr_reader :key, :options, :redis
7
+ def initialize(key, options={})
8
+ @key = key
9
+ @options = options
10
+ @redis = options[:redis] || $redis || Redis::Objects.redis
11
+ @redis.setnx(key, @options[:default]) if @options[:default]
12
+ end
13
+
14
+ def value
15
+ @value ||= get
16
+ end
17
+
18
+ def value=(val)
19
+ redis.set(key, val)
20
+ @value = val
21
+ end
22
+
23
+ def get
24
+ @value = redis.get(key)
25
+ end
26
+
27
+ def delete
28
+ redis.del(key)
29
+ @value = nil
30
+ end
31
+ alias_method :del, :delete
32
+
33
+ def to_s; value.to_s; end
34
+ alias_method :to_str, :to_s
35
+
36
+ def ==(x); value == x; end
37
+ def nil?; value.nil?; end
38
+ end
39
+ end
@@ -0,0 +1,278 @@
1
+
2
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
3
+
4
+ class Roster
5
+ include Redis::Objects
6
+ counter :available_slots, :start => 10
7
+ counter :pitchers, :limit => :max_pitchers
8
+ counter :basic
9
+ counter :all_players_online, :global => true
10
+ lock :resort, :timeout => 2
11
+ value :starting_pitcher
12
+ list :player_stats
13
+
14
+ def initialize(id=1) @id = id end
15
+ def id; @id; end
16
+ def max_pitchers; 3; end
17
+ end
18
+
19
+ describe Redis::Objects do
20
+ before :all do
21
+ @roster = Roster.new
22
+ @roster2 = Roster.new
23
+ end
24
+
25
+ before :each do
26
+ @roster.available_slots.reset
27
+ @roster.pitchers.reset
28
+ @roster.basic.reset
29
+ @roster.resort_lock.clear
30
+ @roster.starting_pitcher.delete
31
+ @roster.player_stats.clear
32
+ end
33
+
34
+ it "should provide a connection method" do
35
+ Roster.redis.should == Redis::Objects.redis
36
+ Roster.redis.should be_kind_of(Redis)
37
+ end
38
+
39
+ it "should create counter accessors" do
40
+ [:available_slots, :pitchers, :basic].each do |m|
41
+ @roster.respond_to?(m).should == true
42
+ end
43
+ end
44
+
45
+ it "should support increment/decrement of counters" do
46
+ @roster.available_slots.key.should == 'roster:1:available_slots'
47
+ @roster.available_slots.should == 10
48
+
49
+ # math proxy ops
50
+ (@roster.available_slots == 10).should be_true
51
+ (@roster.available_slots <= 10).should be_true
52
+ (@roster.available_slots < 11).should be_true
53
+ (@roster.available_slots > 9).should be_true
54
+ (@roster.available_slots >= 10).should be_true
55
+ "#{@roster.available_slots}".should == "10"
56
+
57
+ @roster.available_slots.increment.should == 11
58
+ @roster.available_slots.increment.should == 12
59
+ @roster2.available_slots.increment.should == 13
60
+ @roster2.available_slots.increment(2).should == 15
61
+ @roster.available_slots.decrement.should == 14
62
+ @roster2.available_slots.decrement.should == 13
63
+ @roster.available_slots.decrement.should == 12
64
+ @roster2.available_slots.decrement(4).should == 8
65
+ @roster.available_slots.should == 12
66
+ @roster.available_slots.get.should == 8
67
+ @roster.available_slots.reset.should == 10
68
+ @roster.available_slots.should == 10
69
+ @roster.available_slots.reset(15).should == 15
70
+ @roster.available_slots.should == 15
71
+ @roster.pitchers.increment.should == 1
72
+ @roster.basic.increment.should == 1
73
+ @roster2.basic.decrement.should == 0
74
+ @roster.basic.get.should == 0
75
+ end
76
+
77
+ it "should support class-level increment/decrement of counters" do
78
+ Roster.get_counter(:available_slots, @roster.id).should == 10
79
+ Roster.increment_counter(:available_slots, @roster.id).should == 11
80
+ Roster.increment_counter(:available_slots, @roster.id, 3).should == 14
81
+ Roster.decrement_counter(:available_slots, @roster.id, 2).should == 12
82
+ Roster.decrement_counter(:available_slots, @roster.id).should == 11
83
+ Roster.reset_counter(:available_slots, @roster.id).should == true
84
+ Roster.get_counter(:available_slots, @roster.id).should == 10
85
+ end
86
+
87
+ it "should take an atomic block for increment/decrement" do
88
+ a = false
89
+ @roster.available_slots.should == 10
90
+ @roster.available_slots.decr do |cnt|
91
+ if cnt >= 0
92
+ a = true
93
+ end
94
+ end
95
+ @roster.available_slots.should == 9
96
+ a.should be_true
97
+
98
+ @roster.available_slots.should == 9
99
+ @roster.available_slots.decr do |cnt|
100
+ @roster.available_slots.should == 8
101
+ false
102
+ end
103
+ @roster.available_slots.should == 8
104
+
105
+ @roster.available_slots.should == 8
106
+ @roster.available_slots.decr do |cnt|
107
+ @roster.available_slots.should == 7
108
+ nil # should rewind
109
+ end
110
+ @roster.available_slots.should == 8
111
+
112
+ @roster.available_slots.should == 8
113
+ @roster.available_slots.incr do |cnt|
114
+ if 1 == 2 # should rewind
115
+ true
116
+ end
117
+ end
118
+ @roster.available_slots.should == 8
119
+
120
+ @roster.available_slots.should == 8
121
+ @roster.available_slots.incr do |cnt|
122
+ @roster.available_slots.should == 9
123
+ []
124
+ end
125
+ @roster.available_slots.should == 9
126
+
127
+ @roster.available_slots.should == 9
128
+ begin
129
+ @roster.available_slots.decr do |cnt|
130
+ @roster.available_slots.should == 8
131
+ raise 'oops'
132
+ end
133
+ rescue
134
+ end
135
+ @roster.available_slots.should == 9
136
+
137
+ # check return value from the block
138
+ value =
139
+ @roster.available_slots.decr do |cnt|
140
+ @roster.available_slots.should == 8
141
+ 42
142
+ end
143
+ value.should == 42
144
+ @roster.available_slots.should == 8
145
+ end
146
+
147
+ it "should take an atomic block for increment/decrement class methods" do
148
+ a = false
149
+ Roster.get_counter(:available_slots, @roster.id).should == 10
150
+ Roster.decrement_counter(:available_slots, @roster.id) do |cnt|
151
+ if cnt >= 0
152
+ a = true
153
+ end
154
+ end
155
+ Roster.get_counter(:available_slots, @roster.id).should == 9
156
+ a.should be_true
157
+
158
+ Roster.get_counter(:available_slots, @roster.id).should == 9
159
+ Roster.decrement_counter(:available_slots, @roster.id) do |cnt|
160
+ Roster.get_counter(:available_slots, @roster.id).should == 8
161
+ false
162
+ end
163
+ Roster.get_counter(:available_slots, @roster.id).should == 8
164
+
165
+ Roster.get_counter(:available_slots, @roster.id).should == 8
166
+ Roster.decrement_counter(:available_slots, @roster.id) do |cnt|
167
+ Roster.get_counter(:available_slots, @roster.id).should == 7
168
+ nil # should rewind
169
+ end
170
+ Roster.get_counter(:available_slots, @roster.id).should == 8
171
+
172
+ Roster.get_counter(:available_slots, @roster.id).should == 8
173
+ Roster.increment_counter(:available_slots, @roster.id) do |cnt|
174
+ if 1 == 2 # should rewind
175
+ true
176
+ end
177
+ end
178
+ Roster.get_counter(:available_slots, @roster.id).should == 8
179
+
180
+ Roster.get_counter(:available_slots, @roster.id).should == 8
181
+ Roster.increment_counter(:available_slots, @roster.id) do |cnt|
182
+ Roster.get_counter(:available_slots, @roster.id).should == 9
183
+ []
184
+ end
185
+ Roster.get_counter(:available_slots, @roster.id).should == 9
186
+
187
+ Roster.get_counter(:available_slots, @roster.id).should == 9
188
+ begin
189
+ Roster.decrement_counter(:available_slots, @roster.id) do |cnt|
190
+ Roster.get_counter(:available_slots, @roster.id).should == 8
191
+ raise 'oops'
192
+ end
193
+ rescue
194
+ end
195
+ Roster.get_counter(:available_slots, @roster.id).should == 9
196
+
197
+ # check return value from the block
198
+ value =
199
+ Roster.decrement_counter(:available_slots, @roster.id) do |cnt|
200
+ Roster.get_counter(:available_slots, @roster.id).should == 8
201
+ 42
202
+ end
203
+ value.should == 42
204
+ Roster.get_counter(:available_slots, @roster.id).should == 8
205
+ end
206
+
207
+ it "should properly throw errors on bad counters" do
208
+ error = nil
209
+ begin
210
+ Roster.increment_counter(:badness, 2)
211
+ rescue => error
212
+ end
213
+ error.should be_kind_of(Redis::Objects::UndefinedCounter)
214
+
215
+ error = nil
216
+ begin
217
+ Roster.obtain_lock(:badness, 2){}
218
+ rescue => error
219
+ end
220
+ error.should be_kind_of(Redis::Objects::UndefinedLock)
221
+
222
+ error = nil
223
+ begin
224
+ @roster.available_slots = 42
225
+ rescue => error
226
+ end
227
+ error.should be_kind_of(NoMethodError)
228
+
229
+ error = nil
230
+ begin
231
+ @roster.available_slots += 69
232
+ rescue => error
233
+ end
234
+ error.should be_kind_of(NoMethodError)
235
+
236
+ error = nil
237
+ begin
238
+ @roster.available_slots -= 15
239
+ rescue => error
240
+ end
241
+ error.should be_kind_of(NoMethodError)
242
+ end
243
+
244
+ it "should handle simple values" do
245
+ @roster.starting_pitcher.should == nil
246
+ @roster.starting_pitcher = 'Trevor Hoffman'
247
+ @roster.starting_pitcher.should == 'Trevor Hoffman'
248
+ @roster.starting_pitcher.get.should == 'Trevor Hoffman'
249
+ @roster.starting_pitcher.del
250
+ @roster.starting_pitcher.should be_nil
251
+ end
252
+
253
+ it "should handle lists of simple values" do
254
+ @roster.player_stats.should be_empty
255
+ @roster.player_stats << 'a'
256
+ @roster.player_stats.should == ['a']
257
+ end
258
+
259
+ it "should provide a lock method that accepts a block" do
260
+ @roster.resort_lock.key.should == 'roster:1:resort_lock'
261
+ a = false
262
+ @roster.resort_lock.lock do
263
+ a = true
264
+ end
265
+ a.should be_true
266
+ end
267
+
268
+ xit "should raise an exception if the timeout is exceeded" do
269
+ @roster.redis.set(@roster.resort_lock.key, 1)
270
+ error = nil
271
+ begin
272
+ @roster.resort_lock.lock {}
273
+ rescue => error
274
+ end
275
+ error.should_not be_nil
276
+ error.should be_kind_of(Redis::Lock::LockTimeout)
277
+ end
278
+ end
@@ -0,0 +1,5 @@
1
+ $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
2
+ require 'redis'
3
+ require 'redis/objects'
4
+
5
+ Redis::Objects.redis = Redis.new(:host => ENV['REDIS_HOST'], :port => ENV['REDIS_PORT'])
metadata ADDED
@@ -0,0 +1,73 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: redis-objects
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nate Wiger
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-11-24 00:00:00 -08:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Redis::Objects is a lightweight library that lets you use Redis primitives as Ruby objects from any class or ORM
17
+ email: nate@wiger.org
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - ATOMICITY.rdoc
24
+ - README.rdoc
25
+ files:
26
+ - lib/redis/counter.rb
27
+ - lib/redis/data_types.rb
28
+ - lib/redis/list.rb
29
+ - lib/redis/lock.rb
30
+ - lib/redis/objects/core.rb
31
+ - lib/redis/objects/counters.rb
32
+ - lib/redis/objects/lists.rb
33
+ - lib/redis/objects/locks.rb
34
+ - lib/redis/objects/sets.rb
35
+ - lib/redis/objects/values.rb
36
+ - lib/redis/objects.rb
37
+ - lib/redis/set.rb
38
+ - lib/redis/value.rb
39
+ - spec/redis_objects_model_spec.rb
40
+ - spec/spec_helper.rb
41
+ - ATOMICITY.rdoc
42
+ - README.rdoc
43
+ has_rdoc: true
44
+ homepage: http://github.com/nateware/redis-objects
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options:
49
+ - --title
50
+ - Redis::Objects -- Use Redis types as Ruby objects
51
+ require_paths:
52
+ - lib
53
+ required_ruby_version: !ruby/object:Gem::Requirement
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ version: "0"
58
+ version:
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: "0"
64
+ version:
65
+ requirements: []
66
+
67
+ rubyforge_project: redis-objects
68
+ rubygems_version: 1.3.5
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: Lightweight map of Redis types to Ruby objects
72
+ test_files: []
73
+