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.
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