frivol 0.3.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,127 @@
1
+ require "#{File.expand_path(File.dirname(__FILE__))}/helper.rb"
2
+
3
+ class TestMultiBackend < Test::Unit::TestCase
4
+ if multi_test?
5
+ KEY = :foo
6
+ VALUE = 'bar'
7
+ DATA = Frivol::Helpers.dump_json({ KEY => VALUE })
8
+
9
+ def test_backends_must_be_uniq
10
+ assert_raises Frivol::Backend::Multi::BackendsNotUniqueError do
11
+ Frivol::Backend::Multi.new([ @old_backend, @old_backend ])
12
+ end
13
+
14
+ assert_raises Frivol::Backend::Multi::BackendsNotUniqueError do
15
+ Frivol::Backend::Multi.new([ @new_backend, @new_backend ])
16
+ end
17
+ end
18
+
19
+ def test_ttl
20
+ t = TestClass.new
21
+ assert_nil @backend.ttl(t.storage_key)
22
+
23
+ @old_backend.set(t.storage_key, DATA)
24
+ assert_nil @backend.ttl(t.storage_key)
25
+ end
26
+
27
+ def test_exists
28
+ t = TestClass.new
29
+ refute @backend.exists(t.storage_key)
30
+
31
+ @old_backend.set(t.storage_key, DATA)
32
+ assert @backend.exists(t.storage_key)
33
+ end
34
+
35
+ def test_del
36
+ t = TestClass.new
37
+ @old_backend.set(t.storage_key, DATA)
38
+ assert @backend.exists(t.storage_key)
39
+ @backend.del(t.storage_key)
40
+ refute @backend.exists(t.storage_key)
41
+ refute @old_backend.exists(t.storage_key)
42
+ end
43
+
44
+ def test_get
45
+ t = TestClass.new
46
+ @old_backend.set(t.storage_key, DATA)
47
+ assert_equal DATA, @backend.get(t.storage_key)
48
+ # Because get migrates
49
+ assert @new_backend.exists(t.storage_key)
50
+ refute @old_backend.exists(t.storage_key)
51
+ end
52
+
53
+ def test_retrieve_non_existing
54
+ t = TestClass.new
55
+ assert_nothing_raised do
56
+ assert_nil t.retrieve(:nothing => nil)
57
+ end
58
+ end
59
+
60
+ def test_retrieve
61
+ t = TestClass.new
62
+ @old_backend.set(t.storage_key, DATA)
63
+ assert_equal VALUE, t.retrieve(KEY => false)
64
+ # Because get migrates
65
+ assert @new_backend.exists(t.storage_key)
66
+ refute @old_backend.exists(t.storage_key)
67
+ end
68
+
69
+ def test_get_with_bucket
70
+ t = Class.new(TestClass) { storage_bucket :diamonds }.new
71
+ @old_backend.set(t.storage_key(:diamonds), DATA)
72
+ assert_equal DATA, @backend.get(t.storage_key(:diamonds))
73
+ # Because get migrates
74
+ assert @new_backend.exists(t.storage_key(:diamonds))
75
+ refute @old_backend.exists(t.storage_key(:diamonds))
76
+ end
77
+
78
+ def test_retrieve_with_bucket
79
+ t = Class.new(TestClass) { storage_bucket :sapphires }.new
80
+ @old_backend.set(t.storage_key(:sapphires), DATA)
81
+ assert_equal VALUE, t.retrieve_sapphires(KEY => false)
82
+ # Because get migrates
83
+ assert @new_backend.exists(t.storage_key(:sapphires))
84
+ refute @old_backend.exists(t.storage_key(:sapphires))
85
+ end
86
+
87
+ def test_set
88
+ t = TestClass.new
89
+ @old_backend.set(t.storage_key, DATA)
90
+ @backend.set(t.storage_key, DATA)
91
+ assert_equal DATA, @backend.get(t.storage_key)
92
+ # Because set deletes from old backends
93
+ assert @new_backend.exists(t.storage_key)
94
+ refute @old_backend.exists(t.storage_key)
95
+ end
96
+
97
+ def test_store
98
+ t = TestClass.new
99
+ @old_backend.set(t.storage_key, DATA)
100
+ t.store KEY => VALUE
101
+ assert_equal VALUE, t.retrieve(KEY => false)
102
+ # Because set deletes from old backends
103
+ assert @new_backend.exists(t.storage_key)
104
+ refute @old_backend.exists(t.storage_key)
105
+ end
106
+
107
+ def test_set_with_bucket
108
+ t = Class.new(TestClass) { storage_bucket :garnets }.new
109
+ @old_backend.set(t.storage_key(:garnets), DATA)
110
+ @backend.set(t.storage_key(:garnets), DATA)
111
+ assert_equal DATA, @backend.get(t.storage_key(:garnets))
112
+ # Because set deletes from old backends
113
+ assert @new_backend.exists(t.storage_key(:garnets))
114
+ refute @old_backend.exists(t.storage_key(:garnets))
115
+ end
116
+
117
+ def test_store_with_bucket
118
+ t = Class.new(TestClass) { storage_bucket :topaz }.new
119
+ @old_backend.set(t.storage_key(:topaz), DATA)
120
+ t.store_topaz KEY => VALUE
121
+ assert_equal VALUE, t.retrieve_topaz(KEY => false)
122
+ # Because set deletes from old backends
123
+ assert @new_backend.exists(t.storage_key(:topaz))
124
+ refute @old_backend.exists(t.storage_key(:topaz))
125
+ end
126
+ end
127
+ end
@@ -0,0 +1,113 @@
1
+ require "#{File.expand_path(File.dirname(__FILE__))}/helper.rb"
2
+
3
+ class TestMultiBackendCounters < Test::Unit::TestCase
4
+ if multi_test?
5
+ KEY = :foo
6
+ VALUE = 'bar'
7
+ DATA = Frivol::Helpers.dump_json({ KEY => VALUE })
8
+
9
+ def test_ttl
10
+ t = Class.new(TestClass) { storage_bucket :crows, :counter => true }.new
11
+ assert_nil @backend.ttl(t.storage_key(:crows))
12
+
13
+ @old_backend.setc(t.storage_key(:crows), 1)
14
+ assert_nil @backend.ttl(t.storage_key(:crows))
15
+ end
16
+
17
+ def test_existsc
18
+ t = Class.new(TestClass) { storage_bucket :doves, :counter => true }.new
19
+ refute @backend.existsc(t.storage_key(:doves))
20
+
21
+ @old_backend.setc(t.storage_key(:doves), 1)
22
+ assert @backend.existsc(t.storage_key(:doves))
23
+ end
24
+
25
+ def test_delc
26
+ t = Class.new(TestClass) { storage_bucket :toucans, :counter => true }.new
27
+ @old_backend.setc(t.storage_key(:toucans), 2)
28
+ assert @backend.existsc(t.storage_key(:toucans))
29
+ @backend.delc(t.storage_key(:toucans))
30
+ refute @backend.existsc(t.storage_key(:toucans))
31
+ refute @old_backend.existsc(t.storage_key(:toucans))
32
+ end
33
+
34
+ def test_getc
35
+ t = Class.new(TestClass) { storage_bucket :parrots, :counter => true }.new
36
+ @old_backend.setc(t.storage_key(:parrots), 1)
37
+ assert_equal 1, @backend.getc(t.storage_key(:parrots))
38
+ # Because get migrates
39
+ assert @new_backend.existsc(t.storage_key(:parrots))
40
+ refute @old_backend.existsc(t.storage_key(:parrots))
41
+ end
42
+
43
+ def test_retrieve
44
+ t = Class.new(TestClass) { storage_bucket :lovebirds, :counter => true }.new
45
+ @old_backend.setc(t.storage_key(:lovebirds), 1)
46
+ assert_equal 1, t.retrieve_lovebirds(KEY => false)
47
+ # Because get migrates
48
+ assert @new_backend.existsc(t.storage_key(:lovebirds))
49
+ refute @old_backend.existsc(t.storage_key(:lovebirds))
50
+ end
51
+
52
+ def test_setc
53
+ t = Class.new(TestClass) { storage_bucket :parakeets, :counter => true }.new
54
+ @old_backend.setc(t.storage_key(:parakeets), 1)
55
+ @backend.setc(t.storage_key(:parakeets), 2)
56
+ assert_equal 2, @backend.getc(t.storage_key(:parakeets)).to_i
57
+ # Because set deletes from old backends
58
+ assert @new_backend.existsc(t.storage_key(:parakeets))
59
+ refute @old_backend.existsc(t.storage_key(:parakeets))
60
+ end
61
+
62
+ def test_store
63
+ t = Class.new(TestClass) { storage_bucket :macaws, :counter => true }.new
64
+ @old_backend.setc(t.storage_key(:macaws), 1)
65
+ t.store_macaws 2
66
+ assert_equal 2, t.retrieve_macaws(0)
67
+ # Because set deletes from old backends
68
+ assert @new_backend.existsc(t.storage_key(:macaws))
69
+ refute @old_backend.existsc(t.storage_key(:macaws))
70
+ end
71
+
72
+ def test_increment
73
+ t = Class.new(TestClass) { storage_bucket :cockatoos, :counter => true }.new
74
+ @old_backend.setc(t.storage_key(:cockatoos), 2)
75
+ t.increment_cockatoos
76
+ assert_equal 3, t.retrieve_cockatoos(0)
77
+ # Because set deletes from old backends
78
+ assert @new_backend.existsc(t.storage_key(:cockatoos))
79
+ refute @old_backend.existsc(t.storage_key(:cockatoos))
80
+ end
81
+
82
+ def test_increment_by
83
+ t = Class.new(TestClass) { storage_bucket :canaries, :counter => true }.new
84
+ @old_backend.setc(t.storage_key(:canaries), 3)
85
+ t.increment_canaries_by(6)
86
+ assert_equal 9, t.retrieve_canaries(0)
87
+ # Because set deletes from old backends
88
+ assert @new_backend.existsc(t.storage_key(:canaries))
89
+ refute @old_backend.existsc(t.storage_key(:canaries))
90
+ end
91
+
92
+ def test_decrement
93
+ t = Class.new(TestClass) { storage_bucket :budgies, :counter => true }.new
94
+ @old_backend.setc(t.storage_key(:budgies), 7)
95
+ t.decrement_budgies
96
+ assert_equal 6, t.retrieve_budgies(0)
97
+ # Because set deletes from old backends
98
+ assert @new_backend.existsc(t.storage_key(:budgies))
99
+ refute @old_backend.existsc(t.storage_key(:budgies))
100
+ end
101
+
102
+ def test_decrement_by
103
+ t = Class.new(TestClass) { storage_bucket :finches, :counter => true }.new
104
+ @old_backend.setc(t.storage_key(:finches), 8)
105
+ t.decrement_finches_by(2)
106
+ assert_equal 6, t.retrieve_finches(0)
107
+ # Because set deletes from old backends
108
+ assert @new_backend.existsc(t.storage_key(:finches))
109
+ refute @old_backend.existsc(t.storage_key(:finches))
110
+ end
111
+
112
+ end
113
+ end
@@ -0,0 +1,128 @@
1
+ require "#{File.expand_path(File.dirname(__FILE__))}/helper.rb"
2
+
3
+ class TestMultiBackendExpiry < Test::Unit::TestCase
4
+ if multi_test?
5
+ KEY = :foo
6
+ VALUE = 'bar'
7
+ DATA = Frivol::Helpers.dump_json({ KEY => VALUE })
8
+ EXPIRY = 10 #seconds
9
+
10
+ def test_ttl
11
+ t = Class.new(TestClass) { storage_expires_in EXPIRY }.new
12
+ assert_nil @backend.ttl(t.storage_key)
13
+
14
+ @old_backend.set(t.storage_key, DATA)
15
+ @old_backend.expire(t.storage_key, EXPIRY)
16
+ assert_in_delta EXPIRY, @backend.ttl(t.storage_key), 2
17
+ end
18
+
19
+ def test_exists
20
+ t = Class.new(TestClass) { storage_expires_in EXPIRY }.new
21
+ refute @backend.exists(t.storage_key)
22
+
23
+ @old_backend.set(t.storage_key, DATA)
24
+ assert @backend.exists(t.storage_key)
25
+ end
26
+
27
+ def test_exists_after_expire
28
+ t = Class.new(TestClass) { storage_expires_in -1 }.new
29
+ @old_backend.set(t.storage_key, DATA, -1)
30
+ assert_nil @backend.exists(t.storage_key)
31
+ end
32
+
33
+ def test_get
34
+ t = TestClass.new
35
+ @old_backend.set(t.storage_key, DATA, EXPIRY)
36
+ assert_equal DATA, @backend.get(t.storage_key)
37
+ assert_in_delta EXPIRY, @backend.ttl(t.storage_key), 2
38
+ # Because get migrates
39
+ assert @new_backend.exists(t.storage_key)
40
+ refute @old_backend.exists(t.storage_key)
41
+ end
42
+
43
+ def test_retrieve
44
+ t = Class.new(TestClass) { storage_expires_in EXPIRY }.new
45
+ @old_backend.set(t.storage_key, DATA, EXPIRY)
46
+ assert_equal VALUE, t.retrieve(KEY => false)
47
+ # Because get migrates
48
+ assert_in_delta EXPIRY, @backend.ttl(t.storage_key), 2
49
+ assert_in_delta EXPIRY, @new_backend.ttl(t.storage_key), 2
50
+ assert @new_backend.exists(t.storage_key)
51
+ refute @old_backend.exists(t.storage_key)
52
+ end
53
+
54
+ def test_get_with_bucket
55
+ t = Class.new(TestClass) { storage_bucket :diamonds, :expires_in => EXPIRY }.new
56
+ @old_backend.set(t.storage_key(:diamonds), DATA, EXPIRY)
57
+ assert_equal DATA, @backend.get(t.storage_key(:diamonds))
58
+ # Because get migrates
59
+ assert_in_delta EXPIRY, @backend.ttl(t.storage_key(:diamonds)), 2
60
+ assert_in_delta EXPIRY, @new_backend.ttl(t.storage_key(:diamonds)), 2
61
+ assert @new_backend.exists(t.storage_key(:diamonds))
62
+ refute @old_backend.exists(t.storage_key(:diamonds))
63
+ end
64
+
65
+ def test_retrieve_with_bucket
66
+ t = Class.new(TestClass) { storage_bucket :sapphires, :expires_in => EXPIRY }.new
67
+ @old_backend.set(t.storage_key(:sapphires), DATA, EXPIRY)
68
+ assert_equal VALUE, t.retrieve_sapphires(KEY => false)
69
+ # Because get migrates
70
+ assert_in_delta EXPIRY, @backend.ttl(t.storage_key(:sapphires)), 2
71
+ assert_in_delta EXPIRY, @new_backend.ttl(t.storage_key(:sapphires)), 2
72
+ assert @new_backend.exists(t.storage_key(:sapphires))
73
+ refute @old_backend.exists(t.storage_key(:sapphires))
74
+ end
75
+
76
+ def test_set
77
+ t = TestClass.new
78
+ @old_backend.set(t.storage_key, DATA, EXPIRY)
79
+ @backend.set(t.storage_key, DATA, EXPIRY)
80
+ assert_equal DATA, @backend.get(t.storage_key)
81
+ assert_in_delta EXPIRY, @backend.ttl(t.storage_key), 2
82
+ assert_in_delta EXPIRY, @new_backend.ttl(t.storage_key), 2
83
+ assert_nil @old_backend.ttl(t.storage_key)
84
+ # Because set deletes from old backends
85
+ assert @new_backend.exists(t.storage_key)
86
+ refute @old_backend.exists(t.storage_key)
87
+ end
88
+
89
+ def test_store
90
+ t = Class.new(TestClass) { storage_expires_in EXPIRY }.new
91
+ @old_backend.set(t.storage_key, DATA, EXPIRY)
92
+ t.store KEY => VALUE
93
+ assert_equal VALUE, t.retrieve(KEY => false)
94
+ assert_in_delta EXPIRY, @backend.ttl(t.storage_key), 2
95
+ assert_in_delta EXPIRY, @new_backend.ttl(t.storage_key), 2
96
+ assert_nil @old_backend.ttl(t.storage_key)
97
+ # Because set deletes from old backends
98
+ assert @new_backend.exists(t.storage_key)
99
+ refute @old_backend.exists(t.storage_key)
100
+ end
101
+
102
+ def test_set_with_bucket
103
+ t = Class.new(TestClass) { storage_bucket :garnets, :expires_in => EXPIRY }.new
104
+ @old_backend.set(t.storage_key(:garnets), DATA, EXPIRY)
105
+ @backend.set(t.storage_key(:garnets), DATA, EXPIRY)
106
+ assert_equal DATA, @backend.get(t.storage_key(:garnets))
107
+ assert_in_delta EXPIRY, @backend.ttl(t.storage_key(:garnets)), 2
108
+ assert_in_delta EXPIRY, @new_backend.ttl(t.storage_key(:garnets)), 2
109
+ assert_nil @old_backend.ttl(t.storage_key(:garnets))
110
+ # Because set deletes from old backends
111
+ assert @new_backend.exists(t.storage_key(:garnets))
112
+ refute @old_backend.exists(t.storage_key(:garnets))
113
+ end
114
+
115
+ def test_store_with_bucket
116
+ t = Class.new(TestClass) { storage_bucket :topaz, :expires_in => EXPIRY }.new
117
+ @old_backend.set(t.storage_key(:topaz), DATA, EXPIRY)
118
+ t.store_topaz KEY => VALUE
119
+ assert_equal VALUE, t.retrieve_topaz(KEY => false)
120
+ assert_in_delta EXPIRY, @backend.ttl(t.storage_key(:topaz)), 2
121
+ assert_in_delta EXPIRY, @new_backend.ttl(t.storage_key(:topaz)), 2
122
+ assert_nil @old_backend.ttl(t.storage_key(:topaz))
123
+ # Because set deletes from old backends
124
+ assert @new_backend.exists(t.storage_key(:topaz))
125
+ refute @old_backend.exists(t.storage_key(:topaz))
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,41 @@
1
+ require "#{File.expand_path(File.dirname(__FILE__))}/helper.rb"
2
+
3
+ class TestRiak < Test::Unit::TestCase
4
+ if riak_test?
5
+ def test_prefix
6
+ backend = Frivol::Backend::Riak.new(
7
+ :nodes => [ { :host => '127.0.0.1' } ],
8
+ :prefix => 'test_riak_'
9
+ )
10
+ Frivol::Config.backend = backend
11
+
12
+ t = Class.new(TestClass) do
13
+ storage_bucket :meat
14
+ storage_bucket :and, :expires_in => 100
15
+ storage_bucket :veg, :counter => true
16
+ end.new
17
+
18
+ t.store :test_key => 'test_value'
19
+ nonprefixed_bucket = backend.connection.bucket("frivol_objects")
20
+ refute nonprefixed_bucket.exist?(t.storage_key)
21
+ prefixed_bucket = backend.connection.bucket("test_riak_frivol_objects")
22
+ assert prefixed_bucket.exist?(t.storage_key)
23
+
24
+ t.store_meat :test_key => 'test_value'
25
+ refute nonprefixed_bucket.exist?(t.storage_key(:meat))
26
+ assert prefixed_bucket.exist?(t.storage_key(:meat))
27
+
28
+ t.store_and :test_key => 'test_value'
29
+ nonprefixed_bucket = backend.connection.bucket("frivol_expires")
30
+ refute nonprefixed_bucket.exist?(t.storage_key(:and))
31
+ prefixed_bucket = backend.connection.bucket("test_riak_frivol_expires")
32
+ assert prefixed_bucket.exist?(t.storage_key(:and))
33
+
34
+ t.store_veg 5 # a day
35
+ nonprefixed_bucket = backend.connection.bucket("frivol_counters")
36
+ refute nonprefixed_bucket.exist?(t.storage_key(:veg))
37
+ prefixed_bucket = backend.connection.bucket("test_riak_frivol_counters")
38
+ assert prefixed_bucket.exist?(t.storage_key(:veg))
39
+ end
40
+ end
41
+ end
@@ -1,15 +1,20 @@
1
1
  require "#{File.expand_path(File.dirname(__FILE__))}/helper.rb"
