juno 0.2.8 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -12,7 +12,7 @@ module Juno
12
12
  # Options:
13
13
  # * :file - Database file
14
14
  def initialize(options = {})
15
- raise 'Option :file is required' unless options[:file]
15
+ raise ArgumentError, 'Option :file is required' unless options[:file]
16
16
  @memory = ::LocalMemCache.new(:filename => options[:file])
17
17
  end
18
18
 
@@ -29,7 +29,7 @@ module Juno
29
29
 
30
30
  def load(key, options = {})
31
31
  value = @collection.find_one('_id' => key)
32
- value ? value['data'].to_s : nil
32
+ value ? value['value'].to_s : nil
33
33
  end
34
34
 
35
35
  def delete(key, options = {})
@@ -40,7 +40,7 @@ module Juno
40
40
 
41
41
  def store(key, value, options = {})
42
42
  @collection.update({ '_id' => key },
43
- { '_id' => key, 'data' => ::BSON::Binary.new(value) },
43
+ { '_id' => key, 'value' => ::BSON::Binary.new(value) },
44
44
  { :upsert => true })
45
45
  value
46
46
  end
@@ -12,7 +12,7 @@ module Juno
12
12
  # Options:
13
13
  # * :file - PStore file
14
14
  def initialize(options = {})
15
- raise 'Option :file is required' unless options[:file]
15
+ raise ArgumentError, 'Option :file is required' unless options[:file]
16
16
  FileUtils.mkpath(::File.dirname(options[:file]))
17
17
  @pstore = new_store(options)
18
18
  end
@@ -14,9 +14,11 @@ module Juno
14
14
  #
15
15
  # Options:
16
16
  # * :bucket - Bucket name (default juno)
17
+ # * :content_type - Default content type (default application/octet-stream)
17
18
  # * All other options passed to Riak::Client#new
18
19
  def initialize(options = {})
19
20
  bucket = options.delete(:bucket) || 'juno'
21
+ @content_type = options.delete(:content_type) || 'application/octet-stream'
20
22
  @bucket = ::Riak::Client.new(options).bucket(bucket)
21
23
  end
22
24
 
@@ -38,7 +40,7 @@ module Juno
38
40
 
39
41
  def store(key, value, options = {})
40
42
  obj = ::Riak::RObject.new(@bucket, key)
41
- obj.content_type = 'application/octet-stream'
43
+ obj.content_type = options[:content_type] || @content_type
42
44
  obj.raw_data = value
43
45
  obj.store(options)
44
46
  value
@@ -12,7 +12,7 @@ module Juno
12
12
  # Options:
13
13
  # * :file - Database file
14
14
  def initialize(options = {})
15
- raise 'Option :file is required' unless options[:file]
15
+ raise ArgumentError, 'Option :file is required' unless options[:file]
16
16
  @memory = ::SDBM.new(options[:file])
17
17
  end
18
18
 
@@ -14,7 +14,7 @@ module Juno
14
14
  # * :table - Table name (default :juno)
15
15
  # * All other options passed to Sequel#connect
16
16
  def initialize(options = {})
17
- raise 'Option :db is required' unless db = options.delete(:db)
17
+ raise ArgumentError, 'Option :db is required' unless db = options.delete(:db)
18
18
  @table = options.delete(:table) || :juno
19
19
  @db = ::Sequel.connect(db, options)
20
20
  @db.create_table?(@table) do
@@ -13,7 +13,7 @@ module Juno
13
13
  # * :file - Database file
14
14
  # * :table - Table name (default juno)
15
15
  def initialize(options = {})
16
- raise 'Option :file is required' unless options[:file]
16
+ raise ArgumentError, 'Option :file is required' unless options[:file]
17
17
  table = options[:table] || 'juno'
18
18
  @db = ::SQLite3::Database.new(options[:file])
19
19
  @db.execute("create table if not exists #{table} (k blob not null primary key, v blob)")
@@ -14,7 +14,7 @@ module Juno
14
14
  # * :type - Database type (default :hdb, :bdb and :hdb possible)
15
15
  def initialize(options = {})
16
16
  file = options[:file]
17
- raise 'Option :file is required' unless options[:file]
17
+ raise ArgumentError, 'Option :file is required' unless options[:file]
18
18
  if options[:type] == :bdb
