moneta 0.7.17 → 0.7.18

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 (135) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +1 -0
  3. data/.travis.yml +15 -9
  4. data/CHANGES +12 -1
  5. data/CONTRIBUTORS +2 -2
  6. data/Gemfile +3 -0
  7. data/README.md +30 -5
  8. data/Rakefile +1 -1
  9. data/lib/active_support/cache/moneta_store.rb +5 -2
  10. data/lib/moneta/adapters/couch.rb +63 -5
  11. data/lib/moneta/adapters/file.rb +29 -12
  12. data/lib/moneta/adapters/fog.rb +1 -0
  13. data/lib/moneta/adapters/mongo.rb +74 -22
  14. data/lib/moneta/adapters/sqlite.rb +6 -5
  15. data/lib/moneta/pool.rb +28 -5
  16. data/lib/moneta/transformer/config.rb +37 -34
  17. data/lib/moneta/version.rb +1 -1
  18. data/script/contributors +7 -0
  19. data/script/generate-specs +89 -30
  20. data/script/install-bundle +1 -1
  21. data/script/start-services +6 -12
  22. data/script/wait-services +15 -0
  23. data/spec/active_support/cache_moneta_store_spec.rb +3 -3
  24. data/spec/moneta/adapter_couch_spec.rb +14 -6
  25. data/spec/moneta/adapter_memory_spec.rb +29 -0
  26. data/spec/moneta/adapter_mongo_spec.rb +7 -0
  27. data/spec/moneta/adapter_mongo_with_default_expires_spec.rb +7 -0
  28. data/spec/moneta/adapter_pstore_spec.rb +42 -0
  29. data/spec/moneta/expires_file_spec.rb +4 -0
  30. data/spec/moneta/expires_memory_spec.rb +29 -0
  31. data/spec/moneta/expires_memory_with_default_expires_spec.rb +29 -0
  32. data/spec/moneta/lock_spec.rb +29 -0
  33. data/spec/moneta/null_adapter_spec.rb +13 -0
  34. data/spec/moneta/proxy_expires_memory_spec.rb +29 -0
  35. data/spec/moneta/simple_activerecord_spec.rb +42 -0
  36. data/spec/moneta/simple_activerecord_with_expires_spec.rb +42 -0
  37. data/spec/moneta/simple_cassandra_spec.rb +42 -0
  38. data/spec/moneta/simple_client_tcp_spec.rb +42 -0
  39. data/spec/moneta/simple_client_unix_spec.rb +42 -0
  40. data/spec/moneta/simple_couch_spec.rb +45 -2
  41. data/spec/moneta/simple_couch_with_expires_spec.rb +46 -2
  42. data/spec/moneta/simple_datamapper_spec.rb +42 -0
  43. data/spec/moneta/simple_datamapper_with_expires_spec.rb +42 -0
  44. data/spec/moneta/simple_datamapper_with_repository_spec.rb +42 -0
  45. data/spec/moneta/simple_daybreak_spec.rb +42 -0
  46. data/spec/moneta/simple_daybreak_with_expires_spec.rb +42 -0
  47. data/spec/moneta/simple_dbm_spec.rb +42 -0
  48. data/spec/moneta/simple_dbm_with_expires_spec.rb +42 -0
  49. data/spec/moneta/simple_file_spec.rb +42 -0
  50. data/spec/moneta/simple_file_with_expires_spec.rb +42 -0
  51. data/spec/moneta/simple_fog_spec.rb +42 -0
  52. data/spec/moneta/simple_fog_with_expires_spec.rb +42 -0
  53. data/spec/moneta/simple_gdbm_spec.rb +42 -0
  54. data/spec/moneta/simple_gdbm_with_expires_spec.rb +42 -0
  55. data/spec/moneta/simple_hashfile_spec.rb +42 -0
  56. data/spec/moneta/simple_hashfile_with_expires_spec.rb +42 -0
  57. data/spec/moneta/simple_hbase_spec.rb +42 -0
  58. data/spec/moneta/simple_hbase_with_expires_spec.rb +42 -0
  59. data/spec/moneta/simple_kyotocabinet_spec.rb +42 -0
  60. data/spec/moneta/simple_kyotocabinet_with_expires_spec.rb +42 -0
  61. data/spec/moneta/simple_leveldb_spec.rb +42 -0
  62. data/spec/moneta/simple_leveldb_with_expires_spec.rb +42 -0
  63. data/spec/moneta/simple_localmemcache_spec.rb +42 -0
  64. data/spec/moneta/simple_localmemcache_with_expires_spec.rb +42 -0
  65. data/spec/moneta/simple_lruhash_spec.rb +29 -0
  66. data/spec/moneta/simple_lruhash_with_expires_spec.rb +29 -0
  67. data/spec/moneta/simple_memcached_dalli_spec.rb +42 -0
  68. data/spec/moneta/simple_memcached_native_spec.rb +42 -0
  69. data/spec/moneta/simple_memcached_spec.rb +42 -0
  70. data/spec/moneta/simple_memory_spec.rb +29 -0
  71. data/spec/moneta/simple_memory_with_compress_spec.rb +29 -0
  72. data/spec/moneta/simple_memory_with_expires_spec.rb +29 -0
  73. data/spec/moneta/simple_memory_with_json_key_serializer_spec.rb +6 -0
  74. data/spec/moneta/simple_memory_with_json_value_serializer_spec.rb +8 -0
  75. data/spec/moneta/simple_memory_with_prefix_spec.rb +29 -0
  76. data/spec/moneta/simple_memory_with_snappy_compress_spec.rb +29 -0
  77. data/spec/moneta/simple_mongo_spec.rb +42 -0
  78. data/spec/moneta/simple_null_spec.rb +16 -0
  79. data/spec/moneta/simple_pstore_spec.rb +42 -0
  80. data/spec/moneta/simple_pstore_with_expires_spec.rb +42 -0
  81. data/spec/moneta/simple_redis_spec.rb +42 -0
  82. data/spec/moneta/simple_restclient_spec.rb +42 -0
  83. data/spec/moneta/simple_riak_spec.rb +42 -0
  84. data/spec/moneta/simple_riak_with_expires_spec.rb +42 -0
  85. data/spec/moneta/simple_sdbm_spec.rb +42 -0
  86. data/spec/moneta/simple_sdbm_with_expires_spec.rb +42 -0
  87. data/spec/moneta/simple_sequel_spec.rb +42 -0
  88. data/spec/moneta/simple_sequel_with_expires_spec.rb +42 -0
  89. data/spec/moneta/simple_sqlite_spec.rb +42 -0
  90. data/spec/moneta/simple_sqlite_with_expires_spec.rb +42 -0
  91. data/spec/moneta/simple_tdb_spec.rb +42 -0
  92. data/spec/moneta/simple_tdb_with_expires_spec.rb +42 -0
  93. data/spec/moneta/simple_tokyocabinet_spec.rb +42 -0
  94. data/spec/moneta/simple_tokyocabinet_with_expires_spec.rb +42 -0
  95. data/spec/moneta/simple_tokyotyrant_spec.rb +42 -0
  96. data/spec/moneta/simple_tokyotyrant_with_expires_spec.rb +42 -0
  97. data/spec/moneta/simple_yaml_spec.rb +42 -0
  98. data/spec/moneta/simple_yaml_with_expires_spec.rb +42 -0
  99. data/spec/moneta/transformer_bzip2_spec.rb +3 -0
  100. data/spec/moneta/transformer_key_inspect_spec.rb +6 -0
  101. data/spec/moneta/transformer_key_marshal_spec.rb +29 -0
  102. data/spec/moneta/transformer_key_to_s_spec.rb +6 -0
  103. data/spec/moneta/transformer_key_yaml_spec.rb +29 -0
  104. data/spec/moneta/transformer_lz4_spec.rb +3 -0
  105. data/spec/moneta/transformer_lzma_spec.rb +3 -0
  106. data/spec/moneta/transformer_lzo_spec.rb +3 -0
  107. data/spec/moneta/transformer_marshal_base64_spec.rb +29 -0
  108. data/spec/moneta/transformer_marshal_city128_spec.rb +152 -0
  109. data/spec/moneta/transformer_marshal_city32_spec.rb +152 -0
  110. data/spec/moneta/transformer_marshal_city64_spec.rb +152 -0
  111. data/spec/moneta/transformer_marshal_escape_spec.rb +29 -0
  112. data/spec/moneta/transformer_marshal_hmac_spec.rb +29 -0
  113. data/spec/moneta/transformer_marshal_md5_spec.rb +29 -0
  114. data/spec/moneta/transformer_marshal_md5_spread_spec.rb +29 -0
  115. data/spec/moneta/transformer_marshal_prefix_spec.rb +29 -0
  116. data/spec/moneta/transformer_marshal_qp_spec.rb +29 -0
  117. data/spec/moneta/transformer_marshal_rmd160_spec.rb +29 -0
  118. data/spec/moneta/transformer_marshal_sha1_spec.rb +29 -0
  119. data/spec/moneta/transformer_marshal_sha256_spec.rb +29 -0
  120. data/spec/moneta/transformer_marshal_sha384_spec.rb +29 -0
  121. data/spec/moneta/transformer_marshal_sha512_spec.rb +29 -0
  122. data/spec/moneta/transformer_marshal_spec.rb +29 -0
  123. data/spec/moneta/transformer_marshal_truncate_spec.rb +29 -0
  124. data/spec/moneta/transformer_marshal_uuencode_spec.rb +29 -0
  125. data/spec/moneta/transformer_ox_spec.rb +29 -0
  126. data/spec/moneta/transformer_quicklz_spec.rb +3 -0
  127. data/spec/moneta/transformer_snappy_spec.rb +3 -0
  128. data/spec/moneta/transformer_value_marshal_spec.rb +29 -0
  129. data/spec/moneta/transformer_value_yaml_spec.rb +29 -0
  130. data/spec/moneta/transformer_yaml_spec.rb +29 -0
  131. data/spec/moneta/transformer_zlib_spec.rb +3 -0
  132. data/spec/moneta/weak_create_spec.rb +2 -1
  133. data/spec/moneta/weak_increment_spec.rb +2 -1
  134. data/spec/monetaspecs.rb +29879 -6167
  135. metadata +10 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: f0fbb029ab09dd2b73f2b41c9019956b0dee395e