2
+ require "thread"
2
3
 
3
4
  class TestThreads < Test::Unit::TestCase
4
5
  def test_each_thread_gets_its_own_connection
5
6
  threads = []
7
+ queue = Queue.new
6
8
  2.times do
7
9
  threads << Thread.new do
8
10
  t = TestClass.new
9
11
  t.retrieve :nothing => nil
12
+ queue << @backend.connection
10
13
  end
11
14
  end
12
15
  threads.each { |thread| thread.join }
13
- assert_not_equal threads.first[:frivol_redis], threads.last[:frivol_redis]
16
+ connection1 = queue.pop
17
+ connection2 = queue.pop
18
+ assert_not_equal connection2, connection1
14
19
  end
15
20
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: frivol
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Marc Heiligers
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-06 00:00:00.000000000 Z
11
+ date: 2015-06-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: multi_json
@@ -25,7 +25,7 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: redis
28
+ name: rake
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - ">="
@@ -39,13 +39,13 @@ dependencies:
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rake
42
+ name: jeweler
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
47
  version: '0'
48
- type: :runtime
48
+ type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
@@ -53,7 +53,21 @@ dependencies:
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
55
  - !ruby/object:Gem::Dependency
56
- name: jeweler
56
+ name: pry
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: pry-debugger
57
71
  requirement: !ruby/object:Gem::Requirement
