moneta 0.7.6 → 0.7.8

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 (200) hide show
  1. data/CHANGES +15 -0
  2. data/README.md +160 -50
  3. data/Rakefile +11 -2
  4. data/SPEC.md +7 -2
  5. data/lib/moneta.rb +7 -1
  6. data/lib/moneta/adapters/activerecord.rb +27 -3
  7. data/lib/moneta/adapters/cassandra.rb +1 -1
  8. data/lib/moneta/adapters/client.rb +8 -2
  9. data/lib/moneta/adapters/cookie.rb +1 -1
  10. data/lib/moneta/adapters/couch.rb +2 -1
  11. data/lib/moneta/adapters/datamapper.rb +16 -0
  12. data/lib/moneta/adapters/daybreak.rb +19 -1
  13. data/lib/moneta/adapters/file.rb +12 -0
  14. data/lib/moneta/adapters/hbase.rb +1 -2
  15. data/lib/moneta/adapters/lruhash.rb +4 -1
  16. data/lib/moneta/adapters/memcached.rb +7 -3
  17. data/lib/moneta/adapters/memcached/dalli.rb +8 -2
  18. data/lib/moneta/adapters/memcached/native.rb +10 -3
  19. data/lib/moneta/adapters/memory.rb +1 -0
  20. data/lib/moneta/adapters/mongo.rb +32 -5
  21. data/lib/moneta/adapters/pstore.rb +13 -2
  22. data/lib/moneta/adapters/redis.rb +19 -0
  23. data/lib/moneta/adapters/restclient.rb +1 -1
  24. data/lib/moneta/adapters/sequel.rb +20 -6
  25. data/lib/moneta/adapters/sqlite.rb +13 -1
  26. data/lib/moneta/adapters/tokyocabinet.rb +5 -0
  27. data/lib/moneta/cache.rb +10 -0
  28. data/lib/moneta/expires.rb +19 -22
  29. data/lib/moneta/lock.rb +2 -2
  30. data/lib/moneta/mixins.rb +33 -11
  31. data/lib/moneta/optionmerger.rb +1 -1
  32. data/lib/moneta/proxy.rb +13 -8
  33. data/lib/moneta/server.rb +13 -6
  34. data/lib/moneta/shared.rb +6 -10
  35. data/lib/moneta/synchronize.rb +125 -0
  36. data/lib/moneta/transformer.rb +50 -42
  37. data/lib/moneta/transformer/config.rb +34 -32
  38. data/lib/moneta/utils.rb +20 -0
  39. data/lib/moneta/version.rb +1 -1
  40. data/lib/moneta/weak.rb +17 -0
  41. data/lib/moneta/wrapper.rb +5 -0
  42. data/lib/rack/moneta_cookies.rb +2 -2
  43. data/lib/rack/moneta_store.rb +4 -4
  44. data/script/benchmarks +3 -9
  45. data/script/generate-specs +278 -41
  46. data/script/install-bundle +2 -2
  47. data/spec/moneta/adapter_activerecord_spec.rb +1 -0
  48. data/spec/moneta/adapter_cassandra_spec.rb +1 -0
  49. data/spec/moneta/adapter_cassandra_with_default_expires_spec.rb +1 -0
  50. data/spec/moneta/adapter_client_spec.rb +1 -0
  51. data/spec/moneta/adapter_cookie_spec.rb +1 -0
  52. data/spec/moneta/adapter_couch_spec.rb +1 -0
  53. data/spec/moneta/adapter_datamapper_spec.rb +1 -0
  54. data/spec/moneta/adapter_daybreak_spec.rb +1 -0
  55. data/spec/moneta/adapter_dbm_spec.rb +1 -0
  56. data/spec/moneta/adapter_file_spec.rb +1 -0
  57. data/spec/moneta/adapter_fog_spec.rb +1 -0
  58. data/spec/moneta/adapter_gdbm_spec.rb +1 -1
  59. data/spec/moneta/adapter_hbase_spec.rb +1 -0
  60. data/spec/moneta/adapter_leveldb_spec.rb +1 -0
  61. data/spec/moneta/adapter_localmemcache_spec.rb +1 -0
  62. data/spec/moneta/adapter_lruhash_spec.rb +1 -0
  63. data/spec/moneta/adapter_memcached_dalli_spec.rb +2 -0
  64. data/spec/moneta/adapter_memcached_dalli_with_default_expires_spec.rb +2 -0
  65. data/spec/moneta/adapter_memcached_native_spec.rb +2 -0
  66. data/spec/moneta/adapter_memcached_native_with_default_expires_spec.rb +2 -0
  67. data/spec/moneta/adapter_memcached_spec.rb +2 -0
  68. data/spec/moneta/adapter_memcached_with_default_expires_spec.rb +2 -0
  69. data/spec/moneta/adapter_memory_spec.rb +1 -0
  70. data/spec/moneta/adapter_mongo_spec.rb +2 -0
  71. data/spec/moneta/adapter_mongo_with_default_expires_spec.rb +2 -0
  72. data/spec/moneta/adapter_pstore_spec.rb +1 -0
  73. data/spec/moneta/adapter_redis_spec.rb +2 -0
  74. data/spec/moneta/adapter_redis_with_default_expires_spec.rb +2 -0
  75. data/spec/moneta/adapter_restclient_spec.rb +1 -0
  76. data/spec/moneta/adapter_riak_spec.rb +1 -0
  77. data/spec/moneta/adapter_sdbm_spec.rb +1 -0
  78. data/spec/moneta/adapter_sequel_spec.rb +1 -0
  79. data/spec/moneta/adapter_sqlite_spec.rb +1 -0
  80. data/spec/moneta/adapter_tdb_spec.rb +1 -0
  81. data/spec/moneta/adapter_tokyocabinet_bdb_spec.rb +1 -0
  82. data/spec/moneta/adapter_tokyocabinet_hdb_spec.rb +1 -0
  83. data/spec/moneta/adapter_yaml_spec.rb +1 -0
  84. data/spec/moneta/cache_file_memory_spec.rb +1 -0
  85. data/spec/moneta/cache_memory_null_spec.rb +1 -0
  86. data/spec/moneta/expires_file_spec.rb +3 -1
  87. data/spec/moneta/expires_memory_spec.rb +2 -0
  88. data/spec/moneta/expires_memory_with_default_expires_spec.rb +2 -0
  89. data/spec/moneta/lock_spec.rb +1 -0
  90. data/spec/moneta/mutex_spec.rb +71 -0
  91. data/spec/moneta/null_adapter_spec.rb +1 -0
  92. data/spec/moneta/optionmerger_spec.rb +5 -7
  93. data/spec/moneta/pool_spec.rb +1 -0
  94. data/spec/moneta/proxy_expires_memory_spec.rb +2 -0
  95. data/spec/moneta/proxy_redis_spec.rb +2 -0
  96. data/spec/moneta/semaphore_spec.rb +84 -0
  97. data/spec/moneta/{shared_spec.rb → shared_tcp_spec.rb} +4 -3
  98. data/spec/moneta/shared_unix_spec.rb +37 -0
  99. data/spec/moneta/simple_activerecord_spec.rb +1 -0
  100. data/spec/moneta/simple_activerecord_with_expires_spec.rb +3 -1
  101. data/spec/moneta/simple_cassandra_spec.rb +1 -0
  102. data/spec/moneta/simple_client_tcp_spec.rb +1 -0
  103. data/spec/moneta/simple_client_unix_spec.rb +3 -2
  104. data/spec/moneta/simple_couch_spec.rb +1 -0
  105. data/spec/moneta/simple_couch_with_expires_spec.rb +2 -1
  106. data/spec/moneta/simple_datamapper_spec.rb +1 -0
  107. data/spec/moneta/simple_datamapper_with_expires_spec.rb +3 -1
  108. data/spec/moneta/simple_datamapper_with_repository_spec.rb +1 -0
  109. data/spec/moneta/simple_daybreak_spec.rb +1 -0
  110. data/spec/moneta/simple_daybreak_with_expires_spec.rb +3 -1
  111. data/spec/moneta/simple_dbm_spec.rb +1 -0
  112. data/spec/moneta/simple_dbm_with_expires_spec.rb +3 -1
  113. data/spec/moneta/simple_file_spec.rb +1 -0
  114. data/spec/moneta/simple_file_with_expires_spec.rb +3 -1
  115. data/spec/moneta/simple_fog_spec.rb +1 -0
  116. data/spec/moneta/simple_fog_with_expires_spec.rb +2 -1
  117. data/spec/moneta/simple_gdbm_spec.rb +1 -0
  118. data/spec/moneta/simple_gdbm_with_expires_spec.rb +3 -1
  119. data/spec/moneta/simple_hashfile_spec.rb +1 -0
  120. data/spec/moneta/simple_hashfile_with_expires_spec.rb +3 -1
  121. data/spec/moneta/simple_hbase_spec.rb +1 -0
  122. data/spec/moneta/simple_hbase_with_expires_spec.rb +3 -1
  123. data/spec/moneta/simple_leveldb_spec.rb +1 -0
  124. data/spec/moneta/simple_leveldb_with_expires_spec.rb +3 -1
  125. data/spec/moneta/simple_localmemcache_spec.rb +1 -0
  126. data/spec/moneta/simple_localmemcache_with_expires_spec.rb +2 -1
  127. data/spec/moneta/simple_lruhash_spec.rb +1 -0
  128. data/spec/moneta/simple_lruhash_with_expires_spec.rb +3 -1
  129. data/spec/moneta/simple_memcached_dalli_spec.rb +2 -0
  130. data/spec/moneta/simple_memcached_native_spec.rb +2 -0
  131. data/spec/moneta/simple_memcached_spec.rb +2 -0
  132. data/spec/moneta/simple_memory_spec.rb +1 -0
  133. data/spec/moneta/simple_memory_with_compress_spec.rb +1 -0
  134. data/spec/moneta/simple_memory_with_expires_spec.rb +3 -1
  135. data/spec/moneta/simple_memory_with_json_key_serializer_spec.rb +1 -0
  136. data/spec/moneta/simple_memory_with_json_serializer_spec.rb +1 -0
  137. data/spec/moneta/simple_memory_with_json_value_serializer_spec.rb +1 -0
  138. data/spec/moneta/simple_memory_with_prefix_spec.rb +1 -0
  139. data/spec/moneta/simple_memory_with_snappy_compress_spec.rb +1 -0
  140. data/spec/moneta/simple_mongo_spec.rb +2 -0
  141. data/spec/moneta/simple_null_spec.rb +1 -0
  142. data/spec/moneta/simple_pstore_spec.rb +1 -0
  143. data/spec/moneta/simple_pstore_with_expires_spec.rb +3 -1
  144. data/spec/moneta/simple_redis_spec.rb +2 -0
  145. data/spec/moneta/simple_restclient_spec.rb +1 -0
  146. data/spec/moneta/simple_riak_spec.rb +1 -0
  147. data/spec/moneta/simple_riak_with_expires_spec.rb +2 -1
  148. data/spec/moneta/simple_sdbm_spec.rb +1 -0
  149. data/spec/moneta/simple_sdbm_with_expires_spec.rb +3 -1
  150. data/spec/moneta/simple_sequel_spec.rb +1 -0
  151. data/spec/moneta/simple_sequel_with_expires_spec.rb +3 -1
  152. data/spec/moneta/simple_sqlite_spec.rb +1 -0
  153. data/spec/moneta/simple_sqlite_with_expires_spec.rb +3 -1
  154. data/spec/moneta/simple_tdb_spec.rb +1 -0
  155. data/spec/moneta/simple_tdb_with_expires_spec.rb +3 -1
  156. data/spec/moneta/simple_tokyocabinet_spec.rb +1 -0
  157. data/spec/moneta/simple_tokyocabinet_with_expires_spec.rb +3 -1
  158. data/spec/moneta/simple_yaml_spec.rb +1 -0
  159. data/spec/moneta/simple_yaml_with_expires_spec.rb +3 -1
  160. data/spec/moneta/stack_file_memory_spec.rb +1 -0
  161. data/spec/moneta/stack_memory_file_spec.rb +1 -0
  162. data/spec/moneta/transformer_bencode_spec.rb +1 -0
  163. data/spec/moneta/transformer_bert_spec.rb +1 -0
  164. data/spec/moneta/transformer_bson_spec.rb +1 -0
  165. data/spec/moneta/transformer_bzip2_spec.rb +1 -0
  166. data/spec/moneta/transformer_json_spec.rb +1 -0
  167. data/spec/moneta/transformer_key_inspect_spec.rb +73 -0
  168. data/spec/moneta/transformer_key_marshal_spec.rb +1 -0
  169. data/spec/moneta/transformer_key_to_s_spec.rb +1 -0
  170. data/spec/moneta/transformer_key_yaml_spec.rb +1 -0
  171. data/spec/moneta/transformer_lzma_spec.rb +1 -0
  172. data/spec/moneta/transformer_lzo_spec.rb +1 -0
  173. data/spec/moneta/transformer_marshal_base64_spec.rb +1 -0
  174. data/spec/moneta/transformer_marshal_escape_spec.rb +1 -0
  175. data/spec/moneta/transformer_marshal_hmac_spec.rb +1 -0
  176. data/spec/moneta/transformer_marshal_md5_spec.rb +1 -0
  177. data/spec/moneta/transformer_marshal_md5_spread_spec.rb +1 -0
  178. data/spec/moneta/transformer_marshal_prefix_spec.rb +1 -0
  179. data/spec/moneta/transformer_marshal_rmd160_spec.rb +1 -0
  180. data/spec/moneta/transformer_marshal_sha1_spec.rb +1 -0
  181. data/spec/moneta/transformer_marshal_sha256_spec.rb +1 -0
  182. data/spec/moneta/transformer_marshal_sha384_spec.rb +1 -0
  183. data/spec/moneta/transformer_marshal_sha512_spec.rb +1 -0
  184. data/spec/moneta/transformer_marshal_spec.rb +1 -0
  185. data/spec/moneta/transformer_marshal_truncate_spec.rb +1 -0
  186. data/spec/moneta/transformer_marshal_uuencode_spec.rb +1 -0
  187. data/spec/moneta/transformer_msgpack_spec.rb +1 -0
  188. data/spec/moneta/transformer_ox_spec.rb +1 -0
  189. data/spec/moneta/transformer_quicklz_spec.rb +1 -0
  190. data/spec/moneta/transformer_snappy_spec.rb +1 -0
  191. data/spec/moneta/transformer_tnet_spec.rb +1 -0
  192. data/spec/moneta/transformer_value_marshal_spec.rb +1 -0
  193. data/spec/moneta/transformer_value_yaml_spec.rb +1 -0
  194. data/spec/moneta/transformer_yaml_spec.rb +1 -0
  195. data/spec/moneta/transformer_zlib_spec.rb +1 -0
  196. data/spec/moneta/weak_create_spec.rb +114 -0
  197. data/spec/moneta/weak_increment_spec.rb +114 -0
  198. data/spec/monetaspecs.rb +75 -2
  199. data/spec/rack/moneta_cookies_spec.rb +1 -1
  200. metadata +20 -4
