juno 0.2.0 → 0.2.1
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.
- 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
|
|