juno 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (161) hide show
  1. data/.gitignore +2 -1
  2. data/.travis.yml +6 -0
  3. data/Gemfile +16 -9
  4. data/README.md +92 -34
  5. data/Rakefile +23 -5
  6. data/benchmarks/run.rb +19 -22
  7. data/juno.gemspec +0 -3
  8. data/lib/juno/adapters/activerecord.rb +58 -0
  9. data/lib/juno/adapters/cassandra.rb +47 -0
  10. data/lib/juno/adapters/couch.rb +43 -0
  11. data/lib/juno/adapters/datamapper.rb +64 -0
  12. data/lib/juno/adapters/dbm.rb +17 -0
  13. data/lib/juno/adapters/file.rb +58 -0
  14. data/lib/juno/adapters/fog.rb +42 -0
  15. data/lib/juno/adapters/gdbm.rb +17 -0
  16. data/lib/juno/adapters/localmemcache.rb +18 -0
  17. data/lib/juno/adapters/memcached.rb +11 -0
  18. data/lib/juno/adapters/memcached_dalli.rb +46 -0
  19. data/lib/juno/adapters/memcached_native.rb +47 -0
  20. data/lib/juno/adapters/memory.rb +30 -0
  21. data/lib/juno/adapters/mongo.rb +43 -0
  22. data/lib/juno/adapters/null.rb +28 -0
  23. data/lib/juno/adapters/pstore.rb +51 -0
  24. data/lib/juno/adapters/redis.rb +43 -0
  25. data/lib/juno/adapters/riak.rb +46 -0
  26. data/lib/juno/adapters/sdbm.rb +27 -0
  27. data/lib/juno/adapters/sequel.rb +50 -0
  28. data/lib/juno/adapters/sqlite.rb +52 -0
  29. data/lib/juno/adapters/tokyocabinet.rb +33 -0
  30. data/lib/juno/adapters/yaml.rb +13 -0
  31. data/lib/juno/base.rb +11 -89
  32. data/lib/juno/builder.rb +30 -0
  33. data/lib/juno/cache.rb +64 -0
  34. data/lib/juno/expires.rb +6 -10
  35. data/lib/juno/proxy.rb +62 -3
  36. data/lib/juno/stack.rb +27 -11
  37. data/lib/juno/transformer.rb +106 -0
  38. data/lib/juno/version.rb +1 -1
  39. data/lib/juno.rb +81 -29
  40. data/spec/adapter_activerecord_spec.rb +41 -0
  41. data/spec/adapter_cassandra_spec.rb +27 -0
  42. data/spec/adapter_couch_spec.rb +27 -0
  43. data/spec/adapter_datamapper_spec.rb +61 -0
  44. data/spec/adapter_dbm_spec.rb +27 -0
  45. data/spec/adapter_file_spec.rb +27 -0
  46. data/spec/adapter_fog_spec.rb +35 -0
  47. data/spec/adapter_gdbm_spec.rb +27 -0
  48. data/spec/adapter_localmemcache_spec.rb +27 -0
  49. data/spec/adapter_memcached_dalli_spec.rb +28 -0
  50. data/spec/adapter_memcached_native_spec.rb +28 -0
  51. data/spec/adapter_memcached_spec.rb +28 -0
  52. data/spec/adapter_memory_spec.rb +42 -0
  53. data/spec/adapter_mongo_spec.rb +27 -0
  54. data/spec/adapter_pstore_spec.rb +30 -0
  55. data/spec/adapter_redis_spec.rb +28 -0
  56. data/spec/adapter_riak_spec.rb +31 -0
  57. data/spec/adapter_sdbm_spec.rb +27 -0
  58. data/spec/adapter_sequel_spec.rb +27 -0
  59. data/spec/adapter_sqlite_spec.rb +27 -0
  60. data/spec/adapter_tokyocabinet_spec.rb +27 -0
  61. data/spec/adapter_yaml_spec.rb +30 -0
  62. data/spec/cache_file_memory_spec.rb +50 -0
  63. data/spec/cache_memory_null_spec.rb +39 -0
  64. data/spec/expires_file_spec.rb +82 -0
  65. data/spec/expires_memory_spec.rb +59 -0
  66. data/spec/generate.rb +736 -0
  67. data/spec/helper.rb +39 -0
  68. data/spec/junospecs.rb +1540 -0
  69. data/spec/null_adapter_spec.rb +33 -0
  70. data/spec/proxy_expires_memory_spec.rb +63 -0
  71. data/spec/proxy_redis_spec.rb +38 -0
  72. data/spec/simple_activerecord_spec.rb +52 -0
  73. data/spec/simple_cassandra_spec.rb +53 -0
  74. data/spec/simple_couch_spec.rb +52 -0
  75. data/spec/simple_datamapper_spec.rb +54 -0
  76. data/spec/simple_datamapper_with_repository_spec.rb +54 -0
  77. data/spec/simple_dbm_spec.rb +52 -0
  78. data/spec/simple_file_spec.rb +52 -0
  79. data/spec/simple_fog_spec.rb +60 -0
  80. data/spec/simple_gdbm_spec.rb +52 -0
  81. data/spec/simple_hashfile_spec.rb +52 -0
  82. data/spec/simple_localmemcache_spec.rb +52 -0
  83. data/spec/simple_memcached_dalli_spec.rb +53 -0
  84. data/spec/simple_memcached_native_spec.rb +53 -0
  85. data/spec/simple_memcached_spec.rb +53 -0
  86. data/spec/simple_memory_spec.rb +52 -0
  87. data/spec/simple_mongo_spec.rb +52 -0
  88. data/spec/simple_null_spec.rb +43 -0
  89. data/spec/simple_pstore_spec.rb +52 -0
  90. data/spec/simple_redis_spec.rb +53 -0
  91. data/spec/simple_riak_spec.rb +56 -0
  92. data/spec/simple_sdbm_spec.rb +52 -0
  93. data/spec/simple_sequel_spec.rb +52 -0
  94. data/spec/simple_sqlite_spec.rb +52 -0
  95. data/spec/simple_tokyocabinet_spec.rb +52 -0
  96. data/spec/simple_yaml_spec.rb +52 -0
  97. data/spec/stack_file_memory_spec.rb +43 -0
  98. data/spec/stack_memory_file_spec.rb +42 -0
  99. data/spec/transformer_bson_spec.rb +44 -0
  100. data/spec/transformer_json_spec.rb +44 -0
  101. data/spec/transformer_marshal_base64_spec.rb +60 -0
  102. data/spec/transformer_marshal_escape_spec.rb +60 -0
  103. data/spec/transformer_marshal_md5_spec.rb +60 -0
  104. data/spec/transformer_marshal_md5_spread_spec.rb +60 -0
  105. data/spec/transformer_msgpack_spec.rb +44 -0
  106. data/spec/transformer_yaml_spec.rb +59 -0
  107. metadata +164 -108
  108. data/lib/juno/activerecord.rb +0 -55
  109. data/lib/juno/cassandra.rb +0 -45
  110. data/lib/juno/couch.rb +0 -43
  111. data/lib/juno/datamapper.rb +0 -63
  112. data/lib/juno/dbm.rb +0 -15
  113. data/lib/juno/file.rb +0 -62
  114. data/lib/juno/fog.rb +0 -48
  115. data/lib/juno/gdbm.rb +0 -15
  116. data/lib/juno/hashfile.rb +0 -12
  117. data/lib/juno/localmemcache.rb +0 -16
  118. data/lib/juno/memcached.rb +0 -7
  119. data/lib/juno/memcached_dalli.rb +0 -55
  120. data/lib/juno/memcached_native.rb +0 -56
  121. data/lib/juno/memory.rb +0 -7
  122. data/lib/juno/mongodb.rb +0 -43
  123. data/lib/juno/null.rb +0 -23
  124. data/lib/juno/pstore.rb +0 -49
  125. data/lib/juno/redis.rb +0 -46
  126. data/lib/juno/riak.rb +0 -45
  127. data/lib/juno/sdbm.rb +0 -15
  128. data/lib/juno/sequel.rb +0 -48
  129. data/lib/juno/sqlite.rb +0 -50
  130. data/lib/juno/tokyocabinet.rb +0 -36
  131. data/lib/juno/yaml.rb +0 -9
  132. data/test/helper.rb +0 -212
  133. data/test/test_activerecord.rb +0 -33
  134. data/test/test_cassandra.rb +0 -13
  135. data/test/test_couch.rb +0 -13
  136. data/test/test_datamapper.rb +0 -64
  137. data/test/test_dbm.rb +0 -13
  138. data/test/test_expires.rb +0 -9
  139. data/test/test_file.rb +0 -9
  140. data/test/test_fog.rb +0 -17
  141. data/test/test_gdbm.rb +0 -13
  142. data/test/test_hashfile.rb +0 -9
  143. data/test/test_localmemcache.rb +0 -13
  144. data/test/test_memcached.rb +0 -14
  145. data/test/test_memcached_dalli.rb +0 -14
  146. data/test/test_memcached_native.rb +0 -14
  147. data/test/test_memory.rb +0 -9
  148. data/test/test_mongodb.rb +0 -13
  149. data/test/test_null.rb +0 -9
  150. data/test/test_proxy.rb +0 -9
  151. data/test/test_pstore.rb +0 -9
  152. data/test/test_redis.rb +0 -13
  153. data/test/test_riak.rb +0 -13
  154. data/test/test_sdbm.rb +0 -13
  155. data/test/test_sequel.rb +0 -13
  156. data/test/test_sqlite.rb +0 -13
  157. data/test/test_stack.rb +0 -10
  158. data/test/test_tokyocabinet.rb +0 -13
  159. data/test/test_yaml.rb +0 -9
  160. data/unsupported/test_tokyotyrant.rb +0 -13
  161. data/unsupported/tokyotyrant.rb +0 -29