19
19
  @memory = ::TokyoCabinet::BDB.new
20
20
  @memory.open(file, ::TokyoCabinet::BDB::OWRITER | ::TokyoCabinet::BDB::OCREAT)
data/lib/juno/base.rb CHANGED
@@ -7,15 +7,27 @@ module Juno
7
7
  def close
8
8
  end
9
9
 
10
- # Fetch value with key. Return default if value is nil.
10
+ # Fetch value with key
11
+ #
12
+ # This is a overloaded method:
13
+ #
14
+ # * fetch(key, options = {}, &block) retrieve a key. if the key is not available, execute the
15
+ # block and return its return value.
16
+ #
17
+ # * fetch(key, value, options = {}) retrieve a key. if the key is not available, return the value.
11
18
  #
12
19
  # @param [Object] key
13
- # @param [Object] value Default value
20
+ # @param [Object] default Default value
14
21
  # @param [Hash] options
15
22
  # @return [Object] value from store
16
23
  # @api public
17
- def fetch(key, value = nil, options = {})
18
- load(key, options) || (block_given? && yield(key)) || value
24
+ def fetch(key, default = nil, options = nil)
25
+ if block_given?
26
+ raise ArgumentError, 'Only one argument accepted if block is given' if options
27
+ load(key, default || {}) || yield(key)
28
+ else
29
+ load(key, options || {}) || default
30
+ end
19
31
  end
20
32
 
21
33
  # Fetch value with key. Return nil if the key doesn't exist
data/lib/juno/builder.rb CHANGED
@@ -12,7 +12,7 @@ module Juno
12
12
  end
13
13
 
14
14
  def initialize(&block)
15
- raise 'No block given' unless block_given?
15
+ raise ArgumentError, 'No block given' unless block_given?
16
16
  @proxies = []
17
17
  instance_eval(&block)
18
18
  end
@@ -23,7 +23,7 @@ module Juno
23
23
  # @param [Hash] options Options hash
24
24
  def use(proxy, options = {}, &block)
25
25
  proxy = Juno.const_get(proxy) if Symbol === proxy
26
- raise 'You must give a Class or a Symbol' unless Class === proxy
26
+ raise ArgumentError, 'You must give a Class or a Symbol' unless Class === proxy
27
27
  @proxies.unshift [proxy, options, block]
28
28
  nil
29
29
  end
data/lib/juno/cache.rb CHANGED
@@ -20,13 +20,13 @@ module Juno
20
20
 
21
21
  def backend(store = nil, &block)
22
22
  raise 'Backend already set' if @backend
23
- raise 'Only argument or block allowed' if store && block
23
+ raise ArgumentError, 'Only argument or block allowed' if store && block
24
24
  @backend = store || Juno.build(&block)
25
25
  end
26
26
 
27
27
  def cache(store = nil, &block)
28
28
  raise 'Cache already set' if @cache
29
- raise 'Only argument or block allowed' if store && block
29
+ raise ArgumentError, 'Only argument or block allowed' if store && block
30
30
  @cache = store || Juno.build(&block)
31
31
  end
32
32
 
data/lib/juno/stack.rb CHANGED
@@ -23,7 +23,7 @@ module Juno
23
23
  end
24
24
 
25
25
  def add(store = nil, &block)
26
- raise 'Only argument or block allowed' if store && block
26
+ raise ArgumentError, 'Only argument or block allowed' if store && block
27
27
  @stack << (store || Juno.build(&block))
28
28
  nil
29
29
  end
@@ -11,7 +11,7 @@ module Juno
11
11
  class Transformer < Proxy
12
12
  def initialize(adapter, options = {})
13
13
  super
14
- @prefix = options[:prefix]
14
+ @prefix, @secret = options[:prefix], options[:secret]
15
15
  end
16
16
 
17
17
  class << self
@@ -26,14 +26,14 @@ module Juno
26
26
  # * :key - List of key transformers in the order in which they should be applied
27
27
  # * :value - List of value transformers in the order in which they should be applied
28
28
  # * :prefix - Prefix string for key namespacing (Used by the :prefix key transformer)
29
+ # * :secret - HMAC secret to verify values (Used by the :hmac value transformer)
29
30
  def new(adapter, options = {})
30
31
  keys = [options[:key]].flatten.compact
