redis-objects 0.5.2 → 0.5.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -26,7 +26,9 @@ you want to create.
26
26
 
27
27
  == Installation
28
28
 
29
- gem install redis-objects
29
+ Add it to your Gemfile as:
30
+
31
+ gem 'redis-objects', :require => 'redis/objects'
30
32
 
31
33
  == Example 1: Model Class Usage
32
34
 
@@ -45,7 +47,7 @@ config/initializers/redis.rb is a good place for this.)
45
47
 
46
48
  require 'redis'
47
49
  require 'redis/objects'
48
- Redis::Objects.redis = Redis.new(:host => '127.0.0.1', :port => 6379)
50
+ Redis.current = Redis.new(:host => '127.0.0.1', :port => 6379)
49
51
 
50
52
  Remember you can use Redis::Objects in any Ruby code. There are *no* dependencies
51
53
  on Rails. Standalone, Sinatra, Resque - no problem.
@@ -124,15 +126,15 @@ which may already exist on the redis-server.
124
126
  === Initialization
125
127
 
126
128
  Redis::Objects needs a handle to the +redis+ server. For standalone use, you
127
- can either set the $redis global variable:
129
+ can either set Redis.current:
128
130
 
129
- $redis = Redis.new(:host => 'localhost', :port => 6379)
130
- @list = Redis::List.new('mylist')
131
+ Redis.current = Redis.new(:host => 'localhost', :port => 6379)
132
+ @list = Redis::List.new('mylist')
131
133
 
132
134
  Or you can pass the Redis handle into the new method for each type:
133
135
 
134
- redis = Redis.new(:host => 'localhost', :port => 6379)
135
- @list = Redis::List.new('mylist', redis)
136
+ @redis = Redis.new(:host => 'localhost', :port => 6379)
137
+ @list = Redis::List.new('mylist', @redis)
136
138
 
137
139
  === Counters
138
140
 
@@ -351,8 +353,8 @@ Atomic counters are a good way to handle concurrency:
351
353
  @team.drafted_players.decrement
352
354
  end
353
355
 
354
- Atomic block - a cleaner way to do the above. Exceptions or return nil
355
- rewind counter back to previous state:
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>:
356
358
 
357
359
  @team.drafted_players.increment do |val|
358
360
  raise Team::TeamFullError if val > @team.max_players
@@ -411,6 +413,6 @@ lock time.
411
413
 
412
414
  == Author
413
415
 
