redis-objects 0.6.1 → 0.7.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.
data/CHANGELOG.rdoc CHANGED
@@ -1,6 +1,22 @@
1
1
  = Changelog for Redis::Objects
2
2
 
3
- == 0.6.0 (24 October 2012)
3
+ == 0.7.0 (27 Feb 2013)
4
+
5
+ * Enable inheritance of Redis::Objects models [rossta]
6
+
7
+ * Finally fix require/autoload so "require 'redis/foo'" is not needed [nateware]
8
+
9
+ * Redis::Objects.redis= now properly sets subclass handles as expected [nateware]
10
+
11
+ * Add lib/redis-objects.rb for easier Gemfile require [notEthan]
12
+
13
+ * Fix wrong readme line, fix some indentation [giglemad]
14
+
15
+ == 0.6.1 (13 Dec 2012)
16
+
17
+ * Fixed error that incorrectly specified activerecord as a gem dep [nateware]
18
+
19
+ == 0.6.0 (13 Dec 2012)
4
20
 
5
21
  * Add +@set.merge()+ method to add multiple members at once [hfwang]
6
22
 
@@ -24,7 +40,7 @@
24
40
 
25
41
  * group_set_with_scores is no longer needed.
26
42
 
27
- == 0.5.2 (13 June 2012)
43
+ == 0.5.2 (13 Jun 2012)
28
44
 
29
45
  * Added Redis::SortedSet#member? method [Karl Varga]
30
46
 
@@ -40,7 +56,7 @@
40
56
 
41
57
  * Updated URLs to reflect new redis.io website [Jérémy Lecour]
42
58
 
43
- == 0.5.0 (8 November 2010)
59
+ == 0.5.0 (8 Nov 2010)
44
60
 
45
61
  * Incompatible change: Had to rename Redis::Hash to Redis::HashKey due to internal conflicts with Redis lib and Ruby [Nate Wiger]
46
62
 
@@ -54,7 +70,7 @@
54
70
 
55
71
  * Updated Redis DEL semantics per API change [Gabe da Silveira]
56
72
 
57
- == 0.4.1 (23 August 2010)
73
+ == 0.4.1 (23 Aug 2010)
58
74
 
59
75
  * Fixes for Ruby 1.8 failures due to missing flatten() [Gabe da Silveira]
60
76
 
@@ -64,7 +80,7 @@
64
80
 
65
81
  * Fixed a typo in delete_if and added missing test coverage [Julio Capote, Nate Wiger]
66
82
 
67
- == 0.4.0 (11 August 2010)
83
+ == 0.4.0 (11 Aug 2010)
68
84
 
69
85
  * Full support for redis hashes via new Redis::Hash class [Julio Capote, Nate Wiger]
70
86
 
@@ -78,7 +94,7 @@
78
94
 
79
95
  * Renamed :withscores option to :with_scores for consistency with redis-rb 2.0, but kept backwards compat [Tom Stuart, Nate Wiger]
80
96
 
81
- == 0.3.2 (21 July 2010)
97
+ == 0.3.2 (21 Jul 2010)
82
98
 
83
99
  * New "maxlength" option to Redis::List can create length-limited lists (eg, like a ring buffer) from dbalatero [David Balatero]
84
100
 
@@ -86,21 +102,21 @@
86
102
 
87
103
  * Switched from rspec to bacon for tests
88
104
 
89
- == 0.3.1 (1 June 2010)
105
+ == 0.3.1 (1 Jun 2010)
90
106
 
91
107
  * Integrated fixes for sorted_set deletions from capotej [Julio Capote]
92
108
 
93
- == 0.3.0 (14 April 2010)
109
+ == 0.3.0 (14 Apr 2010)
94
110
 
95
111
  * Due to Ruby 1.9 bugs and performance considerations, marshaling of data types is now OFF by default. You must say :marshal => true for any objects that you want serialization enabled on. [Nate Wiger]
96
112
 
97
113
  * Sorted Set class changed slightly due to feedback. You can now get an individual element back via @set['item'] since it acts like a Hash.
98
114
 
