redis-objects 0.9.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4ddda2781d9f2c900706e383d1079321a1499153
4
- data.tar.gz: 0120673e2299b6739dd24d4a1e8aea2ee42b1d18
3
+ metadata.gz: c4689abbe0340e8c683371bac1b94b29893a7c5b
4
+ data.tar.gz: 41678b7dd50984a9b7c0c974e2a9eadf6d8a712d
5
5
  SHA512:
6
- metadata.gz: 5e677129dc40412d9a6c11585f84c4703703006d6b02b5a71e047a6bcd8fabf44a22c90073f1f730eec0025776689c75e586ceb8203a7791167f6528b177d96e
7
- data.tar.gz: d7c948a177c2dea105ce215e4792f490324331e1f9a2acefaa95ee053be71acb3c4065a3517b801125e7f4952161565bd9bf579aac31639e4857641b88cea5e0
6
+ metadata.gz: 917dad1c9f8edd6eb355dbdec952d7ade90b45b6fa7b1dd81266dff885ff09bf7d5fbf2e01a3d0ffec05e6c7dccff46d6f14b52fae790e1030a7c30d0c9da84f
7
+ data.tar.gz: e64a57836c26bcc882517f2de31fb1c9b3a95394c9794f10646fd77f244e3db454bec529e92376fb7ff802dec048ec355b3f7c254e3140022a52b32bbc7fda7b
data/CHANGELOG.rdoc CHANGED
@@ -1,6 +1,18 @@
1
1
  = Changelog for Redis::Objects
2
2
 
3
- == 0.9.1 (25 Mar 2024)
3
+ == 1.0.0 (25 Jul 2014)
4
+
5
+ * Fix expiration filter to handle atomic blocks, remove method aliasing [nateware]
6
+
7
+ * Fix incrbyfloat to actually return a float [james-lawrence]
8
+
9
+ * Allow false as default: value (bugfix) [james-lawrence]
10
+
11
+ * Allow unionstore and interstore between sorted sets and sets [jrdi]
12
+
13
+ * Add syntax highlighting to README.md [bartolsthoorn]
14
+
15
+ == 0.9.1 (25 Mar 2014)
4
16
 
5
17
  * Fix bad marshal calls in SortedSet [Fleurer, nateware]
6
18
 
data/README.md CHANGED
@@ -32,13 +32,17 @@ Installation and Setup
32
32
  ----------------------
33
33
  Add it to your Gemfile as:
34
34
 
35
- gem 'redis-objects'
35
+ ~~~ruby
36
+ gem 'redis-objects'
37
+ ~~~
36
38
 
37
39
  Redis::Objects needs a handle created by `Redis.new`. The recommended approach
38
40
  is to set `Redis.current` to point to your server, which Redis::Objects will
39
41
  pick up automatically.
40
42
 
41
- Redis.current = Redis.new(:host => '127.0.0.1', :port => 6379)
43
+ ~~~ruby
44
+ Redis.current = Redis.new(:host => '127.0.0.1', :port => 6379)
45
+ ~~~
42
46
 