414
- Copyright (c) 2009-2010 {Nate Wiger}[http://nate.wiger.org]. All Rights Reserved.
416
+ Copyright (c) 2009-2012 {Nate Wiger}[http://nateware.com]. All Rights Reserved.
415
417
  Released under the {Artistic License}[http://www.opensource.org/licenses/artistic-license-2.0.php].
416
418
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.2
1
+ 0.5.3
@@ -70,7 +70,7 @@ class Redis
70
70
 
71
71
  # Retrieve the entire hash. Redis: HGETALL
72
72
  def all
73
- h = redis.hgetall(key)
73
+ h = redis.hgetall(key) || {}
74
74
  h.each { |k,v| h[k] = from_redis(v, options[:marshal_keys][k]) }
75
75
  h
76
76
  end
@@ -115,6 +115,15 @@ class Redis
115
115
  arr + [kv[0], to_redis(kv[1], options[:marshal_keys][kv[0]])]
116
116
  })
117
117
  end
118
+ alias_method :update, :bulk_set
119
+
120
+ # Set keys in bulk if they do not exist. Takes a hash of field/values {'field1' => 'val1'}. Redis: HSETNX
121
+ def fill(pairs={})
122
+ raise ArgumentErorr, "Arugment to fill must be a hash of key/value pairs" unless pairs.is_a?(::Hash)
123
+ pairs.each do |field, value|
124
+ redis.hsetnx(key, field, to_redis(value, options[:marshal_keys][field]))
125
+ end
126
+ end
118
127
 
119
128
  # Get keys in bulk, takes an array of fields as arguments. Redis: HMGET
120
129
  def bulk_get(*fields)
@@ -38,8 +38,12 @@ class Redis
38
38
  redis.expire key, unixtime
39
39
  end
40
40
 
41
+ def persist
42
+ redis.persist key
43
+ end
44
+
41
45
  def ttl
42
- redis.ttl(@key).seconds
46
+ redis.ttl(@key)
43
47
  end
44
48
 
45
49
  def move(dbindex)
@@ -32,6 +32,18 @@ class Redis
32
32
  from_redis redis.rpop(key)
33
33
  end
34
34
 
35
+ # Atomically pops a value from one list, pushes to another and returns the
36
+ # value. Destination can be a String or a Redis::List
37
+ #
38
+ # list.rpoplpush(destination)
39
+ #
40
+ # Returns the popped/pushed value.
41
+ #
42
+ # Redis: RPOPLPUSH
43
+ def rpoplpush(destination)
44
+ from_redis redis.rpoplpush(key, destination.is_a?(Redis::List) ? destination.key : destination.to_s)
45
+ end
46
+
35
47
  # Add a member to the start of the list. Redis: LPUSH
36
48
  def unshift(value)
37
49
  redis.lpush(key, to_redis(value))
@@ -26,6 +26,11 @@ class Redis
26
26
  redis.sadd(key, to_redis(value))
27
27
  end
28
28
 
29
+ # Remove and return a random member. Redis:SPOP
30
+ def pop
31
+ from_redis redis.spop(key)
32
+ end
33
+
29
34
  # Return all members in the set. Redis: SMEMBERS
30
35
  def members
31
36
  v = from_redis redis.smembers(key)
@@ -133,6 +138,19 @@ class Redis
133
138
  redis.sdiffstore(name, key, *keys_from_objects(sets))
134
139
  end
135
140
 
141
+ # Moves value from one set to another. Destination can be a String
142
+ # or Redis::Set.
143
+ #
144
+ # set.move(value, "name_of_key_in_redis")
145
+ # set.move(value, set2)
146
+ #
147
+ # Returns true if moved successfully.
148
+ #
149
+ # Redis: SMOVE
150
+ def move(value, destination)
151
+ redis.smove(key, destination.is_a?(Redis::Set) ? destination.key : destination.to_s, value)
152
+ end
153
+
136
154
  # The number of members in the set. Aliased as size. Redis: SCARD
137
155
  def length
138
156
  redis.scard(key)
@@ -58,11 +58,19 @@ class Redis
58
58
  # When the given member does not exist in the sorted set, nil is returned.
59
59
  # The returned rank (or index) of the member is 0-based for both commands
60
60
  def rank(member)
61
- redis.zrank(key, to_redis(member)).to_i
61
+ if n = redis.zrank(key, to_redis(member))
62
+ n.to_i
63
+ else
64
+ nil
65
+ end
62
66
  end
63
67
 
64
68
  def revrank(member)
65
- redis.zrevrank(key, to_redis(member)).to_i
69
+ if n = redis.zrevrank(key, to_redis(member))
70
+ n.to_i
71
+ else
72
+ nil
73
+ end
66
74
  end
67
75
 
68
76
  # Return all members of the sorted set with their scores. Extremely CPU-intensive.
@@ -13,11 +13,15 @@ class Redis
13
13
  attr_reader :key, :options, :redis
14
14
  def initialize(key, *args)
15
15
  super(key, *args)
16
- @redis.setnx(key, @options[:default]) if @options[:default]
16
+ @redis.setnx(key, to_redis(@options[:default])) if @options[:default]
17
17
  end
18
18
 
19
19
  def value=(val)
20
- redis.set key, to_redis(val)
20
+ if val.nil?
21
+ delete
22
+ else
23
+ redis.set key, to_redis(val)
24
+ end
21
25
  end
22
26
  alias_method :set, :value=
23
27
 
@@ -26,8 +30,17 @@ class Redis
26
30
  end
27
31
  alias_method :get, :value
28
32
 
29
- def to_s; value.to_s; end
30
- def ==(x); value == x; end
31
- def nil?; value.nil?; end
33
+ def inspect
34
+ "#<Redis::Value #{value.inspect}>"
35
+ end
36
+
37
+ def ==(other); value == other end
38
+ def nil?; value.nil? end
39
+ def as_json(*args); value.as_json *args end
40
+ def to_json(*args); value.to_json *args end
41
+
42
+ def method_missing(*args)
43
+ self.value.send *args
44
+ end
32
45
  end
33
46
  end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "redis-objects"
8
- s.version = "0.5.2"
8
+ s.version = "0.5.3"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Nate Wiger"]
12
- s.date = "2011-09-20"
12
+ s.date = "2012-06-13"
13
13
  s.description = "Map Redis types directly to Ruby objects. Works with any class or ORM."
14
14
  s.email = "nate@wiger.org"
15
15
  s.extra_rdoc_files = [
@@ -14,6 +14,11 @@ describe Redis::Value do
14
14
  @value = Redis::Value.new('spec/value')
15
15
  @value.delete
16
16
  end
17
+
18
+ it "should marshal default value" do
19
+ @value = Redis::Value.new('spec/value', :default => {:json => 'data'}, :marshal => true)
20
+ @value.value.should == {:json => 'data'}
21
+ end
17
22
 
18
23
  it "should handle simple values" do
19
24
  @value.should == nil
@@ -58,6 +63,33 @@ describe Redis::Value do
58
63
  @value.value.should == 'Peter Pan'
59
64
  end
60
65
 
66
+ it "should provide a readable inspect" do
67
+ @value.value = 'monkey'
68
+ @value.inspect.should == '#<Redis::Value "monkey">'
69
+ @value.value = 1234
70
+ @value.inspect.should == '#<Redis::Value "1234">'
71
+ end
72
+
73
+ it 'should delegate unrecognized methods to the value' do
74
+ @value.value = 'monkey'
75
+ @value.to_sym.should == :monkey
76
+ end
77
+
78
+ it 'should properly pass equality operations on to the value' do
79
+ @value.value = 'monkey'
80
+ @value.should == 'monkey'
81
+ end
82
+
83
+ it 'should properly pass nil? on to the value' do
84
+ @value.delete
85
+ @value.nil?.should == true
86
+ end
87
+
88
+ it 'should equate setting the value to nil to deletion' do
89
+ @value.value = nil
90
+ @value.nil?.should == true
91
+ end
92
+
61
93
  after do
62
94
  @value.delete
63
95
  end
@@ -187,6 +219,17 @@ describe Redis::List do
187
219
  @list.get.should == ['a','c','f','j','a']
188
220
  end
189
221
 
222
+ it "should handle rpoplpush" do
223
+ list2 = Redis::List.new("spec/list2")
224
+ list2.clear
225
+
226
+ @list << "a" << "b"
227
+ result = @list.rpoplpush(list2)
228
+ result.should == "b"
229
+ @list.should == ["a"]
230
+ list2.should == ["b"]
231
+ end
232
+
190
233
  it "should handle lists of complex data types" do
191
234
  @list.options[:marshal] = true
192
235
  v1 = {:json => 'data'}
@@ -520,6 +563,15 @@ describe Redis::HashKey do
520
563
  @hash.incr('counter')
521
564
  @hash['counter'].to_i.should == 2
522
565
  end
566
+
567
+ it "should respond to fill" do
568
+ @hash['foo'] = 'bar'
569
+
570
+ @hash.fill('abc' => '123', 'bang' => 'michael')
571
+ @hash['foo'].should == 'bar'
572
+ @hash['abc'].should == '123'
573
+ @hash['bang'].should == 'michael'
574
+ end
523
575
 
524
576
 
525
577
  after do
@@ -559,7 +611,12 @@ describe Redis::Set do
559
611
  @set.get.sort.should == ['a','b']
560
612
  @set.length.should == 2
561
613
  @set.size.should == 2
614
+ @set.delete('a')
615
+ @set.pop.should == 'b'
562
616
 
617
+ @set.add('a')
618
+ @set.add('b')
619
+
563
620
  i = 0
564
621
  @set.each do |st|
565
622
  i += 1
@@ -742,6 +799,14 @@ describe Redis::SortedSet do
742
799
  # @set.rank('b').should == 2
743
800
  # @set.revrank('b').should == 3
744
801
 
802
+ # shouldn't report a rank for a key that doesn't exist
803
+ @set.rank('foo').should.not == @set.rank(@set.first)
804
+ @set.rank('foo').should == nil
805
+
806
+ # shouldn't report a rank for a key that doesn't exist
807
+ @set.revrank('foo').should.not == @set.revrank(@set.first)
808
+ @set.revrank('foo').should == nil
809
+
745
810
  @set['f'] = 100
746
811
  @set['g'] = 110
747
812
  @set['h'] = 120
@@ -142,6 +142,26 @@ describe Redis::Objects do
142
142
  @roster.contact_information.size.should == 2
143
143
  end
144
144
 
145
+ it 'should be able to expire keys and then persist them' do
146
+ # on a hash_key
147
+ @roster.contact_information['Jenny_Phone'] = '8675309'
148
+ @roster.contact_information.expire 30
149
+ @roster.contact_information.ttl.should > -1
150
+ @roster.contact_information.ttl.should <= 30
151
+ @roster.contact_information.persist
152
+ @roster.contact_information.ttl.should == -1
153
+ @roster.contact_information['Jenny_Phone'].should == '8675309'
154
+
155
+ # on a value
156
+ @roster.my_rank = 42
157
+ @roster.my_rank.expire 30
158
+ @roster.my_rank.ttl.should > -1
159
+ @roster.my_rank.ttl.should <= 30
160
+ @roster.my_rank.persist
161
+ @roster.my_rank.ttl.should == -1
162
+ @roster.my_rank.to_i.should == 42
163
+ end
164
+
145
165
  it "should be marshalling hash keys" do
146
166
  @roster.contact_information['updated_at'] = Time.now
147
167
  @roster.contact_information['updated_at'].class.should == Time
@@ -5,6 +5,9 @@ require 'redis'
5
5
  $LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__) + '/../lib')