31
32
  values = [options[:value]].flatten.compact
32
- raise 'Option :key or :value is required' if keys.empty? && values.empty?
33
- raise 'Option :prefix is required' if keys.include?(:prefix) && !options[:prefix]
34
- name = ''
35
- name << keys.map(&:to_s).map(&:capitalize).join << 'Key' unless keys.empty?
36
- name << values.map(&:to_s).map(&:capitalize).join << 'Value' unless values.empty?
33
+ raise ArgumentError, 'Option :key or :value is required' if keys.empty? && values.empty?
34
+ raise ArgumentError, 'Option :prefix is required for :prefix key transformer' if keys.include?(:prefix) && !options[:prefix]
35
+ raise ArgumentError, 'Option :secret is required for :hmac value transformer' if values.include?(:hmac) && !options[:secret]
36
+ name = class_name(keys, values)
37
37
  const_set(name, compile(keys, values)) unless const_defined?(name)
38
38
  const_get(name).original_new(adapter, options)
39
39
  end
@@ -41,12 +41,12 @@ module Juno
41
41
  private
42
42
 
43
43
  def compile(keys, values)
44
- raise 'Invalid key transformer chain' if KEY_TRANSFORMER !~ keys.map(&:inspect).join
45
- raise 'Invalid value transformer chain' if VALUE_TRANSFORMER !~ values.map(&:inspect).join
44
+ raise ArgumentError, 'Invalid key transformer chain' if KEY_TRANSFORMER !~ keys.map(&:inspect).join
45
+ raise ArgumentError, 'Invalid value transformer chain' if VALUE_TRANSFORMER !~ values.map(&:inspect).join
46
46
 
47
47
  key = compile_transformer(keys, 'key')
48
48
 
49
- klass = Class.new(Transformer)
49
+ klass = Class.new(self)
50
50
  if values.empty?
51
51
  klass.class_eval <<-end_eval, __FILE__, __LINE__
52
52
  def key?(key, options = {})
@@ -97,7 +97,7 @@ module Juno
97
97
  # Returned compiled transformer code string
98
98
  def compile_transformer(transformer, var, i = 2)
99
99
  transformer.inject(var) do |value, name|
100
- raise "Unknown transformer #{name}" unless t = TRANSFORMER[name]
100
+ raise ArgumentError, "Unknown transformer #{name}" unless t = TRANSFORMER[name]
101
101
  require t[3] if t[3]
102
102
  code = t[i]
103
103
  if t[0] == :serialize && var == 'key'
@@ -107,6 +107,11 @@ module Juno
107
107
  end
108
108
  end
109
109
  end
110
+
111
+ def class_name(keys, values)
112
+ (keys.empty? ? '' : keys.map(&:to_s).map(&:capitalize).join << 'Key') <<
113
+ (values.empty? ? '' : values.map(&:to_s).map(&:capitalize).join << 'Value')
114
+ end
110
115
  end
111
116
 
112
117
  # Available key/value transformers
@@ -128,8 +133,8 @@ module Juno
128
133
  :zlib => [ :compress, '::Zlib::Inflate.inflate(value)', '::Zlib::Deflate.deflate(value)', 'zlib' ],
129
134
  :base64 => [ :encode, "value.unpack('m').first", "[value].pack('m').strip" ],
130
135
  :uuencode => [ :encode, "value.unpack('u').first", "[value].pack('u').strip" ],
131
- :escape => [ :encode, "value.gsub(/((?:%[0-9a-fA-F]{2})+)/){ [$1.delete('%')].pack('H*') }",
132
- "value.gsub(/[^a-zA-Z0-9_-]+/){ '%' + $&.unpack('H2' * $&.bytesize).join('%').upcase }" ],
136
+ :escape => [ :encode, 'Escape.unescape(value)', 'Escape.escape(value)' ],
137
+ :hmac => [ :hmac, 'HMAC.verify(value, @secret)', 'HMAC.sign(value, @secret)' , 'openssl' ],
133
138
  :md5 => [ :digest, nil, '::Digest::MD5.hexdigest(value)', 'digest/md5' ],
134
139
  :rmd160 => [ :digest, nil, '::Digest::RMD160.hexdigest(value)', 'digest/rmd160' ],