99
- == 0.2.4 (9 April 2010)
115
+ == 0.2.4 (9 Apr 2010)
100
116
 
101
117
  * Added sorted set support via Redis::SortedSet [Nate Wiger]
102
118
 
103
- == 0.2.3 (18 February 2010)
119
+ == 0.2.3 (18 Feb 2010)
104
120
 
105
121
  * Added lock expiration to Redis::Lock [Ben VandenBos]
106
122
 
@@ -108,7 +124,7 @@
108
124
 
109
125
  * Added lock tests and test helpers [Ben VandenBos]
110
126
 
111
- == 0.2.2 (14 December 2009)
127
+ == 0.2.2 (14 Dec 2009)
112
128
 
113
129
  * Added @set.diff(@set2) with "^" and "-" synonyms (oversight). [Nate Wiger]
114
130
 
@@ -118,7 +134,7 @@
118
134
 
119
135
  * More spec coverage. [Nate Wiger]
120
136
 
121
- == 0.2.1 (27 November 2009)
137
+ == 0.2.1 (27 Nov 2009)
122
138
 
123
139
  * First worthwhile public release, with good spec coverage and functionality. [Nate Wiger]
124
140
 
@@ -1,14 +1,15 @@
1
- = Redis::Objects - Map Redis types directly to Ruby objects
1
+ Redis::Objects - Map Redis types directly to Ruby objects
2
+ =========================================================
2
3
 
3
- This is *not* an ORM. People that are wrapping ORM’s around Redis are missing the point.
4
+ This is **not** an ORM. People that are wrapping ORM’s around Redis are missing the point.
4
5
 
5
6
  The killer feature of Redis is that it allows you to perform _atomic_ operations
6
- on _individual_ data structures, like counters, lists, and sets. The *atomic* part is HUGE.
7
+ on _individual_ data structures, like counters, lists, and sets. The **atomic** part is HUGE.
7
8
  Using an ORM wrapper that retrieves a "record", updates values, then sends those values back,
8
9
  _removes_ the atomicity, cutting the nuts off the major advantage of Redis. Just use MySQL, k?
9
10
 
