frivol 0.2.0 → 0.3.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.
- 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
|