moneta 1.4.1 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +425 -0
  3. data/CHANGES +17 -0
  4. data/CONTRIBUTORS +2 -0
  5. data/Gemfile +161 -60
  6. data/README.md +21 -17
  7. data/lib/moneta/adapter.rb +52 -0
  8. data/lib/moneta/adapters/activerecord.rb +77 -68
  9. data/lib/moneta/adapters/activesupportcache.rb +22 -31
  10. data/lib/moneta/adapters/cassandra.rb +114 -116
  11. data/lib/moneta/adapters/client.rb +17 -18
  12. data/lib/moneta/adapters/couch.rb +31 -26
  13. data/lib/moneta/adapters/datamapper.rb +9 -5
  14. data/lib/moneta/adapters/daybreak.rb +15 -21
  15. data/lib/moneta/adapters/dbm.rb +6 -12
  16. data/lib/moneta/adapters/file.rb +21 -13
  17. data/lib/moneta/adapters/fog.rb +5 -6
  18. data/lib/moneta/adapters/gdbm.rb +6 -12
  19. data/lib/moneta/adapters/hbase.rb +10 -12
  20. data/lib/moneta/adapters/kyotocabinet.rb +22 -27
  21. data/lib/moneta/adapters/leveldb.rb +14 -20
  22. data/lib/moneta/adapters/lmdb.rb +19 -22
  23. data/lib/moneta/adapters/localmemcache.rb +7 -13
  24. data/lib/moneta/adapters/lruhash.rb +20 -20
  25. data/lib/moneta/adapters/memcached/dalli.rb +25 -33
  26. data/lib/moneta/adapters/memcached/native.rb +14 -20
  27. data/lib/moneta/adapters/memory.rb +5 -7
  28. data/lib/moneta/adapters/mongo.rb +53 -52
  29. data/lib/moneta/adapters/pstore.rb +21 -27
  30. data/lib/moneta/adapters/redis.rb +42 -37
  31. data/lib/moneta/adapters/restclient.rb +17 -25
  32. data/lib/moneta/adapters/riak.rb +8 -9
  33. data/lib/moneta/adapters/sdbm.rb +6 -12
  34. data/lib/moneta/adapters/sequel/mysql.rb +8 -8
  35. data/lib/moneta/adapters/sequel/postgres.rb +17 -17
  36. data/lib/moneta/adapters/sequel/postgres_hstore.rb +47 -47
  37. data/lib/moneta/adapters/sequel/sqlite.rb +9 -9
  38. data/lib/moneta/adapters/sequel.rb +56 -65
  39. data/lib/moneta/adapters/sqlite.rb +37 -35
  40. data/lib/moneta/adapters/tdb.rb +8 -14
  41. data/lib/moneta/adapters/tokyocabinet.rb +19 -17
  42. data/lib/moneta/adapters/tokyotyrant.rb +29 -30
  43. data/lib/moneta/adapters/yaml.rb +1 -5
  44. data/lib/moneta/config.rb +101 -0
  45. data/lib/moneta/expires.rb +0 -1
  46. data/lib/moneta/expires_support.rb +3 -4
  47. data/lib/moneta/pool.rb +27 -7
  48. data/lib/moneta/proxy.rb +29 -0
  49. data/lib/moneta/server.rb +21 -14
  50. data/lib/moneta/version.rb +1 -1
  51. data/lib/moneta/wrapper.rb +5 -0
  52. data/lib/moneta.rb +2 -0
  53. data/moneta.gemspec +1 -0
  54. data/spec/active_support/cache_moneta_store_spec.rb +13 -13
  55. data/spec/features/null.rb +28 -28
  56. data/spec/features/persist.rb +3 -3
  57. data/spec/features/returndifferent.rb +4 -4
  58. data/spec/features/returnsame.rb +4 -4
  59. data/spec/features/store.rb +104 -104
  60. data/spec/helper.rb +15 -4
  61. data/spec/moneta/adapters/activerecord/adapter_activerecord_existing_connection_spec.rb +3 -1
  62. data/spec/moneta/adapters/activerecord/adapter_activerecord_spec.rb +15 -7
  63. data/spec/moneta/adapters/activerecord/standard_activerecord_spec.rb +5 -2
  64. data/spec/moneta/adapters/activerecord/standard_activerecord_with_expires_spec.rb +5 -2
  65. data/spec/moneta/adapters/activesupportcache/adapter_activesupportcache_spec.rb +3 -3
  66. data/spec/moneta/adapters/activesupportcache/adapter_activesupportcache_with_default_expires_spec.rb +2 -2
  67. data/spec/moneta/adapters/cassandra/adapter_cassandra_spec.rb +1 -1
  68. data/spec/moneta/adapters/cassandra/adapter_cassandra_with_default_expires_spec.rb +1 -1
  69. data/spec/moneta/adapters/cassandra/standard_cassandra_spec.rb +1 -1
  70. data/spec/moneta/adapters/client/client_helper.rb +4 -3
  71. data/spec/moneta/adapters/datamapper/adapter_datamapper_spec.rb +25 -8
  72. data/spec/moneta/adapters/datamapper/standard_datamapper_spec.rb +2 -2
  73. data/spec/moneta/adapters/datamapper/standard_datamapper_with_expires_spec.rb +2 -2
  74. data/spec/moneta/adapters/datamapper/standard_datamapper_with_repository_spec.rb +2 -2
  75. data/spec/moneta/adapters/faraday_helper.rb +3 -2
  76. data/spec/moneta/adapters/lruhash/adapter_lruhash_spec.rb +10 -6
  77. data/spec/moneta/adapters/memcached/dalli/adapter_memcached_dalli_spec.rb +13 -3
  78. data/spec/moneta/adapters/memcached/native/adapter_memcached_native_spec.rb +13 -3
  79. data/spec/moneta/adapters/mongo/adapter_mongo_spec.rb +2 -2
  80. data/spec/moneta/adapters/mongo/adapter_mongo_with_default_expires_spec.rb +1 -1
  81. data/spec/moneta/adapters/redis/adapter_redis_spec.rb +13 -3
  82. data/spec/moneta/adapters/redis/standard_redis_spec.rb +8 -1
  83. data/spec/moneta/adapters/sequel/adapter_sequel_spec.rb +4 -4
  84. data/spec/moneta/adapters/sequel/helper.rb +10 -5
  85. data/spec/moneta/adapters/sequel/standard_sequel_spec.rb +1 -1
  86. data/spec/moneta/adapters/sequel/standard_sequel_with_expires_spec.rb +1 -1
  87. data/spec/moneta/adapters/sqlite/adapter_sqlite_spec.rb +1 -1
  88. data/spec/moneta/adapters/sqlite/standard_sqlite_spec.rb +1 -1
  89. data/spec/moneta/adapters/sqlite/standard_sqlite_with_expires_spec.rb +1 -1
  90. data/spec/moneta/config_spec.rb +219 -0
  91. data/spec/moneta/proxies/enumerable/enumerable_spec.rb +2 -2
  92. data/spec/moneta/proxies/pool/pool_spec.rb +31 -3
  93. data/spec/moneta/proxies/transformer/transformer_bson_spec.rb +3 -1
  94. data/spec/moneta/proxies/transformer/transformer_marshal_escape_spec.rb +2 -0
  95. data/spec/rack/session_moneta_spec.rb +44 -25
  96. data/spec/restserver.rb +3 -14
  97. metadata +25 -15
  98. data/.travis.yml +0 -146
  99. data/spec/moneta/adapters/memcached/dalli/adapter_memcached_dalli_with_default_expires_spec.rb +0 -15
  100. data/spec/moneta/adapters/memcached/native/adapter_memcached_native_with_default_expires_spec.rb +0 -15
  101. data/spec/moneta/adapters/redis/adapter_redis_with_default_expires_spec.rb +0 -10
  102. data/spec/moneta/proxies/proxy/proxy_redis_spec.rb +0 -13