data/spec/generate.rb ADDED
@@ -0,0 +1,736 @@
1
+ ADAPTER_SPECS = [:null_stringkey_stringvalue, :store_stringkey_stringvalue, :returndifferent_stringkey_stringvalue]
2
+ SIMPLE_SPECS = [:null, :store, :returndifferent, :marshallable_key]
3
+ EXPIRES_SPECS = SIMPLE_SPECS + [:expires_stringkey_stringvalue]
4
+
5
+ TESTS = {
6
+ 'simple_memory' => {
7
+ :store => :Memory
8
+ },
9
+ 'simple_file' => {
10
+ :store => :File,
11
+ :options => ':dir => File.join(make_tempdir, "simple_file")'
12
+ },
13
+ 'simple_hashfile' => {
14
+ :store => :HashFile,
15
+ :options => ':dir => File.join(make_tempdir, "simple_hashfile")'
16
+ },
17
+ 'simple_cassandra' => {
18
+ :store => :Cassandra,
19
+ :specs => EXPIRES_SPECS,
20
+ },
21
+ 'simple_dbm' => {
22
+ :store => :DBM,
23
+ :options => ':file => File.join(make_tempdir, "simple_dbm")'
24
+ },
25
+ 'simple_gdbm' => {
26
+ :store => :GDBM,
27
+ :options => ':file => File.join(make_tempdir, "simple_gdbm")'
28
+ },
29
+ 'simple_sdbm' => {
30
+ :store => :SDBM,
31
+ :options => ':file => File.join(make_tempdir, "simple_sdbm")'
32
+ },
33
+ 'simple_pstore' => {
34
+ :store => :PStore,
35
+ :options => ':file => File.join(make_tempdir, "simple_pstore")'
36
+ },
37
+ 'simple_yaml' => {
38
+ :store => :YAML,
39
+ :options => ':file => File.join(make_tempdir, "simple_yaml")'
40
+ },
41
+ 'simple_localmemcache' => {
42
+ :store => :LocalMemCache,
43
+ :options => ':file => File.join(make_tempdir, "simple_localmemcache")'
44
+ },
45
+ 'simple_tokyocabinet' => {
46
+ :store => :TokyoCabinet,
47
+ :options => ':file => File.join(make_tempdir, "simple_tokyocabinet")'
48
+ },
49
+ 'simple_sqlite' => {
50
+ :store => :Sqlite,
51
+ :options => ':file => ":memory:"'
52
+ },
53
+ 'simple_redis' => {
54
+ :store => :Redis,
55
+ :specs => EXPIRES_SPECS,
56
+ },
57
+ 'simple_memcached' => {
58
+ :store => :Memcached,
59
+ :specs => EXPIRES_SPECS,
60
+ :options => ':server => "localhost:22122", :namespace => "simple_memcached"'
61
+ },
62
+ 'simple_memcached_dalli' => {
63
+ :store => :MemcachedDalli,
64
+ :specs => EXPIRES_SPECS,
65
+ :options => ':server => "localhost:22122", :namespace => "simple_memcached_dalli"'
66
+ },
67
+ 'simple_memcached_native' => {
68
+ :store => :MemcachedNative,
69
+ :specs => EXPIRES_SPECS,
70
+ :options => ':server => "localhost:22122", :namespace => "simple_memcached_native"'
71
+ },
72
+ 'simple_riak' => {
73
+ :store => :Riak,
74
+ :options => ":bucket => 'simple_riak'",
75
+ # We don't want Riak warnings in tests
76
+ :preamble => "require 'riak'\n\nRiak.disable_list_keys_warnings = true\n\n"
77
+ },
78
+ 'simple_couch' => {
79
+ :store => :Couch,
80
+ :options => ":db => 'simple_couch'"
81
+ },
82
+ 'simple_mongo' => {
83
+ :store => :Mongo,
84
+ :options => ":db => 'simple_mongo'"
85
+ },
86
+ 'simple_null' => {
87
+ :store => :Null,
88
+ :specs => [:null, :marshallable_key, :returndifferent]
89
+ },
90
+ 'null_adapter' => {
91
+ :build => 'Juno::Adapters::Null.new',
92
+ :specs => :null
93
+ },
94
+ 'simple_sequel' => {
95
+ :store => :Sequel,
96
+ :options => ":db => (defined?(JRUBY_VERSION) ? 'jdbc:sqlite:/' : 'sqlite:/')"
97
+ },
98
+ 'simple_datamapper' => {
99
+ :store => :DataMapper,
100
+ :options => ':setup => "sqlite3://#{make_tempdir}/simple_datamapper-default.sqlite3"',
101
+ # DataMapper needs default repository to be setup
102
+ :preamble => "require 'dm-core'\nDataMapper.setup(:default, :adapter => :in_memory)\n"
103
+ },
104
+ 'simple_datamapper_with_repository' => {
105
+ :store => :DataMapper,
106
+ :options => ':repository => :repo, :setup => "sqlite3://#{make_tempdir}/simple_datamapper-repo.sqlite3"',
107
+ # DataMapper needs default repository to be setup
108
+ :preamble => "require 'dm-core'\nDataMapper.setup(:default, :adapter => :in_memory)\n"
109
+ },
110
+ 'simple_activerecord' => {
111
+ :store => :ActiveRecord,
112
+ :options => ":connection => { :adapter => (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'), :database => File.join(make_tempdir, 'simple_activerecord.sqlite3') }"
113
+ },
114
+ 'simple_fog' => {
115
+ :store => :Fog,
116
+ :options => ":aws_access_key_id => 'fake_access_key_id',
117
+ :aws_secret_access_key => 'fake_secret_access_key',
118
+ :provider => 'AWS',
119
+ :dir => 'juno'",
120
+ # Put Fog into testing mode
121
+ :preamble => "require 'fog'\nFog.mock!\n"
122
+ },
123
+ # 'cache' => {
124
+ # },
125
+ 'expires_memory' => {
126
+ :build => %{
127
+ Juno.build do
128
+ use :Expires
129
+ adapter :Memory
130
+ end},
131
+ :specs => [:null, :store, :expires]
132
+ },
133
+ 'expires_file' => {
134
+ :build => %{
135
+ Juno.build do
136
+ use :Expires
137
+ use :Transformer, :key => [:marshal, :escape], :value => :marshal
138
+ adapter :File, :dir => File.join(make_tempdir, "expires-file")
139
+ end},
140
+ :specs => [:null, :store, :expires, :returndifferent, :marshallable_key],
141
+ :tests => %{
142
+ it 'should delete expired value in underlying file storage' do
143
+ @store.store('foo', 'bar', :expires => 2)
144
+ @store['foo'].should == 'bar'
145
+ sleep 1
146
+ @store['foo'].should == 'bar'
147
+ sleep 2
148
+ @store['foo'].should == nil
149
+ @store.adapter['foo'].should == nil
150
+ @store.adapter.adapter['foo'].should == nil
151
+ end
152
+ }
153
+ },
154
+ 'proxy_redis' => {
155
+ :build => %{
156
+ Juno.build do
157
+ use :Proxy
158
+ use :Proxy
159
+ adapter :Redis
160
+ end},
161
+ :specs => ADAPTER_SPECS + [:expires_stringkey_stringvalue]
162
+ },
163
+ 'proxy_expires_memory' => {
164
+ :build => %{
165
+ Juno.build do
166
+ use :Proxy
167
+ use :Expires
168
+ use :Proxy
169
+ adapter :Memory
170
+ end},
171
+ :specs => [:null, :store, :expires]
172
+ },
173
+ 'cache_file_memory' => {
174
+ :build => %{
175
+ Juno.build do
176
+ use(:Cache) do
177
+ backend { adapter :File, :dir => File.join(make_tempdir, "cache_file_memory") }
178
+ cache { adapter :Memory }
179
+ end
180
+ end},
181
+ :specs => ADAPTER_SPECS,
182
+ :tests => %{
183
+ it 'should store loaded values in cache' do
184
+ @store.backend['foo'] = 'bar'
185
+ @store.cache['foo'].should == nil
186
+ @store['foo'].should == 'bar'
187
+ @store.cache['foo'].should == 'bar'
188
+ @store.backend.delete('foo')
189
+ @store['foo'].should == 'bar'
190
+ @store.delete('foo')
191
+ @store['foo'].should == nil
192
+ end
193
+ }
194
+ },
195
+ 'cache_memory_null' => {
196
+ :build => %{
197
+ Juno.build do
198
+ use(:Cache) do
199
+ backend(Juno::Adapters::Memory.new)
200
+ cache(Juno::Adapters::Null.new)
201
+ end
202
+ end},
203
+ :specs => ADAPTER_SPECS
204
+ },
205
+ 'stack_file_memory' => {
206
+ :build => %{
207
+ Juno.build do
208
+ use(:Stack) do
209
+ add(Juno.new(:Null))
210
+ add(Juno::Adapters::Null.new)
211
+ add { adapter :File, :dir => File.join(make_tempdir, "stack-file1") }
212
+ add { adapter :Memory }
213
+ end
214
+ end},
215
+ :specs => ADAPTER_SPECS
216
+ },
217
+ 'stack_memory_file' => {
218
+ :build => %{
219
+ Juno.build do
220
+ use(:Stack) do
221
+ add(Juno.new(:Null))
222
+ add(Juno::Adapters::Null.new)
223
+ add { adapter :Memory }
224
+ add { adapter :File, :dir => File.join(make_tempdir, "stack-file2") }
225
+ end
226
+ end},
227
+ :specs => [:null_stringkey_stringvalue, :store_stringkey_stringvalue]
228
+ },
229
+ 'transformer_json' => {
230
+ :build => %{
231
+ Juno.build do
232
+ use :Transformer, :key => :json, :value => :json
233
+ adapter :Memory
234
+ end},
235
+ :key => %w(Hash String),
236
+ :value => %w(Hash String),
237
+ :specs => [:null, :store, :returndifferent]
238
+ },
239
+ 'transformer_bson' => {
240
+ :build => %{
241
+ Juno.build do
242
+ use :Transformer, :key => :bson, :value => :bson
243
+ adapter :Memory
244
+ end},
245
+ :key => %w(Hash String),
246
+ :value => %w(Hash String),
247
+ :specs => [:null, :store, :returndifferent]
248
+ },
249
+ # 'transformer_tnet' => {
250
+ # :build => %{
251
+ #Juno.build do
252
+ # use :Transformer, :key => :tnet, :value => :tnet
253
+ # adapter :Memory
254
+ #end},
255
+ # :key => %w(Hash String),
256
+ # :value => %w(Hash String),
257
+ # :specs => [:null, :store, :returndifferent]
258
+ # },
259
+ 'transformer_msgpack' => {
260
+ :build => %{
261
+ Juno.build do
262
+ use :Transformer, :key => :msgpack, :value => :msgpack
263
+ adapter :Memory
264
+ end},
265
+ :key => %w(Hash String),
266
+ :value => %w(Hash String),
267
+ :specs => [:null, :store, :returndifferent]
268
+ },
269
+ 'transformer_yaml' => {
270
+ :build => %{
271
+ Juno.build do
272
+ use :Transformer, :key => :yaml, :value => :yaml
273
+ adapter :Memory
274
+ end},
275
+ :specs => [:null, :store, :returndifferent]
276
+ },
277
+ 'transformer_marshal_base64' => {
278
+ :build => %{
279
+ Juno.build do
280
+ use :Transformer, :key => [:marshal, :base64], :value => [:marshal, :base64]
281
+ adapter :Memory
282
+ end},
283
+ :specs => [:null, :store, :returndifferent, :marshallable_key]
284
+ },
285
+ 'transformer_marshal_escape' => {
286
+ :build => %{
287
+ Juno.build do
288
+ use :Transformer, :key => [:marshal, :escape], :value => :marshal
289
+ adapter :Memory
290
+ end},
291
+ :specs => [:null, :store, :returndifferent, :marshallable_key]
292
+ },
293
+ 'transformer_marshal_md5' => {
294
+ :build => %{
295
+ Juno.build do
296
+ use :Transformer, :key => [:marshal, :md5], :value => :marshal
297
+ adapter :Memory
298
+ end},
299
+ :specs => [:null, :store, :returndifferent, :marshallable_key]
300
+ },
301
+ 'transformer_marshal_md5_spread' => {
302
+ :build => %{
303
+ Juno.build do
304
+ use :Transformer, :key => [:marshal, :md5, :spread], :value => :marshal
305
+ adapter :Memory
306
+ end},
307
+ :specs => [:null, :store, :returndifferent, :marshallable_key]
308
+ },
309
+ 'adapter_activerecord' => {
310
+ :build => "Juno::Adapters::ActiveRecord.new(:connection => { :adapter => (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'), :database => File.join(make_tempdir, 'adapter_activerecord.sqlite3') })",
311
+ :specs => ADAPTER_SPECS,
312
+ :tests => %{
313
+ it 'updates an existing key/value' do
314
+ @store['foo/bar'] = '1'
315
+ @store['foo/bar'] = '2'
316
+ records = @store.table.find :all, :conditions => { :key => 'foo/bar' }
317
+ records.count.should == 1
318
+ end
319
+
320
+ it 'uses an existing connection' do
321
+ ActiveRecord::Base.establish_connection :adapter => (defined?(JRUBY_VERSION) ? 'jdbcsqlite3' : 'sqlite3'), :database => File.join(make_tempdir, 'activerecord-existing.sqlite3')
322
+
323
+ store = Juno::Adapters::ActiveRecord.new
324
+ store.table.table_exists?.should == true
325
+ end
326
+ }
327
+ },
328
+ 'adapter_cassandra' => {
329
+ :build => "Juno::Adapters::Cassandra.new",
330
+ :specs => ADAPTER_SPECS
331
+ },
332
+ 'adapter_couch' => {
333
+ :build => "Juno::Adapters::Couch.new(:db => 'adapter_couch')",
334
+ :specs => ADAPTER_SPECS
335
+ },
336
+ 'adapter_datamapper' => {
337
+ :build => 'Juno::Adapters::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/adapter_datamapper.sqlite3")',
338
+ # DataMapper needs default repository to be setup
339
+ :preamble => "require 'dm-core'\nDataMapper.setup(:default, :adapter => :in_memory)\n",
340
+ :specs => ADAPTER_SPECS + [:returndifferent_stringkey_objectvalue,
341
+ :null_stringkey_objectvalue,
342
+ :store_stringkey_objectvalue],
343
+ :tests => %q{
344
+ it 'does not cross contaminate when storing' do
345
+ first = Juno::Adapters::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/datamapper-first.sqlite3")
346
+ first.clear
347
+
348
+ second = Juno::Adapters::DataMapper.new(:repository => :sample, :setup => "sqlite3://#{make_tempdir}/datamapper-second.sqlite3")
349
+ second.clear
350
+
351
+ first['key'] = 'value'
352
+ second['key'] = 'value2'
353
+
354
+ first['key'].should == 'value'
355
+ second['key'].should == 'value2'
356
+ end
357
+
358
+ it 'does not cross contaminate when deleting' do
359
+ first = Juno::Adapters::DataMapper.new(:setup => "sqlite3://#{make_tempdir}/datamapper-first.sqlite3")
360
+ first.clear
361
+
362
+ second = Juno::Adapters::DataMapper.new(:repository => :sample, :setup => "sqlite3://#{make_tempdir}/datamapper-second.sqlite3")
363
+ second.clear
364
+
365
+ first['key'] = 'value'
366
+ second['key'] = 'value2'
367
+
368
+ first.delete('key').should == 'value'
369
+ first.key?('key').should == false
370
+ second['key'].should == 'value2'
371
+ end
372
+ }
373
+ },
374
+ 'adapter_dbm' => {
375
+ :build => 'Juno::Adapters::DBM.new(:file => File.join(make_tempdir, "adapter_dbm"))',
376
+ :specs => ADAPTER_SPECS
377
+ },
378
+ 'adapter_file' => {
379
+ :build => 'Juno::Adapters::File.new(:dir => File.join(make_tempdir, "adapter_file"))',
380
+ :specs => ADAPTER_SPECS
381
+ },
382
+ 'adapter_fog' => {
383
+ :build => "Juno::Adapters::Fog.new(:aws_access_key_id => 'fake_access_key_id',
384
+ :aws_secret_access_key => 'fake_secret_access_key',
385
+ :provider => 'AWS',
386
+ :dir => 'juno')",
387
+ # Put Fog into testing mode
388
+ :preamble => "require 'fog'\nFog.mock!\n",
389
+ :specs => ADAPTER_SPECS
390
+ },
391
+ 'adapter_gdbm' => {
392
+ :build => 'Juno::Adapters::GDBM.new(:file => File.join(make_tempdir, "adapter_gdbm"))',
393
+ :specs => ADAPTER_SPECS
394
+ },
395
+ 'adapter_localmemcache' => {
396
+ :build => 'Juno::Adapters::LocalMemCache.new(:file => File.join(make_tempdir, "adapter_localmemcache"))',
397
+ :specs => ADAPTER_SPECS
398
+ },
399
+ 'adapter_memcached_dalli' => {
400
+ :build => 'Juno::Adapters::MemcachedDalli.new(:server => "localhost:22122", :namespace => "adapter_memcached_dalli")',
401
+ :specs => ADAPTER_SPECS + [:expires_stringkey_stringvalue]
402
+ },
403
+ 'adapter_memcached_native' => {
404
+ :build => 'Juno::Adapters::MemcachedNative.new(:server => "localhost:22122", :namespace => "adapter_memcached_native")',
405
+ :specs => ADAPTER_SPECS + [:expires_stringkey_stringvalue]
406
+ },
407
+ 'adapter_memcached' => {
408
+ :build => 'Juno::Adapters::Memcached.new(:server => "localhost:22122", :namespace => "adapter_memcached")',
409
+ :specs => ADAPTER_SPECS + [:expires_stringkey_stringvalue]
410
+ },
411
+ 'adapter_memory' => {
412
+ :build => 'Juno::Adapters::Memory.new',
413
+ :specs => [:null, :store]
414
+ },
415
+ 'adapter_mongo' => {
416
+ :build => 'Juno::Adapters::Mongo.new(:db => "adapter_mongo")',
417
+ :specs => ADAPTER_SPECS
418
+ },
419
+ 'adapter_pstore' => {
420
+ :build => 'Juno::Adapters::PStore.new(:file => File.join(make_tempdir, "adapter_pstore"))',
421
+ :specs => ADAPTER_SPECS + [:null_stringkey_objectvalue,
422
+ :store_stringkey_objectvalue,
423
+ :returndifferent_stringkey_objectvalue]
424
+ },
425
+ 'adapter_redis' => {
426
+ :build => 'Juno::Adapters::Redis.new',
427
+ :specs => ADAPTER_SPECS + [:expires_stringkey_stringvalue]
428
+ },
429
+ 'adapter_riak' => {
430
+ :build => 'Juno::Adapters::Riak.new',
431
+ :options => ":bucket => 'adapter_riak'",
432
+ :specs => ADAPTER_SPECS,
433
+ # We don't want Riak warnings in tests
434
+ :preamble => "require 'riak'\n\nRiak.disable_list_keys_warnings = true\n\n"
435
+ },
436
+ 'adapter_sdbm' => {
437
+ :build => 'Juno::Adapters::SDBM.new(:file => File.join(make_tempdir, "adapter_sdbm"))',
438
+ :specs => ADAPTER_SPECS
439
+ },
440
+ 'adapter_sequel' => {
441
+ :build => "Juno::Adapters::Sequel.new(:db => (defined?(JRUBY_VERSION) ? 'jdbc:sqlite:/' : 'sqlite:/'))",
442
+ :specs => ADAPTER_SPECS
443
+ },
444
+ 'adapter_sqlite' => {
445
+ :build => 'Juno::Adapters::Sqlite.new(:file => ":memory:")',
446
+ :specs => ADAPTER_SPECS
447
+ },
448
+ 'adapter_tokyocabinet' => {
449
+ :build => 'Juno::Adapters::TokyoCabinet.new(:file => File.join(make_tempdir, "adapter_tokyocabinet"))',
450
+ :specs => ADAPTER_SPECS
451
+ },
452
+ 'adapter_yaml' => {
453
+ :build => 'Juno::Adapters::YAML.new(:file => File.join(make_tempdir, "adapter_yaml"))',
454
+ :specs => ADAPTER_SPECS + [:null_stringkey_objectvalue,
455
+ :store_stringkey_objectvalue,
456
+ :returndifferent_stringkey_objectvalue]
457
+ },
458
+ }
459
+
460
+ SPECS = {}
461
+
462
+ KEYS = {
463
+ 'String' => ['strkey1', 'strkey2'].map(&:inspect),
464
+ 'Object' => ['Value.new(:objkey1)', 'Value.new(:objkey2)'],
465
+ 'Hash' => [{'hashkey1' => 'hashkey2'}, {'hashkey3' => 'hashkey4'}].map(&:inspect)
466
+ }
467
+
468
+ VALUES = {
469
+ 'String' => ['strval1', 'strval2'].map(&:inspect),
470
+ 'Object' => ['Value.new(:objval1)', 'Value.new(:objval2)'],
471
+ 'Hash' => [{'hashval1' => 'hashval2'}, {'hashval3' => 'hashval4'}].map(&:inspect)
472
+ }
473
+
474
+ KEYS.each do |key_type, (key1,key2)|
475
+ VALUES.each do |val_type, (val1,val2)|
476
+
477
+ code = %{it "reads from keys that are #{key_type}s like a Hash" do
478
+ @store[#{key1}].should == nil
479
+ @store.load(#{key1}).should == nil
480
+ end
481
+
482
+ it "guarantees that the same #{val_type} value is returned when setting a #{key_type} key" do
483
+ value = #{val1}
484
+ (@store[#{key1}] = value).should equal(value)
485
+ end
486
+
487
+ it "returns false from key? if a #{key_type} key is not available" do
488
+ @store.key?(#{key1}).should == false
489
+ end
490
+
491
+ it "returns nil from delete if an element for a #{key_type} key does not exist" do
492
+ @store.delete(#{key1}).should == nil
493
+ end
494
+
495
+ it "removes all #{key_type} keys from the store with clear" do
496
+ @store[#{key1}] = #{val1}
497
+ @store[#{key2}] = #{val2}
498
+ @store.clear.should equal(@store)
499
+ @store.key?(#{key1}).should_not == true
500
+ @store.key?(#{key2}).should_not == true
501
+ end
502
+
503
+ it "fetches a #{key_type} key with a default value with fetch, if the key is not available" do
504
+ @store.fetch(#{key1}, #{val1}).should == #{val1}
505
+ end
506
+
507
+ it "fetches a #{key_type} key with a block with fetch, if the key is not available" do
508
+ key = #{key1}
509
+ value = #{val1}
510
+ @store.fetch(key) do |k|
511
+ k.should equal(key)
512
+ value
513
+ end.should equal(value)
514
+ end
515
+
516
+ it 'should accept options' do
517
+ @store.key?(#{key1}, :option1 => 1).should == false
518
+ @store.load(#{key1}, :option2 => 2).should == nil
519
+ @store.fetch(#{key1}, nil, :option3 => 3).should == nil
520
+ @store.delete(#{key1}, :option4 => 4).should == nil
521
+ @store.clear(:option5 => 5).should equal(@store)
522
+ @store.store(#{key1}, #{val1}, :option6 => 6).should == #{val1}
523
+ end}
524
+ SPECS["null_#{key_type.downcase}key_#{val_type.downcase}value"] = code
525
+
526
+ code = %{it "writes #{val_type} values to keys that are #{key_type}s like a Hash" do
527
+ @store[#{key1}] = #{val1}
528
+ @store[#{key1}].should == #{val1}
529
+ @store.load(#{key1}).should == #{val1}
530
+ end
531
+
532
+ it "returns true from key? if a #{key_type} key is available" do
533
+ @store[#{key1}] = #{val1}
534
+ @store.key?(#{key1}).should == true
535
+ end
536
+
537
+ it "stores #{val_type} values with #{key_type} keys with #store" do
538
+ value = #{val1}
539
+ @store.store(#{key1}, value).should equal(value)
540
+ @store[#{key1}].should == #{val1}
541
+ @store.load(#{key1}).should == #{val1}
542
+ end
543
+
544
+ it "removes and returns a #{val_type} element with a #{key_type} key from the backing store via delete if it exists" do
545
+ @store[#{key1}] = #{val1}
546
+ @store.delete(#{key1}).should == #{val1}
547
+ @store.key?(#{key1}).should == false
548
+ end
549
+
550
+ it "does not run the block if the #{key_type} key is available" do
551
+ @store[#{key1}] = #{val1}
552
+ unaltered = "unaltered"
553
+ @store.fetch(#{key1}) { unaltered = "altered" }
554
+ unaltered.should == "unaltered"
555
+ end
556
+
557
+ it "fetches a #{key_type} key with a default value with fetch, if the key is available" do
558
+ @store[#{key1}] = #{val1}
559
+ @store.fetch(#{key1}, #{val2}).should == #{val1}
560
+ end}
561
+ SPECS["store_#{key_type.downcase}key_#{val_type.downcase}value"] = code
562
+
563
+ code = %{it "guarantees that a different #{val_type} value is retrieved from the #{key_type} key" do
564
+ value = #{val1}
565
+ @store[#{key1}] = #{val1}
566
+ @store[#{key1}].should_not be_equal(#{val1})
567
+ end}
568
+ SPECS["returndifferent_#{key_type.downcase}key_#{val_type.downcase}value"] = code
569
+
570
+ code = %{it 'should support expires on store and #[]' do
571
+ @store.store(#{key1}, #{val1}, :expires => 2)
572
+ @store[#{key1}].should == #{val1}
573
+ sleep 1
574
+ @store[#{key1}].should == #{val1}
575
+ sleep 2
576
+ @store[#{key1}].should == nil
577
+ end
578
+
579
+ it 'should support expires on store and load' do
580
+ @store.store(#{key1}, #{val1}, :expires => 2)
581
+ @store.load(#{key1}).should == #{val1}
582
+ sleep 1
583
+ @store.load(#{key1}).should == #{val1}
584
+ sleep 2
585
+ @store.load(#{key1}).should == nil
586
+ end
587
+
588
+ it 'should support expires on store and key?' do
589
+ @store.store(#{key1}, #{val1}, :expires => 2)
590
+ @store.key?(#{key1}).should == true
591
+ sleep 1
592
+ @store.key?(#{key1}).should == true
593
+ sleep 2
594
+ @store.key?(#{key1}).should == false
595
+ end
596
+
597
+ it 'should support updating the expiration time in load' do
598
+ @store.store(#{key2}, #{val2}, :expires => 2)
599
+ @store[#{key2}].should == #{val2}
600
+ sleep 1
601
+ @store.load(#{key2}, :expires => 3).should == #{val2}
602
+ @store[#{key2}].should == #{val2}
603
+ sleep 1
604
+ @store[#{key2}].should == #{val2}
605
+ sleep 3
606
+ @store[#{key2}].should == nil
607
+ end
608
+
609
+ it 'should support updating the expiration time in fetch' do
610
+ @store.store(#{key1}, #{val1}, :expires => 2)
611
+ @store[#{key1}].should == #{val1}
612
+ sleep 1
613
+ @store.fetch(#{key1}, nil, :expires => 3).should == #{val1}
614
+ @store[#{key1}].should == #{val1}
615
+ sleep 1
616
+ @store[#{key1}].should == #{val1}
617
+ sleep 3
618
+ @store[#{key1}].should == nil
619
+ end
620
+
621
+ it 'should respect expires in delete' do
622
+ @store.store(#{key2}, #{val2}, :expires => 2)
623
+ @store[#{key2}].should == #{val2}
624
+ sleep 1
625
+ @store[#{key2}].should == #{val2}
626
+ sleep 2
627
+ @store.delete(#{key2}).should == nil
628
+ end}
629
+ SPECS["expires_#{key_type.downcase}key_#{val_type.downcase}value"] = code
630
+
631
+ end
632
+ end
633
+
634
+ SPECS["marshallable_key"] = %{it "refuses to #[] from keys that cannot be marshalled" do
635
+ expect do
636
+ @store[Struct.new(:foo).new(:bar)]
637
+ end.to raise_error(marshal_error)
638
+ end
639
+
640
+ it "refuses to load from keys that cannot be marshalled" do
641
+ expect do
642
+ @store.load(Struct.new(:foo).new(:bar))
643
+ end.to raise_error(marshal_error)
644
+ end
645
+
646
+ it "refuses to fetch from keys that cannot be marshalled" do
647
+ expect do
648
+ @store.fetch(Struct.new(:foo).new(:bar), true)
649
+ end.to raise_error(marshal_error)
650
+ end
651
+
652
+ it "refuses to #[]= to keys that cannot be marshalled" do
653
+ expect do
654
+ @store[Struct.new(:foo).new(:bar)] = 'value'
655
+ end.to raise_error(marshal_error)
656
+ end
657
+
658
+ it "refuses to store to keys that cannot be marshalled" do
659
+ expect do
660
+ @store.store Struct.new(:foo).new(:bar), 'value'
661
+ end.to raise_error(marshal_error)
662
+ end
663
+
664
+ it "refuses to check for key? if the key cannot be marshalled" do
665
+ expect do
666
+ @store.key? Struct.new(:foo).new(:bar)
667
+ end.to raise_error(marshal_error)
668
+ end
669
+
670
+ it "refuses to delete a key if the key cannot be marshalled" do
671
+ expect do
672
+ @store.delete Struct.new(:foo).new(:bar)
673
+ end.to raise_error(marshal_error)
674
+ end}
675
+
676
+ specs_code = ''
677
+ SPECS.each do |key, code|
678
+ specs_code << "#################### #{key} ####################\n\n" <<
679
+ "shared_examples_for '#{key}' do\n " << code.gsub("\n", "\n ") << "\nend\n\n"
680
+ end
681
+ specs_code.gsub!(/\n +\n/, "\n\n")
682
+ File.open(File.join(File.dirname(__FILE__), "junospecs.rb"), 'w') {|out| out << specs_code }
683
+
684
+ TESTS.each do |name, options|
685
+ build = options.delete(:build)
686
+ store = options.delete(:store)
687
+ key = [options.delete(:key) || %w(Object String Hash)].flatten
688
+ value = [options.delete(:value) || %w(Object String Hash)].flatten
689
+
690
+ specs = [options.delete(:specs) || SIMPLE_SPECS].flatten
691
+ specs_code = ''
692
+ specs.each do |s|
693
+ specs_code << " it_should_behave_like '#{s}'\n" if SPECS[s.to_s]
694
+ key.each do |k|
695
+ value.each do |v|
696
+ x = "#{s}_#{k.downcase}key_#{v.downcase}value"
697
+ specs_code << " it_should_behave_like '#{x}'\n" if SPECS[x]
698
+ end
699
+ end
700
+ end
701
+
702
+ preamble = options.delete(:preamble).to_s.gsub("\n", "\n ")
703
+ opts = options.delete(:options)
704
+ opts = ', ' << opts if opts
705
+
706
+ build ||= "Juno.new(#{store.inspect}#{opts})"
707
+
708
+ code = %{# Generated file
709
+ require 'helper'
710
+
711
+ begin
712
+ #{preamble}#{build}.close
713
+
714
+ describe #{name.inspect} do
715
+ before do
716
+ @store = #{build}
717
+ @store.clear
718
+ end
719
+
720
+ after do
721
+ @store.close.should == nil if @store
722
+ end
723
+
724
+ #{specs_code}#{options[:tests].to_s.gsub("\n", "\n ")}
725
+ end
726
+ rescue LoadError => ex
727
+ puts "Test #{name} not executed: \#{ex.message}"
728
+ rescue Exception => ex
729
+ puts "Test #{name} not executed: \#{ex.message}"
730
+ #puts "\#{ex.backtrace.join("\\n")}"
731
+ end
732
+ }
733
+
734
+ code.gsub!(/\n +\n/, "\n\n")
735
+ File.open(File.join(File.dirname(__FILE__), "#{name}_spec.rb"), 'w') {|out| out << code }
736
+ end