6
6
  require 'bacon'
7
7
  Bacon.summary_at_exit
8
+ if $0 =~ /\brspec$/
9
+ raise "\n===\nThese tests are in bacon, not rspec. Try: bacon #{ARGV * ' '}\n===\n"
10
+ end
8
11
 
9
12
  UNIONSTORE_KEY = 'test:unionstore'
10
13
  INTERSTORE_KEY = 'test:interstore'
metadata CHANGED
@@ -1,59 +1,56 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: redis-objects
3
- version: !ruby/object:Gem::Version
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.3
4
5
  prerelease:
5
- version: 0.5.2
6
6
  platform: ruby
7
- authors:
7
+ authors:
8
8
  - Nate Wiger
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
-
13
- date: 2011-09-20 00:00:00 Z
14
- dependencies:
15
- - !ruby/object:Gem::Dependency
12
+ date: 2012-06-13 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
16
15
  name: bacon
17
- prerelease: false
18
- requirement: &id001 !ruby/object:Gem::Requirement
16
+ requirement: &2165783560 !ruby/object:Gem::Requirement
19
17
  none: false
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: "0"
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
24
22
  type: :development
25
- version_requirements: *id001
26
- - !ruby/object:Gem::Dependency
27
- name: redis-namespace
28
23
  prerelease: false
