moneta 0.6.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (212) hide show
  1. data/.gitignore +7 -0
  2. data/.travis.yml +39 -0
  3. data/Gemfile +61 -0
  4. data/LICENSE +2 -2
  5. data/README.md +450 -0
  6. data/Rakefile +29 -51
  7. data/SPEC.md +75 -0
  8. data/benchmarks/run.rb +195 -0
  9. data/lib/action_dispatch/middleware/session/moneta_store.rb +11 -0
  10. data/lib/active_support/cache/moneta_store.rb +55 -0
  11. data/lib/moneta.rb +121 -67
  12. data/lib/moneta/adapters/activerecord.rb +87 -0
  13. data/lib/moneta/adapters/cassandra.rb +91 -0
  14. data/lib/moneta/adapters/client.rb +69 -0
  15. data/lib/moneta/adapters/cookie.rb +35 -0
  16. data/lib/moneta/adapters/couch.rb +57 -0
  17. data/lib/moneta/adapters/datamapper.rb +75 -0
  18. data/lib/moneta/adapters/dbm.rb +25 -0
  19. data/lib/moneta/adapters/file.rb +79 -0
  20. data/lib/moneta/adapters/fog.rb +51 -0
  21. data/lib/moneta/adapters/gdbm.rb +25 -0
  22. data/lib/moneta/adapters/hbase.rb +101 -0
  23. data/lib/moneta/adapters/leveldb.rb +35 -0
  24. data/lib/moneta/adapters/localmemcache.rb +28 -0
  25. data/lib/moneta/adapters/lruhash.rb +85 -0
  26. data/lib/moneta/adapters/memcached.rb +11 -0
  27. data/lib/moneta/adapters/memcached_dalli.rb +69 -0
  28. data/lib/moneta/adapters/memcached_native.rb +70 -0
  29. data/lib/moneta/adapters/memory.rb +10 -0
  30. data/lib/moneta/adapters/mongo.rb +50 -0
  31. data/lib/moneta/adapters/null.rb +30 -0
  32. data/lib/moneta/adapters/pstore.rb +69 -0
  33. data/lib/moneta/adapters/redis.rb +68 -0
  34. data/lib/moneta/adapters/riak.rb +57 -0
  35. data/lib/moneta/adapters/sdbm.rb +35 -0
  36. data/lib/moneta/adapters/sequel.rb +79 -0
  37. data/lib/moneta/adapters/sqlite.rb +65 -0
  38. data/lib/moneta/adapters/tokyocabinet.rb +41 -0
  39. data/lib/moneta/adapters/yaml.rb +15 -0
  40. data/lib/moneta/base.rb +78 -0
  41. data/lib/moneta/builder.rb +39 -0
  42. data/lib/moneta/cache.rb +84 -0
  43. data/lib/moneta/expires.rb +71 -0
  44. data/lib/moneta/lock.rb +25 -0
  45. data/lib/moneta/logger.rb +61 -0
  46. data/lib/moneta/mixins.rb +65 -0
  47. data/lib/moneta/net.rb +18 -0
  48. data/lib/moneta/optionmerger.rb +39 -0
  49. data/lib/moneta/proxy.rb +86 -0
  50. data/lib/moneta/server.rb +81 -0
  51. data/lib/moneta/shared.rb +60 -0
  52. data/lib/moneta/stack.rb +78 -0
  53. data/lib/moneta/transformer.rb +159 -0
  54. data/lib/moneta/transformer/config.rb +42 -0
  55. data/lib/moneta/transformer/helper.rb +37 -0
  56. data/lib/moneta/version.rb +5 -0
  57. data/lib/moneta/wrapper.rb +33 -0
  58. data/lib/rack/cache/moneta.rb +93 -0
  59. data/lib/rack/moneta_cookies.rb +64 -0
  60. data/lib/rack/session/moneta.rb +63 -0
  61. data/moneta.gemspec +19 -0
  62. data/spec/action_dispatch/fixtures/session_autoload_test/foo.rb +10 -0
  63. data/spec/action_dispatch/session_moneta_store_spec.rb +196 -0
  64. data/spec/active_support/cache_moneta_store_spec.rb +197 -0
  65. data/spec/generate.rb +1489 -0
  66. data/spec/helper.rb +91 -0
  67. data/spec/moneta/adapter_activerecord_spec.rb +32 -0
  68. data/spec/moneta/adapter_cassandra_spec.rb +30 -0
  69. data/spec/moneta/adapter_client_spec.rb +19 -0
  70. data/spec/moneta/adapter_cookie_spec.rb +18 -0
  71. data/spec/moneta/adapter_couch_spec.rb +18 -0
  72. data/spec/moneta/adapter_datamapper_spec.rb +49 -0
  73. data/spec/moneta/adapter_dbm_spec.rb +18 -0
  74. data/spec/moneta/adapter_file_spec.rb +18 -0
  75. data/spec/moneta/adapter_fog_spec.rb +23 -0
  76. data/spec/moneta/adapter_gdbm_spec.rb +18 -0
  77. data/spec/moneta/adapter_hbase_spec.rb +18 -0
  78. data/spec/moneta/adapter_leveldb_spec.rb +18 -0
  79. data/spec/moneta/adapter_localmemcache_spec.rb +18 -0
  80. data/spec/moneta/adapter_lruhash_spec.rb +31 -0
  81. data/spec/moneta/adapter_memcached_dalli_spec.rb +30 -0
  82. data/spec/moneta/adapter_memcached_native_spec.rb +31 -0
  83. data/spec/moneta/adapter_memcached_spec.rb +30 -0
  84. data/spec/moneta/adapter_memory_spec.rb +39 -0
  85. data/spec/moneta/adapter_mongo_spec.rb +18 -0
  86. data/spec/moneta/adapter_pstore_spec.rb +21 -0
  87. data/spec/moneta/adapter_redis_spec.rb +30 -0
  88. data/spec/moneta/adapter_riak_spec.rb +22 -0
  89. data/spec/moneta/adapter_sdbm_spec.rb +18 -0
  90. data/spec/moneta/adapter_sequel_spec.rb +18 -0
  91. data/spec/moneta/adapter_sqlite_spec.rb +18 -0
  92. data/spec/moneta/adapter_tokyocabinet_bdb_spec.rb +18 -0
  93. data/spec/moneta/adapter_tokyocabinet_hdb_spec.rb +18 -0
  94. data/spec/moneta/adapter_yaml_spec.rb +21 -0
  95. data/spec/moneta/cache_file_memory_spec.rb +34 -0
  96. data/spec/moneta/cache_memory_null_spec.rb +23 -0
  97. data/spec/moneta/expires_file_spec.rb +76 -0
  98. data/spec/moneta/expires_memory_spec.rb +65 -0
  99. data/spec/moneta/lock_spec.rb +42 -0
  100. data/spec/moneta/null_adapter_spec.rb +26 -0
  101. data/spec/moneta/optionmerger_spec.rb +92 -0
  102. data/spec/moneta/proxy_expires_memory_spec.rb +55 -0
  103. data/spec/moneta/proxy_redis_spec.rb +23 -0
  104. data/spec/moneta/shared_spec.rb +30 -0
  105. data/spec/moneta/simple_activerecord_spec.rb +51 -0
  106. data/spec/moneta/simple_activerecord_with_expires_spec.rb +52 -0
  107. data/spec/moneta/simple_cassandra_spec.rb +52 -0
  108. data/spec/moneta/simple_client_tcp_spec.rb +67 -0
  109. data/spec/moneta/simple_client_unix_spec.rb +53 -0
  110. data/spec/moneta/simple_couch_spec.rb +51 -0
  111. data/spec/moneta/simple_couch_with_expires_spec.rb +52 -0
  112. data/spec/moneta/simple_datamapper_spec.rb +53 -0
  113. data/spec/moneta/simple_datamapper_with_expires_spec.rb +54 -0
  114. data/spec/moneta/simple_datamapper_with_repository_spec.rb +53 -0
  115. data/spec/moneta/simple_dbm_spec.rb +51 -0
  116. data/spec/moneta/simple_dbm_with_expires_spec.rb +52 -0
  117. data/spec/moneta/simple_file_spec.rb +51 -0
  118. data/spec/moneta/simple_file_with_expires_spec.rb +52 -0
  119. data/spec/moneta/simple_fog_spec.rb +56 -0
  120. data/spec/moneta/simple_fog_with_expires_spec.rb +58 -0
  121. data/spec/moneta/simple_gdbm_spec.rb +51 -0
  122. data/spec/moneta/simple_gdbm_with_expires_spec.rb +52 -0
  123. data/spec/moneta/simple_hashfile_spec.rb +51 -0
  124. data/spec/moneta/simple_hashfile_with_expires_spec.rb +52 -0
  125. data/spec/moneta/simple_hbase_spec.rb +51 -0
  126. data/spec/moneta/simple_hbase_with_expires_spec.rb +52 -0
  127. data/spec/moneta/simple_leveldb_spec.rb +51 -0
  128. data/spec/moneta/simple_leveldb_with_expires_spec.rb +52 -0
  129. data/spec/moneta/simple_localmemcache_spec.rb +51 -0
  130. data/spec/moneta/simple_localmemcache_with_expires_spec.rb +52 -0
  131. data/spec/moneta/simple_lruhash_spec.rb +51 -0
  132. data/spec/moneta/simple_lruhash_with_expires_spec.rb +52 -0
  133. data/spec/moneta/simple_memcached_dalli_spec.rb +52 -0
  134. data/spec/moneta/simple_memcached_native_spec.rb +52 -0
  135. data/spec/moneta/simple_memcached_spec.rb +52 -0
  136. data/spec/moneta/simple_memory_spec.rb +51 -0
  137. data/spec/moneta/simple_memory_with_compress_spec.rb +51 -0
  138. data/spec/moneta/simple_memory_with_expires_spec.rb +52 -0
  139. data/spec/moneta/simple_memory_with_json_key_serializer_spec.rb +37 -0
  140. data/spec/moneta/simple_memory_with_json_serializer_spec.rb +28 -0
  141. data/spec/moneta/simple_memory_with_json_value_serializer_spec.rb +35 -0
  142. data/spec/moneta/simple_memory_with_prefix_spec.rb +51 -0
  143. data/spec/moneta/simple_memory_with_snappy_compress_spec.rb +51 -0
  144. data/spec/moneta/simple_mongo_spec.rb +51 -0
  145. data/spec/moneta/simple_mongo_with_expires_spec.rb +52 -0
  146. data/spec/moneta/simple_null_spec.rb +36 -0
  147. data/spec/moneta/simple_pstore_spec.rb +51 -0
  148. data/spec/moneta/simple_pstore_with_expires_spec.rb +52 -0
  149. data/spec/moneta/simple_redis_spec.rb +52 -0
  150. data/spec/moneta/simple_riak_spec.rb +55 -0
  151. data/spec/moneta/simple_riak_with_expires_spec.rb +56 -0
  152. data/spec/moneta/simple_sdbm_spec.rb +51 -0
  153. data/spec/moneta/simple_sdbm_with_expires_spec.rb +52 -0
  154. data/spec/moneta/simple_sequel_spec.rb +51 -0
  155. data/spec/moneta/simple_sequel_with_expires_spec.rb +52 -0
  156. data/spec/moneta/simple_sqlite_spec.rb +51 -0
  157. data/spec/moneta/simple_sqlite_with_expires_spec.rb +52 -0
  158. data/spec/moneta/simple_tokyocabinet_spec.rb +51 -0
  159. data/spec/moneta/simple_tokyocabinet_with_expires_spec.rb +52 -0
  160. data/spec/moneta/simple_yaml_spec.rb +50 -0
  161. data/spec/moneta/simple_yaml_with_expires_spec.rb +51 -0
  162. data/spec/moneta/stack_file_memory_spec.rb +25 -0
  163. data/spec/moneta/stack_memory_file_spec.rb +24 -0
  164. data/spec/moneta/transformer_bencode_spec.rb +30 -0
  165. data/spec/moneta/transformer_bert_spec.rb +30 -0
  166. data/spec/moneta/transformer_bson_spec.rb +30 -0
  167. data/spec/moneta/transformer_bzip2_spec.rb +27 -0
  168. data/spec/moneta/transformer_json_spec.rb +30 -0
  169. data/spec/moneta/transformer_lzma_spec.rb +27 -0
  170. data/spec/moneta/transformer_lzo_spec.rb +27 -0
  171. data/spec/moneta/transformer_marshal_base64_spec.rb +54 -0
  172. data/spec/moneta/transformer_marshal_escape_spec.rb +54 -0
  173. data/spec/moneta/transformer_marshal_hmac_spec.rb +54 -0
  174. data/spec/moneta/transformer_marshal_md5_spec.rb +54 -0
  175. data/spec/moneta/transformer_marshal_md5_spread_spec.rb +54 -0
  176. data/spec/moneta/transformer_marshal_prefix_spec.rb +54 -0
  177. data/spec/moneta/transformer_marshal_rmd160_spec.rb +54 -0
  178. data/spec/moneta/transformer_marshal_sha1_spec.rb +54 -0
  179. data/spec/moneta/transformer_marshal_sha256_spec.rb +54 -0
  180. data/spec/moneta/transformer_marshal_sha384_spec.rb +54 -0
  181. data/spec/moneta/transformer_marshal_sha512_spec.rb +54 -0
  182. data/spec/moneta/transformer_marshal_truncate_spec.rb +54 -0
  183. data/spec/moneta/transformer_marshal_uuencode_spec.rb +54 -0
  184. data/spec/moneta/transformer_msgpack_spec.rb +30 -0
  185. data/spec/moneta/transformer_ox_spec.rb +51 -0
  186. data/spec/moneta/transformer_quicklz_spec.rb +27 -0
  187. data/spec/moneta/transformer_snappy_spec.rb +27 -0
  188. data/spec/moneta/transformer_tnet_spec.rb +30 -0
  189. data/spec/moneta/transformer_yaml_spec.rb +51 -0
  190. data/spec/moneta/transformer_zlib_spec.rb +27 -0
  191. data/spec/monetaspecs.rb +2663 -0
  192. data/spec/rack/cache_moneta_spec.rb +355 -0
  193. data/spec/rack/moneta_cookies_spec.rb +81 -0
  194. data/spec/rack/session_moneta_spec.rb +305 -0
  195. metadata +359 -56
  196. data/README +0 -51
  197. data/TODO +0 -4
  198. data/lib/moneta/basic_file.rb +0 -111
  199. data/lib/moneta/berkeley.rb +0 -53
  200. data/lib/moneta/couch.rb +0 -63
  201. data/lib/moneta/datamapper.rb +0 -117
  202. data/lib/moneta/file.rb +0 -91
  203. data/lib/moneta/lmc.rb +0 -52
  204. data/lib/moneta/memcache.rb +0 -52
  205. data/lib/moneta/memory.rb +0 -11
  206. data/lib/moneta/mongodb.rb +0 -58
  207. data/lib/moneta/redis.rb +0 -49
  208. data/lib/moneta/rufus.rb +0 -41
  209. data/lib/moneta/s3.rb +0 -162
  210. data/lib/moneta/sdbm.rb +0 -33
  211. data/lib/moneta/tyrant.rb +0 -58
  212. 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,5 @@
1
+ module Moneta
2
+ # Moneta version number
3
+ # @api public
4
+ VERSION = '0.7.0'
5
+ 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
+
@@ -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,10 @@
1
+ module SessionAutoloadTest
2
+ class Foo
3
+ def initialize(bar='baz')
4
+ @bar = bar
5
+ end
6
+ def inspect
7
+ "#<#{self.class} bar:#{@bar.inspect}>"
8
+ end
9
+ end
10
+ 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