juno 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +4 -0
- data/.travis.yml +25 -0
- data/Gemfile +19 -0
- data/LICENSE +20 -0
- data/README.md +83 -0
- data/Rakefile +15 -0
- data/SPEC.md +95 -0
- data/juno.gemspec +22 -0
- data/lib/juno.rb +31 -0
- data/lib/juno/activerecord.rb +58 -0
- data/lib/juno/base.rb +113 -0
- data/lib/juno/couch.rb +43 -0
- data/lib/juno/datamapper.rb +63 -0
- data/lib/juno/dbm.rb +15 -0
- data/lib/juno/expires.rb +45 -0
- data/lib/juno/file.rb +62 -0
- data/lib/juno/gdbm.rb +15 -0
- data/lib/juno/hashfile.rb +12 -0
- data/lib/juno/localmemcache.rb +16 -0
- data/lib/juno/memcached.rb +7 -0
- data/lib/juno/memcached_dalli.rb +55 -0
- data/lib/juno/memcached_native.rb +56 -0
- data/lib/juno/memory.rb +7 -0
- data/lib/juno/mongodb.rb +43 -0
- data/lib/juno/proxy.rb +5 -0
- data/lib/juno/pstore.rb +49 -0
- data/lib/juno/redis.rb +34 -0
- data/lib/juno/riak.rb +45 -0
- data/lib/juno/sdbm.rb +15 -0
- data/lib/juno/sequel.rb +51 -0
- data/lib/juno/sqlite.rb +50 -0
- data/lib/juno/tokyocabinet.rb +36 -0
- data/lib/juno/version.rb +3 -0
- data/lib/juno/yaml.rb +9 -0
- data/test/helper.rb +182 -0
- data/test/test_active_record.rb +36 -0
- data/test/test_couch.rb +13 -0
- data/test/test_datamapper.rb +64 -0
- data/test/test_dbm.rb +13 -0
- data/test/test_expires.rb +10 -0
- data/test/test_file.rb +9 -0
- data/test/test_gdbm.rb +13 -0
- data/test/test_hashfile.rb +9 -0
- data/test/test_localmemcache.rb +13 -0
- data/test/test_memcached.rb +15 -0
- data/test/test_memcached_dalli.rb +15 -0
- data/test/test_memcached_native.rb +15 -0
- data/test/test_memory.rb +9 -0
- data/test/test_mongodb.rb +13 -0
- data/test/test_proxy.rb +9 -0
- data/test/test_pstore.rb +9 -0
- data/test/test_redis.rb +13 -0
- data/test/test_riak.rb +13 -0
- data/test/test_sdbm.rb +13 -0
- data/test/test_sequel.rb +13 -0
- data/test/test_sqlite.rb +13 -0
- data/test/test_tokyocabinet.rb +13 -0
- data/test/test_yaml.rb +9 -0
- data/unsupported/benchmarks.rb +234 -0
- data/unsupported/cassandra.rb +45 -0
- data/unsupported/fog.rb +60 -0
- data/unsupported/test_cassandra.rb +13 -0
- data/unsupported/test_rackspace.rb +15 -0
- data/unsupported/test_s3.rb +15 -0
- data/unsupported/test_tokyotyrant.rb +13 -0
- data/unsupported/tokyotyrant.rb +29 -0
- metadata +165 -0
data/lib/juno/proxy.rb
ADDED
data/lib/juno/pstore.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'pstore'
|
2
|
+
|
3
|
+
module Juno
|
4
|
+
class PStore < Base
|
5
|
+
def initialize(options = {})
|
6
|
+
raise 'No option :file specified' unless options[:file]
|
7
|
+
FileUtils.mkpath(::File.dirname(options[:file]))
|
8
|
+
@store = new_store(options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def key?(key, options = {})
|
12
|
+
@store.transaction(true) { @store.root?(key_for(key)) }
|
13
|
+
end
|
14
|
+
|
15
|
+
def [](key)
|
16
|
+
@store.transaction(true) { @store[key_for(key)] }
|
17
|
+
end
|
18
|
+
|
19
|
+
def delete(key, options = {})
|
20
|
+
@store.transaction { @store.delete(key_for(key)) }
|
21
|
+
end
|
22
|
+
|
23
|
+
def store(key, value, options = {})
|
24
|
+
@store.transaction { @store[key_for(key)] = value }
|
25
|
+
end
|
26
|
+
|
27
|
+
def clear(options = {})
|
28
|
+
@store.transaction do
|
29
|
+
@store.roots.each do |key|
|
30
|
+
@store.delete(key)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
protected
|
37
|
+
|
38
|
+
if RUBY_VERSION > '1.9'
|
39
|
+
def new_store(options)
|
40
|
+
# Create a thread-safe pstore by default
|
41
|
+
::PStore.new(options[:file], options.include?(:thread_safe) ? options[:thread_safe] : true)
|
42
|
+
end
|
43
|
+
else
|
44
|
+
def new_store(options)
|
45
|
+
::PStore.new(options[:file])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/juno/redis.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'redis'
|
2
|
+
|
3
|
+
module Juno
|
4
|
+
class Redis < Base
|
5
|
+
def initialize(options = {})
|
6
|
+
@store = ::Redis.new(options)
|
7
|
+
end
|
8
|
+
|
9
|
+
def key?(key, options = {})
|
10
|
+
@store.exists(key_for(key))
|
11
|
+
end
|
12
|
+
|
13
|
+
def [](key)
|
14
|
+
deserialize(@store.get(key_for(key)))
|
15
|
+
end
|
16
|
+
|
17
|
+
def delete(key, options = {})
|
18
|
+
string_key = key_for(key)
|
19
|
+
value = self[key]
|
20
|
+
@store.del(string_key) if value
|
21
|
+
value
|
22
|
+
end
|
23
|
+
|
24
|
+
def store(key, value, options = {})
|
25
|
+
@store.set(key_for(key), serialize(value))
|
26
|
+
value
|
27
|
+
end
|
28
|
+
|
29
|
+
def clear(options = {})
|
30
|
+
@store.flushdb
|
31
|
+
nil
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
data/lib/juno/riak.rb
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# Copyright: 2011 TMX Credit
|
2
|
+
# Author: Potapov Sergey (aka Blake)
|
3
|
+
|
4
|
+
require 'ripple'
|
5
|
+
|
6
|
+
module Juno
|
7
|
+
class Riak < Base
|
8
|
+
def initialize(options = {})
|
9
|
+
bucket_name = options.delete(:bucket) || 'juno'
|
10
|
+
client = ::Riak::Client.new(options)
|
11
|
+
@bucket = client.bucket(bucket_name)
|
12
|
+
end
|
13
|
+
|
14
|
+
def key?(key, options = {})
|
15
|
+
!!self[key]
|
16
|
+
end
|
17
|
+
|
18
|
+
def [](key)
|
19
|
+
deserialize(@bucket[key_for(key)].data)
|
20
|
+
rescue ::Riak::HTTPFailedRequest => err
|
21
|
+
nil
|
22
|
+
end
|
23
|
+
|
24
|
+
def delete(key, options = {})
|
25
|
+
value = self[key]
|
26
|
+
@bucket.delete(key_for(key))
|
27
|
+
value
|
28
|
+
end
|
29
|
+
|
30
|
+
def store(key, value, options = {})
|
31
|
+
obj = ::Riak::RObject.new(@bucket, key_for(key))
|
32
|
+
obj.content_type = 'text/plain'
|
33
|
+
obj.data = serialize(value)
|
34
|
+
obj.store
|
35
|
+
value
|
36
|
+
end
|
37
|
+
|
38
|
+
def clear(options = {})
|
39
|
+
@bucket.keys(:reload => true) do |keys|
|
40
|
+
keys.each{|key| @bucket.delete(key) }
|
41
|
+
end
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/juno/sdbm.rb
ADDED
data/lib/juno/sequel.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
|
3
|
+
module Juno
|
4
|
+
class Sequel < Base
|
5
|
+
def initialize(options = {})
|
6
|
+
raise 'No option :db specified' unless db = options.delete(:db)
|
7
|
+
@table = options.delete(:table) || :juno
|
8
|
+
@db = ::Sequel.connect(db, options)
|
9
|
+
end
|
10
|
+
|
11
|
+
def migrate
|
12
|
+
@db.create_table @table do
|
13
|
+
primary_key :k
|
14
|
+
String :k
|
15
|
+
String :v
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def key?(key, options = {})
|
20
|
+
!!sequel_table[:k => key_for(key)]
|
21
|
+
end
|
22
|
+
|
23
|
+
def [](key)
|
24
|
+
result = sequel_table[:k => key_for(key)]
|
25
|
+
result ? deserialize(result[:v]) : nil
|
26
|
+
end
|
27
|
+
|
28
|
+
def store(key, value, options = {})
|
29
|
+
sequel_table.insert(:k => key_for(key), :v => serialize(value))
|
30
|
+
value
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete(key, options = {})
|
34
|
+
if value = self[key]
|
35
|
+
sequel_table.filter(:k => key_for(key)).delete
|
36
|
+
value
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def clear(options = {})
|
41
|
+
sequel_table.delete
|
42
|
+
nil
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def sequel_table
|
48
|
+
@sequel_table ||= @db[@table]
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
data/lib/juno/sqlite.rb
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'sqlite3'
|
2
|
+
|
3
|
+
module Juno
|
4
|
+
class Sqlite < Base
|
5
|
+
def initialize(options = {})
|
6
|
+
raise 'No option :file specified' unless options[:file]
|
7
|
+
table = options[:table] || 'juno'
|
8
|
+
@db = ::SQLite3::Database.new(options[:file])
|
9
|
+
@db.execute("create table if not exists #{table} (key string primary key, value string)")
|
10
|
+
@select = @db.prepare("select value from #{table} where key = ?")
|
11
|
+
@insert = @db.prepare("insert into #{table} values (?, ?)")
|
12
|
+
@delete = @db.prepare("delete from #{table} where key = ?")
|
13
|
+
@clear = @db.prepare("delete from #{table}")
|
14
|
+
end
|
15
|
+
|
16
|
+
def key?(key, options = {})
|
17
|
+
!@select.execute!(key_for(key)).empty?
|
18
|
+
end
|
19
|
+
|
20
|
+
def [](key)
|
21
|
+
rows = @select.execute!(key_for(key))
|
22
|
+
rows.empty? ? nil : deserialize(rows.first.first)
|
23
|
+
end
|
24
|
+
|
25
|
+
def store(key, value, options = {})
|
26
|
+
@insert.execute!(key_for(key), serialize(value))
|
27
|
+
value
|
28
|
+
end
|
29
|
+
|
30
|
+
def delete(key, options = {})
|
31
|
+
value = self[key]
|
32
|
+
@delete.execute!(key_for(key))
|
33
|
+
value
|
34
|
+
end
|
35
|
+
|
36
|
+
def clear(options = {})
|
37
|
+
@clear.execute!
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def close
|
42
|
+
@select.close
|
43
|
+
@insert.close
|
44
|
+
@delete.close
|
45
|
+
@clear.close
|
46
|
+
@db.close
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'tokyocabinet'
|
2
|
+
|
3
|
+
module Juno
|
4
|
+
class TokyoCabinet < Base
|
5
|
+
def initialize(options = {})
|
6
|
+
file = options[:file]
|
7
|
+
raise 'No option :file specified' unless options[:file]
|
8
|
+
@store = ::TokyoCabinet::HDB.new
|
9
|
+
unless @store.open(file, ::TokyoCabinet::HDB::OWRITER | ::TokyoCabinet::HDB::OCREAT)
|
10
|
+
raise @store.errmsg(@store.ecode)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def key?(key, options = {})
|
15
|
+
!!self[key]
|
16
|
+
end
|
17
|
+
|
18
|
+
def delete(key, options = {})
|
19
|
+
value = self[key]
|
20
|
+
if value
|
21
|
+
@store.delete(key_for(key))
|
22
|
+
value
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def clear(options = {})
|
27
|
+
@store.clear
|
28
|
+
nil
|
29
|
+
end
|
30
|
+
|
31
|
+
def close
|
32
|
+
@store.close
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
data/lib/juno/version.rb
ADDED
data/lib/juno/yaml.rb
ADDED
data/test/helper.rb
ADDED
@@ -0,0 +1,182 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'juno'
|
3
|
+
require 'fileutils'
|
4
|
+
|
5
|
+
module Helper
|
6
|
+
def make_tempdir
|
7
|
+
tmp = File.join(File.dirname(__FILE__), 'tmp')
|
8
|
+
FileUtils.mkpath(tmp)
|
9
|
+
tmp
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
JunoSpecification = proc do
|
14
|
+
include Helper
|
15
|
+
|
16
|
+
before do
|
17
|
+
@store = new_store
|
18
|
+
@store.clear
|
19
|
+
end
|
20
|
+
|
21
|
+
after do
|
22
|
+
@store.close.must_equal nil if @store
|
23
|
+
end
|
24
|
+
|
25
|
+
types = {
|
26
|
+
'String' => ['key', 'key2'],
|
27
|
+
'Object' => [{:foo => :bar}, {:bar => :baz}]
|
28
|
+
}
|
29
|
+
|
30
|
+
types.each do |type, (key, key2)|
|
31
|
+
it "reads from keys that are #{type}s like a Hash" do
|
32
|
+
@store[key].must_equal nil
|
33
|
+
end
|
34
|
+
|
35
|
+
it "writes String values to keys that are #{type}s like a Hash" do
|
36
|
+
@store[key] = 'value'
|
37
|
+
@store[key].must_equal 'value'
|
38
|
+
end
|
39
|
+
|
40
|
+
it "guarantees that a different String value is retrieved from the #{type} key" do
|
41
|
+
value = 'value'
|
42
|
+
(@store[key] = value).must_be_same_as value
|
43
|
+
@store[key].wont_be_same_as(value)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "writes Object values to keys that are #{type}s like a Hash" do
|
47
|
+
value = {:foo => :bar}
|
48
|
+
(@store[key] = value).must_be_same_as value
|
49
|
+
@store[key].must_equal(:foo => :bar)
|
50
|
+
end
|
51
|
+
|
52
|
+
it "guarantees that a different Object value is retrieved from the #{type} key" do
|
53
|
+
value = {:foo => :bar}
|
54
|
+
@store[key] = value
|
55
|
+
@store[key].wont_be_same_as(:foo => :bar)
|
56
|
+
end
|
57
|
+
|
58
|
+
it "returns false from key? if a #{type} key is not available" do
|
59
|
+
@store.key?(key).must_equal false
|
60
|
+
end
|
61
|
+
|
62
|
+
it "returns true from key? if a #{type} key is available" do
|
63
|
+
@store[key] = 'value'
|
64
|
+
@store.key?(key).must_equal true
|
65
|
+
end
|
66
|
+
|
67
|
+
it "removes and returns an element with a #{type} key from the backing store via delete if it exists" do
|
68
|
+
@store[key] = 'value'
|
69
|
+
@store.delete(key).must_equal 'value'
|
70
|
+
@store.key?(key).must_equal false
|
71
|
+
end
|
72
|
+
|
73
|
+
it "returns nil from delete if an element for a #{type} key does not exist" do
|
74
|
+
@store.delete(key).must_equal nil
|
75
|
+
end
|
76
|
+
|
77
|
+
it "removes all #{type} keys from the store with clear" do
|
78
|
+
@store[key] = 'value'
|
79
|
+
@store[key2] = 'value2'
|
80
|
+
@store.clear.must_equal nil
|
81
|
+
@store.key?(key).wont_equal true
|
82
|
+
@store.key?(key2).wont_equal true
|
83
|
+
end
|
84
|
+
|
85
|
+
it "fetches a #{type} key with a default value with fetch, if the key is not available" do
|
86
|
+
@store.fetch(key, 'value').must_equal 'value'
|
87
|
+
end
|
88
|
+
|
89
|
+
it "fetches a #{type} key with a block with fetch, if the key is not available" do
|
90
|
+
@store.fetch(key) { |k| 'value' }.must_equal 'value'
|
91
|
+
end
|
92
|
+
|
93
|
+
it "does not run the block if the #{type} key is available" do
|
94
|
+
@store[key] = 'value'
|
95
|
+
unaltered = "unaltered"
|
96
|
+
@store.fetch(key) { unaltered = "altered" }
|
97
|
+
unaltered.must_equal "unaltered"
|
98
|
+
end
|
99
|
+
|
100
|
+
it "fetches a #{type} key with a default value with fetch, if the key is available" do
|
101
|
+
@store[key] = 'value2'
|
102
|
+
@store.fetch(key, 'value').must_equal 'value2'
|
103
|
+
end
|
104
|
+
|
105
|
+
it "stores #{key} values with #store" do
|
106
|
+
@store.store(key, 'value').must_equal 'value'
|
107
|
+
@store[key].must_equal 'value'
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'must accept options' do
|
111
|
+
@store.store(key, 'value', :foo => 42).must_equal 'value'
|
112
|
+
@store.key?(key, :foo => 42).must_equal true
|
113
|
+
@store.fetch(key, nil, :foo => 42).must_equal 'value'
|
114
|
+
@store.delete(key, :foo => 42).must_equal 'value'
|
115
|
+
@store.key?(key, :foo => 42).must_equal false
|
116
|
+
@store.clear(:foo => 42).must_equal nil
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
it "refuses to #[] from keys that cannot be marshalled" do
|
121
|
+
lambda do
|
122
|
+
@store[Struct.new(:foo).new(:bar)]
|
123
|
+
end.must_raise(TypeError)
|
124
|
+
end
|
125
|
+
|
126
|
+
it "refuses to fetch from keys that cannot be marshalled" do
|
127
|
+
lambda do
|
128
|
+
@store.fetch(Struct.new(:foo).new(:bar), true)
|
129
|
+
end.must_raise(TypeError)
|
130
|
+
end
|
131
|
+
|
132
|
+
it "refuses to #[]= to keys that cannot be marshalled" do
|
133
|
+
lambda do
|
134
|
+
@store[Struct.new(:foo).new(:bar)] = 'value'
|
135
|
+
end.must_raise(TypeError)
|
136
|
+
end
|
137
|
+
|
138
|
+
it "refuses to store to keys that cannot be marshalled" do
|
139
|
+
lambda do
|
140
|
+
@store.store Struct.new(:foo).new(:bar), 'value'
|
141
|
+
end.must_raise(TypeError)
|
142
|
+
end
|
143
|
+
|
144
|
+
it "refuses to check for key? if the key cannot be marshalled" do
|
145
|
+
lambda do
|
146
|
+
@store.key? Struct.new(:foo).new(:bar)
|
147
|
+
end.must_raise(TypeError)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "refuses to delete a key if the key cannot be marshalled" do
|
151
|
+
lambda do
|
152
|
+
@store.delete Struct.new(:foo).new(:bar)
|
153
|
+
end.must_raise(TypeError)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
JunoExpiresSpecification = proc do
|
158
|
+
it 'should support expires on store' do
|
159
|
+
@store.store('key', 'value', :expires => 2)
|
160
|
+
@store['key'].must_equal 'value'
|
161
|
+
sleep 3
|
162
|
+
@store['key'].must_equal nil
|
163
|
+
end
|
164
|
+
|
165
|
+
it 'should support updating the expiration time in fetch' do
|
166
|
+
@store.store('key2', 'value2', :expires => 2)
|
167
|
+
@store['key2'].must_equal 'value2'
|
168
|
+
sleep 1
|
169
|
+
@store.fetch('key2', nil, :expires => 3).must_equal 'value2'
|
170
|
+
sleep 1
|
171
|
+
@store['key2'].must_equal 'value2'
|
172
|
+
sleep 3
|
173
|
+
@store['key2'].must_equal nil
|
174
|
+
end
|
175
|
+
|
176
|
+
it 'should respect expires in delete' do
|
177
|
+
@store.store('key', 'value', :expires => 2)
|
178
|
+
@store['key'].must_equal 'value'
|
179
|
+
sleep 3
|
180
|
+
@store.delete('key').must_equal nil
|
181
|
+
end
|
182
|
+
end
|