43
47
  (If you're on Rails, `config/initializers/redis.rb` is a good place for this.)
44
48
  Remember you can use Redis::Objects in any Ruby code. There are **no** dependencies
@@ -46,19 +50,23 @@ on Rails. Standalone, Sinatra, Resque - no problem.
46
50
 
47
51
  Alternatively, you can set the `redis` handle directly:
48
52
 
49
- Redis::Objects.redis = Redis.new(...)
53
+ ~~~ruby
54
+ Redis::Objects.redis = Redis.new(...)
55
+ ~~~
50
56
 
51
57
  Finally, you can even set different handles for different classes:
52
58
 
53
- class User
54
- include Redis::Objects
55
- end
56
- class Post
57
- include Redis::Objects
58
- end
59
+ ~~~ruby
60
+ class User
61
+ include Redis::Objects
62
+ end
63
+ class Post
64
+ include Redis::Objects
65
+ end
59
66
 
60
- User.redis = Redis.new(:host => '1.2.3.4')
61
- Post.redis = Redis.new(:host => '5.6.7.8')
67
+ User.redis = Redis.new(:host => '1.2.3.4')
68
+ Post.redis = Redis.new(:host => '5.6.7.8')
69
+ ~~~
62
70
 
63
71
  As of `0.7.0`, `redis-objects` now autoloads the appropriate `Redis::Whatever`
64
72
  classes on demand. Previous strategies of individually requiring `redis/list`
@@ -75,95 +83,111 @@ Redis::Objects automatically creates keys that are unique to each object, in the
75
83
 
76
84
  For illustration purposes, consider this stub class:
77
85
 
78
- class User
79
- include Redis::Objects
80
- counter :my_posts
81
- def id
82
- 1
83
- end
84
- end
85
-
86
- user = User.new
87
- user.id # 1
88
- user.my_posts.increment
89
- user.my_posts.increment
90
- user.my_posts.increment
91
- puts user.my_posts # 3
92
- user.my_posts.reset
93
- puts user.my_posts.value # 0
94
- user.my_posts.reset 5
95
- puts user.my_posts.value # 5
86
+ ~~~ruby
87
+ class User
88
+ include Redis::Objects
89
+ counter :my_posts
90
+ def id
91
+ 1
92
+ end
93
+ end
94
+
95
+ user = User.new
96
+ user.id # 1
97
+ user.my_posts.increment
98
+ user.my_posts.increment
99
+ user.my_posts.increment
100
+ puts user.my_posts # 3
101
+ user.my_posts.reset
102
+ puts user.my_posts.value # 0
103
+ user.my_posts.reset 5
104
+ puts user.my_posts.value # 5
105
+ ~~~
96
106
 
97
107
  Here's an example that integrates several data types with an ActiveRecord model:
98
108
 
99
- class Team < ActiveRecord::Base
100
- include Redis::Objects
101
-
102
- lock :trade_players, :expiration => 15 # sec
103
- value :at_bat
104
- counter :hits
105
- counter :runs
106
- counter :outs
107
- counter :inning, :start => 1
108
- list :on_base
109
- list :coaches, :marshal => true
110
- set :outfielders
111
- hash_key :pitchers_faced # "hash" is taken by Ruby
112
- sorted_set :rank, :global => true
113
- end
109
+ ~~~ruby
110
+ class Team < ActiveRecord::Base
111
+ include Redis::Objects
112
+
113
+ lock :trade_players, :expiration => 15 # sec
114
+ value :at_bat
115
+ counter :hits
116
+ counter :runs
117
+ counter :outs
118
+ counter :inning, :start => 1
119
+ list :on_base
120
+ list :coaches, :marshal => true
121
+ set :outfielders
122
+ hash_key :pitchers_faced # "hash" is taken by Ruby
123
+ sorted_set :rank, :global => true
124
+ end
125
+ ~~~
114
126
 
115
127
  Familiar Ruby array operations Just Work (TM):
116
128
 
117
- @team = Team.find_by_name('New York Yankees')
118
- @team.on_base << 'player1'
119
- @team.on_base << 'player2'
120
- @team.on_base << 'player3'
121
- @team.on_base # ['player1', 'player2', 'player3']
122
- @team.on_base.pop
123
- @team.on_base.shift
124
- @team.on_base.length # 1
125
- @team.on_base.delete('player2')
129
+ ~~~ruby
130
+ @team = Team.find_by_name('New York Yankees')
131
+ @team.on_base << 'player1'
132
+ @team.on_base << 'player2'
133
+ @team.on_base << 'player3'
134
+ @team.on_base # ['player1', 'player2', 'player3']
135
+ @team.on_base.pop
136
+ @team.on_base.shift
137
+ @team.on_base.length # 1
138
+ @team.on_base.delete('player2')
139
+ ~~~
126
140
 
127
141
  Sets work too:
128
142
 
129
- @team.outfielders << 'outfielder1'
130
- @team.outfielders << 'outfielder2'
131
- @team.outfielders << 'outfielder1' # dup ignored
132
- @team.outfielders # ['outfielder1', 'outfielder2']
133
- @team.outfielders.each do |player|
134
- puts player
135
- end
136
- player = @team.outfielders.detect{|of| of == 'outfielder2'}
143
+ ~~~ruby
144
+ @team.outfielders << 'outfielder1'
145
+ @team.outfielders << 'outfielder2'
146
+ @team.outfielders << 'outfielder1' # dup ignored
147
+ @team.outfielders # ['outfielder1', 'outfielder2']
148
+ @team.outfielders.each do |player|
149
+ puts player
150
+ end
151
+ player = @team.outfielders.detect{|of| of == 'outfielder2'}
152
+ ~~~
137
153
 
138
154
  And you can do unions and intersections between objects (kinda cool):
139
155
 
140
- @team1.outfielders | @team2.outfielders # outfielders on both teams
141
- @team1.outfielders & @team2.outfielders # in baseball, should be empty :-)
156
+ ~~~ruby
157
+ @team1.outfielders | @team2.outfielders # outfielders on both teams
158
+ @team1.outfielders & @team2.outfielders # in baseball, should be empty :-)
159
+ ~~~
142
160
 
143
161
  Counters can be atomically incremented/decremented (but not assigned):
144
162
 
145
- @team.hits.increment # or incr
146
- @team.hits.decrement # or decr
147
- @team.hits.incr(3) # add 3
148
- @team.runs = 4 # exception
163
+ ~~~ruby
164
+ @team.hits.increment # or incr
165
+ @team.hits.decrement # or decr
166
+ @team.hits.incr(3) # add 3
167
+ @team.runs = 4 # exception
168
+ ~~~
149
169
 
150
170
  Defining a different method as the `id` field is easy
151
171
 
152
- class User
153
- include Redis::Objects
154
- redis_id_field :uid
155
- counter :my_posts
156
- end
172
+ ~~~ruby
173
+ class User
174
+ include Redis::Objects
175
+ redis_id_field :uid
176
+ counter :my_posts
177
+ end
157
178
 
158
- user.uid # 195137a1bdea4473
159
- user.my_posts.increment # 1
179
+ user.uid # 195137a1bdea4473
180
+ user.my_posts.increment # 1
181
+ ~~~
160
182
 
161
183
  Finally, for free, you get a `redis` method that points directly to a Redis connection:
162
184
 
163
- Team.redis.get('somekey')
164
- @team = Team.new
165
- @team.redis.get('somekey')
166
- @team.redis.smembers('someset')
185
+ ~~~ruby
186
+ Team.redis.get('somekey')
187
+ @team = Team.new
188
+ @team.redis.get('somekey')
189
+ @team.redis.smembers('someset')
190
+ ~~~
167
191
 
168
192
  You can use the `redis` handle to directly call any [Redis API command](http://redis.io/commands).
169
193
 
@@ -179,17 +203,21 @@ Counters
179
203
  --------
180
204
  The `counter_name` is the key stored in Redis.
181
205
 
182
- @counter = Redis::Counter.new('counter_name')
183
- @counter.increment # or incr
184
- @counter.decrement # or decr
185
- @counter.increment(3)
186
- puts @counter.value
206
+ ~~~ruby
207
+ @counter = Redis::Counter.new('counter_name')
208
+ @counter.increment # or incr
209
+ @counter.decrement # or decr
210
+ @counter.increment(3)
211
+ puts @counter.value
212
+ ~~~
187
213
 
188
214
  This gem provides a clean way to do atomic blocks as well:
189
215
 
190
- @counter.increment do |val|
191
- raise "Full" if val > MAX_VAL # rewind counter
192
- end
216
+ ~~~ruby
217
+ @counter.increment do |val|
218
+ raise "Full" if val > MAX_VAL # rewind counter
219
+ end
220
+ ~~~
193
221
 
194
222
  See the section on [Atomic Counters and Locks](#atomicity) for cool uses of atomic counter blocks.
195
223
 
@@ -197,10 +225,12 @@ Locks
197
225
  -----
198
226
  A convenience class that wraps the pattern of [using setnx to perform locking](http://redis.io/commands/setnx).
199
227
 
200
- @lock = Redis::Lock.new('serialize_stuff', :expiration => 15, :timeout => 0.1)
201
- @lock.lock do
202
- # do work
203
- end
228
+ ~~~ruby
229
+ @lock = Redis::Lock.new('serialize_stuff', :expiration => 15, :timeout => 0.1)
230
+ @lock.lock do
231
+ # do work
232
+ end
233
+ ~~~
204
234
 
205
235
  This can be especially useful if you're running batch jobs spread across multiple hosts.
206
236
 
@@ -208,49 +238,59 @@ Values
208
238
  ------
209
239
  Simple values are easy as well:
210
240
 
211
- @value = Redis::Value.new('value_name')
212
- @value.value = 'a'
213
- @value.delete
241
+ ~~~ruby
242
+ @value = Redis::Value.new('value_name')
243
+ @value.value = 'a'
244
+ @value.delete
245
+ ~~~
214
246
 
215
247
  Complex data is no problem with :marshal => true:
216
248
 
217
- @account = Account.create!(params[:account])
218
- @newest = Redis::Value.new('newest_account', :marshal => true)
219
- @newest.value = @account.attributes
220
- puts @newest.value['username']
249
+ ~~~ruby
250
+ @account = Account.create!(params[:account])
251
+ @newest = Redis::Value.new('newest_account', :marshal => true)
252
+ @newest.value = @account.attributes
253
+ puts @newest.value['username']
254
+ ~~~
221
255
 
222
256
  Lists
223
257
  -----
224
258
  Lists work just like Ruby arrays:
225
259
 
226
- @list = Redis::List.new('list_name')
227
- @list << 'a'
228
- @list << 'b'
229
- @list.include? 'c' # false
230
- @list.values # ['a','b']
231
- @list << 'c'
232
- @list.delete('c')
233
- @list[0]
234
- @list[0,1]
235
- @list[0..1]
236
- @list.shift
237
- @list.pop
238
- @list.clear
239
- # etc
260
+ ~~~ruby
261
+ @list = Redis::List.new('list_name')
262
+ @list << 'a'
263
+ @list << 'b'
264
+ @list.include? 'c' # false
265
+ @list.values # ['a','b']
266
+ @list << 'c'
267
+ @list.delete('c')
268
+ @list[0]
269
+ @list[0,1]
270
+ @list[0..1]
271
+ @list.shift
272
+ @list.pop
273
+ @list.clear
274
+ # etc
275
+ ~~~
240
276
 
241
277
  You can bound the size of the list to only hold N elements like so:
242
278
 
243
- # Only holds 10 elements, throws out old ones when you reach :maxlength.
244
- @list = Redis::List.new('list_name', :maxlength => 10)
279
+ ~~~ruby
280
+ # Only holds 10 elements, throws out old ones when you reach :maxlength.
281
+ @list = Redis::List.new('list_name', :maxlength => 10)
282
+ ~~~
245
283
 
246
284
  Complex data types are now handled with :marshal => true:
247
285
 
248
- @list = Redis::List.new('list_name', :marshal => true)
249
- @list << {:name => "Nate", :city => "San Diego"}
250
- @list << {:name => "Peter", :city => "Oceanside"}
251
- @list.each do |el|
252
- puts "#{el[:name]} lives in #{el[:city]}"
253
- end
286
+ ~~~ruby
287
+ @list = Redis::List.new('list_name', :marshal => true)
288
+ @list << {:name => "Nate", :city => "San Diego"}
289
+ @list << {:name => "Peter", :city => "Oceanside"}
290
+ @list.each do |el|
291
+ puts "#{el[:name]} lives in #{el[:city]}"
292
+ end
293
+ ~~~
254
294
 
255
295
  Hashes
256
296
  ------
@@ -258,21 +298,25 @@ Hashes work like a Ruby [Hash](http://ruby-doc.org/core/classes/Hash.html), with
258
298
  a few Redis-specific additions. (The class name is "HashKey" not just "Hash", due to
259
299
  conflicts with the Ruby core Hash class in other gems.)
260
300
 
261
- @hash = Redis::HashKey.new('hash_name')
262
- @hash['a'] = 1
263
- @hash['b'] = 2
264
- @hash.each do |k,v|
265
- puts "#{k} = #{v}"
266
- end
267
- @hash['c'] = 3
268
- puts @hash.all # {"a"=>"1","b"=>"2","c"=>"3"}
269
- @hash.clear
301
+ ~~~ruby
302
+ @hash = Redis::HashKey.new('hash_name')
303
+ @hash['a'] = 1
304
+ @hash['b'] = 2
305
+ @hash.each do |k,v|
306
+ puts "#{k} = #{v}"
307
+ end
308
+ @hash['c'] = 3
309
+ puts @hash.all # {"a"=>"1","b"=>"2","c"=>"3"}
310
+ @hash.clear
311
+ ~~~
270
312
 
271
313
  Redis also adds incrementing and bulk operations:
272
314
 
273
- @hash.incr('c', 6) # 9
274
- @hash.bulk_set('d' => 5, 'e' => 6)
275
- @hash.bulk_get('d','e') # "5", "6"
315
+ ~~~ruby
316
+ @hash.incr('c', 6) # 9
317
+ @hash.bulk_set('d' => 5, 'e' => 6)
318
+ @hash.bulk_get('d','e') # "5", "6"
319
+ ~~~
276
320
 
277
321
  Remember that numbers become strings in Redis. Unlike with other Redis data types,
278
322
  `redis-objects` can't guess at your data type in this situation, since you may
@@ -283,91 +327,101 @@ Sets
283
327
  Sets work like the Ruby [Set](http://ruby-doc.org/core/classes/Set.html) class.
284
328
  They are unordered, but guarantee uniqueness of members.
285
329
 
286
- @set = Redis::Set.new('set_name')
287
- @set << 'a'
288
- @set << 'b'
289
- @set << 'a' # dup ignored
290
- @set.member? 'c' # false
291
- @set.members # ['a','b']
292
- @set.members.reverse # ['b','a']
293
- @set.each do |member|
294
- puts member
295
- end
296
- @set.clear
297
- # etc
330
+ ~~~ruby
331
+ @set = Redis::Set.new('set_name')
332
+ @set << 'a'
333
+ @set << 'b'
334
+ @set << 'a' # dup ignored
335
+ @set.member? 'c' # false
336
+ @set.members # ['a','b']
337
+ @set.members.reverse # ['b','a']
338
+ @set.each do |member|
339
+ puts member
340
+ end
341
+ @set.clear
342
+ # etc
343
+ ~~~
298
344
 
299
345
  You can perform Redis intersections/unions/diffs easily:
300
346
 
301
- @set1 = Redis::Set.new('set1')
302
- @set2 = Redis::Set.new('set2')
303
- @set3 = Redis::Set.new('set3')
304
- members = @set1 & @set2 # intersection
305
- members = @set1 | @set2 # union
306
- members = @set1 + @set2 # union
307
- members = @set1 ^ @set2 # difference
308
- members = @set1 - @set2 # difference
309
- members = @set1.intersection(@set2, @set3) # multiple
310
- members = @set1.union(@set2, @set3) # multiple
311
- members = @set1.difference(@set2, @set3) # multiple
347
+ ~~~ruby
348
+ @set1 = Redis::Set.new('set1')
349
+ @set2 = Redis::Set.new('set2')
350
+ @set3 = Redis::Set.new('set3')
351
+ members = @set1 & @set2 # intersection
352
+ members = @set1 | @set2 # union
353
+ members = @set1 + @set2 # union
354
+ members = @set1 ^ @set2 # difference
355
+ members = @set1 - @set2 # difference
356
+ members = @set1.intersection(@set2, @set3) # multiple
357
+ members = @set1.union(@set2, @set3) # multiple
358
+ members = @set1.difference(@set2, @set3) # multiple
359
+ ~~~
312
360
 
313
361
  Or store them in Redis:
314
362
 
315
- @set1.interstore('intername', @set2, @set3)
316
- members = @set1.redis.get('intername')
317
- @set1.unionstore('unionname', @set2, @set3)
318
- members = @set1.redis.get('unionname')
319
- @set1.diffstore('diffname', @set2, @set3)
320
- members = @set1.redis.get('diffname')
363
+ ~~~ruby
364
+ @set1.interstore('intername', @set2, @set3)
365
+ members = @set1.redis.get('intername')
366
+ @set1.unionstore('unionname', @set2, @set3)
367
+ members = @set1.redis.get('unionname')
368
+ @set1.diffstore('diffname', @set2, @set3)
369
+ members = @set1.redis.get('diffname')
370
+ ~~~
321
371
 
322
372
  And use complex data types too, with :marshal => true:
323
373
 
324
- @set1 = Redis::Set.new('set1', :marshal => true)
325
- @set2 = Redis::Set.new('set2', :marshal => true)
326
- @set1 << {:name => "Nate", :city => "San Diego"}
327
- @set1 << {:name => "Peter", :city => "Oceanside"}
328
- @set2 << {:name => "Nate", :city => "San Diego"}
329
- @set2 << {:name => "Jeff", :city => "Del Mar"}
374
+ ~~~ruby
375
+ @set1 = Redis::Set.new('set1', :marshal => true)
376
+ @set2 = Redis::Set.new('set2', :marshal => true)
377
+ @set1 << {:name => "Nate", :city => "San Diego"}
378
+ @set1 << {:name => "Peter", :city => "Oceanside"}
379
+ @set2 << {:name => "Nate", :city => "San Diego"}
380
+ @set2 << {:name => "Jeff", :city => "Del Mar"}
330
381
 
331
- @set1 & @set2 # Nate
332
- @set1 - @set2 # Peter
333
- @set1 | @set2 # all 3 people
382
+ @set1 & @set2 # Nate
383
+ @set1 - @set2 # Peter
384
+ @set1 | @set2 # all 3 people
385
+ ~~~
334
386
 
335
387
  Sorted Sets
336
388
  -----------
337
389
  Due to their unique properties, Sorted Sets work like a hybrid between
338
390
  a Hash and an Array. You assign like a Hash, but retrieve like an Array:
339
391
 
340
- @sorted_set = Redis::SortedSet.new('number_of_posts')
341
- @sorted_set['Nate'] = 15
342
- @sorted_set['Peter'] = 75
343
- @sorted_set['Jeff'] = 24
392
+ ~~~ruby
393
+ @sorted_set = Redis::SortedSet.new('number_of_posts')
394
+ @sorted_set['Nate'] = 15
395
+ @sorted_set['Peter'] = 75
396
+ @sorted_set['Jeff'] = 24
344
397
 
345
- # Array access to get sorted order
346
- @sorted_set[0..2] # => ["Nate", "Jeff", "Peter"]
347
- @sorted_set[0,2] # => ["Nate", "Jeff"]
398
+ # Array access to get sorted order
399
+ @sorted_set[0..2] # => ["Nate", "Jeff", "Peter"]
400
+ @sorted_set[0,2] # => ["Nate", "Jeff"]
348
401
 
349
- @sorted_set['Peter'] # => 75
350
- @sorted_set['Jeff'] # => 24
351
- @sorted_set.score('Jeff') # same thing (24)
402
+ @sorted_set['Peter'] # => 75
403
+ @sorted_set['Jeff'] # => 24
404
+ @sorted_set.score('Jeff') # same thing (24)
352
405
 
353
- @sorted_set.rank('Peter') # => 2
354
- @sorted_set.rank('Jeff') # => 1
406
+ @sorted_set.rank('Peter') # => 2
407
+ @sorted_set.rank('Jeff') # => 1
355
408
 
356
- @sorted_set.first # => "Nate"
357
- @sorted_set.last # => "Peter"
358
- @sorted_set.revrange(0,2) # => ["Peter", "Jeff", "Nate"]
409
+ @sorted_set.first # => "Nate"
410
+ @sorted_set.last # => "Peter"
411
+ @sorted_set.revrange(0,2) # => ["Peter", "Jeff", "Nate"]
359
412
 
360
- @sorted_set['Newbie'] = 1
361
- @sorted_set.members # => ["Newbie", "Nate", "Jeff", "Peter"]
362
- @sorted_set.members.reverse # => ["Peter", "Jeff", "Nate", "Newbie"]
413
+ @sorted_set['Newbie'] = 1
414
+ @sorted_set.members # => ["Newbie", "Nate", "Jeff", "Peter"]
415
+ @sorted_set.members.reverse # => ["Peter", "Jeff", "Nate", "Newbie"]
363
416
 
364
- @sorted_set.rangebyscore(10, 100, :limit => 2) # => ["Nate", "Jeff"]
365
- @sorted_set.members(:with_scores => true) # => [["Newbie", 1], ["Nate", 16], ["Jeff", 28], ["Peter", 76]]
417
+ @sorted_set.rangebyscore(10, 100, :limit => 2) # => ["Nate", "Jeff"]
418
+ @sorted_set.members(:with_scores => true) # => [["Newbie", 1], ["Nate", 16], ["Jeff", 28], ["Peter", 76]]
366
419
 
367
- # atomic increment
368
- @sorted_set.increment('Nate')
369
- @sorted_set.incr('Peter') # shorthand
370
- @sorted_set.incr('Jeff', 4)
420
+ # atomic increment
421
+ @sorted_set.increment('Nate')
422
+ @sorted_set.incr('Peter') # shorthand
423
+ @sorted_set.incr('Jeff', 4)
424
+ ~~~
371
425
 
372
426
  The other Redis Sorted Set commands are supported as well; see [Sorted Sets API](http://redis.io/commands#sorted_set).
373
427
 
@@ -379,65 +433,79 @@ on the topic, see [An Atomic Rant](http://nateware.com/an-atomic-rant.html).
379
433
 
380
434
  Atomic counters are a good way to handle concurrency:
381
435
 
382
- @team = Team.find(1)
383
- if @team.drafted_players.increment <= @team.max_players
384
- # do stuff
385
- @team.team_players.create!(:player_id => 221)
386
- @team.active_players.increment
387
- else
388
- # reset counter state
389
- @team.drafted_players.decrement
390
- end
436
+ ~~~ruby
437
+ @team = Team.find(1)
438
+ if @team.drafted_players.increment <= @team.max_players
439
+ # do stuff
440
+ @team.team_players.create!(:player_id => 221)
441
+ @team.active_players.increment
442
+ else
443
+ # reset counter state
444
+ @team.drafted_players.decrement
445
+ end
446
+ ~~~
391
447
 
392
448
  An _atomic block_ gives you a cleaner way to do the above. Exceptions or returning nil
393
449
  will rewind the counter back to its previous state:
394
450
 
395
- @team.drafted_players.increment do |val|
396
- raise Team::TeamFullError if val > @team.max_players # rewind
397
- @team.team_players.create!(:player_id => 221)
398
- @team.active_players.increment
399
- end
451
+ ~~~ruby
452
+ @team.drafted_players.increment do |val|
453
+ raise Team::TeamFullError if val > @team.max_players # rewind
454
+ @team.team_players.create!(:player_id => 221)
455
+ @team.active_players.increment
456
+ end
457
+ ~~~
400
458
 
401
459
  Here's a similar approach, using an if block (failure rewinds counter):
402
460
 
403
- @team.drafted_players.increment do |val|
404
- if val <= @team.max_players
405
- @team.team_players.create!(:player_id => 221)
406
- @team.active_players.increment
407
- end
408
- end
461
+ ~~~ruby
462
+ @team.drafted_players.increment do |val|
463
+ if val <= @team.max_players
464
+ @team.team_players.create!(:player_id => 221)
465
+ @team.active_players.increment
466
+ end
467
+ end
468
+ ~~~
409
469
 
410
470
  Class methods work too, using the familiar ActiveRecord counter syntax:
411
471
 
412
- Team.increment_counter :drafted_players, team_id
413
- Team.decrement_counter :drafted_players, team_id, 2
414
- Team.increment_counter :total_online_players # no ID on global counter
472
+ ~~~ruby
473
+ Team.increment_counter :drafted_players, team_id
474
+ Team.decrement_counter :drafted_players, team_id, 2
475
+ Team.increment_counter :total_online_players # no ID on global counter
476
+ ~~~
415
477
 
416
478
  Class-level atomic blocks can also be used. This may save a DB fetch, if you have
417
479
  a record ID and don't need any other attributes from the DB table:
418
480
 
419
- Team.increment_counter(:drafted_players, team_id) do |val|
420
- TeamPitcher.create!(:team_id => team_id, :pitcher_id => 181)
421
- Team.increment_counter(:active_players, team_id)
422
- end
481
+ ~~~ruby
482
+ Team.increment_counter(:drafted_players, team_id) do |val|
483
+ TeamPitcher.create!(:team_id => team_id, :pitcher_id => 181)
484
+ Team.increment_counter(:active_players, team_id)
485
+ end
486
+ ~~~
423
487
 
424
488
  ### Locks ###
425
489
 
426
490
  Locks work similarly. On completion or exception the lock is released:
427
491
 
428
- class Team < ActiveRecord::Base
429
- lock :reorder # declare a lock
430
- end
492
+ ~~~ruby
493
+ class Team < ActiveRecord::Base
494
+ lock :reorder # declare a lock
495
+ end
431
496
 
432
- @team.reorder_lock.lock do
433
- @team.reorder_all_players
434
- end
497
+ @team.reorder_lock.lock do
498
+ @team.reorder_all_players
499
+ end
500
+ ~~~
435
501
 
436
502
  Class-level lock (same concept)
437
503
 
438
- Team.obtain_lock(:reorder, team_id) do
439
- Team.reorder_all_players(team_id)
440
- end
504
+ ~~~ruby
505
+ Team.obtain_lock(:reorder, team_id) do
506
+ Team.reorder_all_players(team_id)
507
+ end
508
+ ~~~
441
509
 
442
510
  Lock expiration. Sometimes you want to make sure your locks are cleaned up should
443
511
  the unthinkable happen (server failure). You can set lock expirations to handle
@@ -445,9 +513,11 @@ this. Expired locks are released by the next process to attempt lock. Just
445
513
  make sure you expiration value is sufficiently large compared to your expected
446
514
  lock time.
447
515
 
448
- class Team < ActiveRecord::Base
449
- lock :reorder, :expiration => 15.minutes
450
- end
516
+ ~~~ruby
517
+ class Team < ActiveRecord::Base
518
+ lock :reorder, :expiration => 15.minutes
519
+ end
520
+ ~~~
451
521
 
452
522
  Keep in mind that true locks serialize your entire application at that point. As
453
523
  such, atomic counters are strongly preferred.
@@ -456,8 +526,10 @@ such, atomic counters are strongly preferred.
456
526
 
457
527
  Use :expiration and :expireat options to set default expiration.
458
528
 
459
- value :value_with_expiration, :expiration => 1.hour
460
- value :value_with_expireat, :expireat => Time.now + 1.hour
529
+ ~~~ruby
530
+ value :value_with_expiration, :expiration => 1.hour
531
+ value :value_with_expireat, :expireat => Time.now + 1.hour
532
+ ~~~
461
533
 
462
534
  Author
463
535
  =======