moneta 1.4.0 → 1.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (95) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ruby.yml +415 -0
  3. data/CHANGES +18 -0
  4. data/CONTRIBUTORS +2 -0
  5. data/Gemfile +144 -56
  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 +59 -50
  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/helper.rb +14 -3
  56. data/spec/moneta/adapters/activerecord/adapter_activerecord_existing_connection_spec.rb +3 -1
  57. data/spec/moneta/adapters/activerecord/adapter_activerecord_spec.rb +15 -7
  58. data/spec/moneta/adapters/activerecord/standard_activerecord_spec.rb +5 -2
  59. data/spec/moneta/adapters/activerecord/standard_activerecord_with_expires_spec.rb +5 -2
  60. data/spec/moneta/adapters/activesupportcache/adapter_activesupportcache_spec.rb +3 -3
  61. data/spec/moneta/adapters/activesupportcache/adapter_activesupportcache_with_default_expires_spec.rb +2 -2
  62. data/spec/moneta/adapters/client/client_helper.rb +4 -3
  63. data/spec/moneta/adapters/datamapper/adapter_datamapper_spec.rb +25 -8
  64. data/spec/moneta/adapters/datamapper/standard_datamapper_spec.rb +2 -2
  65. data/spec/moneta/adapters/datamapper/standard_datamapper_with_expires_spec.rb +2 -2
  66. data/spec/moneta/adapters/datamapper/standard_datamapper_with_repository_spec.rb +2 -2
  67. data/spec/moneta/adapters/faraday_helper.rb +3 -2
  68. data/spec/moneta/adapters/lruhash/adapter_lruhash_spec.rb +10 -6
  69. data/spec/moneta/adapters/memcached/dalli/adapter_memcached_dalli_spec.rb +13 -3
  70. data/spec/moneta/adapters/memcached/native/adapter_memcached_native_spec.rb +13 -3
  71. data/spec/moneta/adapters/mongo/adapter_mongo_spec.rb +17 -3
  72. data/spec/moneta/adapters/mongo/adapter_mongo_with_default_expires_spec.rb +4 -4
  73. data/spec/moneta/adapters/mongo/standard_mongo_spec.rb +1 -1
  74. data/spec/moneta/adapters/redis/adapter_redis_spec.rb +13 -3
  75. data/spec/moneta/adapters/redis/standard_redis_spec.rb +8 -1
  76. data/spec/moneta/adapters/sequel/adapter_sequel_spec.rb +4 -4
  77. data/spec/moneta/adapters/sequel/helper.rb +10 -5
  78. data/spec/moneta/adapters/sequel/standard_sequel_spec.rb +1 -1
  79. data/spec/moneta/adapters/sequel/standard_sequel_with_expires_spec.rb +1 -1
  80. data/spec/moneta/adapters/sqlite/adapter_sqlite_spec.rb +1 -1
  81. data/spec/moneta/adapters/sqlite/standard_sqlite_spec.rb +1 -1
  82. data/spec/moneta/adapters/sqlite/standard_sqlite_with_expires_spec.rb +1 -1
  83. data/spec/moneta/config_spec.rb +219 -0
  84. data/spec/moneta/proxies/pool/pool_spec.rb +31 -3
  85. data/spec/moneta/proxies/transformer/transformer_bson_spec.rb +3 -1
  86. data/spec/moneta/proxies/transformer/transformer_marshal_escape_spec.rb +2 -0
  87. data/spec/rack/session_moneta_spec.rb +44 -25
  88. data/spec/restserver.rb +3 -14
  89. metadata +25 -17
  90. data/.travis.yml +0 -146
  91. data/spec/moneta/adapters/memcached/dalli/adapter_memcached_dalli_with_default_expires_spec.rb +0 -15
  92. data/spec/moneta/adapters/memcached/native/adapter_memcached_native_with_default_expires_spec.rb +0 -15
  93. data/spec/moneta/adapters/redis/adapter_redis_with_default_expires_spec.rb +0 -10
  94. data/spec/moneta/proxies/proxy/proxy_redis_spec.rb +0 -13
  95. data/spec/support/mongo_helper.rb +0 -12
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
@@ -2,32 +2,23 @@ module Moneta
2
2
  module Adapters
3
3
  # ActiveSupport::Cache::Store adapter
4
4
  # @api public
5
- class ActiveSupportCache
6
- include Defaults
5
+ class ActiveSupportCache < Adapter
7
6
  include ExpiresSupport
8
7
 
9
8
  supports :increment
10
9
 