data/CHANGES CHANGED
@@ -1,3 +1,18 @@
1
+ 0.7.8
2
+
3
+ * Adapters::Memcached: switched to Dalli by default
4
+ * Adapters::Daybreak: add option :sync to load and store
5
+ * Adapters::LRUHash: add option :max_count
6
+ * Adapters::Mongo: add options :user and :password
7
+ * Adapters::Mongo: Correctly close connection
8
+ * Adapters::Redis: Correctly close connection
9
+ * Transformer: add inspect key transformer
10
+ * Added #create method to atomically create entries
11
+ * Added WeakCreate and WeakIncrement proxies
12
+ * Added Mutex and Semaphore synchronization primitives for
13
+ shared/distributed database locks
14
+ * Rename unix socket options from :file to :socket
15
+
1
16
  0.7.6
2
17
 
3
18
  * Adapters::Daybreak: api changed
data/README.md CHANGED
@@ -4,15 +4,18 @@
4
4
 
5
5
  Moneta provides a standard interface for interacting with various kinds of key/value stores. A short overview of the features:
6
6
 
7
- * Supports a lot of backends (See below)
7
+ * Supports a lot of backends with consistent behaviour (See below)
8
8
  * Allows a full configuration of the serialization -> compression -> adapter stack using proxies (Similar to [Rack middlewares](http://rack.github.com/))
9
9
  * Configurable serialization via `Moneta::Transformer` proxy (Marshal/JSON/YAML and many more)
10
10
  * Configurable value compression via `Moneta::Transformer` proxy (Zlib, Snappy, LZMA, ...)
11
11
  * Configurable key transformation via `Moneta::Transformer` proxy
12
12
  * Expiration for all stores (Added via proxy `Moneta::Expires` if not supported natively)
13
- * Atomic incrementation and decrementation for most stores (Method `#increment` and `#decrement`)
14
- * Includes a very simple key/value server (`Moneta::Server`) and client (`Moneta::Adapters::Client`)
15
- * Integration with [Rails](http://rubyonrails.org/), [Rack](http://rack.github.com/), [Sinatra](http://sinatrarb.com/) and [Rack-Cache](https://github.com/rtomayko/rack-cache)
13
+ * Atomic operations
14
+ * Atomic incrementation and decrementation for most stores (Method `#increment` and `#decrement`)
15
+ * Atomic creation of entries (Method `#create`)
16
+ * Shared/distributed database-wide synchronization primitives `Moneta::Mutex` and `Moneta::Semaphore`
17
+ * Includes a simple pure-ruby key/value server (`Moneta::Server`) and client (`Moneta::Adapters::Client`)
18
+ * Integration with [Rails](http://rubyonrails.org/), [Rack](http://rack.github.com/)/[Rack-Cache](https://github.com/rtomayko/rack-cache), [Sinatra](http://sinatrarb.com/) and [Ramaze](http://ramaze.net/).
16
19
 
17
20
  If you are not yet convinced, you might ask why? What are the goals of the project?
18
21
 
@@ -21,11 +24,43 @@ same for template languages.
21
24
  * Make it easy to compare different key/value stores and benchmark them
22
25
  * To hide a lot of different and maybe complex APIs behind one well-designed and simple Moneta API
23
26
  * Give people a starting point or example code to start working with their favourite key/value store. Feel free to copy code, please mention Moneta then :)
24
- * Create a reusable piece of code, since similar things are solved over and over again ([Rails](http://rubyonrails.org/ brings its own cache stores, and many frameworks do the same...)
27
+ * Create a reusable piece of code, since similar things are solved over and over again ([Rails](http://rubyonrails.org/) brings its own cache stores, and many frameworks do the same...)
25
28
  * See also http://yehudakatz.com/2009/02/12/whats-the-point/
26
29
 
27
30
  Moneta is tested thoroughly using [Travis-CI](http://travis-ci.org/minad/moneta).
28
31
 
32
+ ## Getting started
33
+
34
+ Install Moneta via Rubygems
35
+
36
+ ~~~
37
+ $ gem install moneta
38
+ ~~~
39
+
40
+ or add it to your Gemfile
41
+
42
+ ~~~ ruby
43
+ gem 'moneta'
44
+ ~~~
45
+
46
+ Now you are ready to go:
47
+
48
+ ~~~ ruby
49
+ require 'moneta'
50
+
51
+ # Create a simple file store
52
+ store = Moneta.new(:File, :dir => 'moneta')
53
+
54
+ # Store some entries
55
+ store['key'] = 'value'
56
+
57
+ # Read entry
58
+ store.key?('key') # returns true
59
+ store['key'] # returns 'value'
60
+
61
+ store.close
62
+ ~~~
63
+
29
64
  ## Links
30
65
 
31
66
  * Source: <http://github.com/minad/moneta>
@@ -36,7 +71,7 @@ Moneta is tested thoroughly using [Travis-CI](http://travis-ci.org/minad/moneta)
36
71
 
37
72
  ## Supported backends
38
73
 
39
- Out of the box, it supports the following backends:
74
+ Out of the box, it supports the following backends. Use the backend name symbol in the Moneta constructor (e.g. `Moneta.new(:Memory)`).
40
75
 
41
76
  * Memory:
42
77
  * In-memory store (`:Memory`)
@@ -83,49 +118,51 @@ to upgrade to a real key/value store.
83
118
  ### Backend feature matrix
84
119
 
85
120
  <table>
86
- <thead style="font-weight:bold"><tr><th>Adapter</th><th>Required gems</th><th>Multi-thread safe<sup>[1]</sup></th><th>Multi-process safe<sup>[2]</sup></th><th>Atomic increment</th><th>Native expires<sup>[3]</sup></th><th>Persistent</th><th>Description</th></tr></thead>
121
+ <thead style="font-weight:bold"><tr><th>Adapter</th><th>Required gems</th><th>Multi-thread safe<sup>[1]</sup></th><th>Multi-process safe<sup>[2]</sup></th><th>Atomic increment<sup>[8]</sup></th><th>Atomic create<sup>[9]</sup></th><th>Native expires<sup>[3]</sup></th><th>Persistent</th><th>Description</th></tr></thead>
87
122
  <tbody>
88
- <tr><td>ActiveRecord</td><td>activerecord</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:green">✓</td><td><a href="https://rubygems.org/gems/activerecord">ActiveRecord</a> ORM</td></tr>
89
- <tr><td>Cassandra</td><td>cassandra</td><td style="color:blue">?</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:green">✓</td><td style="color:green">✓</td><td><a href="http://cassandra.apache.org/">Cassandra</a> distributed database</td></tr>
90
- <tr><td>Client</td><td>-</td><td style="color:red">✗</td><td style="color:green">✓</td><td style="color:blue">?<sup>[5]</sup></td><td style="color:blue">?<sup>[5]</sup></td><td style="color:blue">?<sup>[5]</sup></td><td>Moneta client adapter</td></tr>
91
- <tr><td>Cookie</td><td>-</td><td style="color:red">✗</td><td style="color:blue">(✓)<sup>[6]</sup></td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:red">✗</td><td>Cookie in memory store</td></tr>
92
- <tr><td>Couch</td><td>couchrest</td><td style="color:blue">?</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:red">✗</td><td style="color:green">✓</td><td><a href="http://couchdb.apache.org/">CouchDB</a> database</td></tr>
93
- <tr><td>DataMapper</td><td>dm-core, dm-migrations</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:red">✗</td><td style="color:green">✓</td><td><a href="http://datamapper.org/">DataMapper</a> ORM</td></tr>
94
- <tr><td>Daybreak</td><td>daybreak</td><td style="color:red">✗</td><td style="color:blue">(✓)<sup>[7]</sup></td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:green">✓</td><td>Incredibly fast pure-ruby key/value store <a href="http://propublica.github.com/daybreak/">Daybreak</a></td></tr>
95
- <tr><td>DBM</td><td>-</td><td style="color:red">✗</td><td style="color:red">✗</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:green">✓</td><td><a href="http://www.ruby-doc.org/stdlib/libdoc/dbm/rdoc/DBM.html">Berkeley DB</a></td></tr>
96
- <tr><td>File</td><td>-</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:green">✓</td><td>File store</td></tr>
97
- <tr><td>Fog</td><td>fog</td><td style="color:blue">?</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:red">✗</td><td style="color:green">✓</td><td><a href="http://fog.io/">Fog</a> cloud store</td></tr>
98
- <tr><td>GDBM</td><td>-</td><td style="color:red">✗</td><td style="color:red">✗</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:green">✓</td><td><a href="http://www.ruby-doc.org/stdlib/libdoc/gdbm/rdoc/GDBM.html">GDBM</a> database</td></tr>
99
- <tr><td>HBase</td><td>hbase</td><td style="color:blue">?</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:red">✗</td><td style="color:green">✓</td><td><a href="http://hbase.apache.org/">HBase</a> database</td></tr>
100
- <tr><td>LevelDB</td><td>leveldb</td><td style="color:red">✗</td><td style="color:red">✗</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:green">✓</td><td><a href="http://code.google.com/p/leveldb/">LevelDB</a> database</td></tr>
101
- <tr><td>LocalMemCache</td><td>localmemcache</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:red">✗</td><td style="color:green">✓</td><td><a href="http://localmemcache.rubyforge.org/">LocalMemCache</a> database</td></tr>
102
- <tr><td>LRUHash</td><td>-</td><td style="color:red">✗</td><td style="color:blue">(✓)<sup>[6]</sup></td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:red">✗</td><td>LRU memory store</td></tr>
103
- <tr><td>Memcached</td><td>dalli or memcached</td><td style="color:blue">?</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:red">✗<sup>[4]</sup></td><td><a href="http://memcached.org/">Memcached</a> database</td></tr>
104
- <tr><td>MemcachedDalli</td><td>dalli</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:red">✗<sup>[4]</sup></td><td><a href="http://memcached.org/">Memcached</a> database with Dalli library</td></tr>
105
- <tr><td>MemcachedNative</td><td>memcached</td><td style="color:red">✗</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:red">✗<sup>[4]</sup></td><td>Memcached database with native library</td></tr>
106
- <tr><td>Memory</td><td>-</td><td style="color:red">✗</td><td style="color:blue">(✓)<sup>[6]</sup></td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:red">✗</td><td>Memory store</td></tr>
107
- <tr><td>Mongo</td><td>mongo</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:green">✓</td><td><a href="http://www.mongodb.org/">MongoDB</a> database</td></tr>
108
- <tr><td>Null</td><td>-</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:red">✗</td><td style="color:red">✗</td><td>No database</td></tr>
109
- <tr><td>PStore</td><td>-</td><td style="color:red">✗</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:green">✓</td><td><a href="http://ruby-doc.org/stdlib/libdoc/pstore/rdoc/PStore.html">PStore</a> store</td></tr>
110
- <tr><td>Redis</td><td>redis</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:green">✓</td><td><a href="http://redis.io/">Redis</a> database</td></tr>
111
- <tr><td>RestClient</td><td>-</td><td style="color:red">✗</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:red">✗</td><td style="color:blue">?<sup>[5]</sup></td><td>Moneta REST client adapter</td></tr>
112
- <tr><td>Riak</td><td>riak-client</td><td style="color:red">✗</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:red">✗</td><td style="color:green">✓</td><td><a href="http://docs.basho.com/">Riak</a> database</td></tr>
113
- <tr><td>SDBM</td><td>-</td><td style="color:red">✗</td><td style="color:red">✗</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:green">✓</td><td><a href="http://www.ruby-doc.org/stdlib/libdoc/sdbm/rdoc/SDBM.html">SDBM</a> database</td></tr>
114
- <tr><td>Sequel</td><td>sequel</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:green">✓</td><td><a href="http://sequel.rubyforge.org/">Sequel</a> ORM</td></tr>
115
- <tr><td>Sqlite</td><td>sqlite3</td><td style="color:blue">?</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:green">✓</td><td><a href="http://sqlite.org/">Sqlite3</a> database</td></tr>
116
- <tr><td>TDB</td><td>tdb</td><td style="color:red">✗</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:green">✓</td><td><a href="http://tdb.samba.org/">TDB</a> database</td></tr>
117
- <tr><td>TokyoCabinet</td><td>tokoycabinet</td><td style="color:red">✗</td><td style="color:red">✗</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:green">✓</td><td><a href="http://fallabs.com/tokyocabinet/">TokyoCabinet</a> database</td></tr>
118
- <tr><td>YAML</td><td>-</td><td style="color:red">✗</td><td style="color:green">✓</td><td style="color:green">✓</td><td style="color:red">✗</td><td style="color:green">✓</td><td><a href="http://www.ruby-doc.org/stdlib/libdoc/yaml/rdoc/YAML/Store.html">YAML</a> store</td></tr>
123
+ <tr><td>ActiveRecord</td><td>activerecord</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td><a href="https://rubygems.org/gems/activerecord">ActiveRecord</a> ORM</td></tr>
124
+ <tr><td>Cassandra</td><td>cassandra</td><td style="text-align:center;color:blue">?</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td><a href="http://cassandra.apache.org/">Cassandra</a> distributed database</td></tr>
125
+ <tr><td>Client</td><td>-</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:blue">?<sup>[5]</sup></td><td style="text-align:center;color:blue">?<sup>[5]</sup></td><td style="text-align:center;color:blue">?<sup>[5]</sup></td><td style="text-align:center;color:blue">?<sup>[5]</sup></td><td>Moneta client adapter</td></tr>
126
+ <tr><td>Cookie</td><td>-</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:blue">(✓)<sup>[6]</sup></td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td>Cookie in memory store</td></tr>
127
+ <tr><td>Couch</td><td>couchrest</td><td style="text-align:center;color:blue">?</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td><a href="http://couchdb.apache.org/">CouchDB</a> database</td></tr>
128
+ <tr><td>DataMapper</td><td>dm-core, dm-migrations</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td><a href="http://datamapper.org/">DataMapper</a> ORM</td></tr>
129
+ <tr><td>Daybreak</td><td>daybreak</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:blue">(✓)<sup>[7]</sup></td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td>Incredibly fast pure-ruby key/value store <a href="http://propublica.github.com/daybreak/">Daybreak</a></td></tr>
130
+ <tr><td>DBM</td><td>-</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td><a href="http://www.ruby-doc.org/stdlib/libdoc/dbm/rdoc/DBM.html">Berkeley DB</a></td></tr>
131
+ <tr><td>File</td><td>-</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td>File store</td></tr>
132
+ <tr><td>Fog</td><td>fog</td><td style="text-align:center;color:blue">?</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td><a href="http://fog.io/">Fog</a> cloud store</td></tr>
133
+ <tr><td>GDBM</td><td>-</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td><a href="http://www.ruby-doc.org/stdlib/libdoc/gdbm/rdoc/GDBM.html">GDBM</a> database</td></tr>
134
+ <tr><td>HBase</td><td>hbase</td><td style="text-align:center;color:blue">?</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td><a href="http://hbase.apache.org/">HBase</a> database</td></tr>
135
+ <tr><td>LevelDB</td><td>leveldb</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td><a href="http://code.google.com/p/leveldb/">LevelDB</a> database</td></tr>
136
+ <tr><td>LocalMemCache</td><td>localmemcache</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td><a href="http://localmemcache.rubyforge.org/">LocalMemCache</a> database</td></tr>
137
+ <tr><td>LRUHash</td><td>-</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:blue">(✓)<sup>[6]</sup></td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td>LRU memory store</td></tr>
138
+ <tr><td>Memcached</td><td>dalli or memcached</td><td style="text-align:center;color:blue">?</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗<sup>[4]</sup></td><td><a href="http://memcached.org/">Memcached</a> database</td></tr>
139
+ <tr><td>MemcachedDalli</td><td>dalli</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗<sup>[4]</sup></td><td><a href="http://memcached.org/">Memcached</a> database with Dalli library</td></tr>
140
+ <tr><td>MemcachedNative</td><td>memcached</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗<sup>[4]</sup></td><td>Memcached database with native library</td></tr>
141
+ <tr><td>Memory</td><td>-</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:blue">(✓)<sup>[6]</sup></td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td>Memory store</td></tr>
142
+ <tr><td>Mongo</td><td>mongo</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td><a href="http://www.mongodb.org/">MongoDB</a> database</td></tr>
143
+ <tr><td>Null</td><td>-</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td>No database</td></tr>
144
+ <tr><td>PStore</td><td>-</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td><a href="http://ruby-doc.org/stdlib/libdoc/pstore/rdoc/PStore.html">PStore</a> store</td></tr>
145
+ <tr><td>Redis</td><td>redis</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td><a href="http://redis.io/">Redis</a> database</td></tr>
146
+ <tr><td>RestClient</td><td>-</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:blue">?<sup>[5]</sup></td><td>Moneta REST client adapter</td></tr>
147
+ <tr><td>Riak</td><td>riak-client</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td><a href="http://docs.basho.com/">Riak</a> database</td></tr>
148
+ <tr><td>SDBM</td><td>-</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td><a href="http://www.ruby-doc.org/stdlib/libdoc/sdbm/rdoc/SDBM.html">SDBM</a> database</td></tr>
149
+ <tr><td>Sequel</td><td>sequel</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td><a href="http://sequel.rubyforge.org/">Sequel</a> ORM</td></tr>
150
+ <tr><td>Sqlite</td><td>sqlite3</td><td style="text-align:center;color:blue">?</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td><a href="http://sqlite.org/">Sqlite3</a> database</td></tr>
151
+ <tr><td>TDB</td><td>tdb</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td><a href="http://tdb.samba.org/">TDB</a> database</td></tr>
152
+ <tr><td>TokyoCabinet</td><td>tokoycabinet</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td><a href="http://fallabs.com/tokyocabinet/">TokyoCabinet</a> database</td></tr>
153
+ <tr><td>YAML</td><td>-</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:green">✓</td><td style="text-align:center;color:red">✗</td><td style="text-align:center;color:green">✓</td><td><a href="http://www.ruby-doc.org/stdlib/libdoc/yaml/rdoc/YAML/Store.html">YAML</a> store</td></tr>
119
154
  </tbody>
120
155
  </table>
121
156
 
122
157
  * [1]: Make adapters thread-safe by using `Moneta::Lock` or by passing the option `:threadsafe => true` to `Moneta#new`. There is also `Moneta::Pool` which can be used to share a store between multiple threads if the store is multi-process safe. I recommend to add the option `:threadsafe` to ensure thread-safety since for example under JRuby and Rubinius even the basic datastructures are not thread safe due to the lack of a global interpreter lock (GIL). This differs from MRI where some adapters might appear thread safe already but only due to the GIL.
123
158
  * [2]: Share a Moneta store between multiple processes using `Moneta::Shared` (See below).
124
159
  * [3]: Add expiration support by using `Moneta::Expires` or by passing the option `:expires => true` to `Moneta#new`.
125
- * [4]: There are some servers which use the memcached protocol but which are persistent (e.g. MemcacheDB, Kai, IronCache, ...)
160
+ * [4]: There are some servers which use the memcached protocol but which are persistent (e.g. [MemcacheDB](http://memcachedb.org/), [Kai](http://sourceforge.net/apps/mediawiki/kai), [IronCache](http://dev.iron.io/cache/reference/memcache/))
126
161
  * [5]: Depends on server
127
162
  * [6]: Store is multi-process safe because it is an in-memory store, values are not shared between multiple processes
128
163
  * [7]: Store is multi-process safe, but not synchronized automatically between multiple processes
164
+ * [8]: If a store provides atomic increment it can be used with `Moneta::Semaphore`. You can add weak `#increment` using the `Moneta::WeakIncrement` proxy.
165
+ * [8]: If a store provides atomic creation it can be used with `Moneta::Mutex`. You can add weak `#create` using the `Moneta::WeakCreate` proxy.
129
166
 
130
167
  ## Proxies
131
168
 
@@ -141,6 +178,7 @@ add additional features to storage backends:
141
178
  * `Moneta::Pool` to create a pool of stores as a means of making the store thread safe. Add it in the builder using `use(:Pool) {}`.
142
179
  * `Moneta::Logger` to log database accesses. Add it in the builder using `use :Logger`.
143
180
  * `Moneta::Shared` to share a store between multiple processes. Add it in the builder using `use(:Shared) {}`.
181
+ * `Moneta::WeakIncrement` and `Moneta::WeakCreate` to add `#create` and `#increment` support without atomicity (weak) to stores which don't support it.
144
182
 
145
183
  ### Serializers and compressors (`Moneta::Transformer`)
146
184
 
@@ -173,7 +211,7 @@ Special transformers:
173
211
  ## Moneta API
174
212
 
175
213
  The Moneta API is purposely extremely similar to the Hash API with a few minor additions.
176
- There are the additional methods `#load`, `#increment`, `#decrement` and `#close`. Every method takes also a optional
214
+ There are the additional methods `#load`, `#increment`, `#decrement`, `#create` and `#close`. Every method takes also a optional
177
215
  option hash. In order so support an identical API across stores, Moneta does not support iteration or partial matches.
178
216
 
179
217
  ~~~
@@ -197,13 +235,16 @@ option hash. In order so support an identical API across stores, Moneta does not
197
235
 
198
236
  #key?(key, options = {}) true if the key exists, false if it does not.
199
237
 
200
- #increment(key, amount = 1, options = {}) increment numeric value. This is a atomic operation
238
+ #increment(key, amount = 1, options = {}) increment numeric value. This is an atomic operation
201
239
  which is not supported by all stores. Returns current value.
202
240
 
203
- #decrement(key, amount = 1, options = {}) increment numeric value. This is a atomic operation
241
+ #decrement(key, amount = 1, options = {}) increment numeric value. This is an atomic operation
204
242
  which is not supported by all stores. Returns current value.
205
243
  This is just syntactic sugar for incrementing with a negative value.
206
244
 
245
+ #create(key, value, options = {}) create entry. This is an atomic operation which is not supported by all stores.
246
+ Returns true if the value was created.
247
+
207
248
  #clear(options = {}) clear all keys in this store.
208
249
 
209
250
  #close close database connection.
@@ -275,7 +316,9 @@ cache = Moneta.build do
275
316
  end
276
317
  ~~~
277
318
 
278
- ### Incrementation and raw access
319
+ ### Atomic operations
320
+
321
+ #### Atomic incrementation and raw access
279
322
 
280
323
  The stores support the `#increment` which allows atomic increments of unsigned integer values. If you increment
281
324
  a non existing value, it will be created. If you increment a non integer value an exception will be raised.
@@ -326,6 +369,69 @@ counters['counter'] = '10'
326
369
  counters.increment('counter') # returns 11
327
370
  ~~~
328
371
 
372
+ #### Atomic create
373
+
374
+ The stores support the `#create` which allows atomic creation of entries. `#create` returns true
375
+ if the value was created.
376
+
377
+ ~~~ ruby
378
+ store.create('key', 'value') # returns true
379
+ store.create('key', 'other value') # returns false
380
+ ~~~
381
+
382
+ #### Shared/distributed synchronization primitives
383
+
384
+ Moneta provides shared/distributed synchronization primitives which are shared database-wide between
385
+ all clients.
386
+
387
+ * `Moneta::Mutex`
388
+
389
+ ~~~ ruby
390
+ mutex = Moneta::Mutex.new(store, 'mutex_key')
391
+
392
+ mutex.synchronize do
393
+ mutex.locked? # returns true
394
+
395
+ # Synchronized access to counter
396
+ store['counter'] += 1
397
+ end
398
+
399
+ begin
400
+ mutex.lock
401
+ mutex.locked? # returns true
402
+ ...
403
+ ensure
404
+ mutex.unlock
405
+ end
406
+ ~~~
407
+
408
+ * `Moneta::Semaphore`
409
+
410
+ ~~~ ruby
411
+ semaphore = Moneta::Semaphore.new(store, 'semaphore_counter', max_concurrent)
412
+
413
+ semaphore.synchronize do
414
+ semaphore.locked? # returns true
415
+ ...
416
+ end
417
+
418
+ begin
419
+ semaphore.enter
420
+ semaphore.locked? # returns true
421
+ ...
422
+ ensure
423
+ semaphore.leave
424
+ end
425
+ ~~~
426
+
427
+ #### Weak atomic operations
428
+
429
+ If an underlying adapter doesn't provide atomic `#create` or `#increment` and `#decrement` you can
430
+ use the proxies `Moneta::WeakIncrement` and `Moneta::WeakCreate` to add support without atomicity.
431
+
432
+ But then you have to ensure that the store is not shared by multiple processes and thread-safety is
433
+ provided by `Moneta::Lock`.
434
+
329
435
  ### Syntactic sugar and option merger
330
436
 
331
437
  For raw data access as described before the class `Moneta::OptionMerger` is used. It works like this:
@@ -367,7 +473,7 @@ compressed_store['key'] = 'value will be compressed'
367
473
 
368
474
  ## Framework Integration
369
475
 
370
- Inspired by [redis-store](https://github.com/jodosha/redis-store) there exist integration classes for [Rails](http://rubyonrails.org/), [Rack](http://rack.github.com/) and [Rack-Cache](https://github.com/rtomayko/rack-cache). You can also use all the Rack middlewares together with Rails and the [Sinatra](http://sinatrarb.com/) framework. There exist the following integration classes:
476
+ Inspired by [redis-store](https://github.com/jodosha/redis-store) there exist integration classes for [Rails](http://rubyonrails.org/) and [Rack](http://rack.github.com/)/[Rack-Cache](https://github.com/rtomayko/rack-cache). You can also use all the Rack middlewares together with Rails and the [Sinatra](http://sinatrarb.com/) framework. There exist the following integration classes:
371
477
 
372
478
  * Rack, Rails and Sinatra
373
479
  * `Rack::Session::Moneta` is a Rack middleware to use Moneta for storing sessions
@@ -378,6 +484,9 @@ Inspired by [redis-store](https://github.com/jodosha/redis-store) there exist in
378
484
  * Rails
379
485
  * `ActionDispatch::Session::MonetaStore` is a Rails middleware to use Moneta for storing sessions
380
486
  * `ActiveSupport::Cache::MonetaStore` is a Rails cache implementation which uses a Moneta store as backend
487
+ * Ramaze
488
+ * `Ramaze::Cache::Moneta` is integrated into the [Ramaze](http://ramaze.net/) project and allows Ramaze to use
489
+ Moneta as caching store
381
490
 
382
491
  ### Rack
383
492
 
@@ -410,9 +519,9 @@ caching if you add the option `:cache => true`. Use it in your `config.ru` like
410
519
  # Add Rack::MonetaStore somewhere in your rack stack
411
520
  use Rack::MonetaStore, :Memory, :cache => true
412
521
 
413
- run lambda do |env|
522
+ run lambda { |env|
414
523
  env['rack.moneta_store'] # is a Moneta store with per-request caching
415
- end
524
+ }
416
525
 
417
526
  # Pass it a block like the one passed to Moneta.build
418
527
  use Rack::MonetaStore do
@@ -420,9 +529,9 @@ use Rack::MonetaStore do
420
529
  adapter :Cookie
421
530
  end
422
531
 
423
- run lambda do |env|
532
+ run lambda { |env|
424
533
  env['rack.moneta_store'] # is a Moneta store without caching
425
- end
534
+ }
426
535
  ~~~
427
536
 
428
537
  #### REST server
@@ -474,7 +583,7 @@ to use all the transformers on the cookies (e.g. `:prefix`, `:marshal` and `:hma
474
583
  require 'rack/moneta_cookies'
475
584
 
476
585
  use Rack::MonetaCookies, :domain => 'example.com', :path => '/path'
477
- run lambda do |env|
586
+ run lambda { |env|
478
587
  req = Rack::Request.new(env)
479
588
  req.cookies #=> is now a Moneta store!
480
589
  env['rack.request.cookie_hash'] #=> is now a Moneta store!
@@ -482,7 +591,7 @@ run lambda do |env|
482
591
  req.cookies['key'] = 'value' #=> sets 'key'
483
592
  req.cookies.delete('key') #=> removes 'key'
484
593
  [200, {}, []]
485
- end
594
+ }
486
595
  ~~~
487
596
 
488
597
  ### Rails
@@ -588,6 +697,7 @@ on top of the different stores.
588
697
 
589
698
  * [Horcrux](https://github.com/technoweenie/horcrux): Used at github, supports batch operations but only Memcached backend
590
699
  * [ActiveSupport::Cache::Store](http://api.rubyonrails.org/classes/ActiveSupport/Cache/Store.html): The Rails cache store abstraction
700
+ * [Padrino::Cache::Store](http://www.padrinorb.com/api/Padrino/Cache/Store.html): The Padrino cache store abstraction
591
701
  * [ToyStore](https://github.com/jnunemaker/toystore): ORM mapper for key/value stores
592
702
  * [ToyStore Adapter](https://github.com/jnunemaker/adapter): Adapter to key/value stores used by ToyStore, Moneta can be used directly with the ToyStore Memory adapter
593
703
 
data/Rakefile CHANGED
@@ -24,9 +24,18 @@ task :test do
24
24
  # QuickLZ is also not maintained on Github, but on Bitbucket
25
25
  # and I don't know where the issue tracker is.
26
26
  #
27
- # * Cassandra and Mongo show spurious failures
27
+ # * Cassandra show spurious failures
28
+ #
29
+ # * action_dispatch cannot be required for an unknown reason
28
30
  if ENV['TEST_GROUP']
29
- unstable = specs.select {|s| s =~ /quicklz|cassandra|mongo/ }
31
+ # Shuffle specs to ensure equal distribution over the test groups
32
+ # We have to shuffle with the same seed every time because rake is started
33
+ # multiple times!
34
+ old_seed = srand(42)
35
+ specs.shuffle!
36
+ srand(old_seed)
37
+
38
+ unstable = specs.select {|s| s =~ /quicklz|cassandra|action_dispatch/ }
30
39
  specs -= unstable
31
40
  end
32
41
 
data/SPEC.md CHANGED
@@ -43,12 +43,17 @@ Behaves the same as <code>[]=</code>, but allows the client to send additional o
43
43
  ### <code>increment(key[Object], amount[Integer] = 1, options[Hash] => {}) => Integer(value)</code>
44
44
 
45
45
  Increments a value atomically. This method is not supported by all stores and might raise a <code>NotImplementedError</code>.
46
- This method MUST accept negative values, but the result MUST be unsigned.
46
+ This method MUST accept negative amounts, but the result MUST be unsigned.
47
47
 
48
48
  ### <code>decrement(key[Object], amount[Integer] = 1, options[Hash] => {}) => Integer(value)</code>
49
49
 
50
50
  Decrements a value atomically. This method is not supported by all stores and might raise a <code>NotImplementedError</code>.
51
- This method MUST accept negative values, but the result MUST be unsigned.
51
+ This method MUST accept negative amounts, but the result MUST be unsigned.
52
+
53
+ ### <code>create(key[Object], value[Object], options[Hash] => {}) => [TrueClass, FalseClass]</code>
54
+
55
+ Creates a value atomically. This method is not supported by all stores and might raise a <code>NotImplementedError</code>.
56
+ It MUST return true if the value was created.
52
57
 
53
58
  ### <code>clear(options[Hash] => {})</code>
54
59
 
data/lib/moneta.rb CHANGED
@@ -1,6 +1,7 @@
1
1
  module Moneta
2
2
  autoload :Builder, 'moneta/builder'
3
3
  autoload :Cache, 'moneta/cache'
4
+ autoload :CreateSupport, 'moneta/mixins'
4
5
  autoload :Defaults, 'moneta/mixins'
5
6
  autoload :ExpiresSupport, 'moneta/mixins'
6
7
  autoload :Expires, 'moneta/expires'
@@ -8,15 +9,20 @@ module Moneta
8
9
  autoload :IncrementSupport, 'moneta/mixins'
9
10
  autoload :Lock, 'moneta/lock'
10
11
  autoload :Logger, 'moneta/logger'
12
+ autoload :Mutex, 'moneta/synchronize'
11
13
  autoload :Net, 'moneta/mixins'
12
14
  autoload :OptionMerger, 'moneta/optionmerger'
13
15
  autoload :OptionSupport, 'moneta/mixins'
14
16
  autoload :Pool, 'moneta/pool'
15
17
  autoload :Proxy, 'moneta/proxy'
18
+ autoload :Semaphore, 'moneta/synchronize'
16
19
  autoload :Server, 'moneta/server'
17
20
  autoload :Shared, 'moneta/shared'
18
21
  autoload :Stack, 'moneta/stack'
19
22
  autoload :Transformer, 'moneta/transformer'
23
+ autoload :Utils, 'moneta/utils'
24
+ autoload :WeakCreate, 'moneta/weak'
25
+ autoload :WeakIncrement, 'moneta/weak'
20
26
  autoload :Wrapper, 'moneta/wrapper'
21
27
 
22
28
  module Adapters
@@ -63,7 +69,7 @@ module Moneta
63
69
  # @param [Hash] options
64
70
  # @return [Moneta store] newly created Moneta store
65
71
  # @option options [Boolean/Integer] :expires Ensure that store supports expiration by inserting
66
- # `Moneta::Expires` if the underlying adapter doesn't support it natively
72
+ # {Expires} if the underlying adapter doesn't support it natively
67
73
  # and set default expiration time
68
74
  # @option options [Boolean] :threadsafe (false) Ensure that the store is thread safe by inserting Moneta::Lock
69
75
  # @option options [Boolean/Hash] :logger (false) Add logger to proxy stack (Hash is passed to logger as options)
@@ -6,7 +6,6 @@ module Moneta
6
6
  # @api public
7
7
  class ActiveRecord
8
8
  include Defaults
9
- include IncrementSupport
10
9
 
11
10
  def self.tables
12
11
  @tables ||= {}
@@ -26,7 +25,16 @@ module Moneta
26
25
  c.primary_key = :k
27
26
  c
28
27
  end
29
- @table.establish_connection(options[:connection]) if options[:connection]
28
+
29
+ if options[:connection]
30
+ begin
31
+ @table.establish_connection(options[:connection])
32
+ rescue
33
+ tries ||= 0
34
+ (tries += 1) < 3 ? retry : raise
35
+ end
36
+ end
37
+
30
38
  unless @table.table_exists?
31
39
  @table.connection.create_table(@table.table_name, :id => false) do |t|
32
40
  # Do not use binary columns (Issue #17)
@@ -54,6 +62,9 @@ module Moneta
54
62
  record.v = value
55
63
  record.save
56
64
  value
65
+ rescue
66
+ tries ||= 0
67
+ (tries += 1) < 10 ? retry : raise
57
68
  end
58
69
 
59
70
  # (see Proxy#delete)
@@ -67,12 +78,25 @@ module Moneta
67
78
  # (see Proxy#increment)
68
79
  def increment(key, amount = 1, options = {})
69
80
  record = @table.where(:k => key).lock.first_or_initialize
70
- value = convert_for_increment(record.v) + amount
81
+ value = Utils.to_int(record.v) + amount
71
82
  record.v = value.to_s
72
83
  record.save
73
84
  value
74
85
  end
75
86
 
87
+ # (see Proxy#create)
88
+ def create(key, value, options = {})
89
+ record = @table.new
90
+ record.k = key
91
+ record.v = value
92
+ record.save
93
+ true
94
+ rescue
95
+ # FIXME: This catches too many errors
96
+ # it should only catch a not-unique-exception
97
+ false
98
+ end
99
+
76
100
  # (see Proxy#clear)
77
101
  def clear(options = {})
78
102
  @table.delete_all