4
- data.tar.gz: 7f5c47a13a9957dc1508b2b95d4aee866809c8b9
3
+ metadata.gz: 40e5bf4f97796468cef521c2b103f72d7be29811
4
+ data.tar.gz: d9ab4368e38146049f4e92a0cf2337b2148fcf10
5
5
  SHA512:
6
- metadata.gz: 4fa4a788c787c8750b824c28229757abf3f7cbedabea7e117108bdce6b0f4d540c7f5a61c51b4bfd2195d92c06b43d390695bac8a41192434ae65fb62b0b3984
7
- data.tar.gz: 2b3a44cb243a4e82764727461cec52597b3a0d71be81fd637042bbebcc6efc810502a0d8328bb13e0ab78293dab56d3eb10fdb738ae6c246f067f94ad2650c27
6
+ metadata.gz: 2fadbaee29f91f2259b44cf7140d64e3ab12196ee2e29c6c9ccc5db2518b0a06a4db0d81f654e7625dceaa3ead6a7636aeb2240d9cff0bd17161b2f1865a6f19
7
+ data.tar.gz: f1751faff2cef9302fa0b3125e421e6632fa580978ab548fb13afd53f7dc37fe0e586821a657b3c79298a44b9ba66ccf68edacbf6efcc89e3b134176dfa2b7c9
data/.gitignore CHANGED
@@ -9,3 +9,4 @@ spec/tmp
9
9
  script/benchmarks.tmp
