frivol 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.travis.yml +15 -0
- data/Gemfile +13 -0
- data/LICENSE +1 -1
- data/README.rdoc +58 -38
- data/Rakefile +16 -26
- data/VERSION +1 -1
- data/frivol.gemspec +74 -62
- data/lib/frivol.rb +14 -363
- data/lib/frivol/class_methods.rb +195 -0
- data/lib/frivol/config.rb +39 -0
- data/lib/frivol/functor.rb +37 -0
- data/lib/frivol/helpers.rb +133 -0
- data/lib/frivol/time_extensions.rb +45 -0
- data/test/fake_redis.rb +36 -11
- data/test/helper.rb +16 -11
- data/test/test_buckets.rb +53 -0
- data/test/test_condition.rb +43 -0
- data/test/test_condition_with_counters.rb +90 -0
- data/test/test_counters.rb +72 -0
- data/test/test_else_with_counters.rb +39 -0
- data/test/test_extensions.rb +15 -0
- data/test/test_frivol.rb +96 -388
- data/test/test_frivolize.rb +81 -0
- data/test/test_seeds.rb +53 -0
- data/test/test_threads.rb +15 -0
- metadata +88 -88
- data/.gitignore +0 -21
data/test/fake_redis.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
FAKE_REDIS = true
|
2
|
+
|
1
3
|
class Redis
|
2
4
|
def initialize(config)
|
3
5
|
@storage = {}
|
@@ -21,45 +23,68 @@ class Redis
|
|
21
23
|
@storage.delete key
|
22
24
|
@expires.delete key
|
23
25
|
end
|
24
|
-
|
26
|
+
|
25
27
|
def incr(key)
|
26
28
|
# puts "incr #{key}"
|
29
|
+
@storage[key] ||= 0
|
27
30
|
@storage[key] += 1
|
28
31
|
end
|
29
|
-
|
32
|
+
|
33
|
+
def incrby(key, amount)
|
34
|
+
# puts "incr #{key}"
|
35
|
+
@storage[key] ||= 0
|
36
|
+
@storage[key] += amount
|
37
|
+
end
|
38
|
+
|
39
|
+
def decr(key)
|
40
|
+
# puts "decr #{key}"
|
41
|
+
@storage[key] ||= 0
|
42
|
+
@storage[key] -= 1
|
43
|
+
end
|
44
|
+
|
45
|
+
def decrby(key, amount)
|
46
|
+
# puts "decr #{key}"
|
47
|
+
@storage[key] ||= 0
|
48
|
+
@storage[key] -= amount
|
49
|
+
end
|
50
|
+
|
30
51
|
def expire(key, time)
|
31
52
|
begin
|
32
|
-
t = Integer(time)
|
33
|
-
rescue
|
53
|
+
t = Integer(time)
|
54
|
+
rescue
|
34
55
|
raise RuntimeError.new("-ERR value is not an integer")
|
35
56
|
end
|
36
57
|
@expires[key] = Time.now + t
|
37
58
|
end
|
38
|
-
|
59
|
+
|
39
60
|
def exists(key)
|
40
61
|
@storage.key? key
|
41
62
|
end
|
42
|
-
|
63
|
+
|
43
64
|
def ttl(key)
|
44
65
|
# puts "ttl #{key}"
|
45
66
|
return -1 if @expires[key].nil? || @expires[key] < Time.now
|
46
67
|
(@expires[key] - Time.now).to_i
|
47
68
|
end
|
48
|
-
|
69
|
+
|
49
70
|
def multi(&block)
|
50
71
|
yield(self)
|
51
72
|
end
|
52
|
-
|
73
|
+
|
53
74
|
def flushdb
|
54
75
|
@storage = {}
|
55
76
|
end
|
56
|
-
|
77
|
+
|
57
78
|
# Help with debugging
|
58
79
|
def inspect
|
59
|
-
@storage.keys.sort.map do |key|
|
80
|
+
@storage.keys.sort.map do |key|
|
60
81
|
result = "\n#{key} => #{@storage[key].inspect}"
|
61
82
|
result << "\n\texpires at #{@expires[key]}" if @expires.key?(key)
|
62
83
|
result
|
63
84
|
end
|
64
85
|
end
|
65
|
-
|
86
|
+
|
87
|
+
def method_missing(meth, *args, &block)
|
88
|
+
puts "Missing method: #{meth} called with #{args.inspect}"
|
89
|
+
end
|
90
|
+
end
|
data/test/helper.rb
CHANGED
@@ -1,30 +1,35 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'test/unit'
|
3
|
-
require 'shoulda'
|
4
|
-
require 'mocha'
|
5
3
|
|
6
4
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
7
5
|
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
8
6
|
require 'frivol'
|
9
7
|
|
10
8
|
class Test::Unit::TestCase
|
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
|
13
|
+
end
|
14
|
+
|
15
|
+
def teardown
|
16
|
+
# puts Frivol::Config.redis.inspect
|
17
|
+
Frivol::Config.redis.flushdb
|
18
|
+
end
|
19
|
+
|
11
20
|
def fake_redis
|
12
21
|
require 'fake_redis'
|
13
22
|
end
|
23
|
+
|
24
|
+
def ruby_one_eight?
|
25
|
+
@ruby_one_eight || `ruby -v`.include?('1.8')
|
26
|
+
end
|
14
27
|
end
|
15
28
|
|
16
29
|
class TestClass
|
17
30
|
include Frivol
|
18
|
-
|
31
|
+
|
19
32
|
def id
|
20
33
|
1
|
21
34
|
end
|
22
|
-
|
23
|
-
def save
|
24
|
-
store :value => "value"
|
25
|
-
end
|
26
|
-
|
27
|
-
def load
|
28
|
-
retrieve :value => "default"
|
29
|
-
end
|
30
35
|
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require "#{File.expand_path(File.dirname(__FILE__))}/helper.rb"
|
2
|
+
|
3
|
+
class TestBuckets < Test::Unit::TestCase
|
4
|
+
def test_be_able_to_create_and_use_buckets
|
5
|
+
t = Class.new(TestClass) { storage_bucket :blue }.new
|
6
|
+
|
7
|
+
assert t.respond_to?(:store_blue)
|
8
|
+
assert t.respond_to?(:retrieve_blue)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_store_different_values_in_different_buckets
|
12
|
+
t = Class.new(TestClass) { storage_bucket :blue }.new
|
13
|
+
|
14
|
+
t.store :value => 'value'
|
15
|
+
t.store_blue :value => 'blue value'
|
16
|
+
|
17
|
+
assert_equal "value", t.retrieve(:value => 'default')
|
18
|
+
assert_equal "blue value", t.retrieve_blue(:value => 'default')
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_be_able_to_delete_storage_for_a_bucket
|
22
|
+
t = Class.new(TestClass) { storage_bucket :silver }.new
|
23
|
+
t.store_silver :value => 'value'
|
24
|
+
assert_equal "value", t.retrieve_silver(:value => 'default')
|
25
|
+
t.delete_silver
|
26
|
+
assert_equal "default", t.retrieve_silver(:value => 'default')
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_have_different_expiry_times_for_different_buckets
|
30
|
+
klass = Class.new(TestClass) do
|
31
|
+
storage_bucket :blue, :expires_in => 1
|
32
|
+
storage_expires_in 2
|
33
|
+
end
|
34
|
+
|
35
|
+
assert_equal 1, klass.storage_expiry(:blue)
|
36
|
+
assert_equal 2, klass.storage_expiry
|
37
|
+
end
|
38
|
+
|
39
|
+
def test_expire_data_in_buckets
|
40
|
+
klass = Class.new(TestClass) do
|
41
|
+
storage_bucket :blue, :expires_in => -1
|
42
|
+
storage_expires_in 1
|
43
|
+
end
|
44
|
+
|
45
|
+
t = klass.new
|
46
|
+
t.store :value => 'value'
|
47
|
+
t.store_blue :value => 'value'
|
48
|
+
|
49
|
+
t = klass.new # get a new instance so @frivol_data is empty
|
50
|
+
assert_equal "value", t.retrieve(:value => 'default')
|
51
|
+
assert_equal "blue default", t.retrieve_blue(:value => 'blue default')
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
require "#{File.expand_path(File.dirname(__FILE__))}/helper.rb"
|
2
|
+
|
3
|
+
class TestCondition < Test::Unit::TestCase
|
4
|
+
def test_stores_if_CONDITION_method_evaluates_to_truthy
|
5
|
+
klass = Class.new(TestClass) do
|
6
|
+
storage_bucket :stars, :condition => :something_truthy
|
7
|
+
|
8
|
+
def something_truthy(*args)
|
9
|
+
'Wax museum'
|
10
|
+
end
|
11
|
+
end
|
12
|
+
t = klass.new
|
13
|
+
|
14
|
+
t.store_stars :value => 88
|
15
|
+
assert_equal 88, t.retrieve_stars(:value => 0)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_stores_with_positive_CONDITION_proc
|
19
|
+
t = Class.new(TestClass) { storage_bucket :stars, :condition => Proc.new{true} }.new
|
20
|
+
t.store_stars :value => 77
|
21
|
+
assert_equal 77, t.retrieve_stars(:value => 0)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_stores_positive_CONDITION_proc_which_takes_an_instance
|
25
|
+
klass = Class.new(TestClass) do
|
26
|
+
storage_bucket :stars, :condition => Proc.new{ |instance, frivol_method, *args| instance.something_truthy }
|
27
|
+
|
28
|
+
def something_truthy(*args)
|
29
|
+
'Wax museum'
|
30
|
+
end
|
31
|
+
end
|
32
|
+
t = klass.new
|
33
|
+
|
34
|
+
t.store_stars :value => 66
|
35
|
+
assert_equal 66, t.retrieve_stars(:value => 0)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_does_not_store_with_negative_CONDITION
|
39
|
+
t = Class.new(TestClass) { storage_bucket :stars, :condition => Proc.new{false} }.new
|
40
|
+
t.store_stars :value => 55
|
41
|
+
assert_equal 0, t.retrieve_stars(:value => 0)
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
require "#{File.expand_path(File.dirname(__FILE__))}/helper.rb"
|
2
|
+
|
3
|
+
class TestConditionCounters < Test::Unit::TestCase
|
4
|
+
def test_increments_a_counter_with_no_conditions
|
5
|
+
t = Class.new(TestClass) { storage_bucket :stars, :counter => true }.new
|
6
|
+
t.increment_stars
|
7
|
+
assert_equal 1, t.retrieve_stars(0)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_increments_a_counter_if_condition_evaluates_to_true
|
11
|
+
klass = Class.new(TestClass) do
|
12
|
+
storage_bucket :stars, :counter => true, :condition => :something_truthy
|
13
|
+
|
14
|
+
def something_truthy(frivol_method, *args)
|
15
|
+
'Wax museum'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
t = klass.new
|
19
|
+
|
20
|
+
t.increment_stars
|
21
|
+
assert_equal 1, t.retrieve_stars(0)
|
22
|
+
end
|
23
|
+
|
24
|
+
def test_increments_a_counter_with_positive_condition
|
25
|
+
t = Class.new(TestClass) { storage_bucket :stars, :counter => true, :condition => Proc.new{true} }.new
|
26
|
+
t.increment_stars
|
27
|
+
assert_equal 1, t.retrieve_stars(0)
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_increments_a_counter_when_condition_is_a_proc
|
31
|
+
klass = Class.new(TestClass) do
|
32
|
+
storage_bucket :stars, :counter => true, :condition => Proc.new{ |instance, frivol_method, *args| instance.something_truthy }
|
33
|
+
|
34
|
+
def something_truthy
|
35
|
+
'Wax museum'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
t = klass.new
|
39
|
+
|
40
|
+
t.increment_stars
|
41
|
+
assert_equal 1, t.retrieve_stars(0)
|
42
|
+
end
|
43
|
+
|
44
|
+
def test_does_not_increment_a_counter_with_negative_condition
|
45
|
+
t = Class.new(TestClass) { storage_bucket :stars, :counter => true, :condition => Proc.new{false} }.new
|
46
|
+
t.increment_stars
|
47
|
+
assert_equal 0, t.retrieve_stars(0)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_does_not_increment_a_counter_when_condition_is_false
|
51
|
+
t = Class.new(TestClass) { storage_bucket :stars, :counter => true, :condition => false }.new
|
52
|
+
t.increment_stars
|
53
|
+
assert_equal 0, t.retrieve_stars(0)
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_increment_by_does_not_increment_with_condition_is_false
|
57
|
+
t = Class.new(TestClass) { storage_bucket :stars, :counter => true, :condition => false }.new
|
58
|
+
t.increment_stars_by(20)
|
59
|
+
assert_equal 0, t.retrieve_stars(0)
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_store_does_not_set_the_value_if_condition_is_negative
|
63
|
+
t = Class.new(TestClass) { storage_bucket :stars, :counter => true, :condition => Proc.new{ false } }.new
|
64
|
+
t.store_stars(20)
|
65
|
+
assert_equal 0, t.retrieve_stars(0)
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_decrement_only_happens_if_condition_is_positive
|
69
|
+
condition_proc = proc do |instance, method_name, *args|
|
70
|
+
method_name != 'increment_stars'
|
71
|
+
end
|
72
|
+
t = Class.new(TestClass) { storage_bucket :stars, :counter => true, :condition => condition_proc }.new
|
73
|
+
t.store_stars(20)
|
74
|
+
t.increment_stars
|
75
|
+
t.decrement_stars
|
76
|
+
assert_equal 19, t.retrieve_stars(0)
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_decrement_by_only_happens_if_condition_is_positive
|
80
|
+
condition_proc = proc do |instance, method_name, *args|
|
81
|
+
method_name != 'increment_stars'
|
82
|
+
end
|
83
|
+
|
84
|
+
t = Class.new(TestClass) { storage_bucket :stars, :counter => true, :condition => condition_proc }.new
|
85
|
+
t.store_stars(20)
|
86
|
+
t.increment_stars
|
87
|
+
t.decrement_stars_by 10
|
88
|
+
assert_equal 10, t.retrieve_stars(0)
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require "#{File.expand_path(File.dirname(__FILE__))}/helper.rb"
|
2
|
+
|
3
|
+
class TestCounters < Test::Unit::TestCase
|
4
|
+
def test_be_able_to_create_counter_buckets
|
5
|
+
t = Class.new(TestClass) { storage_bucket :blue, :counter => true }.new
|
6
|
+
|
7
|
+
assert t.respond_to?(:store_blue)
|
8
|
+
assert t.respond_to?(:retrieve_blue)
|
9
|
+
assert t.respond_to?(:increment_blue)
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_store_increment_and_retrieve_integers_in_a_counter
|
13
|
+
t = Class.new(TestClass) { storage_bucket :blue, :counter => true }.new
|
14
|
+
t.store_blue 10
|
15
|
+
|
16
|
+
assert_equal 10, t.retrieve_blue(0)
|
17
|
+
assert_equal 11, t.increment_blue
|
18
|
+
assert_equal 11, t.retrieve_blue(0)
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_increment_by_and_retrieve_integers_in_a_counter
|
22
|
+
t = Class.new(TestClass) do
|
23
|
+
storage_bucket :cats, :counter => true
|
24
|
+
|
25
|
+
def kittens
|
26
|
+
increment_cats_by 5
|
27
|
+
end
|
28
|
+
end.new
|
29
|
+
|
30
|
+
t.store_cats 1
|
31
|
+
assert_equal 1, t.retrieve_cats(0)
|
32
|
+
t.kittens
|
33
|
+
assert_equal 6, t.retrieve_cats(0)
|
34
|
+
end
|
35
|
+
|
36
|
+
def test_store_decrement_and_retrieve_integers_in_a_counter
|
37
|
+
t = Class.new(TestClass) { storage_bucket :red, :counter => true }.new
|
38
|
+
|
39
|
+
assert_equal 0, t.retrieve_red(0)
|
40
|
+
t.store_red(10)
|
41
|
+
assert_equal 10, t.retrieve_red(0)
|
42
|
+
assert_equal 9, t.decrement_red
|
43
|
+
assert_equal 9, t.retrieve_red(0)
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_decrement_by_and_retrieve_integers_in_a_counter
|
47
|
+
t = Class.new(TestClass) do
|
48
|
+
storage_bucket :money, :counter => true
|
49
|
+
|
50
|
+
def shopping
|
51
|
+
decrement_money_by 1000
|
52
|
+
end
|
53
|
+
end.new
|
54
|
+
|
55
|
+
t.store_money 2000
|
56
|
+
assert_equal 2000, t.retrieve_money(0)
|
57
|
+
t.shopping
|
58
|
+
assert_equal 1000, t.retrieve_money(0)
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_set_expiry_on_counters
|
62
|
+
klass = Class.new(TestClass) { storage_bucket :sheep, :counter => true, :expires_in => 1 }
|
63
|
+
klass.new
|
64
|
+
assert_equal 1, klass.storage_expiry(:sheep)
|
65
|
+
end
|
66
|
+
|
67
|
+
def test_expire_a_counter_bucket
|
68
|
+
t = Class.new(TestClass) { storage_bucket :yellow, :counter => true, :expires_in => -1 }.new
|
69
|
+
t.store_yellow 10
|
70
|
+
assert_equal 0, t.retrieve_yellow(0)
|
71
|
+
end
|
72
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require "#{File.expand_path(File.dirname(__FILE__))}/helper.rb"
|
2
|
+
|
3
|
+
class TestElseWithCounters < Test::Unit::TestCase
|
4
|
+
def test_given_CONDITION_evaluates_to_true_ELSE_is_not_performed
|
5
|
+
else_proc = Proc.new{raise StandardError.new('You\'ll never catch me!') }
|
6
|
+
t = Class.new(TestClass) { storage_bucket :stars, :counter => true, :condition => Proc.new{true}, :else => else_proc }.new
|
7
|
+
|
8
|
+
assert_nothing_raised do
|
9
|
+
t.increment_stars
|
10
|
+
assert_equal 1, t.retrieve_stars(0)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_given_CONDITION_evaluates_to_false_ELSE_is_performed
|
15
|
+
else_proc = Proc.new{ |instance, method_name, *args| raise StandardError.new('You\'ll never catch me!') if method_name == 'increment_stars' }
|
16
|
+
t = Class.new(TestClass) { storage_bucket :stars, :counter => true, :condition => Proc.new{false}, :else => else_proc }.new
|
17
|
+
|
18
|
+
assert_raises StandardError do
|
19
|
+
t.increment_stars
|
20
|
+
end
|
21
|
+
|
22
|
+
assert_equal 0, t.retrieve_stars(0)
|
23
|
+
end
|
24
|
+
|
25
|
+
def test_given_CONDITION_evaluates_to_false_ELSE_calls_a_method_on_the_object
|
26
|
+
klass = Class.new(TestClass) do
|
27
|
+
condition_proc = Proc.new{ |instance, method_name, *args| method_name != 'increment_stars'}
|
28
|
+
storage_bucket :stars, :counter => true, :condition => condition_proc, :else => :set_stars_to_20
|
29
|
+
|
30
|
+
def set_stars_to_20(frivol_method, *args)
|
31
|
+
store_stars 20
|
32
|
+
end
|
33
|
+
end
|
34
|
+
t = klass.new
|
35
|
+
|
36
|
+
t.increment_stars
|
37
|
+
assert_equal 20, t.retrieve_stars(0)
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require "#{File.expand_path(File.dirname(__FILE__))}/helper.rb"
|
2
|
+
|
3
|
+
class TestExtensions < Test::Unit::TestCase
|
4
|
+
require "#{File.join(File.expand_path(File.dirname(__FILE__)), '../lib/frivol/time_extensions')}"
|
5
|
+
|
6
|
+
def test_time
|
7
|
+
time = Time.local(2014, 3, 4, 17, 53, 29)
|
8
|
+
|
9
|
+
t = TestClass.new
|
10
|
+
t.store(:time => time)
|
11
|
+
|
12
|
+
t = TestClass.new
|
13
|
+
assert_equal time, t.retrieve(:time => nil)
|
14
|
+
end
|
15
|
+
end
|