data/Gemfile CHANGED
@@ -1,90 +1,190 @@
1
1
  source 'https://rubygems.org'
2
2
  gemspec
3
3
 
4
- # Used by multiple backends / transformers
5
- gem 'multi_json', groups: %i{json couch cassandra}
6
- gem 'faraday', groups: %i{couch restclient}
7
- gem 'manticore', groups: %i{couch restclient}, platforms: :jruby
4
+ group :transformers, optional: true do
5
+ group :tnet, optional: true do
6
+ gem 'tnetstring'
7
+ end
8
+
9
+ group :bencode, optional: true do
10
+ gem 'bencode'
11
+ end
12
+
13
+ group :ox, optional: true do
14
+ gem 'ox', platforms: :ruby
15
+ end
16
+
17
+ group :bert, optional: true do
18
+ gem 'bert', platforms: :ruby
19
+ end
20
+
21
+ group :php, optional: true do
22
+ gem 'php-serialize'
23
+ end
24
+
25
+ group :bson, optional: true do
26
+ gem 'bson', '>= 4.0.0'
27
+ end
28
+
29
+ group :msgpack, optional: true do
30
+ gem 'msgpack', platforms: :ruby
31
+ gem 'msgpack-jruby', platforms: :jruby
32
+ end
33
+
34
+ # Compressors used by Transformer
35
+ group :bzip2, optional: true do
36
+ gem 'rbzip2', '>= 0.3.0'
37
+ end
38
+
39
+ group :lz4, optional: true do
40
+ gem 'lz4-ruby', platforms: :ruby
41
+ end
42
+
43
+ group :lzma, optional: true do
44
+ gem 'ruby-lzma', platforms: :ruby
45
+ end
46
+
47
+ group :lzo, optional: true do
48
+ gem 'lzoruby', platforms: :ruby
49
+ end
50
+
51
+ group :snappy, optional: true do
52
+ gem 'snappy', platforms: :ruby
53
+ end
54
+
55
+ group :quicklz, optional: true do
56
+ gem 'qlzruby', platforms: :ruby
57
+ end
58
+
59
+ # Hash transformer library
60
+ group :city, optional: true do
61
+ gem 'cityhash', platforms: :ruby
62
+ end
63
+ end
64
+
65
+ # Backends
66
+ group :Daybreak, optional: true do
67
+ gem 'daybreak'
68
+ end
69
+
70
+ group :ActiveRecord, optional: true do
71
+ gem 'activerecord', '~> 5.2'
72
+ end
8
73
 
