redis-objects 0.6.1 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
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