10
10
  Gemfile.lock
11
11
  logs
12
+ secure
@@ -5,15 +5,16 @@ rvm:
5
5
  - 1.8.7
6
6
  - jruby-19mode
7
7
  - jruby-18mode
8
- # - rbx-19mode
9
- # - rbx-18mode
8
+ - rbx-19mode
9
+ - rbx-18mode
10
10
  before_install:
11
11
  - script/kill-travis
12
12
  #- script/install-kyotocabinet
13
- - sudo apt-get install -qq libtokyocabinet8 libtokyocabinet-dev liblzo2-dev libtdb-dev tokyotyrant
13
+ - sudo apt-get install -qq libtokyocabinet8 libtokyocabinet-dev liblzo2-dev libtdb-dev libleveldb-dev tokyotyrant
14
14
  - script/start-services
15
15
  - script/install-bundle
16
16
  - script/upload-bundle
17
+ - script/wait-services
17
18
  install: 'echo "Bundle installed"'
18
19
  before_script:
19
20
  - mysql -e 'create database moneta;'
@@ -21,13 +22,18 @@ before_script:
21
22
  - mysql -e 'create database moneta_activerecord2;'
22
23
  env:
23
24
  global:
24
- - secure: "B0vx1g1CB1A6mM3B/iy2ATicfS4OXT80bb2RVe8mSRsPzez1B4q4Q4hJcaMI\nrMARONN8Krtnti+IqvmDnB0Z0AKYMEyIc+zT37zJOCjLdkLJl+x/thuU/MbC\nvlLVwjMf6JE2EUzTfORDRFYc5ycCqfsfgNk1Go0D2CPT6P9u9uQ="
25
+ - secure: "dtM4n7FP8P0UI9Iq+nsvQ7/yfDqsxhfCO9i8zMxm/f9Kxj5Z/4C7jsXsLA+e\n/7FZ9+ld2QjPSPU0LUiDpj/z81bxyZHwqocQ7Nb0DVvO3JRHpr4/iBQQQHd3\n0jvou3mRbu5mBlUjf1/ALaZA+b+vcnsF9fd86UnkY+ChriylGnM="
25
26
  matrix:
26
- - "TASK=test TEST_GROUP=1/5"
27
- - "TASK=test TEST_GROUP=2/5"
28
- - "TASK=test TEST_GROUP=3/5"
29
- - "TASK=test TEST_GROUP=4/5"
30
- - "TASK=test TEST_GROUP=5/5"
27
+ - "TASK=test TEST_GROUP=1/10"
28
+ - "TASK=test TEST_GROUP=2/10"
29
+ - "TASK=test TEST_GROUP=3/10"
30
+ - "TASK=test TEST_GROUP=4/10"
31
+ - "TASK=test TEST_GROUP=5/10"
32
+ - "TASK=test TEST_GROUP=6/10"
33
+ - "TASK=test TEST_GROUP=7/10"
34
+ - "TASK=test TEST_GROUP=8/10"
35
+ - "TASK=test TEST_GROUP=9/10"
36
+ - "TASK=test TEST_GROUP=10/10"
31
37
  - "TASK=test TEST_GROUP=unstable"