29
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *2165783560
25
+ - !ruby/object:Gem::Dependency
26
+ name: redis-namespace
27
+ requirement: &2165782960 !ruby/object:Gem::Requirement
30
28
  none: false
31
- requirements:
32
- - - ">="
33
- - !ruby/object:Gem::Version
34
- version: "0"
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
35
33
  type: :development
36
- version_requirements: *id002
37
- - !ruby/object:Gem::Dependency
38
- name: redis
39
34
  prerelease: false
40
- requirement: &id003 !ruby/object:Gem::Requirement
35
+ version_requirements: *2165782960
36
+ - !ruby/object:Gem::Dependency
37
+ name: redis
38
+ requirement: &2165782480 !ruby/object:Gem::Requirement
41
39
  none: false
42
- requirements:
43
- - - ">="
44
- - !ruby/object:Gem::Version
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
45
43
  version: 2.1.1
46
44
  type: :runtime
47
- version_requirements: *id003
45
+ prerelease: false
46
+ version_requirements: *2165782480
48
47
  description: Map Redis types directly to Ruby objects. Works with any class or ORM.
49
48
  email: nate@wiger.org
50
49
  executables: []
51
-
52
50
  extensions: []
53
-
54
- extra_rdoc_files:
51
+ extra_rdoc_files:
55
52
  - README.rdoc
56
- files:
53
+ files:
57
54
  - ATOMICITY.rdoc
58
55
  - CHANGELOG.rdoc
59
56
  - README.rdoc
@@ -85,25 +82,23 @@ files:
85
82
  - spec/spec_helper.rb
86
83
  homepage: http://github.com/nateware/redis-objects
87
84
  licenses: []
88
-
89
85
  post_install_message:
90
86
  rdoc_options: []
91
-
92
- require_paths:
87
+ require_paths:
93
88
  - lib
94
- required_ruby_version: !ruby/object:Gem::Requirement
89
+ required_ruby_version: !ruby/object:Gem::Requirement
95
90
  none: false
96
- requirements:
97
- - - ">="
98
- - !ruby/object:Gem::Version
99
- version: "0"
100
- required_rubygems_version: !ruby/object:Gem::Requirement
91
+ requirements:
92
+ - - ! '>='
93
+ - !ruby/object:Gem::Version
94
+ version: '0'
95
+ required_rubygems_version: !ruby/object:Gem::Requirement
101
96
  none: false
102
- requirements:
103
- - - ">="
104
- - !ruby/object:Gem::Version
105
- version: "0"
106
- requirements:
97
+ requirements:
98
+ - - ! '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ requirements:
107
102
  - redis, v2.1.1 or greater
108
103
  rubyforge_project:
109
104
  rubygems_version: 1.8.10
@@ -111,4 +106,3 @@ signing_key:
111
106
  specification_version: 3
112
107
  summary: Map Redis types directly to Ruby objects
113
108
  test_files: []
114
-