frivol 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -5,18 +5,19 @@ class Redis
5
5
  @storage = {}
6
6
  @expires = Hash.new(nil)
7
7
  @version = config[:version] || "2.2.0"
8
+ @results = []
8
9
  end
9
10
 
10
- def [](key)
11
- # puts "retrieve #{key} => #{@storage[key]} (#{(!@expires[key].nil? && @expires[key] < Time.now) ? 'expired' : 'alive'})"
12
- return nil if !@expires[key].nil? && @expires[key] < Time.now
13
- @storage[key]
11
+ def get(key)
12
+ result = expired?(key) ? nil : @storage[key]
13
+ @results << result
14
+ result
14
15
  end
15
16
 
16
- def []=(key, value)
17
- # puts "store #{key} => #{value}"
17
+ def set(key, value)
18
18
  @storage[key] = value
19
19
  @expires.delete key
20
+ @results << 'OK'
20
21
  end
21
22
 
22
23
  def info(key)
@@ -24,33 +25,38 @@ class Redis
24
25
  end
25
26
 
26
27
  def del(key)
27
- # puts "del #{key}"
28
28
  @storage.delete key
29
29
  @expires.delete key
30
+ @results << 1
31
+ 1
30
32
  end
31
33
 
32
34
  def incr(key)
33
- # puts "incr #{key}"
34
35
  @storage[key] ||= 0
35
- @storage[key] += 1
36
+ result = @storage[key] += 1
37
+ @results << result
38
+ result
36
39
  end
37
40
 
38
41
  def incrby(key, amount)
39
- # puts "incr #{key}"
40
42
  @storage[key] ||= 0
41
- @storage[key] += amount
43
+ result = @storage[key] += amount
44
+ @results << result
45
+ result
42
46
  end
43
47
 
44
48
  def decr(key)
45
- # puts "decr #{key}"
46
49
  @storage[key] ||= 0
47
- @storage[key] -= 1
50
+ result = @storage[key] -= 1
51
+ @results << result
52
+ result
48
53
  end
49
54
 
50
55
  def decrby(key, amount)
51
- # puts "decr #{key}"
52
56
  @storage[key] ||= 0
53
- @storage[key] -= amount
57
+ result = @storage[key] -= amount
58
+ @results << result
59
+ result
54
60
  end
55
61
 
56
62
  def expire(key, time)
@@ -60,24 +66,38 @@ class Redis
60
66
  raise RuntimeError.new("-ERR value is not an integer")
61
67
  end
62
68
  @expires[key] = Time.now + t
69
+ @results << 1
70
+ 1
63
71
  end
64
72
 
65
73
  def exists(key)
66
- @storage.key? key
74
+ result = @storage.key?(key) && !expired?(key)
75
+ @results << result
76
+ result
67
77
  end
68
78
 
69
79
  def ttl(key)
70
- # puts "ttl #{key}"
71
- return -1 if @expires[key].nil? || @expires[key] < Time.now
72
- (@expires[key] - Time.now).to_i
80
+ result = if expired?(key)
81
+ -2
82
+ elsif @expires[key].nil?
83
+ -1
84
+ else
85
+ (@expires[key] - Time.now).to_i
86
+ end
87
+ @results << result
88
+ result
73
89
  end
74
90
 
75
91
  def multi(&block)
92
+ @results = []
76
93
  yield(self)
94
+ @results
77
95
  end
78
96
 
79
97
  def flushdb
80
98
  @storage = {}
99
+ @results << 'OK'
100
+ 'OK'
81
101
  end
82
102
 
83
103
  # Help with debugging
@@ -92,4 +112,9 @@ class Redis
92
112
  def method_missing(meth, *args, &block)
93
113
  puts "Missing method: #{meth} called with #{args.inspect}"
94
114
  end
115
+
116
+ private
117
+ def expired?(key)
118
+ !@expires[key].nil? && @expires[key] < Time.now
119
+ end
95
120
  end
@@ -7,14 +7,110 @@ require 'frivol'
7
7
 
8
8
  class Test::Unit::TestCase
9
9
  def setup
