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,197 @@
1
+ require 'moneta'
2
+ require 'active_support'
3
+ require 'active_support/cache/moneta_store'
4
+ require 'ostruct'
5
+
6
+ describe ActiveSupport::Cache::MonetaStore do
7
+ before(:all) do
8
+ @events = []
9
+ ActiveSupport::Notifications.subscribe(/^cache_(.*)\.active_support$/) do |*args|
10
+ @events << ActiveSupport::Notifications::Event.new(*args)
11
+ end
12
+ end
13
+
14
+ before(:each) do
15
+ @events.clear
16
+ @store = ActiveSupport::Cache::MonetaStore.new(:store => Moneta.new(:Memory))
17
+ @rabbit = OpenStruct.new :name => 'bunny'
18
+ @white_rabbit = OpenStruct.new :color => 'white'
19
+
20
+ @store.write 'rabbit', @rabbit
21
+ @store.delete 'counter'
22
+ @store.delete 'rub-a-dub'
23
+ end
24
+
25
+ it 'reads the data' do
26
+ @store.read('rabbit').should == @rabbit
27
+ end
28
+
29
+ it 'writes the data' do
30
+ @store.write 'rabbit', @white_rabbit
31
+ @store.read('rabbit').should == @white_rabbit
32
+ end
33
+
34
+ it 'writes the data with expiration time' do
35
+ @store.write 'rabbit', @white_rabbit, :expires_in => 1.second
36
+ # @store.read('rabbit').should == @white_rabbit
37
+ sleep 2
38
+ @store.read('rabbit').should be_nil
39
+ end
40
+
41
+ it 'deletes data' do
42
+ @store.delete 'rabbit'
43
+ @store.read('rabbit').should be_nil
44
+ end
45
+
46
+ it 'verifies existence of an object in the store' do
47
+ @store.exist?('rabbit').should == true
48
+ @store.exist?('rab-a-dub').should == false
49
+ end
50
+
51
+ it 'fetches data' do
52
+ @store.fetch('rabbit').should == @rabbit
53
+ @store.fetch('rub-a-dub').should be_nil
54
+ @store.fetch('rub-a-dub') { 'Flora de Cana' }
55
+ @store.fetch('rub-a-dub').should == 'Flora de Cana'
56
+ @store.fetch('rabbit', :force => true) # force cache miss
57
+ @store.fetch('rabbit', :force => true, :expires_in => 1.second) { @white_rabbit }
58
+ # @store.fetch('rabbit').should == @white_rabbit
59
+ sleep 2
60
+ @store.fetch('rabbit').should be_nil
61
+ end
62
+
63
+ it 'reads multiple keys' do
64
+ @store.write 'irish whisky', 'Jameson'
65
+ result = @store.read_multi 'rabbit', 'irish whisky'
66
+ result['rabbit'].should == @rabbit
67
+ result['irish whisky'].should == 'Jameson'
68
+ end
69
+
70
+ it 'reads multiple keys and returns only the matched ones' do
71
+ @store.delete 'irish whisky'
72
+ result = @store.read_multi 'rabbit', 'irish whisky'
73
+ result.should_not include('irish whisky')
74
+ result.should include('rabbit')
75
+ end
76
+
77
+ it 'increments a key' do
78
+ 3.times { @store.increment 'counter' }
79
+ @store.read('counter').should == '3'
80
+ end
81
+
82
+ it 'decrements a key' do
83
+ 3.times { @store.increment 'counter' }
84
+ 2.times { @store.decrement 'counter' }
85
+ @store.read('counter').should == '1'
86
+ end
87
+
88
+ it 'increments a key by given value' do
89
+ @store.increment 'counter', 3
90
+ @store.read('counter').should == '3'
91
+ end
92
+
93
+ it 'decrements a key by given value' do
94
+ 3.times { @store.increment 'counter' }
95
+ @store.decrement 'counter', 2
96
+ @store.read('counter').should == '1'
97
+ end
98
+
99
+ describe 'notifications' do
100
+ it 'notifies on #fetch' do
101
+ with_notifications do
102
+ @store.fetch('radiohead') { 'House Of Cards' }
103
+ end
104
+
105
+ read, generate, write = @events
106
+
107
+ read.name.should == 'cache_read.active_support'
108
+ read.payload.should == { :key => 'radiohead', :super_operation => :fetch }
109
+
110
+ generate.name.should == 'cache_generate.active_support'
111
+ generate.payload.should == { :key => 'radiohead' }
112
+
113
+ write.name.should == 'cache_write.active_support'
114
+ write.payload.should == { :key => 'radiohead' }
115
+ end
116
+
117
+ it 'notifies on #read' do
118
+ with_notifications do
119
+ @store.read 'metallica'
120
+ end
121
+
122
+ read = @events.first
123
+ read.name.should == 'cache_read.active_support'
124
+ read.payload.should == { :key => 'metallica', :hit => false }
125
+ end
126
+
127
+ it 'notifies on #write' do
128
+ with_notifications do
129
+ @store.write 'depeche mode', 'Enjoy The Silence'
130
+ end
131
+
132
+ write = @events.first
133
+ write.name.should == 'cache_write.active_support'
134
+ write.payload.should == { :key => 'depeche mode' }
135
+ end
136
+
137
+ it 'notifies on #delete' do
138
+ with_notifications do
139
+ @store.delete 'the new cardigans'
140
+ end
141
+
142
+ delete = @events.first
143
+ delete.name.should == 'cache_delete.active_support'
144
+ delete.payload.should == { :key => 'the new cardigans' }
145
+ end
146
+
147
+ it 'notifies on #exist?' do
148
+ with_notifications do
149
+ @store.exist? 'the smiths'
150
+ end
151
+
152
+ exist = @events.first
153
+ exist.name.should == 'cache_exist?.active_support'
154
+ exist.payload.should == { :key => 'the smiths' }
155
+ end
156
+
157
+ it 'notifies on #increment' do
158
+ with_notifications do
159
+ @store.increment 'pearl jam'
160
+ end
161
+
162
+ increment = @events.first
163
+ increment.name.should == 'cache_increment.active_support'
164
+ increment.payload.should == { :key => 'pearl jam', :amount => 1 }
165
+ end
166
+
167
+ it 'notifies on #decrement' do
168
+ with_notifications do
169
+ @store.decrement 'placebo'
170
+ end
171
+
172
+ decrement = @events.first
173
+ decrement.name.should == 'cache_decrement.active_support'
174
+ decrement.payload.should == { :key => 'placebo', :amount => 1 }
175
+ end
176
+
177
+ it 'should notify on clear' do
178
+ with_notifications do
179
+ @store.clear
180
+ end
181
+
182
+ clear = @events.first
183
+ clear.name.should == 'cache_clear.active_support'
184
+ clear.payload.should == { :key => nil }
185
+ end
186
+ end
187
+
188
+ private
189
+
190
+ def with_notifications
191
+ ActiveSupport::Cache::MonetaStore.instrument = true
192
+ yield
193
+ ensure
194
+ ActiveSupport::Cache::MonetaStore.instrument = false
195
+ end
196
+ end
197
+
@@ -0,0 +1,1489 @@
1
+ ADAPTER_SPECS = [:null_stringkey_stringvalue, :store_stringkey_stringvalue, :returndifferent_stringkey_stringvalue, :not_increment]
2
+ SIMPLE_SPECS = [:null, :store, :returndifferent, :marshallable_key, :marshallable_value, :transform_value, :not_increment]
3
+ EXPIRES_SPECS = [:null, :store, :returndifferent, :marshallable_key, :marshallable_value, :expires_stringkey_stringvalue, :transform_value_with_expires, :not_increment]
4
+ NATIVE_EXPIRES_SPECS = SIMPLE_SPECS + [:expires_stringkey_stringvalue]
5
+ TRANSFORMER_SPECS = [:null, :store, :returndifferent, :transform_value]
6
+ ADAPTER_EXPIRES_SPECS = ADAPTER_SPECS + [:expires_stringkey_stringvalue]
7
+
8
+ EXPIRES_INCR_SPECS = EXPIRES_SPECS.reject {|s| s == :not_increment } + [:increment]
9
+ NATIVE_EXPIRES_INCR_SPECS = NATIVE_EXPIRES_SPECS.reject {|s| s == :not_increment } + [:increment]
10
+ SIMPLE_INCR_SPECS = SIMPLE_SPECS.reject {|s| s == :not_increment } + [:increment]
11
+ ADAPTER_INCR_SPECS = ADAPTER_SPECS.reject {|s| s == :not_increment } + [:increment]
12
+ ADAPTER_EXPIRES_INCR_SPECS = ADAPTER_EXPIRES_SPECS.reject {|s| s == :not_increment } + [:increment]
13
+ PSTORE_SPECS = ADAPTER_INCR_SPECS + [:null_stringkey_objectvalue,
14
+ :store_stringkey_objectvalue,
15
+ :returndifferent_stringkey_objectvalue]
16
+
17
+ header = "# Generated by #{__FILE__}\n"
18
+
19
+ TESTS = {
20
+ 'simple_client_tcp' => {
21
+ :preamble => "$tcp_server ||= Moneta::Server.new(Moneta::Adapters::Memory.new)\n",
22
+ :store => :Client,
23
+ :specs => SIMPLE_INCR_SPECS,
24
+ :tests => %{
25
+ it 'should support multiple clients' do
26
+ client = Moneta.new(:Client)
27
+ client['shared_key'] = 'shared_val'
28
+ (1..100).each do |i|
29
+ Thread.new do
30
+ client = Moneta.new(:Client)
31
+ (1.100).each do |j|
32
+ client['shared_key'].should == 'shared_val'
33
+ client["key-\#{j}-\#{i}"] = "val-\#{j}-\#{i}"
34
+ client["key-\#{j}-\#{i}"].should == "val-\#{j}-\#{i}"
35
+ end
36
+ end
37
+ end
38
+ end
39
+ }
40
+ },
41
+ 'simple_client_unix' => {
42
+ :preamble => "$unix_server ||= Moneta::Server.new(Moneta::Adapters::Memory.new, :file => File.join(make_tempdir, 'simple_client_unix'))\nafter(:all) { $unix_server.stop; $unix_server = nil }\n",
43
+ :store => :Client,
44
+ :options => ":file => File.join(make_tempdir, 'simple_client_unix')",
45
+ :specs => SIMPLE_INCR_SPECS
46
+ },
47
+ 'simple_memory' => {
48
+ :store => :Memory,
49
+ :specs => SIMPLE_INCR_SPECS
50
+ },
51
+ 'simple_memory_with_expires' => {
52
+ :store => :Memory,
53
+ :options => ':expires => true',
54
+ :specs => EXPIRES_SPECS,
55
+ :specs => EXPIRES_INCR_SPECS
56
+ },
57
+ 'simple_memory_with_compress' => {
58
+ :store => :Memory,
59
+ :options => ':compress => true',
60
+ :load_value => 'Marshal.load(::Zlib::Inflate.inflate(value))',
61
+ :specs => SIMPLE_INCR_SPECS
62
+ },
63
+ 'simple_memory_with_prefix' => {
64
+ :store => :Memory,
65
+ :options => ':prefix => "moneta"',
66
+ :specs => SIMPLE_INCR_SPECS
67
+ },
68
+ 'simple_memory_with_json_serializer' => {
69
+ :store => :Memory,
70
+ :options => ':serializer => :json',
71
+ :load_value => '::MultiJson.load(value).first',
72
+ :key => %w(Hash String),
73
+ :value => %w(Hash String),
74
+ :specs => [:null, :store, :returndifferent, :transform_value, :increment],
75
+ },
76
+ 'simple_memory_with_json_key_serializer' => {
77
+ :store => :Memory,
78
+ :options => ':key_serializer => :json',
79
+ :key => %w(Hash String),
80
+ :specs => [:null, :store, :returndifferent, :increment],
81
+ },
82
+ 'simple_memory_with_json_value_serializer' => {
83
+ :store => :Memory,
84
+ :options => ':value_serializer => :json',
85
+ :value => %w(Hash String),
86
+ :specs => [:null, :store, :returndifferent, :marshallable_key, :transform_value, :increment],
87
+ :load_value => '::MultiJson.load(value).first'
88
+ },
89
+ 'simple_memory_with_snappy_compress' => {
90
+ :store => :Memory,
91
+ :options => ':compress => :snappy',
92
+ :load_value => 'Marshal.load(::Snappy.inflate(value))',
93
+ :specs => SIMPLE_INCR_SPECS
94
+ },
95
+ 'simple_lruhash' => {
96
+ :store => :LRUHash,
97
+ :specs => SIMPLE_INCR_SPECS
98
+ },
99
+ 'simple_lruhash_with_expires' => {
100
+ :store => :LRUHash,
101
+ :options => ':expires => true',
102
+ :specs => EXPIRES_INCR_SPECS,
103
+ },
104
+ 'simple_file' => {
105
+ :store => :File,
106
+ :options => ':dir => File.join(make_tempdir, "simple_file")',
107
+ :specs => SIMPLE_INCR_SPECS
108
+ },
109
+ 'simple_file_with_expires' => {
110
+ :store => :File,
111
+ :options => ':dir => File.join(make_tempdir, "simple_file_with_expires"), :expires => true',
112
+ :specs => EXPIRES_INCR_SPECS
113
+ },
114
+ 'simple_hashfile' => {
115
+ :store => :HashFile,
116
+ :options => ':dir => File.join(make_tempdir, "simple_hashfile")',
117
+ :specs => SIMPLE_INCR_SPECS
118
+ },
119
+ 'simple_hashfile_with_expires' => {
120
+ :store => :HashFile,
121
+ :options => ':dir => File.join(make_tempdir, "simple_hashfile_with_expires"), :expires => true',
122
+ :specs => EXPIRES_INCR_SPECS
123
+ },
124
+ 'simple_cassandra' => {
125
+ :store => :Cassandra,
126
+ :options => ':keyspace => "simple_cassandra"',
127
+ :specs => NATIVE_EXPIRES_SPECS,
128
+ },
129
+ 'simple_hbase' => {
130
+ :store => :HBase,
131
+ :options => ':table => "simple_hbase"',
132
+ :specs => SIMPLE_INCR_SPECS
133
+ },
134
+ 'simple_hbase_with_expires' => {
135
+ :store => :HBase,
136
+ :options => ':table => "simple_hbase", :expires => true',
137
+ :specs => EXPIRES_INCR_SPECS,
138
+ },
139
+ 'simple_dbm' => {
140
+ :store => :DBM,
141
+ :options => ':file => File.join(make_tempdir, "simple_dbm")',
142
+ :specs => SIMPLE_INCR_SPECS
143
+ },
144
+ 'simple_dbm_with_expires' => {
145
+ :store => :DBM,
146
+ :options => ':file => File.join(make_tempdir, "simple_dbm_with_expires"), :expires => true',
147
+ :specs => EXPIRES_INCR_SPECS
148
+ },
149
+ 'simple_gdbm' => {
150
+ :store => :GDBM,
151
+ :options => ':file => File.join(make_tempdir, "simple_gdbm")',
152
+ :specs => SIMPLE_INCR_SPECS
153
+ },
154
+ 'simple_gdbm_with_expires' => {
155
+ :store => :GDBM,
156
+ :options => ':file => File.join(make_tempdir, "simple_gdbm_with_expires"), :expires => true',
157
+ :specs => EXPIRES_INCR_SPECS
158
+ },
159
+ 'simple_sdbm' => {
160
+ :store => :SDBM,
161
+ :options => ':file => File.join(make_tempdir, "simple_sdbm")',
162
+ :specs => SIMPLE_INCR_SPECS
163
+ },
164
+ 'simple_sdbm_with_expires' => {
165
+ :store => :SDBM,
166
+ :options => ':file => File.join(make_tempdir, "simple_sdbm_with_expires"), :expires => true',
167
+ :specs => EXPIRES_INCR_SPECS
168
+ },
169
+ 'simple_leveldb' => {
170
+ :store => :LevelDB,
171
+ :options => ':dir => File.join(make_tempdir, "simple_leveldb")',
172
+ :specs => SIMPLE_INCR_SPECS
173
+ },
174
+ 'simple_leveldb_with_expires' => {
175
+ :store => :LevelDB,
176
+ :options => ':dir => File.join(make_tempdir, "simple_leveldb_with_expires"), :expires => true',
177
+ :specs => EXPIRES_INCR_SPECS
178
+ },
179
+ 'simple_pstore' => {
180
+ :store => :PStore,
181
+ :options => ':file => File.join(make_tempdir, "simple_pstore")',
182
+ :load_value => 'value',
183
+ :specs => SIMPLE_INCR_SPECS
184
+ },
185
+ 'simple_pstore_with_expires' => {
186
+ :store => :PStore,
187
+ :options => ':file => File.join(make_tempdir, "simple_pstore_with_expires"), :expires => true',
188
+ :load_value => 'value',
189
+ :specs => EXPIRES_INCR_SPECS
190
+ },
191
+ 'simple_yaml' => {
192
+ :store => :YAML,
193
+ :options => ':file => File.join(make_tempdir, "simple_yaml")',
194
+ :specs => [:null, :store, :returndifferent, :marshallable_key, :increment, :transform_value],
195
+ :load_value => 'value'
196
+ },
197
+ 'simple_yaml_with_expires' => {
198
+ :store => :YAML,
199
+ :options => ':file => File.join(make_tempdir, "simple_yaml_with_expires"), :expires => true',
200
+ :specs => [:null, :store, :returndifferent, :marshallable_key, :expires_stringkey_stringvalue, :increment, :transform_value_with_expires],
201
+ :load_value => 'value'
202
+ },
203
+ 'simple_localmemcache' => {
204
+ :store => :LocalMemCache,
205
+ :options => ':file => File.join(make_tempdir, "simple_localmemcache")',
206
+ :specs => SIMPLE_SPECS
207
+ },
208
+ 'simple_localmemcache_with_expires' => {
209
+ :store => :LocalMemCache,
210
+ :options => ':file => File.join(make_tempdir, "simple_localmemcache_with_expires"), :expires => true',
211
+ :specs => EXPIRES_SPECS
212
+ },
213
+ 'simple_tokyocabinet' => {
214
+ :store => :TokyoCabinet,
215
+ :options => ':file => File.join(make_tempdir, "simple_tokyocabinet")',
216
+ :specs => SIMPLE_INCR_SPECS
217
+ },
218
+ 'simple_tokyocabinet_with_expires' => {
219
+ :store => :TokyoCabinet,
220
+ :options => ':file => File.join(make_tempdir, "simple_tokyocabinet_with_expires"), :expires => true',
221
+ :specs => EXPIRES_INCR_SPECS
222
+ },
223
+ 'simple_sqlite' => {
224
+ :store => :Sqlite,
225
+ :options => ':file => File.join(make_tempdir, "simple_sqlite")',
226
+ :specs => SIMPLE_INCR_SPECS
227
+ },
228
+ 'simple_sqlite_with_expires' => {
229
+ :store => :Sqlite,
230
+ :options => ':file => File.join(make_tempdir, "simple_sqlite_with_expires"), :expires => true',
231
+ :specs => EXPIRES_INCR_SPECS
232
+ },
233
+ 'simple_redis' => {
234
+ :store => :Redis,
235
+ :specs => NATIVE_EXPIRES_INCR_SPECS,
236
+ },
237
+ 'simple_memcached' => {
238
+ :store => :Memcached,
239
+ :specs => NATIVE_EXPIRES_INCR_SPECS,
240
+ :options => ':namespace => "simple_memcached"'
241
+ },
242
+ 'simple_memcached_dalli' => {
243
+ :store => :MemcachedDalli,
244
+ :specs => NATIVE_EXPIRES_INCR_SPECS,
245
+ :options => ':namespace => "simple_memcached_dalli"'
246
+ },
247
+ 'simple_memcached_native' => {
248
+ :store => :MemcachedNative,
249
+ :specs => NATIVE_EXPIRES_INCR_SPECS,
250
+ :options => ':namespace => "simple_memcached_native"'
251
+ },
252
+ 'simple_riak' => {
253
+ :store => :Riak,
254
+ :options => ":bucket => 'simple_riak'",
255
+ # We don't want Riak warnings in tests
256
+ :preamble => "require 'riak'\n\nRiak.disable_list_keys_warnings = true\n\n",
257
+ :specs => SIMPLE_SPECS
258
+ },
259
+ 'simple_riak_with_expires' => {
260
+ :store => :Riak,
261
+ :options => ":bucket => 'simple_riak_with_expires', :expires => true",
262
+ # We don't want Riak warnings in tests
263
+ :preamble => "require 'riak'\n\nRiak.disable_list_keys_warnings = true\n\n",
264
+ :specs => EXPIRES_SPECS
265
+ },
266
+ 'simple_couch' => {
267
+ :store => :Couch,
268
+ :options => ":db => 'simple_couch'",
269
+ :load_value => '::Marshal.load(value.unpack(\'m\').first)',
270
+ :specs => SIMPLE_SPECS
271
+ },
272
+ 'simple_couch_with_expires' => {
273
+ :store => :Couch,
274
+ :options => ":db => 'simple_couch_with_expires', :expires => true",
275
+ :specs => EXPIRES_SPECS,
276
+ :load_value => '::Marshal.load(value.unpack(\'m\').first)'
277
+ },
278
+ 'simple_mongo' => {
279
+ :store => :Mongo,
280
+ :options => ":db => 'simple_mongo'",
281
+ :specs => SIMPLE_SPECS
282
+ },
283
+ 'simple_mongo_with_expires' => {
284
+ :store => :Mongo,
285
+ :options => ":db => 'simple_mongo_with_expires', :expires => true",
286
+ :specs => EXPIRES_SPECS
287
+ },
288
+ 'simple_null' => {
289
+ :store => :Null,
290
+ :specs => [:null, :marshallable_key, :returndifferent]
291
+ },
292
+ 'null_adapter' => {
293
+ :build => 'Moneta::Adapters::Null.new',
294
+ :specs => [:null]
295
+ },
296
+ 'simple_sequel' => {
297
+ :store => :Sequel,
298
+ :options => ':db => (defined?(JRUBY_VERSION) ? "jdbc:sqlite:" : "sqlite:") + File.join(make_tempdir, "simple_sequel")',
299
+ :load_value => '::Marshal.load(value.unpack(\'m\').first)',
300
+ :specs => SIMPLE_INCR_SPECS
301
+ },
302
+ 'simple_sequel_with_expires' => {
303
+ :store => :Sequel,
304
+ :options => ':db => (defined?(JRUBY_VERSION) ? "jdbc:sqlite:" : "sqlite:") + File.join(make_tempdir, "simple_sequel_with_expires"), :expires => true',
305
+ :specs => EXPIRES_INCR_SPECS,
306
+ :load_value => '::Marshal.load(value.unpack(\'m\').first)'
307
+ },
308
+ 'simple_datamapper' => {
309
+ :store => :DataMapper,
310
+ :specs => SIMPLE_SPECS,
311
+ :options => ':setup => "sqlite3://#{make_tempdir}/simple_datamapper"',
312
+ # DataMapper needs default repository to be setup
313
+ :preamble => "require 'dm-core'\nDataMapper.setup(:default, :adapter => :in_memory)\n",
314
+ :load_value => '::Marshal.load(value.unpack(\'m\').first)'
315
+ },
316
+ 'simple_datamapper_with_expires' => {
317
+ :store => :DataMapper,
318
+ :options => ':setup => "sqlite3://#{make_tempdir}/simple_datamapper_with_expires", :expires => true',
319
+ # DataMapper needs default repository to be setup
320
+ :preamble => "require 'dm-core'\nDataMapper.setup(:default, :adapter => :in_memory)\n",
321
+ :specs => EXPIRES_SPECS,
322
+ :load_value => '::Marshal.load(value.unpack(\'m\').first)'
323
+ },
324
+ 'simple_datamapper_with_repository' => {
325
+ :store => :DataMapper,
326
+ :specs => SIMPLE_SPECS,
327
+ :options => ':repository => :repo, :setup => "sqlite3://#{make_tempdir}/simple_datamapper_with_repository"',
328
+ # DataMapper needs default repository to be setup
329
+ :preamble => "require 'dm-core'\nDataMapper.setup(:default, :adapter => :in_memory)\n",
330
+ :load_value => '::Marshal.load(value.unpack(\'m\').first)'
331
+ },
332
+ 'simple_activerecord' => {
333
+ :store => :ActiveRecord,
334
+ :specs => SIMPLE_INCR_SPECS,
335
+ :options => ":connection => { :adapter => (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'), :database => File.join(make_tempdir, 'simple_activerecord') }",
336
+ :load_value => '::Marshal.load(value.unpack(\'m\').first)'
337
+ },
338
+ 'simple_activerecord_with_expires' => {
339
+ :store => :ActiveRecord,
340
+ :options => ":connection => { :adapter => (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'), :database => File.join(make_tempdir, 'simple_activerecord_with_expires') }, :expires => true",
341
+ :specs => EXPIRES_INCR_SPECS,
342
+ :load_value => '::Marshal.load(value.unpack(\'m\').first)'
343
+ },
344
+ 'simple_fog' => {
345
+ :store => :Fog,
346
+ :specs => SIMPLE_SPECS,
347
+ :options => ":aws_access_key_id => 'fake_access_key_id',
348
+ :aws_secret_access_key => 'fake_secret_access_key',
349
+ :provider => 'AWS',
350
+ :dir => 'moneta'",
351
+ # Put Fog into testing mode
352
+ :preamble => "require 'fog'\nFog.mock!\n"
353
+ },
354
+ 'simple_fog_with_expires' => {
355
+ :store => :Fog,
356
+ :options => ":aws_access_key_id => 'fake_access_key_id',
357
+ :aws_secret_access_key => 'fake_secret_access_key',
358
+ :provider => 'AWS',
359
+ :dir => 'moneta',
360
+ :expires => true",
361
+ # Put Fog into testing mode
362
+ :preamble => "require 'fog'\nFog.mock!\n",
363
+ :specs => EXPIRES_SPECS
364
+ },
365
+ 'expires_memory' => {
366
+ :build => %{Moneta.build do
367
+ use :Expires
368
+ adapter :Memory
369
+ end},
370
+ :specs => [:null, :store, :expires, :increment],
371
+ :tests => %{
372
+ it 'should support default expiration time' do
373
+ store = Moneta.new(:Memory, :expires => 2)
374
+ store.store('key1', 'val1')
375
+ store.store('key2', 'val2', :expires => 60)
376
+ store.load('key1').should == 'val1'
377
+ sleep 1
378
+ store.load('key1').should == 'val1'
379
+ sleep 2
380
+ store.load('key1').should == nil
381
+ store['key2'].should == 'val2'
382
+ end}
383
+ },
384
+ 'expires_file' => {
385
+ :build => %{Moneta.build do
386
+ use :Expires
387
+ use :Transformer, :key => [:marshal, :escape], :value => :marshal
388
+ adapter :File, :dir => File.join(make_tempdir, "expires-file")
389
+ end},
390
+ :specs => [:null, :store, :expires, :returndifferent, :marshallable_key, :increment],
391
+ :tests => %{
392
+ it 'should delete expired value in underlying file storage' do
393
+ store.store('foo', 'bar', :expires => 2)
394
+ store['foo'].should == 'bar'
395
+ sleep 1
396
+ store['foo'].should == 'bar'
397
+ sleep 2
398
+ store['foo'].should == nil
399
+ store.adapter['foo'].should == nil
400
+ store.adapter.adapter['foo'].should == nil
401
+ end
402
+ }
403
+ },
404
+ 'proxy_redis' => {
405
+ :build => %{Moneta.build do
406
+ use :Proxy
407
+ use :Proxy
408
+ adapter :Redis
409
+ end},
410
+ :specs => ADAPTER_EXPIRES_INCR_SPECS
411
+ },
412
+ 'proxy_expires_memory' => {
413
+ :build => %{Moneta.build do
414
+ use :Proxy
415
+ use :Expires
416
+ use :Proxy
417
+ adapter :Memory
418
+ end},
419
+ :specs => [:null, :store, :expires]
420
+ },
421
+ 'cache_file_memory' => {
422
+ :build => %{Moneta.build do
423
+ use(:Cache) do
424
+ backend { adapter :File, :dir => File.join(make_tempdir, "cache_file_memory") }
425
+ cache { adapter :Memory }
426
+ end
427
+ end},
428
+ :specs => ADAPTER_INCR_SPECS,
429
+ :tests => %{
430
+ it 'should store loaded values in cache' do
431
+ store.backend['foo'] = 'bar'
432
+ store.cache['foo'].should == nil
433
+ store['foo'].should == 'bar'
434
+ store.cache['foo'].should == 'bar'
435
+ store.backend.delete('foo')
436
+ store['foo'].should == 'bar'
437
+ store.delete('foo')
438
+ store['foo'].should == nil
439
+ end
440
+ }
441
+ },
442
+ 'cache_memory_null' => {
443
+ :build => %{Moneta.build do
444
+ use(:Cache) do
445
+ backend(Moneta::Adapters::Memory.new)
446
+ cache(Moneta::Adapters::Null.new)
447
+ end
448
+ end},
449
+ :specs => ADAPTER_INCR_SPECS
450
+ },
451
+ 'shared' => {
452
+ :build => %{Moneta.build do
453
+ use(:Shared) do
454
+ adapter :Memory
455
+ end
456
+ end},
457
+ :specs => ADAPTER_INCR_SPECS,
458
+ :tests => %{
459
+ it 'should share values' do
460
+ store['shared_key'] = 'shared_value'
461
+ second = new_store
462
+ second.key?('shared_key').should be_true
463
+ second['shared_key'].should == 'shared_value'
464
+ second.close
465
+ end
466
+ }
467
+ },
468
+ 'stack_file_memory' => {
469
+ :build => %{Moneta.build do
470
+ use(:Stack) do
471
+ add(Moneta.new(:Null))
472
+ add(Moneta::Adapters::Null.new)
473
+ add { adapter :File, :dir => File.join(make_tempdir, "stack-file1") }
474
+ add { adapter :Memory }
475
+ end
476
+ end},
477
+ :specs => ADAPTER_SPECS
478
+ },
479
+ 'stack_memory_file' => {
480
+ :build => %{Moneta.build do
481
+ use(:Stack) do
482
+ add(Moneta.new(:Null))
483
+ add(Moneta::Adapters::Null.new)
484
+ add { adapter :Memory }
485
+ add { adapter :File, :dir => File.join(make_tempdir, "stack-file2") }
486
+ end
487
+ end},
488
+ :specs => [:null_stringkey_stringvalue, :store_stringkey_stringvalue, :not_increment]
489
+ },
490
+ 'lock' => {
491
+ :build => %{Moneta.build do
492
+ use :Lock
493
+ adapter :Memory
494
+ end},
495
+ :specs => [:null, :store, :increment]
496
+ },
497
+ 'transformer_zlib' => {
498
+ :build => %{Moneta.build do
499
+ use :Transformer, :value => :zlib
500
+ adapter :Memory
501
+ end},
502
+ :value => %w(String),
503
+ :specs => TRANSFORMER_SPECS,
504
+ :load_value => '::Zlib::Inflate.inflate(value)'
505
+ },
506
+ 'transformer_bzip2' => {
507
+ :build => %{Moneta.build do
508
+ use :Transformer, :value => :bzip2
509
+ adapter :Memory
510
+ end},
511
+ :value => %w(String),
512
+ :specs => TRANSFORMER_SPECS,
513
+ :load_value => '::Bzip2.uncompress(value)'
514
+ },
515
+ 'transformer_lzo' => {
516
+ :build => %{Moneta.build do
517
+ use :Transformer, :value => :lzo
518
+ adapter :Memory
519
+ end},
520
+ :value => %w(String),
521
+ :specs => TRANSFORMER_SPECS,
522
+ :load_value => '::LZO.decompress(value)'
523
+ },
524
+ 'transformer_lzma' => {
525
+ :build => %{Moneta.build do
526
+ use :Transformer, :value => :lzma
527
+ adapter :Memory
528
+ end},
529
+ :value => %w(String),
530
+ :specs => TRANSFORMER_SPECS,
531
+ :load_value => '::LZMA.decompress(value)'
532
+ },
533
+ 'transformer_snappy' => {
534
+ :build => %{Moneta.build do
535
+ use :Transformer, :value => :snappy
536
+ adapter :Memory
537
+ end},
538
+ :value => %w(String),
539
+ :specs => TRANSFORMER_SPECS,
540
+ :load_value => '::Snappy.inflate(value)'
541
+ },
542
+ 'transformer_quicklz' => {
543
+ :build => %{Moneta.build do
544
+ use :Transformer, :value => :quicklz
545
+ adapter :Memory
546
+ end},
547
+ :value => %w(String),
548
+ :specs => TRANSFORMER_SPECS,
549
+ :load_value => '::QuickLZ.decompress(value)'
550
+ },
551
+ 'transformer_json' => {
552
+ :build => %{Moneta.build do
553
+ use :Transformer, :key => :json, :value => :json
554
+ adapter :Memory
555
+ end},
556
+ :key => %w(Hash String),
557
+ :value => %w(Hash String),
558
+ :specs => TRANSFORMER_SPECS,
559
+ :load_value => '::MultiJson.load(value).first'
560
+ },
561
+ 'transformer_bert' => {
562
+ :build => %{Moneta.build do
563
+ use :Transformer, :key => :bert, :value => :bert
564
+ adapter :Memory
565
+ end},
566
+ :key => %w(Hash String),
567
+ :value => %w(Hash String),
568
+ :specs => TRANSFORMER_SPECS,
569
+ :load_value => '::BERT.decode(value)'
570
+ },
571
+ 'transformer_bencode' => {
572
+ :build => %{Moneta.build do
573
+ use :Transformer, :key => :bencode, :value => :bencode
574
+ adapter :Memory
575
+ end},
576
+ :key => %w(Hash String),
577
+ :value => %w(Hash String),
578
+ :specs => TRANSFORMER_SPECS,
579
+ :load_value => '::BEncode.load(value)'
580
+ },
581
+ 'transformer_bson' => {
582
+ :build => %{Moneta.build do
583
+ use :Transformer, :key => :bson, :value => :bson
584
+ adapter :Memory
585
+ end},
586
+ :key => %w(Hash String),
587
+ :value => %w(Hash String),
588
+ :specs => TRANSFORMER_SPECS,
589
+ :load_value => "::BSON.deserialize(value)['v']"
590
+ },
591
+ 'transformer_ox' => {
592
+ :build => %{Moneta.build do
593
+ use :Transformer, :key => :ox, :value => :ox
594
+ adapter :Memory
595
+ end},
596
+ :specs => TRANSFORMER_SPECS,
597
+ :load_value => '::Ox.parse_obj(value)'
598
+ },
599
+ 'transformer_tnet' => {
600
+ :build => %{Moneta.build do
601
+ use :Transformer, :key => :tnet, :value => :tnet
602
+ adapter :Memory
603
+ end},
604
+ :key => %w(Hash String),
605
+ :value => %w(Hash String),
606
+ :specs => TRANSFORMER_SPECS,
607
+ :load_value => '::TNetstring.parse(value).first'
608
+ },
609
+ 'transformer_msgpack' => {
610
+ :build => %{Moneta.build do
611
+ use :Transformer, :key => :msgpack, :value => :msgpack
612
+ adapter :Memory
613
+ end},
614
+ :key => %w(Hash String),
615
+ :value => %w(Hash String),
616
+ :specs => TRANSFORMER_SPECS,
617
+ :load_value => '::MessagePack.unpack(value)'
618
+ },
619
+ 'transformer_yaml' => {
620
+ :build => %{Moneta.build do
621
+ use :Transformer, :key => :yaml, :value => :yaml
622
+ adapter :Memory
623
+ end},
624
+ :specs => TRANSFORMER_SPECS,
625
+ :load_value => '::YAML.load(value)'
626
+ },
627
+ 'transformer_marshal_hmac' => {
628
+ :build => %{Moneta.build do
629
+ use :Transformer, :key => :marshal, :value => [:marshal, :hmac], :secret => 'secret'
630
+ adapter :Memory
631
+ end},
632
+ :specs => SIMPLE_INCR_SPECS,
633
+ :load_value => '::Marshal.load(::Moneta::Transformer::Helper.hmacverify(value, \'secret\'))'
634
+ },
635
+ 'transformer_marshal_base64' => {
636
+ :build => %{Moneta.build do
637
+ use :Transformer, :key => [:marshal, :base64], :value => [:marshal, :base64]
638
+ adapter :Memory
639
+ end},
640
+ :specs => SIMPLE_INCR_SPECS,
641
+ :load_value => '::Marshal.load(value.unpack(\'m\').first)'
642
+ },
643
+ 'transformer_marshal_prefix' => {
644
+ :build => %{Moneta.build do
645
+ use :Transformer, :key => [:marshal, :prefix], :value => :marshal, :prefix => 'moneta'
646
+ adapter :Memory
647
+ end},
648
+ :specs => SIMPLE_INCR_SPECS
649
+ },
650
+ 'transformer_marshal_uuencode' => {
651
+ :build => %{Moneta.build do
652
+ use :Transformer, :key => [:marshal, :uuencode], :value => [:marshal, :uuencode]
653
+ adapter :Memory
654
+ end},
655
+ :specs => SIMPLE_INCR_SPECS,
656
+ :load_value => '::Marshal.load(value.unpack(\'u\').first)'
657
+ },
658
+ 'transformer_marshal_escape' => {
659
+ :build => %{Moneta.build do
660
+ use :Transformer, :key => [:marshal, :escape], :value => :marshal
661
+ adapter :Memory
662
+ end},
663
+ :specs => SIMPLE_INCR_SPECS
664
+ },
665
+ 'transformer_marshal_md5' => {
666
+ :build => %{Moneta.build do
667
+ use :Transformer, :key => [:marshal, :md5], :value => :marshal
668
+ adapter :Memory
669
+ end},
670
+ :specs => SIMPLE_INCR_SPECS,
671
+ },
672
+ 'transformer_marshal_sha1' => {
673
+ :build => %{Moneta.build do
674
+ use :Transformer, :key => [:marshal, :sha1], :value => :marshal
675
+ adapter :Memory
676
+ end},
677
+ :specs => SIMPLE_INCR_SPECS,
678
+ },
679
+ 'transformer_marshal_sha256' => {
680
+ :build => %{Moneta.build do
681
+ use :Transformer, :key => [:marshal, :sha256], :value => :marshal
682
+ adapter :Memory
683
+ end},
684
+ :specs => SIMPLE_INCR_SPECS,
685
+ },
686
+ 'transformer_marshal_sha384' => {
687
+ :build => %{Moneta.build do
688
+ use :Transformer, :key => [:marshal, :sha384], :value => :marshal
689
+ adapter :Memory
690
+ end},
691
+ :specs => SIMPLE_INCR_SPECS,
692
+ },
693
+ 'transformer_marshal_sha512' => {
694
+ :build => %{Moneta.build do
695
+ use :Transformer, :key => [:marshal, :sha512], :value => :marshal
696
+ adapter :Memory
697
+ end},
698
+ :specs => SIMPLE_INCR_SPECS,
699
+ },
700
+ 'transformer_marshal_rmd160' => {
701
+ :build => %{Moneta.build do
702
+ use :Transformer, :key => [:marshal, :rmd160], :value => :marshal
703
+ adapter :Memory
704
+ end},
705
+ :specs => SIMPLE_INCR_SPECS,
706
+ },
707
+ 'transformer_marshal_md5_spread' => {
708
+ :build => %{Moneta.build do
709
+ use :Transformer, :key => [:marshal, :md5, :spread], :value => :marshal
710
+ adapter :Memory
711
+ end},
712
+ :specs => SIMPLE_INCR_SPECS,
713
+ },
714
+ 'transformer_marshal_truncate' => {
715
+ :build => %{Moneta.build do
716
+ use :Transformer, :key => [:marshal, :truncate], :value => :marshal, :maxlen => 64
717
+ adapter :Memory
718
+ end},
719
+ :specs => SIMPLE_INCR_SPECS,
720
+ },
721
+ 'adapter_activerecord' => {
722
+ :build => "Moneta::Adapters::ActiveRecord.new(:connection => { :adapter => (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'), :database => File.join(make_tempdir, 'adapter_activerecord') })",
723
+ :specs => ADAPTER_INCR_SPECS,
724
+ :tests => %{
725
+ it 'updates an existing key/value' do
726
+ store['foo/bar'] = '1'
727
+ store['foo/bar'] = '2'
728
+ records = store.table.find :all, :conditions => { :k => 'foo/bar' }
729
+ records.count.should == 1
730
+ end
731
+
732
+ it 'uses an existing connection' do
733
+ ActiveRecord::Base.establish_connection :adapter => (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'), :database => File.join(make_tempdir, 'activerecord-existing')
734
+
735
+ store = Moneta::Adapters::ActiveRecord.new
736
+ store.table.should be_table_exists
737
+ end
738
+ }
739
+ },
740
+ 'adapter_client' => {
741
+ :preamble => "$tcp_server ||= Moneta::Server.new(Moneta::Adapters::Memory.new)\n",
742
+ :build => "Moneta::Adapters::Client.new",
743
+ :specs => ADAPTER_INCR_SPECS
744
+ },
745
+ 'adapter_cassandra' => {
746
+ :build => "Moneta::Adapters::Cassandra.new(:keyspace => 'adapter_cassandra')",
747
+ :specs => ADAPTER_EXPIRES_SPECS,
748
+ :tests => %{
749
+ it 'should support default expiration time' do
750
+ store = Moneta::Adapters::Cassandra.new(:expires => 2, :keyspace => 'adapter_cassandra')
751
+ store.store('key1', 'val1')
752
+ store.store('key2', 'val2', :expires => 60)
753
+ store.load('key1').should == 'val1'
754
+ sleep 1
755
+ store.load('key1').should == 'val1'
756
+ sleep 2
757
+ store.load('key1').should == nil
758
+ store['key2'].should == 'val2'
759
+ end}
760
+ },
761
+ 'adapter_hbase' => {
762
+ :build => "Moneta::Adapters::HBase.new(:table => 'adapter_hbase')",
763
+ :specs => ADAPTER_INCR_SPECS
764
+ },
765
+ 'adapter_cookie' => {
766
+ :build => 'Moneta::Adapters::Cookie.new',
767
+ :specs => ADAPTER_INCR_SPECS
768
+ },
769
+ 'adapter_couch' => {
770
+ :build => "Moneta::Adapters::Couch.new(:db => 'adapter_couch')",
771
+ :specs => ADAPTER_SPECS
772
+ },
773
+ 'adapter_datamapper' => {
774
+ :build => 'Moneta::Adapters::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/adapter_datamapper")',
775
+ # DataMapper needs default repository to be setup
776
+ :preamble => "require 'dm-core'\nDataMapper.setup(:default, :adapter => :in_memory)\n",
777
+ :specs => ADAPTER_SPECS,
778
+ :tests => %q{
779
+ it 'does not cross contaminate when storing' do
780
+ first = Moneta::Adapters::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/datamapper-first")
781
+ first.clear
782
+
783
+ second = Moneta::Adapters::DataMapper.new(:repository => :sample, :setup => "sqlite3://#{make_tempdir}/datamapper-second")
784
+ second.clear
785
+
786
+ first['key'] = 'value'
787
+ second['key'] = 'value2'
788
+
789
+ first['key'].should == 'value'
790
+ second['key'].should == 'value2'
791
+ end
792
+
793
+ it 'does not cross contaminate when deleting' do
794
+ first = Moneta::Adapters::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/datamapper-first")
795
+ first.clear
796
+
797
+ second = Moneta::Adapters::DataMapper.new(:repository => :sample, :setup => "sqlite3://#{make_tempdir}/datamapper-second")
798
+ second.clear
799
+
800
+ first['key'] = 'value'
801
+ second['key'] = 'value2'
802
+
803
+ first.delete('key').should == 'value'
804
+ first.key?('key').should == false
805
+ second['key'].should == 'value2'
806
+ end
807
+ }
808
+ },
809
+ 'adapter_dbm' => {
810
+ :build => 'Moneta::Adapters::DBM.new(:file => File.join(make_tempdir, "adapter_dbm"))',
811
+ :specs => ADAPTER_INCR_SPECS
812
+ },
813
+ 'adapter_file' => {
814
+ :build => 'Moneta::Adapters::File.new(:dir => File.join(make_tempdir, "adapter_file"))',
815
+ :specs => ADAPTER_INCR_SPECS
816
+ },
817
+ 'adapter_fog' => {
818
+ :build => "Moneta::Adapters::Fog.new(:aws_access_key_id => 'fake_access_key_id',
819
+ :aws_secret_access_key => 'fake_secret_access_key',
820
+ :provider => 'AWS',
821
+ :dir => 'moneta')",
822
+ # Put Fog into testing mode
823
+ :preamble => "require 'fog'\nFog.mock!\n",
824
+ :specs => ADAPTER_SPECS
825
+ },
826
+ 'adapter_gdbm' => {
827
+ :build => 'Moneta::Adapters::GDBM.new(:file => File.join(make_tempdir, "adapter_gdbm"))',
828
+ :specs => ADAPTER_INCR_SPECS
829
+ },
830
+ 'adapter_localmemcache' => {
831
+ :build => 'Moneta::Adapters::LocalMemCache.new(:file => File.join(make_tempdir, "adapter_localmemcache"))',
832
+ :specs => ADAPTER_SPECS
833
+ },
834
+ 'adapter_memcached_dalli' => {
835
+ :build => 'Moneta::Adapters::MemcachedDalli.new(:namespace => "adapter_memcached_dalli")',
836
+ :specs => ADAPTER_EXPIRES_INCR_SPECS,
837
+ :tests => %{
838
+ it 'should support default expiration time' do
839
+ store = Moneta::Adapters::MemcachedDalli.new(:expires => 2, :namespace => "adapter_memcached_dalli")
840
+ store.store('key1', 'val1')
841
+ store.store('key2', 'val2', :expires => 60)
842
+ store.load('key1').should == 'val1'
843
+ sleep 1
844
+ store.load('key1').should == 'val1'
845
+ sleep 2
846
+ store.load('key1').should == nil
847
+ store['key2'].should == 'val2'
848
+ end}
849
+ },
850
+ 'adapter_memcached_native' => {
851
+ :build => 'Moneta::Adapters::MemcachedNative.new(:namespace => "adapter_memcached_native")',
852
+ :specs => ADAPTER_EXPIRES_INCR_SPECS,
853
+ :tests => %{
854
+ it 'should support default expiration time' do
855
+ store = Moneta::Adapters::MemcachedNative.new(:expires => 2, :namespace => "adapter_memcached_native")
856
+ store.store('key1', 'val1')
857
+ store.store('key2', 'val2', :expires => 60)
858
+ store.load('key1').should == 'val1'
859
+ sleep 1
860
+ store.load('key1').should == 'val1'
861
+ sleep 2
862
+ store.load('key1').should == nil
863
+ store['key2'].should == 'val2'
864
+ end
865
+ }
866
+ },
867
+ 'adapter_memcached' => {
868
+ :build => 'Moneta::Adapters::Memcached.new(:namespace => "adapter_memcached")',
869
+ :specs => ADAPTER_EXPIRES_INCR_SPECS,
870
+ :tests => %{
871
+ it 'should support default expiration time' do
872
+ store = Moneta::Adapters::Memcached.new(:expires => 2, :namespace => "adapter_memcached")
873
+ store.store('key1', 'val1')
874
+ store.store('key2', 'val2', :expires => 60)
875
+ store.load('key1').should == 'val1'
876
+ sleep 1
877
+ store.load('key1').should == 'val1'
878
+ sleep 2
879
+ store.load('key1').should == nil
880
+ store['key2'].should == 'val2'
881
+ end}
882
+ },
883
+ 'adapter_memory' => {
884
+ :build => 'Moneta::Adapters::Memory.new',
885
+ :specs => [:null, :store, :increment]
886
+ },
887
+ 'adapter_lruhash' => {
888
+ :build => 'Moneta::Adapters::LRUHash.new',
889
+ :specs => ADAPTER_INCR_SPECS,
890
+ :tests => %{
891
+ it 'should delete oldest' do
892
+ store = Moneta::Adapters::LRUHash.new(:max_size => 10)
893
+ store[0] = 'y'
894
+ (1..1000).each do |i|
895
+ store[i] = 'x'
896
+ store[0].should == 'y'
897
+ store.instance_variable_get(:@entry).size.should == [10, i+1].min
898
+ (0...[9, i-1].min).each do |j|
899
+ store.instance_variable_get(:@entry)[i-j].should_not be_nil
900
+ end
901
+ store.key?(i-9).should be_false if i > 9
902
+ end
903
+ end}
904
+ },
905
+ 'adapter_mongo' => {
906
+ :build => 'Moneta::Adapters::Mongo.new(:db => "adapter_mongo")',
907
+ :specs => ADAPTER_SPECS
908
+ },
909
+ 'adapter_pstore' => {
910
+ :build => 'Moneta::Adapters::PStore.new(:file => File.join(make_tempdir, "adapter_pstore"))',
911
+ :specs => PSTORE_SPECS
912
+ },
913
+ 'adapter_redis' => {
914
+ :build => 'Moneta::Adapters::Redis.new',
915
+ :specs => ADAPTER_EXPIRES_INCR_SPECS,
916
+ :tests => %{
917
+ it 'should support default expiration time' do
918
+ store = Moneta::Adapters::Redis.new(:expires => 2)
919
+ store.store('key1', 'val1')
920
+ store.store('key2', 'val2', :expires => 60)
921
+ store.load('key1').should == 'val1'
922
+ sleep 1
923
+ store.load('key1').should == 'val1'
924
+ sleep 2
925
+ store.load('key1').should == nil
926
+ store['key2'].should == 'val2'
927
+ end}
928
+ },
929
+ 'adapter_riak' => {
930
+ :build => 'Moneta::Adapters::Riak.new',
931
+ :options => ":bucket => 'adapter_riak'",
932
+ :specs => ADAPTER_SPECS,
933
+ # We don't want Riak warnings in tests
934
+ :preamble => "require 'riak'\n\nRiak.disable_list_keys_warnings = true\n\n"
935
+ },
936
+ 'adapter_sdbm' => {
937
+ :build => 'Moneta::Adapters::SDBM.new(:file => File.join(make_tempdir, "adapter_sdbm"))',
938
+ :specs => ADAPTER_INCR_SPECS
939
+ },
940
+ 'adapter_leveldb' => {
941
+ :build => 'Moneta::Adapters::LevelDB.new(:dir => File.join(make_tempdir, "adapter_leveldb"))',
942
+ :specs => ADAPTER_INCR_SPECS
943
+ },
944
+ 'adapter_sequel' => {
945
+ :build => 'Moneta::Adapters::Sequel.new(:db => (defined?(JRUBY_VERSION) ? "jdbc:sqlite:" : "sqlite:") + File.join(make_tempdir, "adapter_sequel"))',
946
+ :specs => ADAPTER_INCR_SPECS
947
+ },
948
+ 'adapter_sqlite' => {
949
+ :build => 'Moneta::Adapters::Sqlite.new(:file => File.join(make_tempdir, "adapter_sqlite"))',
950
+ :specs => ADAPTER_INCR_SPECS
951
+ },
952
+ 'adapter_tokyocabinet_bdb' => {
953
+ :build => 'Moneta::Adapters::TokyoCabinet.new(:file => File.join(make_tempdir, "adapter_tokyocabinet_bdb"), :type => :bdb)',
954
+ :specs => ADAPTER_INCR_SPECS
955
+ },
956
+ 'adapter_tokyocabinet_hdb' => {
957
+ :build => 'Moneta::Adapters::TokyoCabinet.new(:file => File.join(make_tempdir, "adapter_tokyocabinet_hdb"), :type => :hdb)',
958
+ :specs => ADAPTER_INCR_SPECS
959
+ },
960
+ 'adapter_yaml' => {
961
+ :build => 'Moneta::Adapters::YAML.new(:file => File.join(make_tempdir, "adapter_yaml"))',
962
+ :specs => PSTORE_SPECS
963
+ },
964
+ 'optionmerger' => {
965
+ :store => :Memory,
966
+ :specs => [],
967
+ :tests => %{
968
+ it '#with should return OptionMerger' do
969
+ options = {:optionname => :optionvalue}
970
+ merger = store.with(options)
971
+ merger.should be_instance_of(Moneta::OptionMerger)
972
+ end
973
+
974
+ it 'should save default options' do
975
+ options = {:optionname => :optionvalue}
976
+ merger = store.with(options)
977
+ Moneta::OptionMerger::METHODS.each do |method|
978
+ merger.default_options[method].should equal(options)
979
+ end
980
+ end
981
+
982
+ PREFIX = [['alpha', nil], ['beta', nil], ['alpha', 'beta']]
983
+
984
+ it 'should merge options' do
985
+ merger = store.with(:opt1 => :val1, :opt2 => :val2).with(:opt2 => :overwrite, :opt3 => :val3)
986
+ Moneta::OptionMerger::METHODS.each do |method|
987
+ merger.default_options[method].should == {:opt1 => :val1, :opt2 => :overwrite, :opt3 => :val3}
988
+ end
989
+ end
990
+
991
+ it 'should merge options only for some methods' do
992
+ PREFIX.each do |(alpha,beta)|
993
+ options = {:opt1 => :val1, :opt2 => :val2, :prefix => alpha}
994
+ merger = store.with(options).with(:opt2 => :overwrite, :opt3 => :val3, :prefix => beta, :only => :clear)
995
+ (Moneta::OptionMerger::METHODS - [:clear]).each do |method|
996
+ merger.default_options[method].should equal(options)
997
+ end
998
+ merger.default_options[:clear].should == {:opt1 => :val1, :opt2 => :overwrite, :opt3 => :val3, :prefix => "\#{alpha}\#{beta}"}
999
+
1000
+ merger = store.with(options).with(:opt2 => :overwrite, :opt3 => :val3, :prefix => beta, :only => [:load, :store])
1001
+ (Moneta::OptionMerger::METHODS - [:load, :store]).each do |method|
1002
+ merger.default_options[method].should equal(options)
1003
+ end
1004
+ merger.default_options[:load].should == {:opt1 => :val1, :opt2 => :overwrite, :opt3 => :val3, :prefix => "\#{alpha}\#{beta}"}
1005
+ merger.default_options[:store].should == {:opt1 => :val1, :opt2 => :overwrite, :opt3 => :val3, :prefix => "\#{alpha}\#{beta}"}
1006
+ end
1007
+ end
1008
+
1009
+ it 'should merge options except for some methods' do
1010
+ PREFIX.each do |(alpha,beta)|
1011
+ options = {:opt1 => :val1, :opt2 => :val2, :prefix => alpha}
1012
+ merger = store.with(options).with(:opt2 => :overwrite, :opt3 => :val3, :except => :clear, :prefix => beta)
1013
+ (Moneta::OptionMerger::METHODS - [:clear]).each do |method|
1014
+ merger.default_options[method].should == {:opt1 => :val1, :opt2 => :overwrite, :opt3 => :val3, :prefix => "\#{alpha}\#{beta}"}
1015
+ end
1016
+ merger.default_options[:clear].should equal(options)
1017
+
1018
+ merger = store.with(options).with(:opt2 => :overwrite, :opt3 => :val3, :prefix => beta, :except => [:load, :store])
1019
+ (Moneta::OptionMerger::METHODS - [:load, :store]).each do |method|
1020
+ merger.default_options[method].should == {:opt1 => :val1, :opt2 => :overwrite, :opt3 => :val3, :prefix => "\#{alpha}\#{beta}"}
1021
+ end
1022
+ merger.default_options[:load].should equal(options)
1023
+ merger.default_options[:store].should equal(options)
1024
+ end
1025
+ end
1026
+
1027
+ it 'should have method #raw' do
1028
+ store.raw.default_options.should == {:store=>{:raw=>true},:load=>{:raw=>true},:delete=>{:raw=>true}}
1029
+ store.raw.should equal(store.raw.raw)
1030
+ end
1031
+
1032
+ it 'should have method #prefix' do
1033
+ store.prefix('a').default_options.should == {:store=>{:prefix=>'a'},:load=>{:prefix=>'a'},
1034
+ :delete=>{:prefix=>'a'},:key? => {:prefix=>'a'},:increment=>{:prefix=>'a'}}
1035
+
1036
+ store.prefix('a').prefix('b').default_options.should == {:store=>{:prefix=>'ab'},:load=>{:prefix=>'ab'},
1037
+ :delete=>{:prefix=>'ab'},:key? => {:prefix=>'ab'},:increment=>{:prefix=>'ab'}}
1038
+
1039
+ store.raw.prefix('b').default_options.should == {:store=>{:raw=>true,:prefix=>'b'},:load=>{:raw=>true,:prefix=>'b'},
1040
+ :delete=>{:raw=>true,:prefix=>'b'},:key? => {:prefix=>'b'},:increment=>{:prefix=>'b'}}
1041
+
1042
+ store.prefix('a').raw.default_options.should == {:store=>{:raw=>true,:prefix=>'a'},:load=>{:raw=>true,:prefix=>'a'},
1043
+ :delete=>{:raw=>true,:prefix=>'a'},:key? => {:prefix=>'a'},:increment=>{:prefix=>'a'}}
1044
+ end}
1045
+ },
1046
+ }
1047
+
1048
+ SPECS = {}
1049
+
1050
+ KEYS = {
1051
+ 'String' => ['strkey1', 'strkey2'].map(&:inspect),
1052
+ 'Object' => ['Value.new(:objkey1)', 'Value.new(:objkey2)'],
1053
+ 'Hash' => [{'hashkey1' => 'hashkey2'}, {'hashkey3' => 'hashkey4'}].map(&:inspect)
1054
+ }
1055
+
1056
+ VALUES = {
1057
+ 'Boolean' => [true, false],
1058
+ 'String' => ['strval1', 'strval2'].map(&:inspect),
1059
+ 'Hash' => [{'hashval1' => ['array1', 1]}, {'hashval3' => ['array2', {'hashval4' => 42}]}].map(&:inspect),
1060
+ 'Object' => ['Value.new(:objval1)', 'Value.new(:objval2)'],
1061
+ }
1062
+
1063
+ KEYS.each do |key_type, (key1,key2)|
1064
+ VALUES.each do |val_type, (val1,val2)|
1065
+
1066
+ code = %{it "reads from keys that are #{key_type}s like a Hash" do
1067
+ store[#{key1}].should == nil
1068
+ store.load(#{key1}).should == nil
1069
+ end
1070
+
1071
+ it "guarantees that the same #{val_type} value is returned when setting a #{key_type} key" do
1072
+ value = #{val1}
1073
+ (store[#{key1}] = value).should equal(value)
1074
+ end
1075
+
1076
+ it "returns false from key? if a #{key_type} key is not available" do
1077
+ store.key?(#{key1}).should == false
1078
+ end
1079
+
1080
+ it "returns nil from delete if an element for a #{key_type} key does not exist" do
1081
+ store.delete(#{key1}).should == nil
1082
+ end
1083
+
1084
+ it "removes all #{key_type} keys from the store with clear" do
1085
+ store[#{key1}] = #{val1}
1086
+ store[#{key2}] = #{val2}
1087
+ store.clear.should equal(store)
1088
+ store.key?(#{key1}).should_not == true
1089
+ store.key?(#{key2}).should_not == true
1090
+ end
1091
+
1092
+ it "fetches a #{key_type} key with a default value with fetch, if the key is not available" do
1093
+ store.fetch(#{key1}, #{val1}).should == #{val1}
1094
+ end
1095
+
1096
+ it "fetches a #{key_type} key with a block with fetch, if the key is not available" do
1097
+ key = #{key1}
1098
+ value = #{val1}
1099
+ store.fetch(key) do |k|
1100
+ k.should equal(key)
1101
+ value
1102
+ end.should equal(value)
1103
+ end
1104
+
1105
+ it 'should accept options' do
1106
+ store.key?(#{key1}, :option1 => 1).should == false
1107
+ store.load(#{key1}, :option2 => 2).should == nil
1108
+ store.fetch(#{key1}, 42, :option3 => 3).should == 42
1109
+ store.fetch(#{key1}, :option3 => 3) { 42 }.should == 42
1110
+ store.delete(#{key1}, :option4 => 4).should == nil
1111
+ store.clear(:option5 => 5).should equal(store)
1112
+ store.store(#{key1}, #{val1}, :option6 => 6).should == #{val1}
1113
+ end}
1114
+ SPECS["null_#{key_type.downcase}key_#{val_type.downcase}value"] = code
1115
+
1116
+ code = %{it "writes #{val_type} values to keys that are #{key_type}s like a Hash" do
1117
+ store[#{key1}] = #{val1}
1118
+ store[#{key1}].should == #{val1}
1119
+ store.load(#{key1}).should == #{val1}
1120
+ end
1121
+
1122
+ it "returns true from key? if a #{key_type} key is available" do
1123
+ store[#{key1}] = #{val1}
1124
+ store.key?(#{key1}).should == true
1125
+ store[#{key2}] = #{val2}
1126
+ store.key?(#{key2}).should == true
1127
+ end
1128
+
1129
+ it "stores #{val_type} values with #{key_type} keys with #store" do
1130
+ value = #{val1}
1131
+ store.store(#{key1}, value).should equal(value)
1132
+ store[#{key1}].should == #{val1}
1133
+ store.load(#{key1}).should == #{val1}
1134
+ end
1135
+
1136
+ it "stores #{key_type} after clear" do
1137
+ store[#{key1}] = #{val1}
1138
+ store[#{key2}] = #{val2}
1139
+ store.clear.should equal(store)
1140
+ store[#{key1}] = #{val1}
1141
+ store[#{key1}].should == #{val1}
1142
+ store[#{key2}].should be_nil
1143
+ end
1144
+
1145
+ it "removes and returns a #{val_type} element with a #{key_type} key from the backing store via delete if it exists" do
1146
+ store[#{key1}] = #{val1}
1147
+ store.delete(#{key1}).should == #{val1}
1148
+ store.key?(#{key1}).should == false
1149
+ end
1150
+
1151
+ it "overwrites existing #{val_type} values with #{key_type}" do
1152
+ store[#{key1}] = #{val1}
1153
+ store[#{key1}].should == #{val1}
1154
+ store[#{key1}] = #{val2}
1155
+ store[#{key1}].should == #{val2}
1156
+ end
1157
+
1158
+ it "does not run the block if the #{key_type} key is available" do
1159
+ store[#{key1}] = #{val1}
1160
+ unaltered = "unaltered"
1161
+ store.fetch(#{key1}) { unaltered = "altered" }
1162
+ unaltered.should == "unaltered"
1163
+ end
1164
+
1165
+ it "fetches a #{key_type} key with a default value with fetch, if the key is available" do
1166
+ store[#{key1}] = #{val1}
1167
+ store.fetch(#{key1}, #{val2}).should == #{val1}
1168
+ end}
1169
+ SPECS["store_#{key_type.downcase}key_#{val_type.downcase}value"] = code
1170
+
1171
+ code = %{it "guarantees that a different #{val_type} value is retrieved from the #{key_type} key" do
1172
+ value = #{val1}
1173
+ store[#{key1}] = #{val1}
1174
+ store[#{key1}].should_not be_equal(#{val1})
1175
+ end}
1176
+ if val_type != 'Boolean'
1177
+ SPECS["returndifferent_#{key_type.downcase}key_#{val_type.downcase}value"] = code
1178
+ end
1179
+
1180
+ code = %{it 'should support expires on store and #[]' do
1181
+ store.store(#{key1}, #{val1}, :expires => 2)
1182
+ store[#{key1}].should == #{val1}
1183
+ sleep 1
1184
+ store[#{key1}].should == #{val1}
1185
+ sleep 2
1186
+ store[#{key1}].should == nil
1187
+ end
1188
+
1189
+ it 'should support expires on store and load' do
1190
+ store.store(#{key1}, #{val1}, :expires => 2)
1191
+ store.load(#{key1}).should == #{val1}
1192
+ sleep 1
1193
+ store.load(#{key1}).should == #{val1}
1194
+ sleep 2
1195
+ store.load(#{key1}).should == nil
1196
+ end
1197
+
1198
+ it 'should support expires on store and key?' do
1199
+ store.store(#{key1}, #{val1}, :expires => 2)
1200
+ store.key?(#{key1}).should == true
1201
+ sleep 1
1202
+ store.key?(#{key1}).should == true
1203
+ sleep 2
1204
+ store.key?(#{key1}).should == false
1205
+ end
1206
+
1207
+ it 'should support updating the expiration time in load' do
1208
+ store.store(#{key2}, #{val2}, :expires => 2)
1209
+ store[#{key2}].should == #{val2}
1210
+ sleep 1
1211
+ store.load(#{key2}, :expires => 3).should == #{val2}
1212
+ store[#{key2}].should == #{val2}
1213
+ sleep 1
1214
+ store[#{key2}].should == #{val2}
1215
+ sleep 3
1216
+ store[#{key2}].should == nil
1217
+ end
1218
+
1219
+ it 'should support updating the expiration time in key?' do
1220
+ store.store(#{key2}, #{val2}, :expires => 2)
1221
+ store[#{key2}].should == #{val2}
1222
+ sleep 1
1223
+ store.key?(#{key2}, :expires => 3).should be_true
1224
+ store[#{key2}].should == #{val2}
1225
+ sleep 1
1226
+ store[#{key2}].should == #{val2}
1227
+ sleep 3
1228
+ store[#{key2}].should == nil
1229
+ end
1230
+
1231
+ it 'should support updating the expiration time in fetch' do
1232
+ store.store(#{key1}, #{val1}, :expires => 2)
1233
+ store[#{key1}].should == #{val1}
1234
+ sleep 1
1235
+ store.fetch(#{key1}, nil, :expires => 3).should == #{val1}
1236
+ store[#{key1}].should == #{val1}
1237
+ sleep 1
1238
+ store[#{key1}].should == #{val1}
1239
+ sleep 3
1240
+ store[#{key1}].should == nil
1241
+ end
1242
+
1243
+ it 'should respect expires in delete' do
1244
+ store.store(#{key2}, #{val2}, :expires => 2)
1245
+ store[#{key2}].should == #{val2}
1246
+ sleep 1
1247
+ store[#{key2}].should == #{val2}
1248
+ sleep 2
1249
+ store.delete(#{key2}).should == nil
1250
+ end
1251
+
1252
+ it 'should support the #expires syntactic sugar' do
1253
+ store['longlive_key'] = 'longlive_value'
1254
+ store.expires(2).store(#{key2}, #{val2})
1255
+ store[#{key2}].should == #{val2}
1256
+ sleep 1
1257
+ store[#{key2}].should == #{val2}
1258
+ sleep 2
1259
+ store.delete(#{key2}).should == nil
1260
+ store['longlive_key'].should == 'longlive_value'
1261
+ end}
1262
+ SPECS["expires_#{key_type.downcase}key_#{val_type.downcase}value"] = code
1263
+
1264
+ end
1265
+ end
1266
+
1267
+ SPECS['not_increment'] = %{it 'should not support increment' do
1268
+ expect do
1269
+ store.increment('inckey')
1270
+ end.to raise_error(NotImplementedError)
1271
+ end}
1272
+
1273
+ SPECS['increment'] = %{it 'should initialize in increment with 1' do
1274
+ store.key?('inckey').should be_false
1275
+ store.increment('inckey').should == 1
1276
+ store.key?('inckey').should be_true
1277
+ store.raw['inckey'].should == '1'
1278
+ store.raw.load('inckey').should == '1'
1279
+ store.load('inckey', :raw => true).should == '1'
1280
+ store['inckey'].should == '1'
1281
+
1282
+ store.delete('inckey', :raw => true).should == '1'
1283
+ store.key?('inckey').should be_false
1284
+ end
1285
+
1286
+ it 'should initialize in increment with higher value' do
1287
+ store.increment('inckey', 42).should == 42
1288
+ store.key?('inckey').should be_true
1289
+ store.raw['inckey'].should == '42'
1290
+ store['inckey'].should == '42'
1291
+ store.delete('inckey', :raw => true).should == '42'
1292
+ end
1293
+
1294
+ it 'should initialize in increment with 0' do
1295
+ store.increment('inckey', 0).should == 0
1296
+ store.key?('inckey').should be_true
1297
+ store.raw['inckey'].should == '0'
1298
+ store['inckey'].should == '0'
1299
+ store.delete('inckey', :raw => true).should == '0'
1300
+ end
1301
+
1302
+ it 'should support incrementing existing value by value' do
1303
+ store.increment('inckey').should == 1
1304
+ store.increment('inckey', 42).should == 43
1305
+ store.raw['inckey'].should == '43'
1306
+ end
1307
+
1308
+ it 'should support incrementing existing value by 0' do
1309
+ store.increment('inckey').should == 1
1310
+ store.increment('inckey', 0).should == 1
1311
+ store.raw['inckey'].should == '1'
1312
+ end
1313
+
1314
+ it 'should support deleting integer value' do
1315
+ store.increment('inckey').should == 1
1316
+ store.delete('inckey').should == '1'
1317
+ store.key?('inckey').should be_false
1318
+ end
1319
+
1320
+ it 'should support decrementing existing value' do
1321
+ store.increment('inckey', 10).should == 10
1322
+ store.increment('inckey', -5).should == 5
1323
+ store.raw['inckey'].should == '5'
1324
+ store.increment('inckey', -5).should == 0
1325
+ store.raw['inckey'].should == '0'
1326
+ end
1327
+
1328
+ it 'interpret raw value as integer' do
1329
+ store.store('inckey', '42', :raw => true)
1330
+ store.increment('inckey').should == 43
1331
+ store.raw['inckey'].should == '43'
1332
+ end
1333
+
1334
+ it 'should raise error on non integer value' do
1335
+ store['strkey'] = 'value'
1336
+ expect do
1337
+ store.increment('strkey')
1338
+ end.to raise_error
1339
+ end}
1340
+
1341
+ SPECS['marshallable_key'] = %{it 'refuses to #[] from keys that cannot be marshalled' do
1342
+ expect do
1343
+ store[Struct.new(:foo).new(:bar)]
1344
+ end.to raise_error(marshal_error)
1345
+ end
1346
+
1347
+ it 'refuses to load from keys that cannot be marshalled' do
1348
+ expect do
1349
+ store.load(Struct.new(:foo).new(:bar))
1350
+ end.to raise_error(marshal_error)
1351
+ end
1352
+
1353
+ it 'refuses to fetch from keys that cannot be marshalled' do
1354
+ expect do
1355
+ store.fetch(Struct.new(:foo).new(:bar), true)
1356
+ end.to raise_error(marshal_error)
1357
+ end
1358
+
1359
+ it 'refuses to #[]= to keys that cannot be marshalled' do
1360
+ expect do
1361
+ store[Struct.new(:foo).new(:bar)] = 'value'
1362
+ end.to raise_error(marshal_error)
1363
+ end
1364
+
1365
+ it 'refuses to store to keys that cannot be marshalled' do
1366
+ expect do
1367
+ store.store Struct.new(:foo).new(:bar), 'value'
1368
+ end.to raise_error(marshal_error)
1369
+ end
1370
+
1371
+ it 'refuses to check for key? if the key cannot be marshalled' do
1372
+ expect do
1373
+ store.key? Struct.new(:foo).new(:bar)
1374
+ end.to raise_error(marshal_error)
1375
+ end
1376
+
1377
+ it 'refuses to delete a key if the key cannot be marshalled' do
1378
+ expect do
1379
+ store.delete Struct.new(:foo).new(:bar)
1380
+ end.to raise_error(marshal_error)
1381
+ end}
1382
+
1383
+ SPECS['marshallable_value'] = %{it 'refuses to store values that cannot be marshalled' do
1384
+ expect do
1385
+ store.store 'key', Struct.new(:foo).new(:bar)
1386
+ end.to raise_error(marshal_error)
1387
+ end}
1388
+
1389
+ SPECS['transform_value'] = %{it 'allows to bypass transformer with :raw' do
1390
+ store['key'] = 'value'
1391
+ load_value(store.load('key', :raw => true)).should == 'value'
1392
+
1393
+ store.store('key', 'value', :raw => true)
1394
+ store.load('key', :raw => true).should == 'value'
1395
+ store.delete('key', :raw => true).should == 'value'
1396
+ end
1397
+
1398
+ it 'allows to bypass transformer with raw syntactic sugar' do
1399
+ store['key'] = 'value'
1400
+ load_value(store.raw.load('key')).should == 'value'
1401
+
1402
+ store.raw.store('key', 'value')
1403
+ store.raw['key'].should == 'value'
1404
+ store.raw.load('key').should == 'value'
1405
+ store.raw.delete('key').should == 'value'
1406
+
1407
+ store.raw['key'] = 'value2'
1408
+ store.raw['key'].should == 'value2'
1409
+ end
1410
+
1411
+ it 'should return unmarshalled value' do
1412
+ store.store('key', 'unmarshalled value', :raw => true)
1413
+ store.load('key', :raw => true).should == 'unmarshalled value'
1414
+ store['key'].should == 'unmarshalled value'
1415
+ store.delete('key').should == 'unmarshalled value'
1416
+ end}
1417
+
1418
+ SPECS['transform_value_with_expires'] = %{it 'allows to bypass transformer with :raw' do
1419
+ store['key'] = 'value'
1420
+ load_value(store.load('key', :raw => true)).should == ['value']
1421
+
1422
+ store.store('key', 'value', :expires => 10)
1423
+ load_value(store.load('key', :raw => true)).first.should == 'value'
1424
+ load_value(store.load('key', :raw => true)).last.should respond_to(:to_int)
1425
+
1426
+ store.store('key', 'value', :raw => true)
1427
+ store.load('key', :raw => true).should == 'value'
1428
+ store.delete('key', :raw => true).should == 'value'
1429
+ end
1430
+
1431
+ it 'should return unmarshalled value' do
1432
+ store.store('key', 'unmarshalled value', :raw => true)
1433
+ store.load('key', :raw => true).should == 'unmarshalled value'
1434
+ store['key'].should == 'unmarshalled value'
1435
+ store.delete('key').should == 'unmarshalled value'
1436
+ end}
1437
+
1438
+ specs_code = "#{header}\n"
1439
+ SPECS.each do |key, code|
1440
+ specs_code << "#################### #{key} ####################\n\n" <<
1441
+ "shared_examples_for '#{key}' do\n " << code.gsub("\n", "\n ") << "\nend\n\n"
1442
+ end
1443
+ specs_code.gsub!(/\n +\n/, "\n\n")
1444
+ File.open(File.join(File.dirname(__FILE__), 'monetaspecs.rb'), 'w') {|out| out << specs_code }
1445
+
1446
+ TESTS.each do |name, options|
1447
+ build = options.delete(:build)
1448
+ store = options.delete(:store)
1449
+ key = [options.delete(:key) || %w(Object String Hash)].flatten
1450
+ value = [options.delete(:value) || %w(Object String Hash Boolean)].flatten
1451
+
1452
+ load_value = options.delete(:load_value) || 'Marshal.load(value)'
1453
+
1454
+ specs_code = []
1455
+ options.delete(:specs).each do |s|
1456
+ specs_code << " it_should_behave_like '#{s}'" if SPECS[s.to_s]
1457
+ key.each do |k|
1458
+ value.each do |v|
1459
+ x = "#{s}_#{k.downcase}key_#{v.downcase}value"
1460
+ specs_code << " it_should_behave_like '#{x}'" if SPECS[x]
1461
+ end
1462
+ end
1463
+ end
1464
+
1465
+ preamble = options.delete(:preamble).to_s.gsub("\n", "\n ")
1466
+ opts = options.delete(:options)
1467
+ opts = ', ' << opts if opts
1468
+
1469
+ build ||= "Moneta.new(#{store.inspect}#{opts}, :logger => {:out => File.open(File.join(make_tempdir, '#{name}.log'), 'a')})"
1470
+
1471
+ code = %{#{header}require 'helper'
1472
+
1473
+ describe_moneta #{name.inspect} do
1474
+ #{preamble}def new_store
1475
+ #{build.gsub("\n", "\n ")}
1476
+ end
1477
+
1478
+ def load_value(value)
1479
+ #{load_value}
1480
+ end
1481
+
1482
+ include_context 'setup_store'
1483
+ #{specs_code.join("\n")}#{options[:tests].to_s.gsub("\n", "\n ")}
1484
+ end
1485
+ }
1486
+
1487
+ code.gsub!(/\n +\n/, "\n\n")
1488
+ File.open(File.join(File.dirname(__FILE__), 'moneta', "#{name}_spec.rb"), 'w') {|out| out << code }
1489
+ end