juno 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +3 -2
- data/README.md +6 -1
- data/lib/juno.rb +20 -28
- data/lib/juno/adapters/activerecord.rb +24 -17
- data/lib/juno/adapters/couch.rb +6 -1
- data/lib/juno/adapters/datamapper.rb +7 -5
- data/lib/juno/adapters/sequel.rb +14 -7
- data/lib/juno/adapters/sqlite.rb +4 -4
- data/lib/juno/transformer.rb +14 -19
- data/lib/juno/version.rb +1 -1
- data/spec/adapter_activerecord_spec.rb +4 -4
- data/spec/adapter_datamapper_spec.rb +6 -6
- data/spec/adapter_sequel_spec.rb +2 -2
- data/spec/adapter_sqlite_spec.rb +2 -2
- data/spec/generate.rb +123 -18
- data/spec/junospecs.rb +63 -0
- data/spec/simpl_memory_with_expires_spec.rb +53 -0
- data/spec/simple_activerecord_spec.rb +2 -2
- data/spec/simple_activerecord_with_expires_spec.rb +53 -0
- data/spec/simple_couch_with_expires_spec.rb +53 -0
- data/spec/simple_datamapper_spec.rb +2 -2
- data/spec/simple_datamapper_with_expires_spec.rb +55 -0
- data/spec/simple_datamapper_with_repository_spec.rb +2 -2
- data/spec/simple_dbm_with_expires_spec.rb +53 -0
- data/spec/simple_file_with_expires_spec.rb +53 -0
- data/spec/simple_fog_with_expires_spec.rb +63 -0
- data/spec/simple_gdbm_with_expires_spec.rb +53 -0
- data/spec/simple_hashfile_with_expires_spec.rb +53 -0
- data/spec/simple_localmemcache_with_expires_spec.rb +53 -0
- data/spec/simple_memory_with_expires_spec.rb +53 -0
- data/spec/simple_mongo_with_expires_spec.rb +53 -0
- data/spec/simple_pstore_with_expires_spec.rb +53 -0
- data/spec/simple_riak_with_expires_spec.rb +57 -0
- data/spec/simple_sdbm_with_expires_spec.rb +53 -0
- data/spec/simple_sequel_spec.rb +2 -2
- data/spec/simple_sequel_with_expires_spec.rb +53 -0
- data/spec/simple_sqlite_spec.rb +2 -2
- data/spec/simple_sqlite_with_expires_spec.rb +53 -0
- data/spec/simple_tokyocabinet_with_expires_spec.rb +53 -0
- data/spec/simple_yaml_with_expires_spec.rb +53 -0
- metadata +40 -2
data/Gemfile
CHANGED
@@ -8,13 +8,13 @@ gem 'parallel_tests'
|
|
8
8
|
|
9
9
|
# Serializer
|
10
10
|
#gem 'tnetstring'
|
11
|
-
gem 'msgpack'
|
12
11
|
gem 'bson'
|
13
12
|
gem 'multi_json'
|
14
13
|
gem 'json' # Ripple/Riak needs json
|
15
14
|
|
16
15
|
# Backends
|
17
|
-
gem '
|
16
|
+
gem 'dm-core'
|
17
|
+
gem 'dm-migrations'
|
18
18
|
gem 'dm-sqlite-adapter'
|
19
19
|
gem 'fog'
|
20
20
|
gem 'activerecord'
|
@@ -30,6 +30,7 @@ if defined?(JRUBY_VERSION)
|
|
30
30
|
gem 'activerecord-jdbc-adapter'
|
31
31
|
gem 'activerecord-jdbcsqlite3-adapter'
|
32
32
|
else
|
33
|
+
gem 'msgpack'
|
33
34
|
gem 'tokyocabinet'
|
34
35
|
gem 'memcached'
|
35
36
|
gem 'sqlite3'
|
data/README.md
CHANGED
@@ -106,6 +106,7 @@ store = Juno.build do
|
|
106
106
|
# Memory backend
|
107
107
|
adapter :Memory
|
108
108
|
end
|
109
|
+
~~~
|
109
110
|
|
110
111
|
Expiration
|
111
112
|
----------
|
@@ -126,10 +127,14 @@ end
|
|
126
127
|
You can add the expires feature to other backends using the Expires proxy:
|
127
128
|
|
128
129
|
~~~ ruby
|
130
|
+
# Using the :expires option
|
131
|
+
cache = Juno.new(:File, :dir => '...', :expires => true)
|
132
|
+
|
133
|
+
# or using the proxy...
|
129
134
|
cache = Juno::Expires.new(Juno::Adapters::File.new(:dir => '...'))
|
130
135
|
cache.store(key, value, :expires => 10)
|
131
136
|
|
132
|
-
#
|
137
|
+
# or using the builder...
|
133
138
|
cache = Juno.build do
|
134
139
|
use :Expires
|
135
140
|
adapter :File, :dir => '...'
|
data/lib/juno.rb
CHANGED
@@ -34,46 +34,38 @@ module Juno
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def self.new(name, options = {})
|
37
|
+
expires = options.delete(:expires)
|
38
|
+
transformer = {:key => :marshal, :value => :marshal}
|
37
39
|
raise 'Name must be Symbol' unless Symbol === name
|
38
40
|
case name
|
39
|
-
when :Sequel, :ActiveRecord, :Couch
|
41
|
+
when :Sequel, :ActiveRecord, :Couch, :Mongo
|
40
42
|
# Sequel accept only base64 keys and values
|
41
43
|
# FIXME: ActiveRecord and Couch should work only with :marshal but this
|
42
44
|
# raises an error on 1.9
|
43
|
-
|
44
|
-
|
45
|
-
adapter name
|
46
|
-
end
|
45
|
+
# Mongo accepts only valid UTF-8 strings
|
46
|
+
transformer = {:key => [:marshal, :base64], :value => [:marshal, :base64]}
|
47
47
|
when :Memcached, :MemcachedDalli, :MemcachedNative
|
48
|
-
# Memcached accept only base64 keys
|
49
|
-
|
50
|
-
|
51
|
-
adapter name
|
52
|
-
end
|
48
|
+
# Memcached accept only base64 keys, expires already supported
|
49
|
+
expires = false
|
50
|
+
transformer = {:key => [:marshal, :base64], :value => :marshal}
|
53
51
|
when :PStore, :YAML, :DataMapper, :Null
|
54
52
|
# For PStore, YAML and DataMapper only the key has to be a string
|
55
|
-
|
56
|
-
use :Transformer, :key => :marshal
|
57
|
-
adapter name
|
58
|
-
end
|
53
|
+
transformer = {:key => :marshal}
|
59
54
|
when :HashFile
|
60
55
|
# Use spreading hashes
|
61
|
-
|
62
|
-
|
63
|
-
adapter :File
|
64
|
-
end
|
56
|
+
transformer = {:key => [:marshal, :md5, :spread], :value => :marshal}
|
57
|
+
name = :File
|
65
58
|
when :File
|
66
59
|
# Use escaping
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
end
|
60
|
+
transformer = {:key => [:marshal, :escape], :value => :marshal}
|
61
|
+
when :Cassandra, :Redis
|
62
|
+
# Expires already supported
|
63
|
+
expires = false
|
64
|
+
end
|
65
|
+
build(options) do
|
66
|
+
use :Expires if expires
|
67
|
+
use :Transformer, transformer
|
68
|
+
adapter name
|
77
69
|
end
|
78
70
|
end
|
79
71
|
|
@@ -17,36 +17,43 @@ module Juno
|
|
17
17
|
c
|
18
18
|
end
|
19
19
|
@table.establish_connection(options[:connection]) if options[:connection]
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
20
|
+
unless @table.table_exists?
|
21
|
+
@table.connection.create_table(@table.table_name) do |t|
|
22
|
+
t.binary :k, :null => false
|
23
|
+
t.binary :v
|
24
|
+
end
|
25
|
+
@table.connection.add_index(@table.table_name, :k, :unique => true)
|
26
|
+
end
|
24
27
|
end
|
25
28
|
|
26
29
|
def key?(key, options = {})
|
27
|
-
!!@table.
|
30
|
+
!!@table.find_by_k(key)
|
28
31
|
end
|
29
32
|
|
30
33
|
def load(key, options = {})
|
31
|
-
record = @table.
|
32
|
-
record ? record.
|
34
|
+
record = @table.find_by_k(key)
|
35
|
+
record ? record.v : nil
|
33
36
|
end
|
34
37
|
|
35
38
|
def delete(key, options = {})
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
39
|
+
@table.transaction do
|
40
|
+
record = @table.find_by_k(key)
|
41
|
+
if record
|
42
|
+
value = record.v
|
43
|
+
record.destroy
|
44
|
+
value
|
45
|
+
end
|
41
46
|
end
|
42
47
|
end
|
43
48
|
|
44
49
|
def store(key, value, options = {})
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
+
@table.transaction do
|
51
|
+
record = @table.find_by_k(key)
|
52
|
+
record ||= @table.new(:k => key)
|
53
|
+
record.v = value
|
54
|
+
record.save!
|
55
|
+
value
|
56
|
+
end
|
50
57
|
end
|
51
58
|
|
52
59
|
def clear(options = {})
|
data/lib/juno/adapters/couch.rb
CHANGED
@@ -20,7 +20,12 @@ module Juno
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def store(key, value, options = {})
|
23
|
-
|
23
|
+
doc = {'_id' => key, 'data' => value}
|
24
|
+
begin
|
25
|
+
doc['_rev'] = @db.get(key)['_rev']
|
26
|
+
rescue RestClient::ResourceNotFound
|
27
|
+
end
|
28
|
+
@db.save_doc(doc)
|
24
29
|
value
|
25
30
|
rescue RestClient::RequestFailed
|
26
31
|
value
|
@@ -33,19 +33,21 @@ module Juno
|
|
33
33
|
context do
|
34
34
|
record = Store.get(key)
|
35
35
|
if record
|
36
|
-
record.update(key, value)
|
36
|
+
record.update(:k => key, :v => value)
|
37
37
|
else
|
38
38
|
Store.create(:k => key, :v => value)
|
39
39
|
end
|
40
|
+
value
|
40
41
|
end
|
41
|
-
value
|
42
42
|
end
|
43
43
|
|
44
44
|
def delete(key, options = {})
|
45
45
|
context do
|
46
|
-
|
47
|
-
|
48
|
-
|
46
|
+
if record = Store.get(key)
|
47
|
+
value = record.v
|
48
|
+
record.destroy!
|
49
|
+
value
|
50
|
+
end
|
49
51
|
end
|
50
52
|
end
|
51
53
|
|
data/lib/juno/adapters/sequel.rb
CHANGED
@@ -8,8 +8,7 @@ module Juno
|
|
8
8
|
@table = options.delete(:table) || :juno
|
9
9
|
@db = ::Sequel.connect(db, options)
|
10
10
|
@db.create_table?(@table) do
|
11
|
-
|
12
|
-
String :k
|
11
|
+
String :k, :null => false, :primary_key => true
|
13
12
|
String :v
|
14
13
|
end
|
15
14
|
end
|
@@ -24,14 +23,22 @@ module Juno
|
|
24
23
|
end
|
25
24
|
|
26
25
|
def store(key, value, options = {})
|
27
|
-
|
28
|
-
|
26
|
+
@db.transaction do
|
27
|
+
if key?(key, options)
|
28
|
+
sequel_table.update(:k => key, :v => value)
|
29
|
+
else
|
30
|
+
sequel_table.insert(:k => key, :v => value)
|
31
|
+
end
|
32
|
+
value
|
33
|
+
end
|
29
34
|
end
|
30
35
|
|
31
36
|
def delete(key, options = {})
|
32
|
-
|
33
|
-
|
34
|
-
|
37
|
+
@db.transaction do
|
38
|
+
if value = load(key, options)
|
39
|
+
sequel_table.filter(:k => key).delete
|
40
|
+
value
|
41
|
+
end
|
35
42
|
end
|
36
43
|
end
|
37
44
|
|
data/lib/juno/adapters/sqlite.rb
CHANGED
@@ -7,10 +7,10 @@ module Juno
|
|
7
7
|
raise 'No option :file specified' unless options[:file]
|
8
8
|
table = options[:table] || 'juno'
|
9
9
|
@db = ::SQLite3::Database.new(options[:file])
|
10
|
-
@db.execute("create table if not exists #{table} (
|
11
|
-
@select = @db.prepare("select
|
12
|
-
@insert = @db.prepare("insert into #{table} values (?, ?)")
|
13
|
-
@delete = @db.prepare("delete from #{table} where
|
10
|
+
@db.execute("create table if not exists #{table} (k blob not null primary key, v blob)")
|
11
|
+
@select = @db.prepare("select v from #{table} where k = ?")
|
12
|
+
@insert = @db.prepare("insert or replace into #{table} values (?, ?)")
|
13
|
+
@delete = @db.prepare("delete from #{table} where k = ?")
|
14
14
|
@clear = @db.prepare("delete from #{table}")
|
15
15
|
end
|
16
16
|
|
data/lib/juno/transformer.rb
CHANGED
@@ -1,11 +1,5 @@
|
|
1
1
|
module Juno
|
2
2
|
class Transformer < Proxy
|
3
|
-
@classes = {}
|
4
|
-
|
5
|
-
class << self
|
6
|
-
alias_method :original_new, :new
|
7
|
-
end
|
8
|
-
|
9
3
|
VALUE_TRANSFORMER = {
|
10
4
|
:marshal => { :load => '::Marshal.load(VALUE)', :dump => '::Marshal.dump(VALUE)' },
|
11
5
|
:base64 => { :load => "VALUE.unpack('m').first", :dump => "[VALUE].pack('m').strip" },
|
@@ -29,7 +23,11 @@ module Juno
|
|
29
23
|
:msgpack => { :transform => '(TMP = KEY; String === TMP ? TMP : ::MessagePack.pack(TMP))', :require => 'msgpack' },
|
30
24
|
}
|
31
25
|
|
26
|
+
@classes = {}
|
27
|
+
|
32
28
|
class << self
|
29
|
+
alias_method :original_new, :new
|
30
|
+
|
33
31
|
def compile(keys, values)
|
34
32
|
tmp, key = 0, 'key'
|
35
33
|
keys.each do |tn|
|
@@ -39,20 +37,8 @@ module Juno
|
|
39
37
|
tmp += 1
|
40
38
|
end
|
41
39
|
|
42
|
-
dumper = 'value'
|
43
|
-
values.each do |tn|
|
44
|
-
raise "Unknown value transformer #{tn}" unless t = VALUE_TRANSFORMER[tn]
|
45
|
-
require t[:require] if t[:require]
|
46
|
-
dumper = t[:dump].gsub('VALUE', dumper)
|
47
|
-
end
|
48
|
-
|
49
|
-
loader = 'value'
|
50
|
-
values.reverse.each do |t|
|
51
|
-
loader = VALUE_TRANSFORMER[t][:load].gsub('VALUE', loader)
|
52
|
-
end
|
53
|
-
|
54
40
|
klass = Class.new(Transformer)
|
55
|
-
if
|
41
|
+
if values.empty?
|
56
42
|
klass.class_eval <<-end_eval, __FILE__, __LINE__
|
57
43
|
def key?(key, options = {})
|
58
44
|
@adapter.key?(#{key}, options)
|
@@ -71,6 +57,14 @@ module Juno
|
|
71
57
|
end
|
72
58
|
end_eval
|
73
59
|
else
|
60
|
+
dumper, loader = 'value', 'value'
|
61
|
+
values.each_index do |i|
|
62
|
+
raise "Unknown value transformer #{values[i]}" unless t = VALUE_TRANSFORMER[values[i]]
|
63
|
+
require t[:require] if t[:require]
|
64
|
+
dumper = t[:dump].gsub('VALUE', dumper)
|
65
|
+
loader = VALUE_TRANSFORMER[values[-i-1]][:load].gsub('VALUE', loader)
|
66
|
+
end
|
67
|
+
|
74
68
|
klass.class_eval <<-end_eval, __FILE__, __LINE__
|
75
69
|
def key?(key, options = {})
|
76
70
|
@adapter.key?(#{key}, options)
|
@@ -98,6 +92,7 @@ module Juno
|
|
98
92
|
def new(store, options = {})
|
99
93
|
keys = [options[:key]].flatten.compact
|
100
94
|
values = [options[:value]].flatten.compact
|
95
|
+
raise 'No option :key or :value specified' if keys.empty? && values.empty?
|
101
96
|
klass = @classes["#{keys.join('-')}+#{values.join('-')}"] ||= compile(keys, values)
|
102
97
|
klass.original_new(store, options)
|
103
98
|
end
|
data/lib/juno/version.rb
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
require 'helper'
|
3
3
|
|
4
4
|
begin
|
5
|
-
Juno::Adapters::ActiveRecord.new(:connection => { :adapter => (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'), :database => File.join(make_tempdir, 'adapter_activerecord
|
5
|
+
Juno::Adapters::ActiveRecord.new(:connection => { :adapter => (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'), :database => File.join(make_tempdir, 'adapter_activerecord') }).close
|
6
6
|
|
7
7
|
describe "adapter_activerecord" do
|
8
8
|
before do
|
9
|
-
@store = Juno::Adapters::ActiveRecord.new(:connection => { :adapter => (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'), :database => File.join(make_tempdir, 'adapter_activerecord
|
9
|
+
@store = Juno::Adapters::ActiveRecord.new(:connection => { :adapter => (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'), :database => File.join(make_tempdir, 'adapter_activerecord') })
|
10
10
|
@store.clear
|
11
11
|
end
|
12
12
|
|
@@ -21,12 +21,12 @@ begin
|
|
21
21
|
it 'updates an existing key/value' do
|
22
22
|
@store['foo/bar'] = '1'
|
23
23
|
@store['foo/bar'] = '2'
|
24
|
-
records = @store.table.find :all, :conditions => { :
|
24
|
+
records = @store.table.find :all, :conditions => { :k => 'foo/bar' }
|
25
25
|
records.count.should == 1
|
26
26
|
end
|
27
27
|
|
28
28
|
it 'uses an existing connection' do
|
29
|
-
ActiveRecord::Base.establish_connection :adapter => (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'), :database => File.join(make_tempdir, 'activerecord-existing
|
29
|
+
ActiveRecord::Base.establish_connection :adapter => (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'), :database => File.join(make_tempdir, 'activerecord-existing')
|
30
30
|
|
31
31
|
store = Juno::Adapters::ActiveRecord.new
|
32
32
|
store.table.table_exists?.should == true
|
@@ -4,11 +4,11 @@ require 'helper'
|
|
4
4
|
begin
|
5
5
|
require 'dm-core'
|
6
6
|
DataMapper.setup(:default, :adapter => :in_memory)
|
7
|
-
Juno::Adapters::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/adapter_datamapper
|
7
|
+
Juno::Adapters::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/adapter_datamapper").close
|
8
8
|
|
9
9
|
describe "adapter_datamapper" do
|
10
10
|
before do
|
11
|
-
@store = Juno::Adapters::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/adapter_datamapper
|
11
|
+
@store = Juno::Adapters::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/adapter_datamapper")
|
12
12
|
@store.clear
|
13
13
|
end
|
14
14
|
|
@@ -24,10 +24,10 @@ begin
|
|
24
24
|
it_should_behave_like 'store_stringkey_objectvalue'
|
25
25
|
|
26
26
|
it 'does not cross contaminate when storing' do
|
27
|
-
first = Juno::Adapters::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/datamapper-first
|
27
|
+
first = Juno::Adapters::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/datamapper-first")
|
28
28
|
first.clear
|
29
29
|
|
30
|
-
second = Juno::Adapters::DataMapper.new(:repository => :sample, :setup => "sqlite3://#{make_tempdir}/datamapper-second
|
30
|
+
second = Juno::Adapters::DataMapper.new(:repository => :sample, :setup => "sqlite3://#{make_tempdir}/datamapper-second")
|
31
31
|
second.clear
|
32
32
|
|
33
33
|
first['key'] = 'value'
|
@@ -38,10 +38,10 @@ begin
|
|
38
38
|
end
|
39
39
|
|
40
40
|
it 'does not cross contaminate when deleting' do
|
41
|
-
first = Juno::Adapters::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/datamapper-first
|
41
|
+
first = Juno::Adapters::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/datamapper-first")
|
42
42
|
first.clear
|
43
43
|
|
44
|
-
second = Juno::Adapters::DataMapper.new(:repository => :sample, :setup => "sqlite3://#{make_tempdir}/datamapper-second
|
44
|
+
second = Juno::Adapters::DataMapper.new(:repository => :sample, :setup => "sqlite3://#{make_tempdir}/datamapper-second")
|
45
45
|
second.clear
|
46
46
|
|
47
47
|
first['key'] = 'value'
|
data/spec/adapter_sequel_spec.rb
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
require 'helper'
|
3
3
|
|
4
4
|
begin
|
5
|
-
Juno::Adapters::Sequel.new(:db => (defined?(JRUBY_VERSION) ?
|
5
|
+
Juno::Adapters::Sequel.new(:db => (defined?(JRUBY_VERSION) ? "jdbc:sqlite:" : "sqlite:") + File.join(make_tempdir, "adapter_sequel")).close
|
6
6
|
|
7
7
|
describe "adapter_sequel" do
|
8
8
|
before do
|
9
|
-
@store = Juno::Adapters::Sequel.new(:db => (defined?(JRUBY_VERSION) ?
|
9
|
+
@store = Juno::Adapters::Sequel.new(:db => (defined?(JRUBY_VERSION) ? "jdbc:sqlite:" : "sqlite:") + File.join(make_tempdir, "adapter_sequel"))
|
10
10
|
@store.clear
|
11
11
|
end
|
12
12
|
|
data/spec/adapter_sqlite_spec.rb
CHANGED
@@ -2,11 +2,11 @@
|
|
2
2
|
require 'helper'
|
3
3
|
|
4
4
|
begin
|
5
|
-
Juno::Adapters::Sqlite.new(:file => "
|
5
|
+
Juno::Adapters::Sqlite.new(:file => File.join(make_tempdir, "adapter_sqlite")).close
|
6
6
|
|
7
7
|
describe "adapter_sqlite" do
|
8
8
|
before do
|
9
|
-
@store = Juno::Adapters::Sqlite.new(:file => "
|
9
|
+
@store = Juno::Adapters::Sqlite.new(:file => File.join(make_tempdir, "adapter_sqlite"))
|
10
10
|
@store.clear
|
11
11
|
end
|
12
12
|
|