135
140
  :sha1 => [ :digest, nil, '::Digest::SHA1.hexdigest(value)', 'digest/sha1' ],
@@ -141,9 +146,30 @@ module Juno
141
146
  }
142
147
 
143
148
  # Allowed value transformers (Read it like a regular expression!)
144
- VALUE_TRANSFORMER = compile_validator('serialize? compress? encode?')
149
+ VALUE_TRANSFORMER = compile_validator('serialize? compress? hmac? encode?')
145
150
 
146
151
  # Allowed key transformers (Read it like a regular expression!)
147
152
  KEY_TRANSFORMER = compile_validator('serialize? prefix? (encode | (digest spread?))?')
153
+
154
+ module Escape
155
+ def self.escape(value)
156
+ value.gsub(/[^a-zA-Z0-9_-]+/){ '%' + $&.unpack('H2' * $&.bytesize).join('%').upcase }
157
+ end
158
+
159
+ def self.unescape(value)
160
+ value.gsub(/((?:%[0-9a-fA-F]{2})+)/){ [$1.delete('%')].pack('H*') }
161
+ end
162
+ end
163
+
164
+ module HMAC
165
+ def self.verify(value, secret)
166
+ hash, value = value[0..31], value[32..-1]
167
+ value if hash == OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha256'), secret, value)
168
+ end
169
+
170
+ def self.sign(value, secret)
171
+ OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha256'), secret, value) << value
172
+ end
173
+ end
148
174
  end
149
175
  end
data/lib/juno/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Juno
2
2
  # Juno version number
3
3
  # @api public
4
- VERSION = '0.2.8'
4
+ VERSION = '0.3.0'
5
5
  end
@@ -9,7 +9,7 @@ module Rack
9
9
  # # Add Rack::JunoCookies somewhere in your rack stack
10
10
  # use Rack::JunoCookies
11
11
  #
12
- # run lambda{|env| [200,{},[]] }
12
+ # run lambda { |env| [200, {}, []] }
13
13
  # # But this doesn't do much
14
14
  #
15
15
  # @example config.ru
@@ -23,20 +23,21 @@ module Rack
23
23
  # adapter :Cookie
24
24
  # end
25
25
  #
26
- # run lambda { |env|
26
+ # run lambda do |env|
27
27
  # req = Rack::Request.new(env)
28
- # req.cookies #=> is now a Juno store!!
28
+ # req.cookies #=> is now a Juno store!
29
+ # env['rack.request.cookie_hash'] #=> is now a Juno store!
29
30
  # req.cookies['key'] #=> retrieves 'juno.key'
30
31
  # req.cookies['key'] = 'value' #=> sets 'juno.key'
31
32
  # req.cookies.delete('key') #=> removes 'juno.key'
32
- # [200,{},[]]
33
- # }
33
+ # [200, {}, []]
34
+ # end
34
35
  #
35
36
  class JunoCookies
36
37
  def initialize(app, options = {}, &block)
37
- @app = app
38
+ @app, @pool = app, []
38
39
  if block
39
- raise 'Use either block or options' unless options.empty?
40
+ raise ArgumentError, 'Use either block or options' unless options.empty?
40
41
  @builder = Juno::Builder.new(&block)
41
42
  else
42
43
  @builder = Juno::Builder.new { adapter :Cookie, options }
@@ -44,7 +45,7 @@ module Rack
44
45
  end
45
46
 
46
47
  def call(env)
47
- stores = @builder.build
48
+ stores = @pool.pop || @builder.build
48
49
  env['rack.request.cookie_hash'] = stores.last
49
50
  env['rack.request.cookie_string'] = env['HTTP_COOKIE']
50
51
  stores.first.reset(Rack::Utils.parse_query(env['HTTP_COOKIE']))
@@ -56,6 +57,7 @@ module Rack
56
57
  Rack::Utils.set_cookie_header!(headers, key, cookie)
57
58
  end
58
59
  end
60
+ @pool << stores
59
61
  [status, headers, body]
60
62
  end
61
63
  end
@@ -10,10 +10,10 @@ module Rack
10
10
  def initialize(app, options = {}, &block)
11
11
  super
12
12
  if block
13
- raise 'Use either block or option :store' if options[:store]
13
+ raise ArgumentError, 'Use either block or option :store' if options[:store]
14
14
  @pool = ::Juno.build(&block)
