redis-objects 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,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
+