10
- This gem provides a Rubyish interface to Redis, by mapping {Redis types}[http://redis.io/commands]
11
- to Ruby objects, via a thin layer over Ezra's +redis+ gem. It offers several advantages
11
+ This gem provides a Rubyish interface to Redis, by mapping [Redis types](http://redis.io/commands)
12
+ to Ruby objects, via a thin layer over the `redis` gem. It offers several advantages
12
13
  over the lower-level redis-rb API:
13
14
 
14
15
  1. Easy to integrate directly with existing ORMs - ActiveRecord, DataMapper, etc. Add counters to your model!
@@ -17,42 +18,79 @@ over the lower-level redis-rb API:
17
18
  4. Higher-level types are provided, such as Locks, that wrap multiple calls
18
19
 
19
20
  This gem originally arose out of a need for high-concurrency atomic operations;
20
- for a fun rant on the topic, see {An Atomic Rant}[http://nateware.com/2010/02/18/an-atomic-rant],
21
- or scroll down to "Atomicity" in this README.
21
+ for a fun rant on the topic, see [An Atomic Rant](http://nateware.com/2010/02/18/an-atomic-rant),
22
+ or scroll down to [Atomic Counters and Locks](#atomicity) in this README.
22
23
 
23
24
  There are two ways to use Redis::Objects, either as an include in a model class (to
24
25
  integrate with ORMs or other classes), or by using new with the type of data structure
25
26
  you want to create.
26
27
 
27
- == Installation
28
-
28
+ Installation and Setup
29
+ ----------------------
29
30
  Add it to your Gemfile as:
30
31
 
31
- gem 'redis-objects', :require => 'redis/objects'
32
+ gem 'redis-objects'
33
+
34
+ **Redis::Objects** needs a handle created by `Redis.new`. The recommended approach
35
+ is to set `Redis.current` to point to your server, which **Redis::Objects** will
36
+ pick up automatically.
37
+
38
+ require 'redis/objects'
39
+ Redis.current = Redis.new(:host => '127.0.0.1', :port => 6379)
40
+
41
+ (If you're on Rails, `config/initializers/redis.rb` is a good place for this.)
42
+ Remember you can use **Redis::Objects** in any Ruby code. There are **no** dependencies
43
+ on Rails. Standalone, Sinatra, Resque - no problem.
44
+
45
+ Alternatively, you can set the `redis` handle directly:
46
+
47
+ Redis::Objects.redis = Redis.new(...)
32
48
 
33
- == Example 1: Model Class Usage
49
+ Finally, you can even setup different handles for different classes:
34
50
 
51
+ class User
52
+ include Redis::Objects
53
+ end
54
+ class Post
55
+ include Redis::Objects
56
+ end
57
+
58
+ User.redis = Redis.new(...)
59
+ Post.redis = Redis.new(...)
60
+
61
+ As of `0.7.0`, `redis-objects` now autoloads the appropriate `Redis::Whatever`
62
+ classes on demand. Previous strategies of individually requiring `redis/list`
63
+ or `redis/set` are no longer required.
64
+
65
+ There are two ways to use **Redis::Objects**: As part of an model class (ActiveRecord,
66
+ DataMapper, Mongoid, etc) or as standalong data type classes (`Redis::Set`, `Redis::List`, etc).
67
+
68
+ Option 1: Model Class Usage
69
+ ============================
35
70
  Using Redis::Objects this way makes it trivial to integrate Redis types with an
36
- existing ActiveRecord model, DataMapper resource, or other class. Redis::Objects
37
- will work with _any_ class that provides an +id+ method that returns a unique
38
- value. Redis::Objects will automatically create keys that are unique to
71
+ existing ActiveRecord model, DataMapper resource, or other class. **Redis::Objects**
72
+ will work with _any_ class that provides an `id` method that returns a unique
73
+ value. **Redis::Objects** will then automatically create keys that are unique to
39
74
  each object, in the format:
40
75
 
41
76
  model_name:id:field_name
42
77
 
43
- === Initialization
44
-
45
- Redis::Objects needs a handle created by Redis.new. (If you're on Rails,
46
- config/initializers/redis.rb is a good place for this.)
78
+ For illustration purposes, consider this stub class:
47
79
 
48
- require 'redis'
49
- require 'redis/objects'
50
- Redis.current = Redis.new(:host => '127.0.0.1', :port => 6379)
51
-
52
- Remember you can use Redis::Objects in any Ruby code. There are *no* dependencies
53
- on Rails. Standalone, Sinatra, Resque - no problem.
80
+ class User
81
+ include Redis::Objects
82
+ counter :my_posts
83
+ def id
84
+ 1
85
+ end
86
+ end
54
87
 
55
- === Model Class
88
+ user = User.new
89
+ user.id # 1
90
+ user.my_posts.increment
91
+ user.my_posts.increment
92
+ user.my_posts.increment
93
+ puts user.my_posts # 3
56
94
 
57
95
  You can include Redis::Objects in any type of class:
58
96
 
@@ -60,15 +98,16 @@ You can include Redis::Objects in any type of class:
60
98
  include Redis::Objects
61
99
 
62
100
  lock :trade_players, :expiration => 15 # sec
101
+ value :at_bat
63
102
  counter :hits
64
103
  counter :runs
65
104
  counter :outs
66
105
  counter :inning, :start => 1
67
106
  list :on_base
68
- set :outfielders
69
- value :at_bat
70
- sorted_set :rank, :global => true
107
+ list :coaches, :marshal => true
108
+ set :outfielders
71
109
  hash_key :pitchers_faced # "hash" is taken by Ruby
110
+ sorted_set :rank, :global => true
72
111
  end
73
112
 
74
113
  Familiar Ruby array operations Just Work (TM):
@@ -106,44 +145,31 @@ Counters can be atomically incremented/decremented (but not assigned):
106
145
  @team.hits.incr(3) # add 3
107
146
  @team.runs = 4 # exception
108
147
 
109
- Finally, for free, you get a +redis+ method that points directly to a Redis connection:
148
+ Finally, for free, you get a `redis` method that points directly to a Redis connection:
110
149
 
111
150
  Team.redis.get('somekey')
112
151
  @team = Team.new
113
152
  @team.redis.get('somekey')
114
153
  @team.redis.smembers('someset')
115
154
 
116
- You can use the +redis+ handle to directly call any {Redis API command}[http://redis.io/commands].
117
-
118
- == Example 2: Standalone Usage
155
+ You can use the `redis` handle to directly call any [Redis API command](http://redis.io/commands).
119
156
 
157
+ Option 2: Standalone Usage
158
+ ===========================
120
159
  There is a Ruby class that maps to each Redis type, with methods for each
121
- {Redis API command}[http://redis.io/commands].
122
- Note that calling +new+ does not imply it's actually a "new" value - it just
123
- creates a mapping between that object and the corresponding Redis data structure,
124
- which may already exist on the redis-server.
125
-
126
- === Initialization
127
-
128
- Redis::Objects needs a handle to the +redis+ server. For standalone use, you
129
- can either set Redis.current:
130
-
131
- Redis.current = Redis.new(:host => 'localhost', :port => 6379)
132
- @list = Redis::List.new('mylist')
133
-
134
- Or you can pass the Redis handle into the new method for each type:
160
+ [Redis API command](http://redis.io/commands).
161
+ Note that calling `new` does not imply it's actually a "new" value - it just
162
+ creates a mapping between that Ruby object and the corresponding Redis data
163
+ structure, which may already exist on the `redis-server`.
135
164
 
136
- @redis = Redis.new(:host => 'localhost', :port => 6379)
137
- @list = Redis::List.new('mylist', @redis)
165
+ Counters
166
+ --------
167
+ The `counter_name` is the key stored in Redis.
138
168
 
139
- === Counters
140
-
141
- Create a new counter. The +counter_name+ is the key stored in Redis.
142
-
143
- require 'redis/counter'
144
169
  @counter = Redis::Counter.new('counter_name')
145
- @counter.increment
146
- @counter.decrement
170
+ @counter.increment # or incr
171
+ @counter.decrement # or decr
172
+ @counter.increment(3)
147
173
  puts @counter.value
148
174
 
149
175
  This gem provides a clean way to do atomic blocks as well:
@@ -152,25 +178,23 @@ This gem provides a clean way to do atomic blocks as well:
152
178
  raise "Full" if val > MAX_VAL # rewind counter
153
179
  end
154
180
 
155
- See the section on "Atomicity" for cool uses of atomic counter blocks.
156
-
157
- === Locks
181
+ See the section on [Atomic Counters and Locks](#atomicity) for cool uses of atomic counter blocks.
158
182
 
159
- A convenience class that wraps the pattern of {using +setnx+ to perform locking}[http://redis.io/commands/setnx].
183
+ Locks
184
+ -----
185
+ A convenience class that wraps the pattern of [using setnx to perform locking](http://redis.io/commands/setnx).
160
186
 
161
- require 'redis/lock'
162
- @lock = Redis::Lock.new('image_resizing', :expiration => 15, :timeout => 0.1)
187
+ @lock = Redis::Lock.new('serialize_stuff', :expiration => 15, :timeout => 0.1)
163
188
  @lock.lock do
164
189
  # do work
165
190
  end
166
191
 
167
192
  This can be especially useful if you're running batch jobs spread across multiple hosts.
168
193
 
169
- === Values
170
-
194
+ Values
195
+ ------
171
196
  Simple values are easy as well:
172
197
 
173
- require 'redis/value'
174
198
  @value = Redis::Value.new('value_name')
175
199
  @value.value = 'a'
176
200
  @value.delete
@@ -182,11 +206,10 @@ Complex data is no problem with :marshal => true:
182
206
  @newest.value = @account.attributes
183
207
  puts @newest.value['username']
184
208
 
185
- === Lists
186
-
209
+ Lists
210
+ -----
187
211
  Lists work just like Ruby arrays:
188
212
 
189
- require 'redis/list'
190
213
  @list = Redis::List.new('list_name')
191
214
  @list << 'a'
192
215
  @list << 'b'
@@ -205,9 +228,9 @@ Lists work just like Ruby arrays:
205
228
  You can bound the size of the list to only hold N elements like so:
206
229
 
207
230
  # Only holds 10 elements, throws out old ones when you reach :maxlength.
208
- @list = Redis::List.new('list_name', redis_handle, :maxlength => 10)
231
+ @list = Redis::List.new('list_name', :maxlength => 10)
209
232
 
210
- Complex data types are no problem with :marshal => true:
233
+ Complex data types are no handled with :marshal => true:
211
234
 
212
235
  @list = Redis::List.new('list_name', :marshal => true)
213
236
  @list << {:name => "Nate", :city => "San Diego"}
@@ -216,13 +239,12 @@ Complex data types are no problem with :marshal => true:
216
239
  puts "#{el[:name]} lives in #{el[:city]}"
217
240
  end
218
241
 
219
- === Hashes
220
-
221
- Hashes work like a Ruby {Hash}[http://ruby-doc.org/core/classes/Hash.html], with
242
+ Hashes
243
+ ------
244
+ Hashes work like a Ruby [Hash](http://ruby-doc.org/core/classes/Hash.html), with
222
245
  a few Redis-specific additions. (The class name is "HashKey" not just "Hash", due to
223
246
  conflicts with the Ruby core Hash class in other gems.)
224
247
 
225
- require 'redis/hash_key'
226
248
  @hash = Redis::HashKey.new('hash_name')
227
249
  @hash['a'] = 1
228
250
  @hash['b'] = 2
@@ -239,13 +261,15 @@ Redis also adds incrementing and bulk operations:
239
261
  @hash.bulk_set('d' => 5, 'e' => 6)
240
262
  @hash.bulk_get('d','e') # "5", "6"
241
263
 
242
- Remember that numbers become strings in Redis. Unlike with other Redis data types, redis-objects can't guess at your data type in this situation.
264
+ Remember that numbers become strings in Redis. Unlike with other Redis data types,
265
+ `redis-objects` can't guess at your data type in this situation, since you may
266
+ actually mean to store "1.5".
243
267
 
244
- === Sets
268
+ Sets
269
+ ----
270
+ Sets work like the Ruby [Set](http://ruby-doc.org/core/classes/Set.html) class.
271
+ They are unordered, but guarantee uniqueness of members.
245
272
 
246
- Sets work like the Ruby {Set}[http://ruby-doc.org/core/classes/Set.html] class:
247
-
248
- require 'redis/set'
249
273
  @set = Redis::Set.new('set_name')
250
274
  @set << 'a'
251
275
  @set << 'b'
@@ -286,21 +310,20 @@ And use complex data types too, with :marshal => true:
286
310
 
287
311
  @set1 = Redis::Set.new('set1', :marshal => true)
288
312
  @set2 = Redis::Set.new('set2', :marshal => true)
289
- @set1 << {:name => "Nate", :city => "San Diego"}
313
+ @set1 << {:name => "Nate", :city => "San Diego"}
290
314
  @set1 << {:name => "Peter", :city => "Oceanside"}
291
- @set2 << {:name => "Nate", :city => "San Diego"}
292
- @set2 << {:name => "Jeff", :city => "Del Mar"}
315
+ @set2 << {:name => "Nate", :city => "San Diego"}
316
+ @set2 << {:name => "Jeff", :city => "Del Mar"}
293
317
 
294
318
  @set1 & @set2 # Nate
295
319
  @set1 - @set2 # Peter
296
320
  @set1 | @set2 # all 3 people
297
321
 
298
- === Sorted Sets
299
-
322
+ Sorted Sets
323
+ -----------
300
324
  Due to their unique properties, Sorted Sets work like a hybrid between
301
325
  a Hash and an Array. You assign like a Hash, but retrieve like an Array:
302
326
 
303
- require 'redis/sorted_set'
304
327
  @sorted_set = Redis::SortedSet.new('number_of_posts')
305
328
  @sorted_set['Nate'] = 15
306
329
  @sorted_set['Peter'] = 75
@@ -333,13 +356,13 @@ a Hash and an Array. You assign like a Hash, but retrieve like an Array:
333
356
  @sorted_set.incr('Peter') # shorthand
334
357
  @sorted_set.incr('Jeff', 4)
335
358
 
336
- The other Redis Sorted Set commands are supported as well; see {Sorted Sets API}[http://redis.io/commands#sorted_set].
337
-
338
- == Atomic Counters and Locks
359
+ The other Redis Sorted Set commands are supported as well; see [Sorted Sets API](http://redis.io/commands#sorted_set).
339
360
 
361
+ <a name="atomicity"></a>
362
+ Atomic Counters and Locks
363
+ -------------------------
340
364
  You are probably not handling atomicity correctly in your app. For a fun rant
341
- on the topic, see
342
- {An Atomic Rant}[http://nateware.com/2010/02/18/an-atomic-rant].
365
+ on the topic, see [An Atomic Rant](http://nateware.com/an-atomic-rant.html).
343
366
 
344
367
  Atomic counters are a good way to handle concurrency:
345
368
 
@@ -353,16 +376,16 @@ Atomic counters are a good way to handle concurrency:
353
376
  @team.drafted_players.decrement
354
377
  end
355
378
 
356
- Atomic block - a cleaner way to do the above. <b><em>Exceptions or returning nil
357
- rewind counter back to previous state</em></b>:
379
+ An _atomic block_ gives you a cleaner way to do the above. Exceptions or returning nil
380
+ will rewind the counter back to its previous state:
358
381
 
359
382
  @team.drafted_players.increment do |val|
360
- raise Team::TeamFullError if val > @team.max_players
383
+ raise Team::TeamFullError if val > @team.max_players # rewind
361
384
  @team.team_players.create!(:player_id => 221)
362
385
  @team.active_players.increment
363
386
  end
364
387
 
365
- Similar approach, using an if block (failure rewinds counter):
388
+ Here's a similar approach, using an if block (failure rewinds counter):
366
389
 
367
390
  @team.drafted_players.increment do |val|
368
391
  if val <= @team.max_players
@@ -371,20 +394,23 @@ Similar approach, using an if block (failure rewinds counter):
371
394
  end
372
395
  end
373
396
 
374
- Class methods work too - notice we override ActiveRecord counters:
397
+ Class methods work too, using the familiar ActiveRecord counter syntax:
375
398
 
376
399
  Team.increment_counter :drafted_players, team_id
377
400
  Team.decrement_counter :drafted_players, team_id, 2
378
401
  Team.increment_counter :total_online_players # no ID on global counter
379
402
 
380
- Class-level atomic block (may save a DB fetch depending on your app):
403
+ Class-level atomic blocks can also be used. This may save a DB fetch, if you have
404
+ a record ID and don't need any other attributes from the DB table:
381
405
 
382
406
  Team.increment_counter(:drafted_players, team_id) do |val|
383
407
  TeamPitcher.create!(:team_id => team_id, :pitcher_id => 181)
384
408
  Team.increment_counter(:active_players, team_id)
385
409
  end
386
410
 
387
- Locks with Redis. On completion or exception the lock is released:
411
+ ### Locks ###
412
+
413
+ Locks work similarly. On completion or exception the lock is released:
388
414
 
389
415
  class Team < ActiveRecord::Base
390
416
  lock :reorder # declare a lock
@@ -410,9 +436,11 @@ lock time.
410
436
  lock :reorder, :expiration => 15.minutes
411
437
  end
412
438
 
439
+ Keep in mind that true locks serialize your entire application at that point. As
440
+ such, atomic counters are strongly preferred.
413
441
 
414
- == Author
415
-
416
- Copyright (c) 2009-2012 {Nate Wiger}[http://nateware.com]. All Rights Reserved.
417
- Released under the {Artistic License}[http://www.opensource.org/licenses/artistic-license-2.0.php].
442
+ Author
443
+ =======
444
+ Copyright (c) 2009-2013 [Nate Wiger](http://nateware.com). All Rights Reserved.
445
+ Released under the [Artistic License](http://www.opensource.org/licenses/artistic-license-2.0.php).
418
446