32
38
  - "TASK=benchmarks CONFIG=uniform_small"
33
39
  - "TASK=benchmarks CONFIG=uniform_medium"
data/CHANGES CHANGED
@@ -1,4 +1,15 @@
1
- master
1
+ 0.7.18
2
+
3
+ * Adapters::File#increment and #create fixed on JRuby
4
+ * Adapters::Couch and Adapters::Mongo can store hashes directly as documents.
5
+ It is not necessary to serialize values as strings anymore.
6
+ * Adapters::Couch#create added
7
+ * Pool thread safety improved
8
+ * Transformer: Add CityHash
9
+
10
+ 0.7.17
11
+
12
+ * Transformer: LZ4 compression added
2
13
 
3
14
  0.7.16
4
15
 
@@ -1,7 +1,6 @@
1
1
  Adrian Madrid <aemadrid@gmail.com>
2
2
  Alejandro Crosa <acrosa@sharing.local>
3
3
  Anthony Eden <anthonyeden@gmail.com>
4
- Hendrik Beskow <hendrik-github@beskow.de>
5
4
  Benjamin Yu <benjaminlyu@gmail.com>
6
5
  Ben Schwarz <ben.schwarz@gmail.com>
7
6
  Daniel Mendler <mail@daniel-mendler.de>
@@ -9,7 +8,7 @@ Derek Kastner <dkastner@gmail.com>
9
8
  Dylan Egan <me@dylanegan.com>
10
9
  Hampton Catlin <hcatlin@gmail.com>
11
10
  Hannes Georg <hannes.georg@googlemail.com>
12
- Scott Wadden <scott.wadden@gmail.com>
11
+ Hendrik Beskow <hendrik-github@beskow.de>
13
12
  Jari Bakken <jari.bakken@gmail.com>
14
13
  Jeremy Voorhis <jvoorhis@gmail.com>
15
14
  Jon Crosby <jon@joncrosby.me>
@@ -17,6 +16,7 @@ lakshan <lakshan@web2media.net>
17
16
  Piotr Murach <pmurach@gmail.com>
18
17
  Potapov Sergey <blake131313@gmail.com>
19
18
  Quin Hoxie <quin@aboutus.org>
19
+ Scott Wadden <scott.wadden@gmail.com>
20
20
  Tom Meier <ozmeier@yahoo.co.uk>
21
21
  Xavier Shay <xavier@rhnh.net>
22
22
  Yehuda Katz <wycats@gmail.com>
data/Gemfile CHANGED
@@ -27,6 +27,9 @@ gem 'lzoruby', :platforms => :ruby
27
27
  gem 'snappy', :platforms => :ruby
28
28
  gem 'qlzruby', :platforms => :ruby
29
29
 
30
+ # Hash transformer library
31
+ gem 'cityhash', :platforms => :ruby
32
+
30
33
  # Backends
31
34
  gem 'faraday'
32
35
  gem 'daybreak'
