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