58
72
  requirements:
59
73
  - - ">="
@@ -77,6 +91,7 @@ extra_rdoc_files:
77
91
  files:
78
92
  - ".document"
79
93
  - ".travis.yml"
94
+ - CHANGES.md
80
95
  - Gemfile
81
96
  - LICENSE
82
97
  - README.rdoc
@@ -109,6 +124,10 @@ files:
109
124
  - doc/rdoc-style.css
110
125
  - frivol.gemspec
111
126
  - lib/frivol.rb
127
+ - lib/frivol/backend/multi.rb
128
+ - lib/frivol/backend/redis.rb
129
+ - lib/frivol/backend/redis_distributed.rb
130
+ - lib/frivol/backend/riak.rb
112
131
  - lib/frivol/class_methods.rb
113
132
  - lib/frivol/config.rb
114
133
  - lib/frivol/functor.rb
@@ -116,6 +135,7 @@ files:
116
135
  - lib/frivol/time_extensions.rb
117
136
  - test/fake_redis.rb
118
137
  - test/helper.rb
138
+ - test/test_backend.rb
119
139
  - test/test_buckets.rb
120
140
  - test/test_condition.rb
121
141
  - test/test_condition_with_counters.rb
@@ -124,6 +144,10 @@ files:
124
144
  - test/test_extensions.rb
125
145
  - test/test_frivol.rb
126
146
  - test/test_frivolize.rb
147
+ - test/test_multi_backend.rb
148
+ - test/test_multi_backend_counters.rb
149
+ - test/test_multi_backend_expiry.rb
150
+ - test/test_riak.rb
127
151
  - test/test_seeds.rb
128
152
  - test/test_threads.rb
129
153
  homepage: http://github.com/marcheiligers/frivol