moneta 0.6.0 → 0.7.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.
- data/.gitignore +7 -0
- data/.travis.yml +39 -0
- data/Gemfile +61 -0
- data/LICENSE +2 -2
- data/README.md +450 -0
- data/Rakefile +29 -51
- data/SPEC.md +75 -0
- data/benchmarks/run.rb +195 -0
- data/lib/action_dispatch/middleware/session/moneta_store.rb +11 -0
- data/lib/active_support/cache/moneta_store.rb +55 -0
- data/lib/moneta.rb +121 -67
- data/lib/moneta/adapters/activerecord.rb +87 -0
- data/lib/moneta/adapters/cassandra.rb +91 -0
- data/lib/moneta/adapters/client.rb +69 -0
- data/lib/moneta/adapters/cookie.rb +35 -0
- data/lib/moneta/adapters/couch.rb +57 -0
- data/lib/moneta/adapters/datamapper.rb +75 -0
- data/lib/moneta/adapters/dbm.rb +25 -0
- data/lib/moneta/adapters/file.rb +79 -0
- data/lib/moneta/adapters/fog.rb +51 -0
- data/lib/moneta/adapters/gdbm.rb +25 -0
- data/lib/moneta/adapters/hbase.rb +101 -0
- data/lib/moneta/adapters/leveldb.rb +35 -0
- data/lib/moneta/adapters/localmemcache.rb +28 -0
- data/lib/moneta/adapters/lruhash.rb +85 -0
- data/lib/moneta/adapters/memcached.rb +11 -0
- data/lib/moneta/adapters/memcached_dalli.rb +69 -0
- data/lib/moneta/adapters/memcached_native.rb +70 -0
- data/lib/moneta/adapters/memory.rb +10 -0
- data/lib/moneta/adapters/mongo.rb +50 -0
- data/lib/moneta/adapters/null.rb +30 -0
- data/lib/moneta/adapters/pstore.rb +69 -0
- data/lib/moneta/adapters/redis.rb +68 -0
- data/lib/moneta/adapters/riak.rb +57 -0
- data/lib/moneta/adapters/sdbm.rb +35 -0
- data/lib/moneta/adapters/sequel.rb +79 -0
- data/lib/moneta/adapters/sqlite.rb +65 -0
- data/lib/moneta/adapters/tokyocabinet.rb +41 -0
- data/lib/moneta/adapters/yaml.rb +15 -0
- data/lib/moneta/base.rb +78 -0
- data/lib/moneta/builder.rb +39 -0
- data/lib/moneta/cache.rb +84 -0
- data/lib/moneta/expires.rb +71 -0
- data/lib/moneta/lock.rb +25 -0
- data/lib/moneta/logger.rb +61 -0
- data/lib/moneta/mixins.rb +65 -0
- data/lib/moneta/net.rb +18 -0
- data/lib/moneta/optionmerger.rb +39 -0
- data/lib/moneta/proxy.rb +86 -0
- data/lib/moneta/server.rb +81 -0
- data/lib/moneta/shared.rb +60 -0
- data/lib/moneta/stack.rb +78 -0
- data/lib/moneta/transformer.rb +159 -0
- data/lib/moneta/transformer/config.rb +42 -0
- data/lib/moneta/transformer/helper.rb +37 -0
- data/lib/moneta/version.rb +5 -0
- data/lib/moneta/wrapper.rb +33 -0
- data/lib/rack/cache/moneta.rb +93 -0
- data/lib/rack/moneta_cookies.rb +64 -0
- data/lib/rack/session/moneta.rb +63 -0
- data/moneta.gemspec +19 -0
- data/spec/action_dispatch/fixtures/session_autoload_test/foo.rb +10 -0
- data/spec/action_dispatch/session_moneta_store_spec.rb +196 -0
- data/spec/active_support/cache_moneta_store_spec.rb +197 -0
- data/spec/generate.rb +1489 -0
- data/spec/helper.rb +91 -0
- data/spec/moneta/adapter_activerecord_spec.rb +32 -0
- data/spec/moneta/adapter_cassandra_spec.rb +30 -0
- data/spec/moneta/adapter_client_spec.rb +19 -0
- data/spec/moneta/adapter_cookie_spec.rb +18 -0
- data/spec/moneta/adapter_couch_spec.rb +18 -0
- data/spec/moneta/adapter_datamapper_spec.rb +49 -0
- data/spec/moneta/adapter_dbm_spec.rb +18 -0
- data/spec/moneta/adapter_file_spec.rb +18 -0
- data/spec/moneta/adapter_fog_spec.rb +23 -0
- data/spec/moneta/adapter_gdbm_spec.rb +18 -0
- data/spec/moneta/adapter_hbase_spec.rb +18 -0
- data/spec/moneta/adapter_leveldb_spec.rb +18 -0
- data/spec/moneta/adapter_localmemcache_spec.rb +18 -0
- data/spec/moneta/adapter_lruhash_spec.rb +31 -0
- data/spec/moneta/adapter_memcached_dalli_spec.rb +30 -0
- data/spec/moneta/adapter_memcached_native_spec.rb +31 -0
- data/spec/moneta/adapter_memcached_spec.rb +30 -0
- data/spec/moneta/adapter_memory_spec.rb +39 -0
- data/spec/moneta/adapter_mongo_spec.rb +18 -0
- data/spec/moneta/adapter_pstore_spec.rb +21 -0
- data/spec/moneta/adapter_redis_spec.rb +30 -0
- data/spec/moneta/adapter_riak_spec.rb +22 -0
- data/spec/moneta/adapter_sdbm_spec.rb +18 -0
- data/spec/moneta/adapter_sequel_spec.rb +18 -0
- data/spec/moneta/adapter_sqlite_spec.rb +18 -0
- data/spec/moneta/adapter_tokyocabinet_bdb_spec.rb +18 -0
- data/spec/moneta/adapter_tokyocabinet_hdb_spec.rb +18 -0
- data/spec/moneta/adapter_yaml_spec.rb +21 -0
- data/spec/moneta/cache_file_memory_spec.rb +34 -0
- data/spec/moneta/cache_memory_null_spec.rb +23 -0
- data/spec/moneta/expires_file_spec.rb +76 -0
- data/spec/moneta/expires_memory_spec.rb +65 -0
- data/spec/moneta/lock_spec.rb +42 -0
- data/spec/moneta/null_adapter_spec.rb +26 -0
- data/spec/moneta/optionmerger_spec.rb +92 -0
- data/spec/moneta/proxy_expires_memory_spec.rb +55 -0
- data/spec/moneta/proxy_redis_spec.rb +23 -0
- data/spec/moneta/shared_spec.rb +30 -0
- data/spec/moneta/simple_activerecord_spec.rb +51 -0
- data/spec/moneta/simple_activerecord_with_expires_spec.rb +52 -0
- data/spec/moneta/simple_cassandra_spec.rb +52 -0
- data/spec/moneta/simple_client_tcp_spec.rb +67 -0
- data/spec/moneta/simple_client_unix_spec.rb +53 -0
- data/spec/moneta/simple_couch_spec.rb +51 -0
- data/spec/moneta/simple_couch_with_expires_spec.rb +52 -0
- data/spec/moneta/simple_datamapper_spec.rb +53 -0
- data/spec/moneta/simple_datamapper_with_expires_spec.rb +54 -0
- data/spec/moneta/simple_datamapper_with_repository_spec.rb +53 -0
- data/spec/moneta/simple_dbm_spec.rb +51 -0
- data/spec/moneta/simple_dbm_with_expires_spec.rb +52 -0
- data/spec/moneta/simple_file_spec.rb +51 -0
- data/spec/moneta/simple_file_with_expires_spec.rb +52 -0
- data/spec/moneta/simple_fog_spec.rb +56 -0
- data/spec/moneta/simple_fog_with_expires_spec.rb +58 -0
- data/spec/moneta/simple_gdbm_spec.rb +51 -0
- data/spec/moneta/simple_gdbm_with_expires_spec.rb +52 -0
- data/spec/moneta/simple_hashfile_spec.rb +51 -0
- data/spec/moneta/simple_hashfile_with_expires_spec.rb +52 -0
- data/spec/moneta/simple_hbase_spec.rb +51 -0
- data/spec/moneta/simple_hbase_with_expires_spec.rb +52 -0
- data/spec/moneta/simple_leveldb_spec.rb +51 -0
- data/spec/moneta/simple_leveldb_with_expires_spec.rb +52 -0
- data/spec/moneta/simple_localmemcache_spec.rb +51 -0
- data/spec/moneta/simple_localmemcache_with_expires_spec.rb +52 -0
- data/spec/moneta/simple_lruhash_spec.rb +51 -0
- data/spec/moneta/simple_lruhash_with_expires_spec.rb +52 -0
- data/spec/moneta/simple_memcached_dalli_spec.rb +52 -0
- data/spec/moneta/simple_memcached_native_spec.rb +52 -0
- data/spec/moneta/simple_memcached_spec.rb +52 -0
- data/spec/moneta/simple_memory_spec.rb +51 -0
- data/spec/moneta/simple_memory_with_compress_spec.rb +51 -0
- data/spec/moneta/simple_memory_with_expires_spec.rb +52 -0
- data/spec/moneta/simple_memory_with_json_key_serializer_spec.rb +37 -0
- data/spec/moneta/simple_memory_with_json_serializer_spec.rb +28 -0
- data/spec/moneta/simple_memory_with_json_value_serializer_spec.rb +35 -0
- data/spec/moneta/simple_memory_with_prefix_spec.rb +51 -0
- data/spec/moneta/simple_memory_with_snappy_compress_spec.rb +51 -0
- data/spec/moneta/simple_mongo_spec.rb +51 -0
- data/spec/moneta/simple_mongo_with_expires_spec.rb +52 -0
- data/spec/moneta/simple_null_spec.rb +36 -0
- data/spec/moneta/simple_pstore_spec.rb +51 -0
- data/spec/moneta/simple_pstore_with_expires_spec.rb +52 -0
- data/spec/moneta/simple_redis_spec.rb +52 -0
- data/spec/moneta/simple_riak_spec.rb +55 -0
- data/spec/moneta/simple_riak_with_expires_spec.rb +56 -0
- data/spec/moneta/simple_sdbm_spec.rb +51 -0
- data/spec/moneta/simple_sdbm_with_expires_spec.rb +52 -0
- data/spec/moneta/simple_sequel_spec.rb +51 -0
- data/spec/moneta/simple_sequel_with_expires_spec.rb +52 -0
- data/spec/moneta/simple_sqlite_spec.rb +51 -0
- data/spec/moneta/simple_sqlite_with_expires_spec.rb +52 -0
- data/spec/moneta/simple_tokyocabinet_spec.rb +51 -0
- data/spec/moneta/simple_tokyocabinet_with_expires_spec.rb +52 -0
- data/spec/moneta/simple_yaml_spec.rb +50 -0
- data/spec/moneta/simple_yaml_with_expires_spec.rb +51 -0
- data/spec/moneta/stack_file_memory_spec.rb +25 -0
- data/spec/moneta/stack_memory_file_spec.rb +24 -0
- data/spec/moneta/transformer_bencode_spec.rb +30 -0
- data/spec/moneta/transformer_bert_spec.rb +30 -0
- data/spec/moneta/transformer_bson_spec.rb +30 -0
- data/spec/moneta/transformer_bzip2_spec.rb +27 -0
- data/spec/moneta/transformer_json_spec.rb +30 -0
- data/spec/moneta/transformer_lzma_spec.rb +27 -0
- data/spec/moneta/transformer_lzo_spec.rb +27 -0
- data/spec/moneta/transformer_marshal_base64_spec.rb +54 -0
- data/spec/moneta/transformer_marshal_escape_spec.rb +54 -0
- data/spec/moneta/transformer_marshal_hmac_spec.rb +54 -0
- data/spec/moneta/transformer_marshal_md5_spec.rb +54 -0
- data/spec/moneta/transformer_marshal_md5_spread_spec.rb +54 -0
- data/spec/moneta/transformer_marshal_prefix_spec.rb +54 -0
- data/spec/moneta/transformer_marshal_rmd160_spec.rb +54 -0
- data/spec/moneta/transformer_marshal_sha1_spec.rb +54 -0
- data/spec/moneta/transformer_marshal_sha256_spec.rb +54 -0
- data/spec/moneta/transformer_marshal_sha384_spec.rb +54 -0
- data/spec/moneta/transformer_marshal_sha512_spec.rb +54 -0
- data/spec/moneta/transformer_marshal_truncate_spec.rb +54 -0
- data/spec/moneta/transformer_marshal_uuencode_spec.rb +54 -0
- data/spec/moneta/transformer_msgpack_spec.rb +30 -0
- data/spec/moneta/transformer_ox_spec.rb +51 -0
- data/spec/moneta/transformer_quicklz_spec.rb +27 -0
- data/spec/moneta/transformer_snappy_spec.rb +27 -0
- data/spec/moneta/transformer_tnet_spec.rb +30 -0
- data/spec/moneta/transformer_yaml_spec.rb +51 -0
- data/spec/moneta/transformer_zlib_spec.rb +27 -0
- data/spec/monetaspecs.rb +2663 -0
- data/spec/rack/cache_moneta_spec.rb +355 -0
- data/spec/rack/moneta_cookies_spec.rb +81 -0
- data/spec/rack/session_moneta_spec.rb +305 -0
- metadata +359 -56
- data/README +0 -51
- data/TODO +0 -4
- data/lib/moneta/basic_file.rb +0 -111
- data/lib/moneta/berkeley.rb +0 -53
- data/lib/moneta/couch.rb +0 -63
- data/lib/moneta/datamapper.rb +0 -117
- data/lib/moneta/file.rb +0 -91
- data/lib/moneta/lmc.rb +0 -52
- data/lib/moneta/memcache.rb +0 -52
- data/lib/moneta/memory.rb +0 -11
- data/lib/moneta/mongodb.rb +0 -58
- data/lib/moneta/redis.rb +0 -49
- data/lib/moneta/rufus.rb +0 -41
- data/lib/moneta/s3.rb +0 -162
- data/lib/moneta/sdbm.rb +0 -33
- data/lib/moneta/tyrant.rb +0 -58
- data/lib/moneta/xattr.rb +0 -58
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module Moneta
|
|
2
|
+
class Transformer
|
|
3
|
+
# Available key/value transformers
|
|
4
|
+
TRANSFORMER = {
|
|
5
|
+
# Name => [ Type, Load, Dump, Library ],
|
|
6
|
+
:bencode => [ :serialize, '::BEncode.load(value)', '::BEncode.dump(value)', 'bencode' ],
|
|
7
|
+
:bert => [ :serialize, '::BERT.decode(value)', '::BERT.encode(value)', 'bert' ],
|
|
8
|
+
:bson => [ :serialize, "::BSON.deserialize(value)['v']", "::BSON.serialize('v'=>value).to_s", 'bson' ],
|
|
9
|
+
:json => [ :serialize, '::MultiJson.load(value).first', '::MultiJson.dump([value])', 'multi_json' ],
|
|
10
|
+
:marshal => [ :serialize, '::Marshal.load(value)', '::Marshal.dump(value)' ],
|
|
11
|
+
:msgpack => [ :serialize, '::MessagePack.unpack(value)', '::MessagePack.pack(value)', 'msgpack' ],
|
|
12
|
+
:ox => [ :serialize, '::Ox.parse_obj(value)', '::Ox.dump(value)', 'ox' ],
|
|
13
|
+
:tnet => [ :serialize, '::TNetstring.parse(value).first', '::TNetstring.dump(value)', 'tnetstring' ],
|
|
14
|
+
:yaml => [ :serialize, '::YAML.load(value)', '::YAML.dump(value)', 'yaml' ],
|
|
15
|
+
:bzip2 => [ :compress, '::Bzip2.uncompress(value)', '::Bzip2.compress(value)', 'bzip2' ],
|
|
16
|
+
:lzma => [ :compress, '::LZMA.decompress(value)', '::LZMA.compress(value)', 'lzma' ],
|
|
17
|
+
:lzo => [ :compress, '::LZO.decompress(value)', '::LZO.compress(value)', 'lzoruby' ],
|
|
18
|
+
:snappy => [ :compress, '::Snappy.inflate(value)', '::Snappy.deflate(value)', 'snappy' ],
|
|
19
|
+
:quicklz => [ :compress, '::QuickLZ.decompress(value)', '::QuickLZ.compress(value)', 'qlzruby' ],
|
|
20
|
+
:zlib => [ :compress, '::Zlib::Inflate.inflate(value)', '::Zlib::Deflate.deflate(value)', 'zlib' ],
|
|
21
|
+
:base64 => [ :encode, "value.unpack('m').first", "[value].pack('m').strip" ],
|
|
22
|
+
:uuencode => [ :encode, "value.unpack('u').first", "[value].pack('u').strip" ],
|
|
23
|
+
:escape => [ :encode, 'Helper.unescape(value)', 'Helper.escape(value)' ],
|
|
24
|
+
:hmac => [ :hmac, 'Helper.hmacverify(value, @secret)', 'Helper.hmacsign(value, @secret)', 'openssl' ],
|
|
25
|
+
:truncate => [ :truncate, nil, 'Helper.truncate(value, @maxlen)', 'digest/md5' ],
|
|
26
|
+
:md5 => [ :digest, nil, '::Digest::MD5.hexdigest(value)', 'digest/md5' ],
|
|
27
|
+
:rmd160 => [ :digest, nil, '::Digest::RMD160.hexdigest(value)', 'digest/rmd160' ],
|
|
28
|
+
:sha1 => [ :digest, nil, '::Digest::SHA1.hexdigest(value)', 'digest/sha1' ],
|
|
29
|
+
:sha256 => [ :digest, nil, '::Digest::SHA256.hexdigest(value)', 'digest/sha2' ],
|
|
30
|
+
:sha384 => [ :digest, nil, '::Digest::SHA384.hexdigest(value)', 'digest/sha2' ],
|
|
31
|
+
:sha512 => [ :digest, nil, '::Digest::SHA512.hexdigest(value)', 'digest/sha2' ],
|
|
32
|
+
:prefix => [ :prefix, nil, '(options[:prefix]||@prefix)+value' ],
|
|
33
|
+
:spread => [ :spread, nil, 'Helper.spread(value)' ],
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
# Allowed value transformers (Read it like a regular expression!)
|
|
37
|
+
VALUE_TRANSFORMER = compile_validator('serialize? compress? hmac? encode?')
|
|
38
|
+
|
|
39
|
+
# Allowed key transformers (Read it like a regular expression!)
|
|
40
|
+
KEY_TRANSFORMER = compile_validator('serialize? prefix? ((encode? truncate?) | (digest spread?))?')
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
module Moneta
|
|
2
|
+
class Transformer
|
|
3
|
+
# @api private
|
|
4
|
+
module Helper
|
|
5
|
+
extend self
|
|
6
|
+
|
|
7
|
+
def escape(value)
|
|
8
|
+
value.gsub(/[^a-zA-Z0-9_-]+/){ '%' + $&.unpack('H2' * $&.bytesize).join('%').upcase }
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def unescape(value)
|
|
12
|
+
value.gsub(/((?:%[0-9a-fA-F]{2})+)/){ [$1.delete('%')].pack('H*') }
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def hmacverify(value, secret)
|
|
16
|
+
hash, value = value[0..31], value[32..-1]
|
|
17
|
+
value if hash == OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha256'), secret, value)
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def hmacsign(value, secret)
|
|
21
|
+
OpenSSL::HMAC.digest(OpenSSL::Digest::Digest.new('sha256'), secret, value) << value
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def truncate(value, maxlen)
|
|
25
|
+
if value.size >= maxlen
|
|
26
|
+
digest = Digest::MD5.hexdigest(value)
|
|
27
|
+
value = value[0, value.size-digest.size] << digest
|
|
28
|
+
end
|
|
29
|
+
value
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def spread(value)
|
|
33
|
+
::File.join(value[0..1], value[2..-1])
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
module Moneta
|
|
2
|
+
# Wraps the calls to the adapter
|
|
3
|
+
# @api public
|
|
4
|
+
class Wrapper < Proxy
|
|
5
|
+
def key?(key, options = {})
|
|
6
|
+
wrap(:key?, key, options) { super }
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def load(key, options = {})
|
|
10
|
+
wrap(:load, key, options) { super }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def store(key, value, options = {})
|
|
14
|
+
wrap(:store, key, value, options) { super }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def delete(key, options = {})
|
|
18
|
+
wrap(:delete, key, options) { super }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def increment(key, amount = 1, options = {})
|
|
22
|
+
wrap(:increment, key, amount, options) { super }
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def clear(options = {})
|
|
26
|
+
wrap(:clear, options) { super }
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def close
|
|
30
|
+
wrap(:close) { super }
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
require 'moneta'
|
|
2
|
+
require 'rack/cache/key'
|
|
3
|
+
require 'rack/cache/metastore'
|
|
4
|
+
require 'rack/cache/entitystore'
|
|
5
|
+
|
|
6
|
+
module Rack
|
|
7
|
+
module Cache
|
|
8
|
+
Moneta = {}
|
|
9
|
+
|
|
10
|
+
module MonetaResolver
|
|
11
|
+
include Rack::Utils
|
|
12
|
+
|
|
13
|
+
def resolve(uri)
|
|
14
|
+
cache = Rack::Cache::Moneta[uri.to_s.sub(%r{^moneta://}, '')] ||=
|
|
15
|
+
begin
|
|
16
|
+
options = parse_query(uri.query)
|
|
17
|
+
options.keys.each do |key|
|
|
18
|
+
options[key.to_sym] =
|
|
19
|
+
case value = options.delete(key)
|
|
20
|
+
when 'true'; true
|
|
21
|
+
when 'false'; false
|
|
22
|
+
else value
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
::Moneta.new(uri.host.to_sym, options)
|
|
26
|
+
end
|
|
27
|
+
new(cache)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
class MetaStore
|
|
32
|
+
class Moneta < MetaStore
|
|
33
|
+
extend MonetaResolver
|
|
34
|
+
|
|
35
|
+
def initialize(cache)
|
|
36
|
+
@cache = cache
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def read(key)
|
|
40
|
+
@cache[key] || []
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def write(key, entries)
|
|
44
|
+
@cache[key] = entries
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def purge(key)
|
|
48
|
+
@cache.delete(key)
|
|
49
|
+
nil
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
MONETA = Moneta
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class EntityStore
|
|
57
|
+
class Moneta < EntityStore
|
|
58
|
+
extend MonetaResolver
|
|
59
|
+
|
|
60
|
+
def initialize(cache)
|
|
61
|
+
@cache = cache
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def open(key)
|
|
65
|
+
data = read(key)
|
|
66
|
+
data && [data]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def exist?(key)
|
|
70
|
+
@cache.key?(key)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def read(key)
|
|
74
|
+
@cache[key]
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def write(body, ttl = 0)
|
|
78
|
+
buf = StringIO.new
|
|
79
|
+
key, size = slurp(body) { |part| buf.write(part) }
|
|
80
|
+
@cache.store(key, buf.string, ttl == 0 ? {} : {:expires => ttl})
|
|
81
|
+
[key, size]
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
def purge(key)
|
|
85
|
+
@cache.delete(key)
|
|
86
|
+
nil
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
MONETA = Moneta
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
end
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
require 'moneta'
|
|
2
|
+
require 'rack/utils'
|
|
3
|
+
|
|
4
|
+
module Rack
|
|
5
|
+
# A Rack middleware that was made to reuse all moneta transformers
|
|
6
|
+
# on the cookie hash.
|
|
7
|
+
#
|
|
8
|
+
# @example config.ru
|
|
9
|
+
# # Add Rack::MonetaCookies somewhere in your rack stack
|
|
10
|
+
# use Rack::MonetaCookies
|
|
11
|
+
#
|
|
12
|
+
# run lambda { |env| [200, {}, []] }
|
|
13
|
+
# # But this doesn't do much
|
|
14
|
+
#
|
|
15
|
+
# @example config.ru
|
|
16
|
+
# # Give it some options
|
|
17
|
+
# use Rack::MonetaCookies, :domain => 'example.com', :path => '/path'
|
|
18
|
+
#
|
|
19
|
+
# @example config.ru
|
|
20
|
+
# # Pass it a block like the one passed to Moneta.build
|
|
21
|
+
# use Rack::MonetaCookies do
|
|
22
|
+
# use :Transformer, :key => :prefix, :prefix => 'moneta.'
|
|
23
|
+
# adapter :Cookie
|
|
24
|
+
# end
|
|
25
|
+
#
|
|
26
|
+
# run lambda do |env|
|
|
27
|
+
# req = Rack::Request.new(env)
|
|
28
|
+
# req.cookies #=> is now a Moneta store!
|
|
29
|
+
# env['rack.request.cookie_hash'] #=> is now a Moneta store!
|
|
30
|
+
# req.cookies['key'] #=> retrieves 'moneta.key'
|
|
31
|
+
# req.cookies['key'] = 'value' #=> sets 'moneta.key'
|
|
32
|
+
# req.cookies.delete('key') #=> removes 'moneta.key'
|
|
33
|
+
# [200, {}, []]
|
|
34
|
+
# end
|
|
35
|
+
#
|
|
36
|
+
class MonetaCookies
|
|
37
|
+
def initialize(app, options = {}, &block)
|
|
38
|
+
@app, @pool = app, []
|
|
39
|
+
if block
|
|
40
|
+
raise ArgumentError, 'Use either block or options' unless options.empty?
|
|
41
|
+
@builder = Moneta::Builder.new(&block)
|
|
42
|
+
else
|
|
43
|
+
@builder = Moneta::Builder.new { adapter :Cookie, options }
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def call(env)
|
|
48
|
+
stores = @pool.pop || @builder.build
|
|
49
|
+
env['rack.request.cookie_hash'] = stores.last
|
|
50
|
+
env['rack.request.cookie_string'] = env['HTTP_COOKIE']
|
|
51
|
+
stores.first.reset(Rack::Utils.parse_query(env['HTTP_COOKIE']))
|
|
52
|
+
status, headers, body = @app.call(env)
|
|
53
|
+
stores.first.cookies.each do |key, cookie|
|
|
54
|
+
if cookie == nil
|
|
55
|
+
Rack::Utils.delete_cookie_header!(headers, key)
|
|
56
|
+
else
|
|
57
|
+
Rack::Utils.set_cookie_header!(headers, key, cookie)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
@pool << stores
|
|
61
|
+
[status, headers, body]
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
require 'moneta'
|
|
2
|
+
require 'rack/session/abstract/id'
|
|
3
|
+
require 'thread'
|
|
4
|
+
|
|
5
|
+
module Rack
|
|
6
|
+
module Session
|
|
7
|
+
class Moneta < Abstract::ID
|
|
8
|
+
attr_reader :mutex, :pool
|
|
9
|
+
|
|
10
|
+
def initialize(app, options = {}, &block)
|
|
11
|
+
super
|
|
12
|
+
if block
|
|
13
|
+
raise ArgumentError, 'Use either block or option :store' if options[:store]
|
|
14
|
+
@pool = ::Moneta.build(&block)
|
|
15
|
+
else
|
|
16
|
+
raise ArgumentError, 'Option :store is required' unless @pool = options[:store]
|
|
17
|
+
@pool = ::Moneta.new(@pool, :expires => true) if Symbol === @pool
|
|
18
|
+
end
|
|
19
|
+
@mutex = Mutex.new
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def generate_sid
|
|
23
|
+
loop do
|
|
24
|
+
sid = super
|
|
25
|
+
break sid unless @pool.key?(sid)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def get_session(env, sid)
|
|
30
|
+
with_lock(env) do
|
|
31
|
+
unless sid && session = @pool[sid]
|
|
32
|
+
sid, session = generate_sid, {}
|
|
33
|
+
@pool[sid] = session
|
|
34
|
+
end
|
|
35
|
+
[sid, session]
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def set_session(env, session_id, new_session, options)
|
|
40
|
+
with_lock(env) do
|
|
41
|
+
@pool.store(session_id, new_session,
|
|
42
|
+
options[:expire_after] ? {:expires => options[:expire_after]} : {})
|
|
43
|
+
session_id
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def destroy_session(env, session_id, options)
|
|
48
|
+
with_lock(env) do
|
|
49
|
+
@pool.delete(session_id)
|
|
50
|
+
generate_sid unless options[:drop]
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def with_lock(env)
|
|
55
|
+
@mutex.lock if env['rack.multithread']
|
|
56
|
+
yield
|
|
57
|
+
ensure
|
|
58
|
+
@mutex.unlock if @mutex.locked?
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
data/moneta.gemspec
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
require File.dirname(__FILE__) + '/lib/moneta/version'
|
|
3
|
+
require 'date'
|
|
4
|
+
|
|
5
|
+
Gem::Specification.new do |s|
|
|
6
|
+
s.name = 'moneta'
|
|
7
|
+
s.version = Moneta::VERSION
|
|
8
|
+
s.date = Date.today.to_s
|
|
9
|
+
s.authors = ['Daniel Mendler', 'Yehuda Katz' 'Hannes Georg']
|
|
10
|
+
s.email = %w{mail@daniel-mendler.de wycats@gmail.com hannes.georg@googlemail.com}
|
|
11
|
+
s.description = 'A unified interface to key/value stores'
|
|
12
|
+
s.extra_rdoc_files = %w{README.md SPEC.md LICENSE}
|
|
13
|
+
s.files = `git ls-files`.split("\n")
|
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
|
16
|
+
s.homepage = 'http://github.com/minad/moneta'
|
|
17
|
+
s.require_paths = ['lib']
|
|
18
|
+
s.summary = %{A unified interface to key/value stores, including MongoDB, Redis, Tokyo, and ActiveRecord}
|
|
19
|
+
end
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
require 'action_dispatch'
|
|
2
|
+
require 'action_controller'
|
|
3
|
+
require 'action_dispatch/middleware/session/moneta_store'
|
|
4
|
+
require 'minitest/autorun'
|
|
5
|
+
|
|
6
|
+
class MonetaStoreTest < ActionDispatch::IntegrationTest
|
|
7
|
+
class TestController < ActionController::Base
|
|
8
|
+
def no_session_access
|
|
9
|
+
head :ok
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def set_session_value
|
|
13
|
+
session[:foo] = "bar"
|
|
14
|
+
head :ok
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def set_serialized_session_value
|
|
18
|
+
session[:foo] = SessionAutoloadTest::Foo.new
|
|
19
|
+
head :ok
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def get_session_value
|
|
23
|
+
render :text => "foo: #{session[:foo].inspect}"
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def get_session_id
|
|
27
|
+
render :text => "#{request.session_options[:id]}"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def call_reset_session
|
|
31
|
+
session[:bar]
|
|
32
|
+
reset_session
|
|
33
|
+
session[:bar] = "baz"
|
|
34
|
+
head :ok
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def test_setting_and_getting_session_value
|
|
39
|
+
with_test_route_set do
|
|
40
|
+
get '/set_session_value'
|
|
41
|
+
assert_response :success
|
|
42
|
+
assert cookies['_session_id']
|
|
43
|
+
|
|
44
|
+
get '/get_session_value'
|
|
45
|
+
assert_response :success
|
|
46
|
+
assert_equal 'foo: "bar"', response.body
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def test_getting_nil_session_value
|
|
51
|
+
with_test_route_set do
|
|
52
|
+
get '/get_session_value'
|
|
53
|
+
assert_response :success
|
|
54
|
+
assert_equal 'foo: nil', response.body
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_getting_session_value_after_session_reset
|
|
59
|
+
with_test_route_set do
|
|
60
|
+
get '/set_session_value'
|
|
61
|
+
assert_response :success
|
|
62
|
+
assert cookies['_session_id']
|
|
63
|
+
session_cookie = cookies.send(:hash_for)['_session_id']
|
|
64
|
+
|
|
65
|
+
get '/call_reset_session'
|
|
66
|
+
assert_response :success
|
|
67
|
+
assert_not_equal [], headers['Set-Cookie']
|
|
68
|
+
|
|
69
|
+
cookies << session_cookie # replace our new session_id with our old, pre-reset session_id
|
|
70
|
+
|
|
71
|
+
get '/get_session_value'
|
|
72
|
+
assert_response :success
|
|
73
|
+
assert_equal 'foo: nil', response.body, "data for this session should have been obliterated from cache"
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def test_getting_from_nonexistent_session
|
|
78
|
+
with_test_route_set do
|
|
79
|
+
get '/get_session_value'
|
|
80
|
+
assert_response :success
|
|
81
|
+
assert_equal 'foo: nil', response.body
|
|
82
|
+
assert_nil cookies['_session_id'], "should only create session on write, not read"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def test_setting_session_value_after_session_reset
|
|
87
|
+
with_test_route_set do
|
|
88
|
+
get '/set_session_value'
|
|
89
|
+
assert_response :success
|
|
90
|
+
assert cookies['_session_id']
|
|
91
|
+
session_id = cookies['_session_id']
|
|
92
|
+
|
|
93
|
+
get '/call_reset_session'
|
|
94
|
+
assert_response :success
|
|
95
|
+
assert_not_equal [], headers['Set-Cookie']
|
|
96
|
+
|
|
97
|
+
get '/get_session_value'
|
|
98
|
+
assert_response :success
|
|
99
|
+
assert_equal 'foo: nil', response.body
|
|
100
|
+
|
|
101
|
+
get '/get_session_id'
|
|
102
|
+
assert_response :success
|
|
103
|
+
assert_not_equal session_id, response.body
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def test_getting_session_id
|
|
108
|
+
with_test_route_set do
|
|
109
|
+
get '/set_session_value'
|
|
110
|
+
assert_response :success
|
|
111
|
+
assert cookies['_session_id']
|
|
112
|
+
session_id = cookies['_session_id']
|
|
113
|
+
|
|
114
|
+
get '/get_session_id'
|
|
115
|
+
assert_response :success
|
|
116
|
+
assert_equal session_id, response.body, "should be able to read session id without accessing the session hash"
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def test_deserializes_unloaded_class
|
|
121
|
+
with_test_route_set do
|
|
122
|
+
with_autoload_path do
|
|
123
|
+
get '/set_serialized_session_value'
|
|
124
|
+
assert_response :success
|
|
125
|
+
assert cookies['_session_id']
|
|
126
|
+
end
|
|
127
|
+
with_autoload_path do
|
|
128
|
+
get '/get_session_id'
|
|
129
|
+
assert_response :success
|
|
130
|
+
end
|
|
131
|
+
with_autoload_path do
|
|
132
|
+
get '/get_session_value'
|
|
133
|
+
assert_response :success
|
|
134
|
+
assert_equal 'foo: #<SessionAutoloadTest::Foo bar:"baz">', response.body, "should auto-load unloaded class"
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
def test_doesnt_write_session_cookie_if_session_id_is_already_exists
|
|
140
|
+
with_test_route_set do
|
|
141
|
+
get '/set_session_value'
|
|
142
|
+
assert_response :success
|
|
143
|
+
assert cookies['_session_id']
|
|
144
|
+
|
|
145
|
+
get '/get_session_value'
|
|
146
|
+
assert_response :success
|
|
147
|
+
assert_equal nil, headers['Set-Cookie'], "should not resend the cookie again if session_id cookie is already exists"
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
def test_prevents_session_fixation
|
|
152
|
+
with_test_route_set do
|
|
153
|
+
get '/get_session_value'
|
|
154
|
+
assert_response :success
|
|
155
|
+
assert_equal 'foo: nil', response.body
|
|
156
|
+
session_id = cookies['_session_id']
|
|
157
|
+
|
|
158
|
+
reset!
|
|
159
|
+
|
|
160
|
+
get '/set_session_value', :_session_id => session_id
|
|
161
|
+
assert_response :success
|
|
162
|
+
assert_not_equal session_id, cookies['_session_id']
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
private
|
|
167
|
+
|
|
168
|
+
def with_autoload_path
|
|
169
|
+
path = File.join(File.dirname(__FILE__), 'fixtures')
|
|
170
|
+
if ActiveSupport::Dependencies.autoload_paths.include?(path)
|
|
171
|
+
yield
|
|
172
|
+
else
|
|
173
|
+
begin
|
|
174
|
+
ActiveSupport::Dependencies.autoload_paths << path
|
|
175
|
+
yield
|
|
176
|
+
ensure
|
|
177
|
+
ActiveSupport::Dependencies.autoload_paths.reject! {|p| p == path}
|
|
178
|
+
ActiveSupport::Dependencies.clear
|
|
179
|
+
end
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
def with_test_route_set
|
|
184
|
+
with_routing do |set|
|
|
185
|
+
set.draw do
|
|
186
|
+
get ':action', :to => ::MonetaStoreTest::TestController
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
@app = ActionDispatch::MiddlewareStack.new do |middleware|
|
|
190
|
+
middleware.use ActionDispatch::Session::MonetaStore, :key => '_session_id', :store => :Memory
|
|
191
|
+
end.build(set)
|
|
192
|
+
|
|
193
|
+
yield
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|