moneta 0.7.17 → 0.7.18

Sign up to get free protection for your applications and to get access to all the features.
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