9
- # Serializers used by Transformer
10
- gem 'tnetstring', group: :tnet
11
- gem 'bencode', group: :bencode
12
- gem 'ox', platforms: :ruby, group: :ox
13
- gem 'bert', platforms: :ruby, group: :bert
14
- gem 'php-serialize', group: :php
74
+ group :Redis, optional: true do
75
+ gem 'redis', '~> 4.2'
76
+ end
15
77
 
16
- group :bson do
17
- gem 'bson', '>= 4.0.0'
78
+ group :Mongo, optional: true do
79
+ gem 'mongo', '>= 2'
18
80
  end
19
81
 
20
- group :msgpack do
21
- gem 'msgpack', platforms: :ruby
22
- gem 'msgpack-jruby', platforms: :jruby
82
+ group :Sequel, optional: true do
83
+ gem 'sequel', '5.52.0'
23
84
  end
24
85
 
25
- # Compressors used by Transformer
26
- gem 'rbzip2', '>= 0.3.0', group: :bzip2
27
- gem 'lz4-ruby', platforms: :ruby, group: :lz4
28
- gem 'ruby-lzma', platforms: :ruby, group: :lzma
29
- gem 'lzoruby', platforms: :ruby, group: :lzo
30
- gem 'snappy', platforms: :ruby, group: :snappy
31
- gem 'qlzruby', platforms: :ruby, group: :quicklz
86
+ group :Memcached, optional: true do
87
+ group :MemcachedDalli, optional: true do
88
+ gem 'dalli', '~> 2.7.11'
89
+ end
32
90
 
33
- # Hash transformer library
34
- gem 'cityhash', platforms: :ruby, group: :city
91
+ group :MemcachedNative, optional: true do
92
+ gem 'jruby-memcached', platforms: :jruby
93
+ gem 'memcached', platforms: :ruby
94
+ end
95
+ end
35
96
 
