frivol 0.3.1 → 0.4.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 +4 -4
- data/.travis.yml +7 -6
- data/CHANGES.md +15 -0
- data/Gemfile +4 -1
- data/README.rdoc +56 -24
- data/Rakefile +17 -0
- data/VERSION +1 -1
- data/frivol.gemspec +19 -6
- data/lib/frivol.rb +1 -3
- data/lib/frivol/backend/multi.rb +159 -0
- data/lib/frivol/backend/redis.rb +93 -0
- data/lib/frivol/backend/redis_distributed.rb +51 -0
- data/lib/frivol/backend/riak.rb +186 -0
- data/lib/frivol/class_methods.rb +10 -4
- data/lib/frivol/config.rb +8 -22
- data/lib/frivol/helpers.rb +32 -34
- data/test/fake_redis.rb +44 -19
- data/test/helper.rb +116 -6
- data/test/test_backend.rb +33 -0
- data/test/test_counters.rb +31 -0
- data/test/test_frivol.rb +24 -9
- data/test/test_frivolize.rb +11 -11
- data/test/test_multi_backend.rb +127 -0
- data/test/test_multi_backend_counters.rb +113 -0
- data/test/test_multi_backend_expiry.rb +128 -0
- data/test/test_riak.rb +41 -0
- data/test/test_threads.rb +6 -1
- metadata +30 -6
data/test/fake_redis.rb
CHANGED
@@ -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
|
11
|
-
|
12
|
-
|
13
|
-
|
11
|
+
def get(key)
|
12
|
+
result = expired?(key) ? nil : @storage[key]
|
13
|
+
@results << result
|
14
|
+
result
|
14
15
|
end
|
15
16
|
|
16
|
-
def
|
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
|
-
|
71
|
-
|
72
|
-
|
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
|
data/test/helper.rb
CHANGED
@@ -7,14 +7,110 @@ require 'frivol'
|
|
7
7
|
|
8
8
|
class Test::Unit::TestCase
|
9
9
|
def setup
|
10
|
-
|
11
|
-
|
12
|
-
|
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
|
-
|
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
|
-
|
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
|
data/test/test_counters.rb
CHANGED
@@ -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
|
data/test/test_frivol.rb
CHANGED
@@ -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
|
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
|
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 -
|
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
|
120
|
+
assert @backend.ttl(t.storage_key) > 0
|
105
121
|
|
106
122
|
t.store :value => 'value' # a second time
|
107
|
-
assert
|
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
|
-
|
123
|
-
def
|
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
|
-
|
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
|
|
data/test/test_frivolize.rb
CHANGED
@@ -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
|
36
|
+
def dinosaurus_count
|
37
37
|
@@count += 1
|
38
38
|
end
|
39
|
-
frivolize :
|
39
|
+
frivolize :dinosaurus_count, { :bucket => :dinosauruss, :expires_in => -1 }
|
40
40
|
end
|
41
41
|
|
42
42
|
t = klass.new
|
43
|
-
assert_equal 1, t.
|
44
|
-
assert_equal 1, t.
|
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.
|
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
|
56
|
+
def dinosaurii_count
|
57
57
|
@@count += 1
|
58
58
|
end
|
59
|
-
frivolize :
|
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? ? '
|
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.
|
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.
|
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
|