juno 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/Gemfile +3 -2
  2. data/README.md +6 -1
  3. data/lib/juno.rb +20 -28
  4. data/lib/juno/adapters/activerecord.rb +24 -17
  5. data/lib/juno/adapters/couch.rb +6 -1
  6. data/lib/juno/adapters/datamapper.rb +7 -5
  7. data/lib/juno/adapters/sequel.rb +14 -7
  8. data/lib/juno/adapters/sqlite.rb +4 -4
  9. data/lib/juno/transformer.rb +14 -19
  10. data/lib/juno/version.rb +1 -1
  11. data/spec/adapter_activerecord_spec.rb +4 -4
  12. data/spec/adapter_datamapper_spec.rb +6 -6
  13. data/spec/adapter_sequel_spec.rb +2 -2
  14. data/spec/adapter_sqlite_spec.rb +2 -2
  15. data/spec/generate.rb +123 -18
  16. data/spec/junospecs.rb +63 -0
  17. data/spec/simpl_memory_with_expires_spec.rb +53 -0
  18. data/spec/simple_activerecord_spec.rb +2 -2
  19. data/spec/simple_activerecord_with_expires_spec.rb +53 -0
  20. data/spec/simple_couch_with_expires_spec.rb +53 -0
  21. data/spec/simple_datamapper_spec.rb +2 -2
  22. data/spec/simple_datamapper_with_expires_spec.rb +55 -0
  23. data/spec/simple_datamapper_with_repository_spec.rb +2 -2
  24. data/spec/simple_dbm_with_expires_spec.rb +53 -0
  25. data/spec/simple_file_with_expires_spec.rb +53 -0
  26. data/spec/simple_fog_with_expires_spec.rb +63 -0
  27. data/spec/simple_gdbm_with_expires_spec.rb +53 -0
  28. data/spec/simple_hashfile_with_expires_spec.rb +53 -0
  29. data/spec/simple_localmemcache_with_expires_spec.rb +53 -0
  30. data/spec/simple_memory_with_expires_spec.rb +53 -0
  31. data/spec/simple_mongo_with_expires_spec.rb +53 -0
  32. data/spec/simple_pstore_with_expires_spec.rb +53 -0
  33. data/spec/simple_riak_with_expires_spec.rb +57 -0
  34. data/spec/simple_sdbm_with_expires_spec.rb +53 -0
  35. data/spec/simple_sequel_spec.rb +2 -2
  36. data/spec/simple_sequel_with_expires_spec.rb +53 -0
  37. data/spec/simple_sqlite_spec.rb +2 -2
  38. data/spec/simple_sqlite_with_expires_spec.rb +53 -0
  39. data/spec/simple_tokyocabinet_with_expires_spec.rb +53 -0
  40. data/spec/simple_yaml_with_expires_spec.rb +53 -0
  41. 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 'datamapper'
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
- # Or using the builder...
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
- build(options) do
44
- use :Transformer, :key => [:marshal, :base64], :value => [:marshal, :base64]
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
- build(options) do
50
- use :Transformer, :key => [:marshal, :base64], :value => :marshal
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
- build(options) do
56
- use :Transformer, :key => :marshal
57
- adapter name
58
- end
53
+ transformer = {:key => :marshal}
59
54
  when :HashFile
60
55
  # Use spreading hashes
61
- build(options) do
62
- use :Transformer, :key => [:marshal, :md5, :spread], :value => :marshal
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
- build(options) do
68
- use :Transformer, :key => [:marshal, :escape], :value => :marshal
69
- adapter :File
70
- end
71
- else
72
- # For all other stores marshal key and value
73
- build(options) do
74
- use :Transformer, :key => :marshal, :value => :marshal
75
- adapter name
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
- @table.connection.create_table @table.table_name do |t|
21
- t.binary 'key', :primary => true
22
- t.binary 'value'
23
- end unless @table.table_exists?
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.find_by_key(key)
30
+ !!@table.find_by_k(key)
28
31
  end
29
32
 
30
33
  def load(key, options = {})
31
- record = @table.find_by_key(key)
32
- record ? record.value : nil
34
+ record = @table.find_by_k(key)
35
+ record ? record.v : nil
33
36
  end
34
37
 
35
38
  def delete(key, options = {})
36
- record = @table.find_by_key(key)
37
- if record
38
- value = record.value
39
- record.destroy
40
- value
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
- record = @table.find_by_key(key)
46
- record ||= @table.new(:key => key)
47
- record.value = value
48
- record.save!
49
- value
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 = {})
@@ -20,7 +20,12 @@ module Juno
20
20
  end
21
21
 
22
22
  def store(key, value, options = {})
23
- @db.save_doc('_id' => key, 'data' => value)
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
- value = load(key, options)
47
- Store.all(:k => key).destroy!
48
- value
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
 
@@ -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
- primary_key :k
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
- sequel_table.insert(:k => key, :v => value)
28
- value
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
- if value = load(key, options)
33
- sequel_table.filter(:k => key).delete
34
- value
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
 
@@ -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} (key string primary key, value string)")
11
- @select = @db.prepare("select value from #{table} where key = ?")
12
- @insert = @db.prepare("insert into #{table} values (?, ?)")
13
- @delete = @db.prepare("delete from #{table} where key = ?")
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
 
@@ -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 loader == 'value'
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
@@ -1,3 +1,3 @@
1
1
  module Juno
2
- VERSION = '0.2.0'
2
+ VERSION = '0.2.1'
3
3
  end
@@ -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.sqlite3') }).close
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.sqlite3') })
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 => { :key => 'foo/bar' }
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.sqlite3')
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.sqlite3").close
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.sqlite3")
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.sqlite3")
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.sqlite3")
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.sqlite3")
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.sqlite3")
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'
@@ -2,11 +2,11 @@
2
2
  require 'helper'
3
3
 
4
4
  begin
5
- Juno::Adapters::Sequel.new(:db => (defined?(JRUBY_VERSION) ? 'jdbc:sqlite:/' : 'sqlite:/')).close
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) ? 'jdbc:sqlite:/' : 'sqlite:/'))
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
 
@@ -2,11 +2,11 @@
2
2
  require 'helper'
3
3
 
4
4
  begin
5
- Juno::Adapters::Sqlite.new(:file => ":memory:").close
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 => ":memory:")
9
+ @store = Juno::Adapters::Sqlite.new(:file => File.join(make_tempdir, "adapter_sqlite"))
10
10
  @store.clear
11
11
  end
12
12