10
- fake_redis # Comment out this line to test against a real live Redis
11
- Frivol::Config.redis_config = { :db => 10 } # This will connect to a default Redis setup, otherwise set to { :host => "localhost", :port => 6379 }, for example
12
- Frivol::Config.redis.flushdb
10
+ case ENV['backend']
11
+ when 'redis'
12
+ require 'frivol/backend/redis'
13
+ @backend = Frivol::Backend::Redis.new(:db => 10)
14
+ @backend.flushdb
15
+ Frivol::Config.backend = @backend
16
+ when 'redis_distributed'
17
+ require 'frivol/backend/redis_distributed'
18
+ @backend = Frivol::Backend::RedisDistributed.new([{:db => 11}, {:db => 12}])
19
+ @backend.flushdb
20
+ Frivol::Config.backend = @backend
21
+ when 'riak'
22
+ require 'frivol/backend/riak'
23
+ I18n.enforce_available_locales = false
24
+ Riak.disable_list_keys_warnings = true
25
+ @backend = Frivol::Backend::Riak.new(:nodes => [ { :host => '127.0.0.1' } ])
26
+ @backend.flushdb
27
+ Frivol::Config.backend = @backend
28
+ when 'multi'
29
+ require 'frivol/backend/redis'
30
+ fake_redis
31
+ require 'frivol/backend/multi'
32
+ @old_backend = Frivol::Backend::Redis.new(:db => 10)
33
+ @new_backend = Frivol::Backend::Redis.new(:db => 11)
34
+ @backend = Frivol::Backend::Multi.new([ @new_backend, @old_backend ])
35
+ @backend.flushdb
36
+ Frivol::Config.backend = @backend
37
+ when 'multi_redis_redis'
38
+ require 'frivol/backend/redis'
39
+ require 'frivol/backend/multi'
40
+ @old_backend = Frivol::Backend::Redis.new(:db => 10)
41
+ @new_backend = Frivol::Backend::Redis.new(:db => 11)
42
+ @backend = Frivol::Backend::Multi.new([ @new_backend, @old_backend ])
43
+ @backend.flushdb
44
+ Frivol::Config.backend = @backend
45
+ when 'multi_redis_redis_distributed'
46
+ require 'frivol/backend/redis'
47
+ require 'frivol/backend/redis_distributed'
48
+ require 'frivol/backend/multi'
49
+ @old_backend = Frivol::Backend::Redis.new(:db => 10)
50
+ @new_backend = Frivol::Backend::RedisDistributed.new(["redis://127.0.0.1:6379/11", "redis://127.0.0.1:6379/12"])
51
+ @backend = Frivol::Backend::Multi.new([ @new_backend, @old_backend ])
52
+ @backend.flushdb
53
+ Frivol::Config.backend = @backend
54
+ when 'multi_redis_riak'
55
+ require 'frivol/backend/redis'
56
+ require 'frivol/backend/riak'
57
+ require 'frivol/backend/multi'
58
+ I18n.enforce_available_locales = false
59
+ Riak.disable_list_keys_warnings = true
60
+ @old_backend = Frivol::Backend::Redis.new(:db => 10)
61
+ @new_backend = Frivol::Backend::Riak.new(:nodes => [ { :host => '127.0.0.1' } ])
62
+ @backend = Frivol::Backend::Multi.new([ @new_backend, @old_backend ])
63
+ @backend.flushdb
64
+ Frivol::Config.backend = @backend
65
+ when 'multi_redis_distributed_riak'
66
+ require 'frivol/backend/redis'
67
+ require 'frivol/backend/redis_distributed'
68
+ require 'frivol/backend/riak'
69
+ require 'frivol/backend/multi'
70
+ I18n.enforce_available_locales = false
71
+ Riak.disable_list_keys_warnings = true
72
+ @old_backend = Frivol::Backend::RedisDistributed.new(["redis://127.0.0.1:6379/11", "redis://127.0.0.1:6379/12"])
73
+ @new_backend = Frivol::Backend::Riak.new(:nodes => [ { :host => '127.0.0.1' } ])
74
+ @backend = Frivol::Backend::Multi.new([ @new_backend, @old_backend ])
75
+ @backend.flushdb
76
+ Frivol::Config.backend = @backend
77
+ when 'multi_riak_redis'
78
+ require 'frivol/backend/riak'
79
+ require 'frivol/backend/redis'
80
+ require 'frivol/backend/multi'
81
+ I18n.enforce_available_locales = false
82
+ Riak.disable_list_keys_warnings = true
83
+ @old_backend = Frivol::Backend::Riak.new(:nodes => [ { :host => '127.0.0.1' } ])
84
+ @new_backend = Frivol::Backend::Redis.new(:db => 10)
85
+ @backend = Frivol::Backend::Multi.new([ @new_backend, @old_backend ])
86
+ @backend.flushdb
87
+ Frivol::Config.backend = @backend
88
+ when 'multi_riak_redis_distributed'
89
+ require 'frivol/backend/riak'
90
+ require 'frivol/backend/redis'
91
+ require 'frivol/backend/redis_distributed'
92
+ require 'frivol/backend/multi'
93
+ I18n.enforce_available_locales = false
94
+ Riak.disable_list_keys_warnings = true
95
+ @old_backend = Frivol::Backend::Riak.new(:nodes => [ { :host => '127.0.0.1' } ])
96
+ @new_backend = Frivol::Backend::RedisDistributed.new(["redis://127.0.0.1:6379/11", "redis://127.0.0.1:6379/12"])
97
+ @backend = Frivol::Backend::Multi.new([ @new_backend, @old_backend ])
98
+ @backend.flushdb
99
+ Frivol::Config.backend = @backend
100
+ else
101
+ require 'frivol/backend/redis'
102
+ fake_redis
103
+ @backend = Frivol::Backend::Redis.new(:db => 10)
104
+ @backend.flushdb
105
+ Frivol::Config.backend = @backend
106
+ end
107
+ # NOTE: Because some backends like Riak are eventually consistent,
108
+ # we're changing the id of the test class per test.
109
+ @test_id = TestClass.incr_id
13
110
  end
