juno 0.1.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.
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