data/README.md CHANGED
@@ -2,7 +2,10 @@
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/moneta.png)](http://rubygems.org/gems/moneta) [![Build Status](https://secure.travis-ci.org/minad/moneta.png?branch=master)](http://travis-ci.org/minad/moneta) [![Dependency Status](https://gemnasium.com/minad/moneta.png?travis)](https://gemnasium.com/minad/moneta) [![Code Climate](https://codeclimate.com/github/minad/moneta.png)](https://codeclimate.com/github/minad/moneta)
4
4
 
5
- Moneta provides a standard interface for interacting with various kinds of key/value stores. A short overview of the features:
5
+ Moneta provides a standard interface for interacting with various kinds of key/value stores. Moneta supports the well-known
6
+ NoSQL and document based stores.
7
+
8
+ A short overview of the features:
6
9
 
7
10
  * Supports a lot of backends with consistent behaviour (See below)
8
11
  * Allows a full configuration of the serialization -> compression -> adapter stack using proxies (Similar to [Rack middlewares](http://rack.github.com/))
@@ -170,14 +173,14 @@ __NOTE:__ <a name="backend-matrix">The backend matrix</a> is much more readable
170
173
 
171
174
  <tr><td>DataMapper</td><td>dm-core, dm-migrations</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#5F5">✓</td><td><a href="http://datamapper.org/">DataMapper</a> ORM</td></tr>
172
175
 
176
+ <tr><td>Couch</td><td>faraday, multi_json</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#5F5">✓</td><td><a href="http://couchdb.apache.org/">CouchDB</a> database</td></tr>
177
+
173
178
  <tr><td>HBase</td><td>hbaserb</td><td style="text-align:center;background:#55F">?</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#5F5">✓</td><td><a href="http://hbase.apache.org/">HBase</a> database</td></tr>
174
179
 
175
180
  <tr><td>Cassandra</td><td>cassandra</td><td style="text-align:center;background:#55F">?</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td><a href="http://cassandra.apache.org/">Cassandra</a> distributed database</td></tr>
176
181
 
177
182
  <tr><td>LocalMemCache</td><td>localmemcache</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#5F5">✓</td><td><a href="http://localmemcache.rubyforge.org/">LocalMemCache</a> database</td></tr>
178
183
 
179
- <tr><td>Couch</td><td>faraday, multi_json</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#5F5">✓</td><td><a href="http://couchdb.apache.org/">CouchDB</a> database</td></tr>
180
-
181
184
  <tr><td>Fog</td><td>fog</td><td style="text-align:center;background:#55F">?</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#5F5">✓</td><td><a href="http://fog.io/">Fog</a> cloud store</td></tr>
182
185
 
183
186
  <tr><td>Riak</td><td>riak-client</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#5F5">✓</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#F44">✗</td><td style="text-align:center;background:#5F5">✓</td><td><a href="http://docs.basho.com/">Riak</a> database</td></tr>
@@ -258,7 +261,7 @@ Supported value compressors:
258
261
 
259
262
  Special transformers:
260
263
 
261
- * Digests (MD5, Shas, ...)
264
+ * Digests (MD5, Shas, CityHash, ...)
262
265
  * Add prefix to keys (`:prefix`)
263
266
  * HMAC to verify values (`:hmac`, useful for `Rack::MonetaCookies`)
264
267
 
@@ -312,10 +315,16 @@ Moneta does not support iteration or partial matches.
312
315
 
313
316
  ### Creating a Store
314
317
 
315
- There is a simple interface to create a store using `Moneta.new`:
318
+ There is a simple interface to create a store using `Moneta.new`. You will
319
+ get automatic key and value serialization which is provided by `Moneta::Transformer`.
320
+ This allows you to store arbitrary Ruby objects. You can tune some options
321
+ when you call `Moneta.new`. However for very fine tuning use `Moneta.build`.
316
322
 
317
323
  ~~~ ruby
318
324
  store = Moneta.new(:Memcached, :server => 'localhost:11211')
325
+ store['key'] = 'value'
326
+ store['hash_key'] = {:a => 1, :b => 2}
327
+ store['object_key'] = MarshallableRubyObject.new
319
328
  ~~~
320
329
 
321
330
  If you want to have control over the proxies, you have to use `Moneta.build`:
@@ -335,6 +344,22 @@ store = Moneta.build do
335
344
  end
336
345
  ~~~
337
346
 
347
+ You can also directly access the underlying adapters if you don't want
348
+ to use the Moneta stack.
349
+
350
+ ~~~ ruby
351
+ db = Moneta::Adapters::File.new(:dir => 'directory')
352
+ db['key'] = {:a => 1, :b => 2} # This will fail since you can only store Strings
353
+
354
+ # However for Mongo and Couch this works
355
+ # The hash will be mapped directly to a Mongo/Couch document.
356
+ db = Moneta::Adapters::Couch.new
357
+ db['key'] = {:a => 1, :b => 2}
358
+
359
+ db = Moneta::Adapters::Mongo.new
360
+ db['key'] = {:a => 1, :b => 2}
361
+ ~~~
362
+
338
363
  ### Expiration
339
364
 
340
365
  The Cassandra, Memcached, Redis and Mongo backends support expiration natively.
data/Rakefile CHANGED
@@ -82,7 +82,7 @@ task :test do
82
82
  end
83
83
  end
84
84
  sleep 0.1
85
- sleep 0.1 while threads.size >= 10
85
+ sleep 0.1 while threads.size >= 5
86
86
  end
87
87
  sleep 0.1 until threads.empty?
88
88
  if failed
@@ -10,18 +10,21 @@ module ActiveSupport
10
10
  end
11
11
 
12
12
  def increment(key, amount = 1, options = nil)
13
+ options = merged_options(options)
13
14
  instrument(:increment, key, :amount => amount) do
14
- @store.increment(key, amount, moneta_options(options))
15
+ @store.increment(namespaced_key(key, options), amount, moneta_options(options))
15
16
  end
16
17
  end
17
18
 
18
19
  def decrement(key, amount = 1, options = nil)
20
+ options = merged_options(options)
19
21
  instrument(:decrement, key, :amount => amount) do
20
- @store.increment(key, -amount, moneta_options(options))
22
+ @store.increment(namespaced_key(key, options), -amount, moneta_options(options))
21
23
  end
22
24
  end
23
25
 
24
26
  def clear(options = nil)
27
+ options = merged_options(options)
25
28
  instrument(:clear, nil, nil) do
26
29
  @store.clear(moneta_options(options))
27
30
  end
@@ -4,18 +4,31 @@ require 'multi_json'
4
4
  module Moneta
5
5
  module Adapters
6
6
  # CouchDB backend
7
+ #
8
+ # You can store hashes directly using this adapter.
9
+ #
10
+ # @example Store hashes
11
+ # db = Moneta::Adapters::Mongo.new
12
+ # db['key'] = {a: 1, b: 2}
13
+ #
7
14
  # @api public
8
15
  class Couch
9
16
  include Defaults
10
17
 
11
18
  attr_reader :backend
12
19
 
20
+ supports :create
21
+
13
22
  # @param [Hash] options
14
23
  # @option options [String] :host ('127.0.0.1') Couch host
15
24
  # @option options [String] :port (5984) Couch port
16
25
  # @option options [String] :db ('moneta') Couch database
26
+ # @option options [String] :value_field ('value') Document field to store value
27
+ # @option options [String] :type_field ('type') Document field to store value type
17
28
  # @option options [Faraday connection] :backend Use existing backend instance
18
29
  def initialize(options = {})
30
+ @value_field = options[:value_field] || 'value'
31
+ @type_field = options[:type_field] || 'type'
19
32
  url = "http://#{options[:host] || '127.0.0.1'}:#{options[:port] || 5984}/#{options[:db] || 'moneta'}"
20
33
  @backend = options[:backend] || ::Faraday.new(:url => url)
21
34
  create_db
@@ -29,15 +42,14 @@ module Moneta
29
42
  # (see Proxy#load)
30
43
  def load(key, options = {})
31
44
  response = @backend.get(key)
32
- response.status == 200 ? MultiJson.load(response.body)['value'] : nil
45
+ response.status == 200 ? body_to_value(response.body) : nil
33
46
  end
34
47
 
35
48
  # (see Proxy#store)
36
49
  def store(key, value, options = {})
37
50
  response = @backend.head(key)
38
- doc = { 'value' => value }
39
- doc['_rev'] = response['etag'][1..-2] if response.status == 200
40
- response = @backend.put(key, MultiJson.dump(doc), 'Content-Type' => 'application/json')
51
+ body = value_to_body(value, response.status == 200 && response['etag'][1..-2])
52
+ response = @backend.put(key, body, 'Content-Type' => 'application/json')
41
53
  raise "HTTP error #{response.status}" unless response.status == 201
42
54
  value
43
55
  rescue
@@ -49,7 +61,7 @@ module Moneta
49
61
  def delete(key, options = {})
50
62
  response = @backend.get(key)
51
63
  if response.status == 200
52
- value = MultiJson.load(response.body)['value']
64
+ value = body_to_value(response.body)
53
65
  response = @backend.delete("#{key}?rev=#{response['etag'][1..-2]}")
54
66
  raise "HTTP error #{response.status}" unless response.status == 200
55
67
  value
@@ -66,8 +78,54 @@ module Moneta
66
78
  self
67
79
  end
68
80
 
81
+ # (see Proxy#create)
82
+ def create(key, value, options = {})
83
+ body = value_to_body(value, nil)
84
+ response = @backend.put(key, body, 'Content-Type' => 'application/json')
85
+ case response.status
86
+ when 201
87
+ true
88
+ when 409
89
+ false
90
+ else
91
+ raise "HTTP error #{response.status}"
92
+ end
93
+ rescue
94
+ tries ||= 0
95
+ (tries += 1) < 10 ? retry : raise
96
+ end
97
+
69
98
  private
70
99
 
100
+ def body_to_value(body)
101
+ doc = MultiJson.load(body)
102
+ case doc[@type_field]
103
+ when 'Hash'
104
+ doc = doc.dup
105
+ doc.delete('_id')
106
+ doc.delete('_rev')
107
+ doc.delete(@type_field)
108
+ doc
109
+ else
110
+ doc[@value_field]
111
+ end
112
+ end
113
+
114
+ def value_to_body(value, rev)
115
+ case value
116
+ when Hash
117
+ doc = value.merge(@type_field => 'Hash')
118
+ when String
119
+ doc = { @value_field => value, @type_field => 'String' }
120
+ when Float, Fixnum
121
+ doc = { @value_field => value, @type_field => 'Number' }
122
+ else
123
+ raise ArgumentError, "Invalid value type: #{value.class}"
124
+ end
125
+ doc['_rev'] = rev if rev
126
+ MultiJson.dump(doc)
127
+ end
128
+
71
129
  def create_db
72
130
  response = @backend.put '', ''
73
131
  raise "HTTP error #{response.status}" unless response.status == 201 || response.status == 412
@@ -66,24 +66,41 @@ module Moneta
66
66
  FileUtils.mkpath(::File.dirname(path))
67
67
  ::File.open(path, ::File::RDWR | ::File::CREAT) do |f|
68
68
  Thread.pass until f.flock(::File::LOCK_EX)
69
- content = ::File.read(path)
69
+ content = f.read
70
70
  amount += Utils.to_int(content) unless content.empty?
71
- ::File.open(path, 'wb') {|o| o.write(amount.to_s) }
71
+ content = amount.to_s
72
+ f.pos = 0
73
+ f.write(content)
74
+ f.truncate(content.bytesize)
72
75
  amount
73
76
  end
74
77
  end
75
78
 
76
- # (see Proxy#create)
77
- def create(key, value, options = {})
78
- path = store_path(key)
79
- FileUtils.mkpath(::File.dirname(path))
80
- ::File.open(path, ::File::WRONLY | ::File::CREAT | ::File::EXCL) do |f|
81
- f.binmode
82
- f.write(value)
79
+ # HACK: The implementation using File::EXCL is not atomic under JRuby 1.7.4
80
+ # See https://github.com/jruby/jruby/issues/827
81
+ if defined?(JRUBY_VERSION)
82
+ # (see Proxy#create)
83
+ def create(key, value, options = {})
84
+ path = store_path(key)
85
+ FileUtils.mkpath(::File.dirname(path))
86
+ # Call native java.io.File#createNewFile
87
+ return false unless ::Java::JavaIo::File.new(path).createNewFile
88
+ ::File.open(path, 'wb+') {|f| f.write(value) }
89
+ true
90
+ end
91
+ else
92
+ # (see Proxy#create)
93
+ def create(key, value, options = {})
94
+ path = store_path(key)
95
+ FileUtils.mkpath(::File.dirname(path))
96
+ ::File.open(path, ::File::WRONLY | ::File::CREAT | ::File::EXCL) do |f|
97
+ f.binmode
98
+ f.write(value)
99
+ end
100
+ true
101
+ rescue Errno::EEXIST
102
+ false
83
103
  end
84
- true
85
- rescue Errno::EEXIST
86
- false
87
104
  end
88
105
 
89
106
  protected
@@ -41,6 +41,7 @@ module Moneta
41
41
 
42
42
  # (see Proxy#store)
43
43
  def store(key, value, options = {})
44
+ value = value.dup if value.frozen? # HACK: Fog needs unfrozen string
44
45
  @directory.files.create(options.merge(:key => key, :body => value))
45
46
  value
46
47
  end
@@ -7,6 +7,12 @@ module Moneta
7
7
  # Supports expiration, documents will be automatically removed starting
8
8
  # with mongodb >= 2.2 (see {http://docs.mongodb.org/manual/tutorial/expire-data/}).
9
9
  #
10
+ # You can store hashes directly using this adapter.
11
+ #
12
+ # @example Store hashes
13
+ # db = Moneta::Adapters::Mongo.new
14
+ # db['key'] = {a: 1, b: 2}
15
+ #
10
16
  # @api public
11
17
  class Mongo
12
18
  include Defaults
@@ -23,11 +29,18 @@ module Moneta
23
29
  # @option options [Integer] :port (MongoDB default port) MongoDB server port
24
30
  # @option options [String] :db ('moneta') MongoDB database
25
31
  # @option options [Integer] :expires Default expiration time
32
+ # @option options [String] :expires_field ('expiresAt') Document field to store expiration time
33
+ # @option options [String] :value_field ('value') Document field to store value
34
+ # @option options [String] :type_field ('type') Document field to store value type
26
35
  # @option options [::Mongo::MongoClient] :backend Use existing backend instance
36
+ # @option options Other options passed to `Mongo::MongoClient#new`
27
37
  def initialize(options = {})
28
38
  self.default_expires = options.delete(:expires)
29
39
  collection = options.delete(:collection) || 'moneta'
30
40
  db = options.delete(:db) || 'moneta'
41
+ @expires_field = options.delete(:expires_field) || 'expiresAt'
42
+ @value_field = options.delete(:value_field) || 'value'
43
+ @type_field = options.delete(:type_field) || 'type'
31
44
  @backend = options[:backend] ||
32
45
  begin
33
46
  host = options.delete(:host) || '127.0.0.1'
@@ -40,7 +53,7 @@ module Moneta
40
53
  db.authenticate(user, password, true) if user && password
41
54
  @collection = db.collection(collection)
42
55
  if @backend.server_version >= '2.2'
43
- @collection.ensure_index([['expiresAt', ::Mongo::ASCENDING]], :expireAfterSeconds => 0)
56
+ @collection.ensure_index([[@expires_field, ::Mongo::ASCENDING]], :expireAfterSeconds => 0)
44
57
  else
45
58
  warn 'Moneta::Adapters::Mongo - You are using MongoDB version < 2.2, expired documents will not be deleted'
46
59
  end
@@ -48,26 +61,22 @@ module Moneta
48
61
 
49
62
  # (see Proxy#load)
50
63
  def load(key, options = {})
51
- key = ::BSON::Binary.new(key)
64
+ key = to_binary(key)
52
65
  doc = @collection.find_one('_id' => key)
53
- if doc && (!doc['expiresAt'] || doc['expiresAt'] >= Time.now)
66
+ if doc && (!doc[@expires_field] || doc[@expires_field] >= Time.now)
54
67
  expires = expires_at(options, nil)
55
68
  @collection.update({ '_id' => key },
56
- # expiresAt must be a Time object (BSON date datatype)
57
- { '$set' => { 'expiresAt' => expires || nil } }) if expires != nil
58
- doc['value'].to_s
69
+ # @expires_field must be a Time object (BSON date datatype)
70
+ { '$set' => { @expires_field => expires || nil } }) if expires != nil
71
+ doc_to_value(doc)
59
72
  end
60
73
  end
61
74
 
62
75
  # (see Proxy#store)
63
76
  def store(key, value, options = {})
64
- key = ::BSON::Binary.new(key)
65
- intvalue = value.to_i
77
+ key = to_binary(key)
66
78
  @collection.update({ '_id' => key },
67
- { '_id' => key,
68
- 'value' => intvalue.to_s == value ? intvalue : ::BSON::Binary.new(value),
69
- # expiresAt must be a Time object (BSON date datatype)
70
- 'expiresAt' => expires_at(options) || nil },
79
+ value_to_doc(key, value, options),
71
80
  { :upsert => true })
72
81
  value
73
82
  end
@@ -75,26 +84,22 @@ module Moneta
75
84
  # (see Proxy#delete)
76
85
  def delete(key, options = {})
77
86
  value = load(key, options)
78
- @collection.remove('_id' => ::BSON::Binary.new(key)) if value
87
+ @collection.remove('_id' => to_binary(key)) if value
79
88
  value
80
89
  end
81
90
 
82
91
  # (see Proxy#increment)
83
92
  def increment(key, amount = 1, options = {})
84
- @collection.find_and_modify(:query => { '_id' => ::BSON::Binary.new(key) },
85
- :update => { '$inc' => { 'value' => amount } },
93
+ @collection.find_and_modify(:query => { '_id' => to_binary(key) },
94
+ :update => { '$inc' => { @value_field => amount } },
86
95
  :new => true,
87
- :upsert => true)['value']
96
+ :upsert => true)[@value_field]
88
97
  end
89
98
 
90
99
  # (see Proxy#create)
91
100
  def create(key, value, options = {})
92
- key = ::BSON::Binary.new(key)
93
- intvalue = value.to_i
94
- @collection.insert('_id' => key,
95
- 'value' => intvalue.to_s == value ? intvalue : ::BSON::Binary.new(value),
96
- # expiresAt must be a Time object (BSON date datatype)
97
- 'expiresAt' => expires_at(options) || nil)
101
+ key = to_binary(key)
102
+ @collection.insert(value_to_doc(key, value, options))
98
103
  true
99
104
  rescue ::Mongo::OperationFailure
100
105
  # FIXME: This catches too many errors
@@ -113,6 +118,53 @@ module Moneta
113
118
  @backend.close
114
119
  nil
115
120
  end
121
+
122
+ protected
123
+
124
+ def doc_to_value(doc)
125
+ case doc[@type_field]
126
+ when 'Hash'
127
+ doc = doc.dup
128
+ doc.delete('_id')
129
+ doc.delete(@type_field)
130
+ doc.delete(@expires_field)
131
+ doc
132
+ when 'Number'
133
+ doc[@value_field]
134
+ else
135
+ doc[@value_field].to_s
136
+ end
137
+ end
138
+
139
+ def value_to_doc(key, value, options)
140
+ case value
141
+ when Hash
142
+ value.merge('_id' => key,
143
+ @type_field => 'Hash',
144
+ # @expires_field must be a Time object (BSON date datatype)
145
+ @expires_field => expires_at(options) || nil)
146
+ when Float, Fixnum
147
+ { '_id' => key,
148
+ @type_field => 'Number',
149
+ @value_field => value,
150
+ # @expires_field must be a Time object (BSON date datatype)
151
+ @expires_field => expires_at(options) || nil }
152
+ when String
153
+ intvalue = value.to_i
154
+ { '_id' => key,
155
+ @type_field => 'String',
156
+ @value_field => intvalue.to_s == value ? intvalue : to_binary(value),
157
+ # @expires_field must be a Time object (BSON date datatype)
158
+ @expires_field => expires_at(options) || nil }
159
+ else
160
+ raise ArgumentError, "Invalid value type: #{value.class}"
161
+ end
162
+ end
163
+
164
+ def to_binary(s)
165
+ s = s.dup if s.frozen? # HACK: BSON::Binary needs unfrozen string
166
+ ::BSON::Binary.new(s)
167
+ end
116
168
  end
117
169
  end
118
170
  end