14
111
 
15
112
  def teardown
16
- # puts Frivol::Config.redis.inspect
17
- Frivol::Config.redis.flushdb
113
+ @backend.flushdb
18
114
  end
19
115
 
20
116
  def fake_redis
@@ -24,12 +120,26 @@ class Test::Unit::TestCase
24
120
  def ruby_one_eight?
25
121
  @ruby_one_eight || `ruby -v`.include?('1.8')
26
122
  end
123
+
124
+ def self.multi_test?
125
+ ENV['backend'].to_s.start_with?('multi')
126
+ end
127
+
128
+ def self.riak_test?
129
+ ENV['backend'].to_s.include?('riak')
130
+ end
27
131
  end
28
132
 
29
133
  class TestClass
30
134
  include Frivol
31
135
 
136
+ @@id = 1
137
+
138
+ def self.incr_id
139
+ @@id += 1
140
+ end
141
+
32
142
  def id
33
- 1
143
+ @@id
34
144
  end
35
145
  end
@@ -0,0 +1,33 @@
1
+ require "#{File.expand_path(File.dirname(__FILE__))}/helper.rb"
2
+
3
+ class TestBackends < Test::Unit::TestCase
4
+ def test_ttl
5
+ t = TestClass.new
6
+ assert_nil @backend.ttl(t.storage_key)
7
+
8
+ t.store :something => 'somewhere'
9
+ assert_nil @backend.ttl(t.storage_key)
10
+
11
+ t.expire_storage 10
12
+ assert_in_delta 10, @backend.ttl(t.storage_key), 2
13
+ end
14
+
15
+ def test_exists
16
+ t = TestClass.new
17
+ refute @backend.exists(t.storage_key)
18
+
19
+ t.store :something => 'somewhere'
20
+ assert @backend.exists(t.storage_key)
21
+
22
+ t.delete_storage
23
+ refute @backend.exists(t.storage_key)
24
+ end
25
+
26
+ def test_exists_with_expiry
27
+ t = Class.new(TestClass) { storage_expires_in -1 }.new
28
+ refute @backend.exists(t.storage_key)
29
+
30
+ t.store :something => 'somewhere'
31
+ refute @backend.exists(t.storage_key)
32
+ end
33
+ end
@@ -69,4 +69,35 @@ class TestCounters < Test::Unit::TestCase
69
69
  t.store_yellow 10
70
70
  assert_equal 0, t.retrieve_yellow(0)
71
71
  end
