juno 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/.gitignore +4 -0
  2. data/.travis.yml +25 -0
  3. data/Gemfile +19 -0
  4. data/LICENSE +20 -0
  5. data/README.md +83 -0
  6. data/Rakefile +15 -0
  7. data/SPEC.md +95 -0
  8. data/juno.gemspec +22 -0
  9. data/lib/juno.rb +31 -0
  10. data/lib/juno/activerecord.rb +58 -0
  11. data/lib/juno/base.rb +113 -0
  12. data/lib/juno/couch.rb +43 -0
  13. data/lib/juno/datamapper.rb +63 -0
  14. data/lib/juno/dbm.rb +15 -0
  15. data/lib/juno/expires.rb +45 -0
  16. data/lib/juno/file.rb +62 -0
  17. data/lib/juno/gdbm.rb +15 -0
  18. data/lib/juno/hashfile.rb +12 -0
  19. data/lib/juno/localmemcache.rb +16 -0
  20. data/lib/juno/memcached.rb +7 -0
  21. data/lib/juno/memcached_dalli.rb +55 -0
  22. data/lib/juno/memcached_native.rb +56 -0
  23. data/lib/juno/memory.rb +7 -0
  24. data/lib/juno/mongodb.rb +43 -0
  25. data/lib/juno/proxy.rb +5 -0
  26. data/lib/juno/pstore.rb +49 -0
  27. data/lib/juno/redis.rb +34 -0
  28. data/lib/juno/riak.rb +45 -0
  29. data/lib/juno/sdbm.rb +15 -0
  30. data/lib/juno/sequel.rb +51 -0
  31. data/lib/juno/sqlite.rb +50 -0
  32. data/lib/juno/tokyocabinet.rb +36 -0
  33. data/lib/juno/version.rb +3 -0
  34. data/lib/juno/yaml.rb +9 -0
  35. data/test/helper.rb +182 -0
  36. data/test/test_active_record.rb +36 -0
  37. data/test/test_couch.rb +13 -0
  38. data/test/test_datamapper.rb +64 -0
  39. data/test/test_dbm.rb +13 -0
  40. data/test/test_expires.rb +10 -0
  41. data/test/test_file.rb +9 -0
  42. data/test/test_gdbm.rb +13 -0
  43. data/test/test_hashfile.rb +9 -0
  44. data/test/test_localmemcache.rb +13 -0
  45. data/test/test_memcached.rb +15 -0
  46. data/test/test_memcached_dalli.rb +15 -0
  47. data/test/test_memcached_native.rb +15 -0
  48. data/test/test_memory.rb +9 -0
  49. data/test/test_mongodb.rb +13 -0
  50. data/test/test_proxy.rb +9 -0
  51. data/test/test_pstore.rb +9 -0
  52. data/test/test_redis.rb +13 -0
  53. data/test/test_riak.rb +13 -0
  54. data/test/test_sdbm.rb +13 -0
  55. data/test/test_sequel.rb +13 -0
  56. data/test/test_sqlite.rb +13 -0
  57. data/test/test_tokyocabinet.rb +13 -0
  58. data/test/test_yaml.rb +9 -0
  59. data/unsupported/benchmarks.rb +234 -0
  60. data/unsupported/cassandra.rb +45 -0
  61. data/unsupported/fog.rb +60 -0
  62. data/unsupported/test_cassandra.rb +13 -0
  63. data/unsupported/test_rackspace.rb +15 -0
  64. data/unsupported/test_s3.rb +15 -0
  65. data/unsupported/test_tokyotyrant.rb +13 -0
  66. data/unsupported/tokyotyrant.rb +29 -0
  67. metadata +165 -0
@@ -0,0 +1,5 @@
1
+ require 'delegate'
2
+
3
+ module Juno
4
+ Proxy = DelegateClass(Base)
5
+ end
@@ -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
@@ -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
@@ -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
@@ -0,0 +1,15 @@
1
+ require 'sdbm'
2
+
3
+ module Juno
4
+ class SDBM < Base
5
+ def initialize(options = {})
6
+ raise 'No option :file specified' unless options[:file]
7
+ @store = ::SDBM.new(options[:file])
8
+ end
9
+
10
+ def close
11
+ @store.close
12
+ nil
13
+ end
14
+ end
15
+ end
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ module Juno
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,9 @@
1
+ require 'yaml/store'
2
+
3
+ module Juno
4
+ class YAML < Juno::PStore
5
+ def new_store(options)
6
+ ::YAML::Store.new(options[:file])
7
+ end
8
+ end
9
+ end
@@ -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