36
- # Backends
37
- gem 'daybreak', group: :daybreak
38
- gem 'activerecord', '~> 5.2', group: :activerecord
39
- gem 'redis', '~> 4.0.0', group: :redis
40
- gem 'mongo', '>= 2', group: :mongo_official
41
- gem 'sequel', group: :sequel
42
- gem 'dalli', group: :memcached_dalli
43
- gem 'riak-client', group: :riak
44
- gem 'cassandra-driver', group: :cassandra
45
- gem 'tokyotyrant', group: :tokyotyrant
46
- gem 'hbaserb', group: :hbase
47
- gem 'localmemcache', platforms: :ruby, group: :localmemcache
48
- gem 'tdb', platforms: :ruby, group: :tdb
49
- gem 'leveldb-ruby', platforms: :ruby, group: :leveldb
50
- gem 'lmdb', platforms: :mri, group: :lmdb
51
- gem 'tokyocabinet', platforms: :ruby, group: :tokyocabinet
52
- gem 'kyotocabinet-ruby-reanimated', platforms: [:ruby_23, :ruby_24, :ruby_25, :ruby_26], group: :kyotocabinet
53
- gem 'memcached', platforms: :ruby, group: :memcached_native
54
- gem 'jruby-memcached', platforms: :jruby, group: :memcached_native
55
- gem 'activerecord-jdbch2-adapter', platforms: :jruby, group: :h2, github: 'jruby/activerecord-jdbc-adapter', glob: 'activerecord-jdbch2-adapter/*.gemspec', branch: '52-stable'
56
- gem 'ffi-gdbm', platforms: :jruby, group: :gdbm
57
- group :restclient do
58
- gem 'fishwife', platforms: :jruby
59
- gem 'rjack-logback', platforms: :jruby
60
- end
61
-
62
- group :datamapper do
63
- gem 'dm-core'
64
- gem 'dm-migrations'
65
- gem 'dm-mysql-adapter'
66
- end
67
-
68
- group :fog do
97
+ group :Riak, optional: true do
98
+ gem 'riak-client'
99
+ end
100
+
101
+ group :Cassandra, optional: true do
102
+ install_if lambda { RUBY_ENGINE != 'ruby' || Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.0.0') } do
103
+ gem 'cassandra-driver'
104
+ end
105
+ end
106
+
107
+ group :TokyoTyrant, optional: true do
108
+ gem 'tokyotyrant'
109
+ end
110
+
111
+ group :HBase, optional: true do
112
+ gem 'hbaserb'
113
+ end
114
+
115
+ group :LocalMemCache, optional: true do
116
+ gem 'localmemcache', platforms: :ruby
117
+ end
118
+
119
+ group :TDB, optional: true do
120
+ gem 'tdb', platforms: :ruby
121
+ end
122
+
123
+ group :LevelDB, optional: true do
124
+ gem 'leveldb-ruby', platforms: :ruby
125
+ end
126
+
127
+ group :LMDB, optional: true do
128
+ gem 'lmdb', platforms: :mri
129
+ end
130
+
131
+ group :TokyoCabinet, optional: true do
132
+ gem 'tokyocabinet', platforms: :ruby
133
+ end
134
+
135
+ group :KyotoCabinet, optional: true do
136
+ install_if lambda { Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.7.0') } do
137
+ gem 'kyotocabinet-ruby-reanimated', platforms: :ruby
138
+ end
139
+ end
140
+
141
+ group :H2, optional: true do
142
+ gem 'activerecord-jdbch2-adapter', platforms: :jruby, github: 'jruby/activerecord-jdbc-adapter', glob: 'activerecord-jdbch2-adapter/*.gemspec', branch: '52-stable'
143
+ end
144
+
145
+ group :GDBM, optional: true do
146
+ gem 'ffi-gdbm', platforms: :jruby
147
+ end
148
+
149
+ group :RestClient do
150
+ gem 'faraday'
151
+ gem 'webrick'
152
+ end
153
+
154
+ group :DataMapper, optional: true do
155
+ install_if lambda { RUBY_ENGINE == 'ruby' && Gem::Version.new(RUBY_VERSION) < Gem::Version.new('3.0.0') } do
156
+ gem 'dm-core'
157
+ gem 'dm-migrations'
158
+ gem 'dm-mysql-adapter'
159
+ end
160
+ end
161
+
162
+ group :Fog, optional: true do
69
163
  gem 'fog-aws', '>= 1.11.1'
70
164
  gem 'mime-types'
71
165
  end
72
166
 
73
- group :mysql do
167
+ group :mysql, optional: true do
74
168
  gem 'activerecord-jdbcmysql-adapter', platforms: :jruby
75
169
  gem 'mysql2', platforms: :ruby
76
170
  end
77
171
 
78
- group :sqlite do
172
+ group :sqlite, optional: true do
79
173
  gem 'activerecord-jdbcsqlite3-adapter', platforms: :jruby
80
174
  gem 'sqlite3', '~> 1.3.6', platforms: :ruby
81
175
  end
82
176
 
83
- group :postgresql do
177
+ group :postgresql, optional: true do
84
178
  gem 'activerecord-jdbcpostgresql-adapter', platforms: :jruby
85
179
  gem 'pg', platforms: :ruby
86
180
  end
87
181
 
182
+ group :SDBM, optional: true do
183
+ install_if lambda { Gem::Version.new(RUBY_VERSION) >= Gem::Version.new('3.0.0') } do
184
+ gem 'sdbm', platforms: :ruby
185
+ end
186
+ end
187
+
88
188
  # Rack integration testing
89
189
  group :rack do
90
190
  gem 'rack'
@@ -106,4 +206,5 @@ end
106
206
  # Used for running a dev console
107
207
  group :console, optional: true do
108
208
  gem 'irb'
209
+ gem 'rdoc'
109
210
  end
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Moneta: A unified interface for key/value stores
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/moneta.svg)](http://rubygems.org/gems/moneta)
4
- [![Build Status](https://secure.travis-ci.org/moneta-rb/moneta.svg?branch=master)](http://travis-ci.org/moneta-rb/moneta)
4
+ [![Build Status](https://github.com/moneta-rb/moneta/actions/workflows/ruby.yml/badge.svg)](https://github.com/moneta-rb/moneta/actions/workflows/ruby.yml)
5
5
  [![Code Climate](https://codeclimate.com/github/moneta-rb/moneta.svg)](https://codeclimate.com/github/moneta-rb/moneta)
6
6
  [![Flattr this git repo](http://api.flattr.com/button/flattr-badge-large.png)](https://flattr.com/submit/auto?user_id=min4d&url=https://github.com/moneta-rb/moneta&title=Moneta&language=&tags=github&category=software)
7
7
 
@@ -32,7 +32,7 @@ same for template languages.
32
32
  * 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 :)
33
33
  * 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...)
34
34
 
35
- Moneta is tested thoroughly using [Travis-CI](http://travis-ci.org/moneta-rb/moneta).
35
+ Moneta is tested thoroughly using [GitHub Actions](https://github.com/moneta-rb/moneta/actions).
36
36
 
37
37
  ------
38
38
 
@@ -74,11 +74,11 @@ store.close
74
74
 
75
75
  * Source: <http://github.com/moneta-rb/moneta>
76
76
  * Bugs: <http://github.com/moneta-rb/moneta/issues>
77
- * Tests and benchmarks: <http://travis-ci.org/moneta-rb/moneta>
77
+ * Tests and benchmarks: <https://github.com/moneta-rb/moneta/actions>
78
78
  * API documentation:
79
79
  * Latest Gem: <http://rubydoc.info/gems/moneta/frames>
80
- * GitHub master: <http://rubydoc.info/github/moneta-rb/moneta/master/frames>
81
- * Changelog: <https://github.com/moneta-rb/moneta/blob/master/CHANGES>
80
+ * GitHub main: <http://rubydoc.info/github/moneta-rb/moneta/main/frames>
81
+ * Changelog: <https://github.com/moneta-rb/moneta/blob/main/CHANGES>
82
82
 
83
83
  In case you are wondering, Moneta uses [Semantic Versioning](https://semver.org/) since v1.0.0.
84
84
 
@@ -135,7 +135,7 @@ to upgrade to a real key/value store.
135
135
 
136
136
  ### Backend feature matrix
137
137
 
138
- __NOTE:__ <a name="backend-matrix"></a>The backend matrix is much more readable on rubydoc.info than on github. [Go there!](http://rubydoc.info/github/moneta-rb/moneta/master/file/README.md#backend-matrix)
138
+ __NOTE:__ <a name="backend-matrix"></a>The backend matrix is much more readable on rubydoc.info than on github. [Go there!](http://rubydoc.info/github/moneta-rb/moneta/main/file/README.md#backend-matrix)
139
139
 
140
140
  <table>
141
141
  <tr><th>Adapter</th><th>Required gems</th><th style="writing-mode:tb">MRI support<sup>1</sup></th><th style="writing-mode:tb">JRuby support<sup>1</sup></th><th style="writing-mode:tb">Multi-thread safe<sup>2</sup></th><th style="writing-mode:tb">Multi-process safe<sup>3</sup></th><th style="writing-mode:tb">Atomic increment<sup>4</sup></th><th style="writing-mode:tb">Atomic create<sup>5</sup></th><th style="writing-mode:tb">Native expires<sup>6</sup></th><th style="writing-mode:tb">Persistent</th><th style="writing-mode:tb">Key Traversal</th><th style="writing-mode:tb">Bulk read<sup>7</sup></th><th style="writing-mode:tb">Bulk write<sup>8</sup></th><th>Description</th></tr>
@@ -221,7 +221,9 @@ __NOTE:__ <a name="backend-matrix"></a>The backend matrix is much more readable
221
221
  3. Share a Moneta store between multiple processes using `Moneta::Shared` (See below).
222
222
  4. If a store provides atomic increment it can be used with `Moneta::Semaphore`. You can add weak `#increment` support using the `Moneta::WeakIncrement` proxy.
223
223
  5. If a store provides atomic creation it can be used with `Moneta::Mutex`. You can add weak `#create` support using the `Moneta::WeakCreate` proxy.
224
- 6. Add expiration support by using `Moneta::Expires` or by passing the option `expires: true` to `Moneta#new`.
224
+ 6. Add expiration support by using `Moneta::Expires` or by passing the option
225
+ `expires: true` (or `expires: <seconds>` if you want all values to expire by
226
+ default) to `Moneta#new`.
225
227
  7. This indicates that there is some performance gain when fetching multiple values at once using `#values_at`/`#fetch_values` or `#slice`. For instance, the `MGET` instruction in Redis, or the ability to retrieve several rows in one query in SQL.
226
228
  8. This indicates that there is some performance gain when storing multiple key/value pairs at once using `#merge!`/`#update`.
227
229
  9. Sqlite/YAML/PStore are multiprocess safe, but the performance suffers badly since the whole database file must be locked for writing. Use a key/value server if you want multiprocess concurrency!
@@ -909,15 +911,17 @@ Person.adapter :memory, Moneta.new(:Redis)
909
911
 
910
912
  ## Testing and Benchmarks
911
913
 
912
- Testing is done using [Travis-CI](http://travis-ci.org/moneta-rb/moneta).
913
- Currently we support MRI Ruby >= 2.3.0 and the JRuby >= 9.2.9.0.
914
+ Testing is done using [GitHub Actions](https://github.com/moneta-rb/moneta/actions).
915
+ Currently we support MRI Ruby >= 2.4.0 (but not yet 3.x) and the JRuby >=
916
+ 9.2.9.0. MRI 2.3.0 should mostly still work, but is no longer tested in CI.
914
917
 
915
- Benchmarks for each store are done on
916
- [Travis-CI](http://travis-ci.org/moneta-rb/moneta) for each build. Take a look
917
- there to compare the speed of the different key value stores for different
918
- key/value sizes and size distributions. Feel free to add your own
919
- configurations! The impact of Moneta should be minimal since it is only a thin
920
- layer on top of the different stores.
918
+ ~~Benchmarks for each store are done on
919
+ [Travis-CI](http://travis-ci.org/moneta-rb/moneta) for each build.~~ At the
920
+ time of writing, benchmarks still need to be migrated from Travis to GitHub
921
+ Actions. Take a look there to compare the speed of the different key value
922
+ stores for different key/value sizes and size distributions. Feel free to add
923
+ your own configurations! The impact of Moneta should be minimal since it is
924
+ only a thin layer on top of the different stores.
921
925
 
922
926
 
923
927
  ------
@@ -937,8 +941,8 @@ If you want support for another adapter you can at first at it to the list of
937
941
  missing adapters at https://github.com/moneta-rb/moneta/issues/16
938
942
 
939
943
  If you choose to implement an adapter please also add tests. Please check also
940
- if travis.yml needs changes, for example if you need to start additional
941
- services.
944
+ if anything in `.github/workflows` needs changes, for example if you need to
945
+ start additional services.
942
946
 
943
947
  Check if the default settings in Moneta#new are appropriate for your adapter. If
944
948
  not specify a better one.
@@ -0,0 +1,52 @@
1
+ module Moneta
2
+ # Adapter base class
3
+ # @api public
4
+ class Adapter
5
+ include Defaults
6
+ include Config
7
+
8
+ attr_reader :backend
9
+
10
+ class << self
11
+ # Define a block used to build this adapter's backend. The block will
12
+ # receive as keyword arguments any options passed to the adapter during
13
+ # initialization that are not config settings.
14
+ #
15
+ # If the adapter is initialized with a `:backend` option, this will be used
16
+ # instead, and the block won't be called.
17
+ #
18
+ # @param [Boolean] required
19
+ # @yield [**options] options passed to the adapter's initialize method
20
+ # @yieldreturn [Object] The backend to use
21
+ def backend(required: true, &block)
22
+ raise "backend block already set" if class_variables(false).include?(:@@backend_block)
23
+ class_variable_set(:@@backend_block, block)
24
+ class_variable_set(:@@backend_required, true) if required
25
+ end
26
+
27
+ def backend_block
28
+ class_variable_get(:@@backend_block) if class_variable_defined?(:@@backend_block)
29
+ end
30
+
31
+ def backend_required?
32
+ class_variable_defined?(:@@backend_required)
33
+ end
34
+ end
35
+
36
+ # @param [Hash] options
37
+ def initialize(options = {})
38
+ set_backend(**configure(**options))
39
+ end
40
+
41
+ private
42
+
43
+ def set_backend(backend: nil, **options)
44
+ @backend = backend ||
45
+ if backend_block = self.class.backend_block
46
+ instance_exec(**options, &backend_block)
47
+ end
48
+
49
+ raise ArgumentError, 'backend needs to be set - refer to adapter documentation' if !@backend && self.class.backend_required?
50
+ end
51
+ end
52
+ end
@@ -5,12 +5,10 @@ module Moneta
5
5
  module Adapters
6
6
  # ActiveRecord as key/value stores
7
7
  # @api public
8
- class ActiveRecord
9
- include Defaults
10
-
8
+ class ActiveRecord < Adapter
11
9
  supports :create, :increment, :each_key
12
10
 
13
- attr_reader :table, :key_column, :value_column
11
+ attr_reader :table
14
12
  delegate :with_connection, to: :connection_pool
15
13
 
16
14
  @connection_lock = ::Mutex.new
@@ -37,9 +35,30 @@ module Moneta
37
35
  end
38
36
  end
39
37
 
38
+ config :key_column, default: :k
39
+ config :value_column, default: :v
40
+
41
+ backend required: false do |table: :moneta, connection: nil, create_table: nil|
42
+ @spec = spec_for_connection(connection)
43
+
44
+ # Ensure the table name is a symbol.
45
+ table_name = table.to_sym
46
+
47
+ if create_table == nil
48
+ default_create_table(table_name)
49
+ elsif create_table
50
+ with_connection(&create_table)
51
+ end
52
+
53
+ @table = ::Arel::Table.new(table_name)
54
+
55
+ # backend is only used if there's an existing ActiveRecord model
56
+ nil
57
+ end
58
+
40
59
  # @param [Hash] options
41
60
  # @option options [Object] :backend A class object inheriting from ActiveRecord::Base to use as a table
42
- # @option options [String] :table ('moneta') Table name
61
+ # @option options [String,Symbol] :table (:moneta) Table name
43
62
  # @option options [Hash/String/Symbol] :connection ActiveRecord connection configuration (`Hash` or `String`), or
44
63
  # symbol giving the name of a Rails connection (e.g. :production)
45
64
  # @option options [Proc, Boolean] :create_table Proc called with a connection if table
@@ -47,54 +66,12 @@ module Moneta
47
66
  # @option options [Symbol] :key_column (:k) The name of the column to use for keys
48
67
  # @option options [Symbol] :value_column (:v) The name of the column to use for values
49
68
  def initialize(options = {})
50
- @key_column = options.delete(:key_column) || :k
51
- @value_column = options.delete(:value_column) || :v
69
+ super
52
70
 
53
- if backend = options.delete(:backend)
71
+ # If a :backend was provided, use it to set the spec and table
72
+ if backend
54
73
  @spec = backend.connection_pool.spec
55
- @table = ::Arel::Table.new(backend.table_name.to_sym)
56
- else
57
- # Feed the connection info into ActiveRecord and get back a name to use for getting the
58
- # connection pool
59
- connection = options.delete(:connection)
60
- @spec =
61
- case connection
62
- when Symbol
63
- connection
64
- when Hash, String
65
- # Normalize the connection specification to a hash
66
- resolver = ::ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new \
67
- 'dummy' => connection
68
-
69
- # Turn the config into a standardised hash, sans a couple of bits
70
- hash = resolver.resolve(:dummy)
71
- hash.delete('name')
72
- hash.delete(:password) # For security
73
- # Make a name unique to this config
74
- name = 'moneta?' + URI.encode_www_form(hash.to_a.sort)
75
- # Add into configurations unless its already there (initially done without locking for
76
- # speed)
77
- unless self.class.configurations.key? name
78
- self.class.connection_lock.synchronize do
79
- self.class.configurations[name] = connection \
80
- unless self.class.configurations.key? name
81
- end
82
- end
83
-
84
- name.to_sym
85
- else
86
- ::ActiveRecord::Base.connection_pool.spec.name.to_s
87
- end
88
-
89
- table_name = (options.delete(:table) || :moneta).to_sym
90
- create_table_proc = options.delete(:create_table)
91
- if create_table_proc == nil
92
- create_table(table_name)
93
- elsif create_table_proc
94
- with_connection(&create_table_proc)
95
- end
96
-
97
- @table = ::Arel::Table.new(table_name)
74
+ @table = ::Arel::Table.new(backend.table_name)
98
75
  end
99
76
  end
100
77
 
@@ -110,8 +87,8 @@ module Moneta
110
87
  # (see Proxy#each_key)
111
88
  def each_key(&block)
112
89
  with_connection do |conn|
113
- return enum_for(:each_key) { conn.select_value(arel_sel.project(table[key_column].count)) } unless block_given?
114
- conn.select_values(arel_sel.project(table[key_column])).each { |k| yield(k) }
90
+ return enum_for(:each_key) { conn.select_value(arel_sel.project(table[config.key_column].count)) } unless block_given?
91
+ conn.select_values(arel_sel.project(table[config.key_column])).each { |k| yield(k) }
115
92
  end
116
93
  self
117
94
  end
@@ -136,10 +113,10 @@ module Moneta
136
113
  def delete(key, options = {})
137
114
  with_connection do |conn|
138
115
  conn.transaction do
139
- sel = arel_sel_key(key).project(table[value_column]).lock
116
+ sel = arel_sel_key(key).project(table[config.value_column]).lock
140
117
  value = decode(conn, conn.select_value(sel))
141
118
 
142
- del = arel_del.where(table[key_column].eq(key))
119
+ del = arel_del.where(table[config.key_column].eq(key))
143
120
  conn.delete(del)
144
121
 
145
122
  value
@@ -155,7 +132,7 @@ module Moneta
155
132
  amount
156
133
  rescue ::ActiveRecord::RecordNotUnique
157
134
  conn.transaction do
158
- sel = arel_sel_key(key).project(table[value_column]).lock
135
+ sel = arel_sel_key(key).project(table[config.value_column]).lock
159
136
  value = decode(conn, conn.select_value(sel))
160
137
  value = (value ? Integer(value) : 0) + amount
161
138
  # Re-raise if the upate affects no rows (i.e. row deleted after attempted insert,
@@ -213,13 +190,13 @@ module Moneta
213
190
 
214
191
  sel = arel_sel
215
192
  .join(temp_table)
216
- .on(table[key_column].eq(temp_table[:key]))
217
- .project(table[key_column], table[value_column])
193
+ .on(table[config.key_column].eq(temp_table[:key]))
194
+ .project(table[config.key_column], table[config.value_column])
218
195
  sel = sel.lock if lock
219
196
  result = conn.select_all(sel)
220
197
 
221
- k = key_column.to_s
222
- v = value_column.to_s
198
+ k = config.key_column.to_s
199
+ v = config.value_column.to_s
223
200
  result.map do |row|
224
201
  [row[k], decode(conn, row[v])]
225
202
  end
@@ -275,7 +252,7 @@ module Moneta
275
252
  self.class.retrieve_or_establish_connection_pool(@spec)
276
253
  end
277
254
 
278
- def create_table(table_name)
255
+ def default_create_table(table_name)
279
256
  with_connection do |conn|
280
257
  return if conn.table_exists?(table_name)
281
258
 
@@ -283,10 +260,10 @@ module Moneta
283
260
  self.class.connection_lock.synchronize do
284
261
  conn.create_table(table_name, id: false) do |t|
285
262
  # Do not use binary key (Issue #17)
286
- t.string key_column, null: false
287
- t.binary value_column
263
+ t.string config.key_column, null: false
264
+ t.binary config.value_column
288
265
  end
289
- conn.add_index(table_name, key_column, unique: true)
266
+ conn.add_index(table_name, config.key_column, unique: true)
290
267
  end
291
268
  end
292
269
  end
@@ -304,21 +281,21 @@ module Moneta
304
281
  end
305
282
 
306
283
  def arel_sel_key(key)
307
- arel_sel.where(table[key_column].eq(key))
284
+ arel_sel.where(table[config.key_column].eq(key))
308
285
  end
309
286
 
310
287
  def conn_ins(conn, key, value)
311
288
  ins = ::Arel::InsertManager.new.into(table)
312
- ins.insert([[table[key_column], key], [table[value_column], value]])
289
+ ins.insert([[table[config.key_column], key], [table[config.value_column], value]])
313
290
  conn.insert ins
314
291
  end
315
292
 
316
293
  def conn_upd(conn, key, value)
317
- conn.update arel_upd.where(table[key_column].eq(key)).set([[table[value_column], value]])
294
+ conn.update arel_upd.where(table[config.key_column].eq(key)).set([[table[config.value_column], value]])
318
295
  end
319
296
 
320
297
  def conn_sel_value(conn, key)
321
- decode(conn, conn.select_value(arel_sel_key(key).project(table[value_column])))
298
+ decode(conn, conn.select_value(arel_sel_key(key).project(table[config.value_column])))
322
299
  end
323
300
 
324
301
  def encode(conn, value)
@@ -346,6 +323,38 @@ module Moneta
346
323
  value
347
324
  end
348
325
  end
326
+
327
+ # Feed the connection info into ActiveRecord and get back a name to use
328
+ # for getting the connection pool
329
+ def spec_for_connection(connection)
330
+ case connection
331
+ when Symbol
332
+ connection
333
+ when Hash, String
334
+ # Normalize the connection specification to a hash
335
+ resolver = ::ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new \
336
+ 'dummy' => connection
337
+
338
+ # Turn the config into a standardised hash, sans a couple of bits
339
+ hash = resolver.resolve(:dummy)
340
+ hash.delete('name')
341
+ hash.delete(:password) # For security
342
+ # Make a name unique to this config
343
+ name = 'moneta?' + URI.encode_www_form(hash.to_a.sort)
344
+ # Add into configurations unless its already there (initially done without locking for
345
+ # speed)
346
+ unless self.class.configurations.key? name
347
+ self.class.connection_lock.synchronize do
348
+ self.class.configurations[name] = connection \
349
+ unless self.class.configurations.key? name
350
+ end
351
+ end
352
+
353
+ name.to_sym
354
+ else
355
+ ::ActiveRecord::Base.connection_pool.spec.name.to_s
356
+ end
357
+ end
349
358
  end
350
359
  end
351
360
  end