72
+
73
+ def test_expire_on_a_counter_bucket_using_increment
74
+ t = Class.new(TestClass) { storage_bucket :fuscia, :counter => true, :expires_in => -1 }.new
75
+ assert_equal 1, t.increment_fuscia
76
+ assert_equal 0, t.retrieve_fuscia(0)
77
+ end
78
+
79
+ def test_expire_on_a_counter_bucket_using_increment_by
80
+ t = Class.new(TestClass) { storage_bucket :magenta, :counter => true, :expires_in => -1 }.new
81
+ t.increment_magenta_by 10
82
+ assert_equal 0, t.retrieve_magenta(0)
83
+ end
84
+
85
+ def test_expire_on_a_counter_bucket_using_decrement
86
+ t = Class.new(TestClass) { storage_bucket :rose, :counter => true, :expires_in => -1 }.new
87
+ t.decrement_rose
88
+ assert_equal 0, t.retrieve_rose(0)
89
+ end
90
+
91
+ def test_expire_on_a_counter_bucket_using_decrement_by
92
+ t = Class.new(TestClass) { storage_bucket :orchid, :counter => true, :expires_in => -1 }.new
93
+ t.decrement_orchid_by 10
94
+ assert_equal 0, t.retrieve_orchid(0)
95
+ end
96
+
97
+ def test_delete_a_counter_bucket
98
+ t = Class.new(TestClass) { storage_bucket :debt, :counter => true }.new
99
+ t.store_debt 100_000
100
+ t.delete_debt
101
+ assert_equal 0, t.retrieve_debt(0)
102
+ end
72
103
  end
@@ -3,7 +3,7 @@ require "#{File.expand_path(File.dirname(__FILE__))}/helper.rb"
3
3
  class TestFrivol < Test::Unit::TestCase
4
4
  def test_have_a_default_storage_key_made_up_of_the_class_name_and_id
5
5
  t = TestClass.new
6
- assert_equal "TestClass-1", t.storage_key
6
+ assert_equal "TestClass-#{@test_id}", t.storage_key
7
7
  end
8
8
 
9
9
  def test_store_and_retrieve_data
@@ -12,11 +12,27 @@ class TestFrivol < Test::Unit::TestCase
12
12
  assert_equal "value", t.retrieve(:value => 'default')
13
13
  end
14
14
 
15
+ def test_retrieve_non_existing
16
+ t = TestClass.new
17
+ assert_nothing_raised do
18
+ assert_nil t.retrieve(:nothing => nil)
19
+ end
20
+ end
21
+
15
22
  def test_return_a_default_for_a_value_thats_not_in_storage
16
23
  t = TestClass.new
17
24
  assert_equal "default", t.retrieve(:value => 'default')
18
25
  end
19
26
 
27
+ def test_return_default_when_stored_value_is_emtpy_string
28
+ t = TestClass.new
29
+ key = t.storage_key
30
+ Frivol::Config.backend.set(key, "")
31
+ assert_nothing_raised do
32
+ assert_equal "36", t.retrieve( :some_string => "36")
33
+ end
34
+ end
35
+
20
36
  def test_save_and_retrieve_multiple_values
21
37
  t = TestClass.new
22
38
  t.store :val1 => 1, :val2 => 2
@@ -68,7 +84,7 @@ class TestFrivol < Test::Unit::TestCase
68
84
  end.new
69
85
 
70
86
  t.store :value => 'value'
71
- assert Frivol::Config.redis["my_storage"]
87
+ assert @backend.get("my_storage")
72
88
  end
73
89
 
74
90
  def test_retain_Times_as_Times
@@ -87,7 +103,7 @@ class TestFrivol < Test::Unit::TestCase
87
103
  end
88
104
 
89
105
  def test_use_default_expiry_set_on_the_class
90
- klass = Class.new(TestClass) { storage_expires_in -1 }
106
+ klass = Class.new(TestClass) { storage_expires_in -10 }
91
107
  t = klass.new
92
108
  t.store :value => 'value'
93
109
 
@@ -101,10 +117,10 @@ class TestFrivol < Test::Unit::TestCase
101
117
  t = Class.new(TestClass) { storage_expires_in 2 }.new
102
118
 
103
119
  t.store :value => 'value'
104
- assert Frivol::Config.redis.ttl(t.storage_key) > 0
120
+ assert @backend.ttl(t.storage_key) > 0
105
121
 
