moneta 0.6.0 → 0.7.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|