15
15
  else
16
- raise 'Option :store is required' unless @pool = options[:store]
16
+ raise ArgumentError, 'Option :store is required' unless @pool = options[:store]
17
17
  @pool = ::Juno.new(@pool, :expires => true) if Symbol === @pool
18
18
  end
19
19
  @mutex = Mutex.new
data/spec/generate.rb CHANGED
@@ -76,7 +76,8 @@ TESTS = {
76
76
  },
77
77
  'simple_cassandra' => {
78
78
  :store => :Cassandra,
79
- :specs => EXPIRES_SPECS
79
+ :options => ':keyspace => "simple_cassandra"',
80
+ :specs => EXPIRES_SPECS,
80
81
  },
81
82
  'simple_dbm' => {
82
83
  :store => :DBM,
@@ -105,6 +106,15 @@ TESTS = {
105
106
  :options => ':file => File.join(make_tempdir, "simple_sdbm_with_expires"), :expires => true',
106
107
  :specs => EXPIRES_SPECS
107
108
  },
109
+ 'simple_leveldb' => {
110
+ :store => :LevelDB,
111
+ :options => ':dir => File.join(make_tempdir, "simple_leveldb")'
112
+ },
113
+ 'simple_leveldb_with_expires' => {
114
+ :store => :LevelDB,
115
+ :options => ':dir => File.join(make_tempdir, "simple_leveldb_with_expires"), :expires => true',
116
+ :specs => EXPIRES_SPECS
117
+ },
108
118
  'simple_pstore' => {
109
119
  :store => :PStore,
110
120
  :options => ':file => File.join(make_tempdir, "simple_pstore")'
@@ -476,82 +486,89 @@ end},
476
486
  end},
477
487
  :specs => [:null, :store, :returndifferent]
478
488
  },
489
+ 'transformer_marshal_hmac' => {
490
+ :build => %{Juno.build do
491
+ use :Transformer, :key => :marshal, :value => [:marshal, :hmac], :secret => 'secret'
492
+ adapter :Memory
493
+ end},
494
+ :specs => SIMPLE_SPECS
495
+ },
479
496
  'transformer_marshal_base64' => {
480
497
  :build => %{Juno.build do
481
498
  use :Transformer, :key => [:marshal, :base64], :value => [:marshal, :base64]
482
499
  adapter :Memory
483
500
  end},
484
- :specs => [:null, :store, :returndifferent, :marshallable_key]
501
+ :specs => SIMPLE_SPECS
485
502
  },
486
503
  'transformer_marshal_prefix' => {
487
504
  :build => %{Juno.build do
488
505
  use :Transformer, :key => [:marshal, :prefix], :value => :marshal, :prefix => 'juno'
489
506
  adapter :Memory
490
507
  end},
491
- :specs => [:null, :store, :returndifferent, :marshallable_key]
508
+ :specs => SIMPLE_SPECS
492
509
  },
493
510
  'transformer_marshal_uuencode' => {
494
511
  :build => %{Juno.build do
495
512
  use :Transformer, :key => [:marshal, :uuencode], :value => [:marshal, :uuencode]
496
513
  adapter :Memory
497
514
  end},
498
- :specs => [:null, :store, :returndifferent, :marshallable_key]
515
+ :specs => SIMPLE_SPECS
499
516
  },
500
517
  'transformer_marshal_escape' => {
501
518
  :build => %{Juno.build do
502
519
  use :Transformer, :key => [:marshal, :escape], :value => :marshal
503
520
  adapter :Memory
504
521
  end},
505
- :specs => [:null, :store, :returndifferent, :marshallable_key]
522
+ :specs => SIMPLE_SPECS
506
523
  },
507
524
  'transformer_marshal_md5' => {
508
525
  :build => %{Juno.build do
509
526
  use :Transformer, :key => [:marshal, :md5], :value => :marshal
510
527
  adapter :Memory
511
528
  end},
512
- :specs => [:null, :store, :returndifferent, :marshallable_key]
529
+ :specs => SIMPLE_SPECS
513
530
  },