106
122
  t.store :value => 'value' # a second time
107
- assert Frivol::Config.redis.ttl(t.storage_key) > 0
123
+ assert @backend.ttl(t.storage_key) > 0
108
124
  end
109
125
 
110
126
  def test_be_able_to_include_in_other_classes_with_storage_expiry
@@ -119,8 +135,8 @@ class TestFrivol < Test::Unit::TestCase
119
135
  t = TestClass.new
120
136
  t.retrieve :value => 'default'
121
137
 
122
- redis = Frivol::Config.redis
123
- def redis.[](key); raise 'Onoes, loaded again'; end
138
+ # ensure we're getting the result from the cache and not Redis
139
+ def @backend.get(key); raise 'Onoes, loaded again'; end
124
140
 
125
141
  t.retrieve :value => 'default'
126
142
  end
@@ -141,8 +157,7 @@ class TestFrivol < Test::Unit::TestCase
141
157
  t.clear_gold
142
158
 
143
159
  # ensure we're getting the result from Redis and not the cache
144
- redis = Frivol::Config.redis
145
- def redis.[](key)
160
+ def @backend.get(key, expiry = nil)
146
161
  MultiJson.dump(:value => 'this is what we want')
147
162
  end
148
163
 
@@ -33,18 +33,18 @@ class TestFrivolize < Test::Unit::TestCase
33
33
  @@count = 0
34
34
 
35
35
  # Imagine counting dinosuars takes a long time, what with the need to invent a time machine first and all
36
- def dinosaur_count
36
+ def dinosaurus_count
37
37
  @@count += 1
38
38
  end
39
- frivolize :dinosaur_count, { :bucket => :dinosaurs, :expires_in => -1 }
39
+ frivolize :dinosaurus_count, { :bucket => :dinosauruss, :expires_in => -1 }
40
40
  end
41
41
 
42
42
  t = klass.new
43
- assert_equal 1, t.dinosaur_count
44
- assert_equal 1, t.dinosaur_count # Still 10 because it's coming from the class cache
43
+ assert_equal 1, t.dinosaurus_count
44
+ assert_equal 1, t.dinosaurus_count # Still 1 because it's coming from the class cache
45
45
 
46
46
  t = klass.new
47
- assert_equal 2, t.dinosaur_count
47
+ assert_equal 2, t.dinosaurus_count
48
48
  end
49
49
 
50
50
  def test_frivolize_methods_with_expiry_as_a_counter
@@ -53,19 +53,19 @@ class TestFrivolize < Test::Unit::TestCase
53
53
  @@count = 0
54
54
 
55
55
  # Imagine counting dinosuars takes a long time, what with the need to invent a time machine first and all
56
- def dinosaur_count
56
+ def dinosaurii_count
57
57
  @@count += 1
58
58
  end
59
- frivolize :dinosaur_count, { :expires_in => -1, :counter => true }
59
+ frivolize :dinosaurii_count, { :expires_in => -1, :counter => true }
60
60
  end
61
61
 
62
62
  t = klass.new
63
- assert t.methods.include?(ruby_one_eight? ? 'store_dinosaur_count' : :store_dinosaur_count) # check that the bucket name is the method name
63
+ assert t.methods.include?(ruby_one_eight? ? 'store_dinosaurii_count' : :store_dinosaurii_count) # check that the bucket name is the method name
64
64
 
65
- assert_equal 1, t.dinosaur_count
65
+ assert_equal 1, t.dinosaurii_count
66
66
 
67
67
  t = klass.new # a fresh instance after value expired
68
- assert_equal 2, t.dinosaur_count
68
+ assert_equal 2, t.dinosaurii_count
69
69
  end
70
70
 
71
71
  def test_frivolize_with_seed_as_a_counter_for_increment
@@ -73,7 +73,7 @@ class TestFrivolize < Test::Unit::TestCase
73
73
  def bak_baks
74
74
  88_888
75
75
  end
76
- frivolize :bak_baks, :counter => true, :seed => Proc.new{ |obj| obj.bak_baks}
76
+ frivolize :bak_baks, :counter => true, :seed => Proc.new{ |obj| obj.bak_baks }
77
77
  end.new
78
78
 
79
79
  assert_equal 88_888 + 1, t.increment_bak_baks