juno 0.1.1 → 0.2.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 (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