514
531
  'transformer_marshal_sha1' => {
515
532
  :build => %{Juno.build do
516
533
  use :Transformer, :key => [:marshal, :sha1], :value => :marshal
517
534
  adapter :Memory
518
535
  end},
519
- :specs => [:null, :store, :returndifferent, :marshallable_key]
536
+ :specs => SIMPLE_SPECS
520
537
  },
521
538
  'transformer_marshal_sha256' => {
522
539
  :build => %{Juno.build do
523
540
  use :Transformer, :key => [:marshal, :sha256], :value => :marshal
524
541
  adapter :Memory
525
542
  end},
526
- :specs => [:null, :store, :returndifferent, :marshallable_key]
543
+ :specs => SIMPLE_SPECS
527
544
  },
528
545
  'transformer_marshal_sha384' => {
529
546
  :build => %{Juno.build do
530
547
  use :Transformer, :key => [:marshal, :sha384], :value => :marshal
531
548
  adapter :Memory
532
549
  end},
533
- :specs => [:null, :store, :returndifferent, :marshallable_key]
550
+ :specs => SIMPLE_SPECS
534
551
  },
535
552
  'transformer_marshal_sha512' => {
536
553
  :build => %{Juno.build do
537
554
  use :Transformer, :key => [:marshal, :sha512], :value => :marshal
538
555
  adapter :Memory
539
556
  end},
540
- :specs => [:null, :store, :returndifferent, :marshallable_key]
557
+ :specs => SIMPLE_SPECS
541
558
  },
542
559
  'transformer_marshal_rmd160' => {
543
560
  :build => %{Juno.build do
544
561
  use :Transformer, :key => [:marshal, :rmd160], :value => :marshal
545
562
  adapter :Memory
546
563
  end},
547
- :specs => [:null, :store, :returndifferent, :marshallable_key]
564
+ :specs => SIMPLE_SPECS
548
565
  },
549
566
  'transformer_marshal_md5_spread' => {
550
567
  :build => %{Juno.build do
551
568
  use :Transformer, :key => [:marshal, :md5, :spread], :value => :marshal
552
569
  adapter :Memory
553
570
  end},
554
- :specs => [:null, :store, :returndifferent, :marshallable_key]
571
+ :specs => SIMPLE_SPECS
555
572
  },
556
573
  'adapter_activerecord' => {
557
574
  :build => "Juno::Adapters::ActiveRecord.new(:connection => { :adapter => (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'), :database => File.join(make_tempdir, 'adapter_activerecord') })",
@@ -573,7 +590,7 @@ end
573
590
  }
574
591
  },
575
592
  'adapter_cassandra' => {
576
- :build => "Juno::Adapters::Cassandra.new",
593
+ :build => "Juno::Adapters::Cassandra.new(:keyspace => 'adapter_cassandra')",
577
594
  :specs => ADAPTER_SPECS
578
595
  },
579
596
  'adapter_cookie' => {
@@ -692,6 +709,10 @@ end
692
709
  :build => 'Juno::Adapters::SDBM.new(:file => File.join(make_tempdir, "adapter_sdbm"))',
693
710
  :specs => ADAPTER_SPECS
694
711
  },
712
+ 'adapter_leveldb' => {
713
+ :build => 'Juno::Adapters::LevelDB.new(:dir => File.join(make_tempdir, "adapter_leveldb"))',
714
+ :specs => ADAPTER_SPECS
715
+ },
695
716
  'adapter_sequel' => {
696
717
  :build => 'Juno::Adapters::Sequel.new(:db => (defined?(JRUBY_VERSION) ? "jdbc:sqlite:" : "sqlite:") + File.join(make_tempdir, "adapter_sequel"))',
697
718
  :specs => ADAPTER_SPECS
@@ -775,7 +796,8 @@ end
775
796
  it 'should accept options' do
776
797
  store.key?(#{key1}, :option1 => 1).should == false
777
798
  store.load(#{key1}, :option2 => 2).should == nil
778
- store.fetch(#{key1}, nil, :option3 => 3).should == nil
799
+ store.fetch(#{key1}, 42, :option3 => 3).should == 42
800
+ store.fetch(#{key1}, :option3 => 3) { 42 }.should == 42
779
801
  store.delete(#{key1}, :option4 => 4).should == nil
780
802
  store.clear(:option5 => 5).should equal(store)
781
803
  store.store(#{key1}, #{val1}, :option6 => 6).should == #{val1}