11
- # @param [Hash] options
12
- # @option options [Numeric] :expires default expiration in seconds
13
- def initialize(options = {})
14
- self.default_expires = options.delete(:expires)
15
- @backend =
16
- if options[:backend]
17
- options[:backend]
18
- elsif defined?(Rails)
19
- Rails.cache
20
- else
21
- raise ArgumentError, 'Option :backend is required'
22
- end
23
- end
10
+ # @!method initialize(options = {})
11
+ # @param [Hash] options
12
+ # @option options [ActiveSupport::Cache::Store] :backend (Rails.cache) Cache store to use
13
+ # @option options [Numeric] :expires default expiration in seconds
14
+ backend { Rails.cache if defined?(Rails) }
24
15
 
25
16
  # (see Proxy#key?)
26
17
  def key?(key, options = {})
27
- @backend.exist?(key).tap do |exists|
18
+ backend.exist?(key).tap do |exists|
28
19
  if exists && (expires = expires_value(options, nil)) != nil
29
- value = @backend.read(key, options)
30
- @backend.write(key, value, options.merge(expires_in: expires ? expires.seconds : nil))
20
+ value = backend.read(key, options)
21
+ backend.write(key, value, options.merge(expires_in: expires ? expires.seconds : nil))
31
22
  end
32
23
  end
33
24
  end
@@ -35,9 +26,9 @@ module Moneta
35
26
  # (see Proxy#load)
36
27
  def load(key, options = {})
37
28
  expires = expires_value(options, nil)
38
- value = @backend.read(key, options)
29
+ value = backend.read(key, options)
39
30
  if value and expires != nil
40
- @backend.write(key, value, options.merge(expires_in: expires ? expires.seconds : nil))
31
+ backend.write(key, value, options.merge(expires_in: expires ? expires.seconds : nil))
41
32
  end
42
33
  value
43
34
  end
@@ -45,7 +36,7 @@ module Moneta
45
36
  # (see Proxy#store)
46
37
  def store(key, value, options = {})
47
38
  expires = expires_value(options)
48
- @backend.write(key, value, options.merge(expires_in: expires ? expires.seconds : nil))
39
+ backend.write(key, value, options.merge(expires_in: expires ? expires.seconds : nil))
49
40
  value
50
41
  end
51
42
 
@@ -53,11 +44,11 @@ module Moneta
53
44
  def increment(key, amount = 1, options = {})
54
45
  expires = expires_value(options)
55
46
  options.delete(:raw)
56
- existing = Integer(@backend.fetch(key, options.merge(raw: true)) { 0 })
47
+ existing = Integer(backend.fetch(key, options.merge(raw: true)) { 0 })
57
48
  if amount > 0
58
- @backend.increment(key, amount, options.merge(expires_in: expires ? expires.seconds : nil))
49
+ backend.increment(key, amount, options.merge(expires_in: expires ? expires.seconds : nil))
59
50
  elsif amount < 0
60
- @backend.decrement(key, -amount, options.merge(expires_in: expires ? expires.seconds : nil))
51
+ backend.decrement(key, -amount, options.merge(expires_in: expires ? expires.seconds : nil))
61
52
  else
62
53
  existing
63
54
  end
@@ -65,25 +56,25 @@ module Moneta
65
56
 
66
57
  # (see Proxy#delete)
67
58
  def delete(key, options = {})
68
- value = @backend.read(key, options)
59
+ value = backend.read(key, options)
69
60
  if value != nil
70
- @backend.delete(key, options)
61
+ backend.delete(key, options)
71
62
  options[:raw] ? value.to_s : value
72
63
  end
73
64
  end
74
65
 
75
66
  # (see Proxy#clear)
76
67
  def clear(options = {})
77
- @backend.clear
68
+ backend.clear
78
69
  self
79
70
  end
80
71
 
81
72
  # (see Proxy#slice)
82
73
  def slice(*keys, **options)
83
- hash = @backend.read_multi(*keys)
74
+ hash = backend.read_multi(*keys)
84
75
  if (expires = expires_value(options, nil)) != nil
85
76
  hash.each do |key, value|
86
- @backend.write(key, value, options.merge(expires_in: expires ? expires.seconds : nil))
77
+ backend.write(key, value, options.merge(expires_in: expires ? expires.seconds : nil))
87
78
  end
88
79
  end
89
80
  if options[:raw]
@@ -115,13 +106,13 @@ module Moneta
115
106
 
116
107
  hash = Hash === pairs ? pairs : Hash[pairs.to_a]
117
108
  expires = expires_value(options)
118
- @backend.write_multi(hash, options.merge(expires_in: expires ? expires.seconds : nil))
109
+ backend.write_multi(hash, options.merge(expires_in: expires ? expires.seconds : nil))
119
110
  self
120
111
  end
121
112
 
122
113
  private
123
114
 
124
- def expires_value(options, default = @default_expires)
115
+ def expires_value(options, default = config.expires)
125
116
  super.tap { options.delete(:expires) unless options.frozen? }
126
117
  end
127
118
  end