moneta 1.1.1 → 1.4.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (207) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.rubocop.yml +194 -0
  4. data/.travis.yml +65 -25
  5. data/CHANGES +36 -0
  6. data/CONTRIBUTORS +4 -2
  7. data/Gemfile +94 -65
  8. data/README.md +50 -25
  9. data/feature_matrix.yaml +3 -11
  10. data/lib/action_dispatch/middleware/session/moneta_store.rb +1 -0
  11. data/lib/active_support/cache/moneta_store.rb +5 -5
  12. data/lib/moneta.rb +18 -10
  13. data/lib/moneta/adapters/activerecord.rb +35 -19
  14. data/lib/moneta/adapters/activesupportcache.rb +3 -7
  15. data/lib/moneta/adapters/cassandra.rb +24 -16
  16. data/lib/moneta/adapters/client.rb +62 -21
  17. data/lib/moneta/adapters/couch.rb +225 -80
  18. data/lib/moneta/adapters/datamapper.rb +1 -0
  19. data/lib/moneta/adapters/file.rb +9 -6
  20. data/lib/moneta/adapters/hbase.rb +1 -1
  21. data/lib/moneta/adapters/kyotocabinet.rb +8 -7
  22. data/lib/moneta/adapters/leveldb.rb +1 -1
  23. data/lib/moneta/adapters/lmdb.rb +3 -4
  24. data/lib/moneta/adapters/lruhash.rb +29 -62
  25. data/lib/moneta/adapters/memcached.rb +1 -0
  26. data/lib/moneta/adapters/memcached/dalli.rb +1 -1
  27. data/lib/moneta/adapters/memcached/native.rb +10 -8
  28. data/lib/moneta/adapters/mongo.rb +264 -6
  29. data/lib/moneta/adapters/null.rb +1 -2
  30. data/lib/moneta/adapters/pstore.rb +3 -2
  31. data/lib/moneta/adapters/redis.rb +8 -4
  32. data/lib/moneta/adapters/restclient.rb +12 -3
  33. data/lib/moneta/adapters/riak.rb +2 -2
  34. data/lib/moneta/adapters/sequel.rb +68 -494
  35. data/lib/moneta/adapters/sequel/mysql.rb +66 -0
  36. data/lib/moneta/adapters/sequel/postgres.rb +80 -0
  37. data/lib/moneta/adapters/sequel/postgres_hstore.rb +240 -0
  38. data/lib/moneta/adapters/sequel/sqlite.rb +57 -0
  39. data/lib/moneta/adapters/sqlite.rb +10 -10
  40. data/lib/moneta/adapters/tokyotyrant.rb +1 -1
  41. data/lib/moneta/builder.rb +2 -3
  42. data/lib/moneta/create_support.rb +21 -0
  43. data/lib/moneta/dbm_adapter.rb +31 -0
  44. data/lib/moneta/{mixins.rb → defaults.rb} +3 -302
  45. data/lib/moneta/each_key_support.rb +27 -0
  46. data/lib/moneta/enumerable.rb +38 -0
  47. data/lib/moneta/expires.rb +12 -12
  48. data/lib/moneta/expires_support.rb +60 -0
  49. data/lib/moneta/fallback.rb +84 -0
  50. data/lib/moneta/hash_adapter.rb +68 -0
  51. data/lib/moneta/increment_support.rb +16 -0
  52. data/lib/moneta/lock.rb +7 -2
  53. data/lib/moneta/logger.rb +2 -2
  54. data/lib/moneta/nil_values.rb +35 -0
  55. data/lib/moneta/option_support.rb +51 -0
  56. data/lib/moneta/optionmerger.rb +0 -1
  57. data/lib/moneta/pool.rb +312 -30
  58. data/lib/moneta/proxy.rb +3 -3
  59. data/lib/moneta/server.rb +216 -65
  60. data/lib/moneta/shared.rb +13 -7
  61. data/lib/moneta/stack.rb +6 -6
  62. data/lib/moneta/synchronize.rb +3 -3
  63. data/lib/moneta/transformer.rb +68 -24
  64. data/lib/moneta/transformer/config.rb +63 -43
  65. data/lib/moneta/transformer/helper.rb +3 -3
  66. data/lib/moneta/transformer/helper/bson.rb +7 -14
  67. data/lib/moneta/utils.rb +3 -9
  68. data/lib/moneta/version.rb +1 -1
  69. data/lib/moneta/weak_each_key.rb +2 -4
  70. data/lib/rack/cache/moneta.rb +13 -11
  71. data/lib/rack/moneta_rest.rb +2 -2
  72. data/lib/rack/session/moneta.rb +3 -4
  73. data/moneta.gemspec +18 -4
  74. data/script/benchmarks +61 -34
  75. data/script/contributors +11 -6
  76. data/script/start-couchdb +27 -0
  77. data/script/start-hbase +3 -2
  78. data/script/start-services +3 -11
  79. data/spec/active_support/cache_moneta_store_spec.rb +30 -30
  80. data/spec/features/concurrent_create.rb +31 -10
  81. data/spec/features/concurrent_increment.rb +26 -19
  82. data/spec/features/create_expires.rb +15 -15
  83. data/spec/features/default_expires.rb +11 -12
  84. data/spec/features/expires.rb +215 -210
  85. data/spec/features/increment.rb +41 -41
  86. data/spec/features/store.rb +3 -3
  87. data/spec/helper.rb +21 -82
  88. data/spec/moneta/adapters/activerecord/standard_activerecord_spec.rb +1 -1
  89. data/spec/moneta/adapters/activerecord/standard_activerecord_with_expires_spec.rb +1 -1
  90. data/spec/moneta/adapters/activesupportcache/adapter_activesupportcache_spec.rb +4 -1
  91. data/spec/moneta/adapters/activesupportcache/adapter_activesupportcache_with_default_expires_spec.rb +4 -1
  92. data/spec/moneta/adapters/activesupportcache/standard_activesupportcache_spec.rb +14 -0
  93. data/spec/moneta/adapters/cassandra/standard_cassandra_spec.rb +1 -1
  94. data/spec/moneta/adapters/client/adapter_client_spec.rb +6 -6
  95. data/spec/moneta/adapters/client/client_helper.rb +24 -0
  96. data/spec/moneta/adapters/client/standard_client_tcp_spec.rb +8 -8
  97. data/spec/moneta/adapters/client/standard_client_unix_spec.rb +23 -7
  98. data/spec/moneta/adapters/couch/adapter_couch_spec.rb +199 -2
  99. data/spec/moneta/adapters/couch/standard_couch_spec.rb +9 -3
  100. data/spec/moneta/adapters/couch/standard_couch_with_expires_spec.rb +8 -2
  101. data/spec/moneta/adapters/daybreak/standard_daybreak_spec.rb +1 -1
  102. data/spec/moneta/adapters/daybreak/standard_daybreak_with_expires_spec.rb +1 -1
  103. data/spec/moneta/adapters/dbm/standard_dbm_spec.rb +1 -1
  104. data/spec/moneta/adapters/dbm/standard_dbm_with_expires_spec.rb +1 -1
  105. data/spec/moneta/adapters/faraday_helper.rb +9 -0
  106. data/spec/moneta/adapters/file/standard_file_spec.rb +2 -2
  107. data/spec/moneta/adapters/file/standard_file_with_expires_spec.rb +1 -1
  108. data/spec/moneta/adapters/gdbm/standard_gdbm_spec.rb +1 -1
  109. data/spec/moneta/adapters/gdbm/standard_gdbm_with_expires_spec.rb +1 -1
  110. data/spec/moneta/adapters/kyotocabinet/adapter_kyotocabinet_spec.rb +1 -1
  111. data/spec/moneta/adapters/kyotocabinet/standard_kyotocabinet_spec.rb +2 -2
  112. data/spec/moneta/adapters/kyotocabinet/standard_kyotocabinet_with_expires_spec.rb +2 -2
  113. data/spec/moneta/adapters/leveldb/standard_leveldb_spec.rb +1 -1
  114. data/spec/moneta/adapters/leveldb/standard_leveldb_with_expires_spec.rb +1 -1
  115. data/spec/moneta/adapters/lmdb/standard_lmdb_spec.rb +1 -1
  116. data/spec/moneta/adapters/lmdb/standard_lmdb_with_expires_spec.rb +1 -1
  117. data/spec/moneta/adapters/lruhash/adapter_lruhash_spec.rb +2 -2
  118. data/spec/moneta/adapters/lruhash/standard_lruhash_spec.rb +1 -1
  119. data/spec/moneta/adapters/lruhash/standard_lruhash_with_expires_spec.rb +1 -1
  120. data/spec/moneta/adapters/memcached/adapter_memcached_spec.rb +1 -1
  121. data/spec/moneta/adapters/memcached/dalli/adapter_memcached_dalli_spec.rb +1 -1
  122. data/spec/moneta/adapters/memcached/dalli/adapter_memcached_dalli_with_default_expires_spec.rb +1 -1
  123. data/spec/moneta/adapters/memcached/dalli/standard_memcached_dalli_spec.rb +1 -1
  124. data/spec/moneta/adapters/memcached/native/adapter_memcached_native_spec.rb +1 -1
  125. data/spec/moneta/adapters/memcached/native/adapter_memcached_native_with_default_expires_spec.rb +1 -1
  126. data/spec/moneta/adapters/memcached/native/standard_memcached_native_spec.rb +1 -1
  127. data/spec/moneta/adapters/memcached/standard_memcached_spec.rb +1 -1
  128. data/spec/moneta/adapters/{memcached/helper.rb → memcached_helper.rb} +0 -0
  129. data/spec/moneta/adapters/memory/standard_memory_spec.rb +1 -1
  130. data/spec/moneta/adapters/memory/standard_memory_with_compress_spec.rb +1 -1
  131. data/spec/moneta/adapters/memory/standard_memory_with_expires_spec.rb +1 -1
  132. data/spec/moneta/adapters/memory/standard_memory_with_json_key_serializer_spec.rb +1 -1
  133. data/spec/moneta/adapters/memory/standard_memory_with_json_serializer_spec.rb +1 -1
  134. data/spec/moneta/adapters/memory/standard_memory_with_json_value_serializer_spec.rb +2 -2
  135. data/spec/moneta/adapters/memory/standard_memory_with_prefix_spec.rb +39 -2
  136. data/spec/moneta/adapters/memory/standard_memory_with_snappy_compress_spec.rb +2 -2
  137. data/spec/moneta/adapters/mongo/adapter_mongo_spec.rb +32 -2
  138. data/spec/moneta/adapters/mongo/adapter_mongo_with_default_expires_spec.rb +5 -3
  139. data/spec/moneta/adapters/mongo/standard_mongo_spec.rb +2 -2
  140. data/spec/moneta/adapters/null/null_adapter_spec.rb +1 -1
  141. data/spec/moneta/adapters/pstore/standard_pstore_spec.rb +1 -1
  142. data/spec/moneta/adapters/pstore/standard_pstore_with_expires_spec.rb +1 -1
  143. data/spec/moneta/adapters/redis/standard_redis_spec.rb +1 -1
  144. data/spec/moneta/adapters/restclient/adapter_restclient_spec.rb +7 -5
  145. data/spec/moneta/adapters/restclient/helper.rb +12 -0
  146. data/spec/moneta/adapters/restclient/standard_restclient_spec.rb +9 -6
  147. data/spec/moneta/adapters/riak/standard_riak_with_expires_spec.rb +4 -0
  148. data/spec/moneta/adapters/sdbm/standard_sdbm_spec.rb +1 -1
  149. data/spec/moneta/adapters/sdbm/standard_sdbm_with_expires_spec.rb +1 -1
  150. data/spec/moneta/adapters/sequel/adapter_sequel_spec.rb +7 -34
  151. data/spec/moneta/adapters/sequel/helper.rb +37 -0
  152. data/spec/moneta/adapters/sequel/standard_sequel_spec.rb +5 -11
  153. data/spec/moneta/adapters/sequel/standard_sequel_with_expires_spec.rb +8 -9
  154. data/spec/moneta/adapters/sqlite/standard_sqlite_spec.rb +1 -1
  155. data/spec/moneta/adapters/sqlite/standard_sqlite_with_expires_spec.rb +1 -1
  156. data/spec/moneta/adapters/tdb/standard_tdb_spec.rb +1 -1
  157. data/spec/moneta/adapters/tdb/standard_tdb_with_expires_spec.rb +1 -1
  158. data/spec/moneta/adapters/tokyocabinet/standard_tokyocabinet_spec.rb +1 -1
  159. data/spec/moneta/adapters/tokyocabinet/standard_tokyocabinet_with_expires_spec.rb +1 -1
  160. data/spec/moneta/adapters/tokyotyrant/adapter_tokyotyrant_spec.rb +6 -2
  161. data/spec/moneta/adapters/tokyotyrant/helper.rb +12 -0
  162. data/spec/moneta/adapters/tokyotyrant/standard_tokyotyrant_spec.rb +5 -2
  163. data/spec/moneta/adapters/tokyotyrant/standard_tokyotyrant_with_expires_spec.rb +5 -1
  164. data/spec/moneta/adapters/yaml/standard_yaml_spec.rb +1 -1
  165. data/spec/moneta/adapters/yaml/standard_yaml_with_expires_spec.rb +1 -1
  166. data/spec/moneta/builder_spec.rb +22 -0
  167. data/spec/moneta/proxies/enumerable/enumerable_spec.rb +26 -0
  168. data/spec/moneta/proxies/expires/expires_file_spec.rb +1 -1
  169. data/spec/moneta/proxies/fallback/fallback_spec.rb +42 -0
  170. data/spec/moneta/proxies/pool/pool_spec.rb +319 -6
  171. data/spec/moneta/proxies/shared/shared_tcp_spec.rb +14 -4
  172. data/spec/moneta/proxies/shared/shared_unix_spec.rb +14 -4
  173. data/spec/moneta/proxies/transformer/transformer_bencode_spec.rb +1 -1
  174. data/spec/moneta/proxies/transformer/transformer_bert_spec.rb +3 -3
  175. data/spec/moneta/proxies/transformer/transformer_bson_spec.rb +2 -2
  176. data/spec/moneta/proxies/transformer/transformer_json_spec.rb +1 -1
  177. data/spec/moneta/proxies/transformer/transformer_key_marshal_spec.rb +1 -1
  178. data/spec/moneta/proxies/transformer/transformer_key_yaml_spec.rb +1 -1
  179. data/spec/moneta/proxies/transformer/transformer_marshal_base64_spec.rb +1 -1
  180. data/spec/moneta/proxies/transformer/transformer_marshal_escape_spec.rb +8 -4
  181. data/spec/moneta/proxies/transformer/transformer_marshal_hex_spec.rb +1 -1
  182. data/spec/moneta/proxies/transformer/transformer_marshal_hmac_spec.rb +1 -1
  183. data/spec/moneta/proxies/transformer/transformer_marshal_prefix_base64_spec.rb +33 -0
  184. data/spec/moneta/proxies/transformer/transformer_marshal_prefix_spec.rb +1 -1
  185. data/spec/moneta/proxies/transformer/transformer_marshal_qp_spec.rb +1 -1
  186. data/spec/moneta/proxies/transformer/transformer_marshal_spec.rb +1 -1
  187. data/spec/moneta/proxies/transformer/transformer_marshal_urlsafe_base64_spec.rb +1 -1
  188. data/spec/moneta/proxies/transformer/transformer_marshal_uuencode_spec.rb +1 -1
  189. data/spec/moneta/proxies/transformer/transformer_msgpack_spec.rb +1 -1
  190. data/spec/moneta/proxies/transformer/transformer_ox_spec.rb +1 -1
  191. data/spec/moneta/proxies/transformer/transformer_php_spec.rb +1 -1
  192. data/spec/moneta/proxies/transformer/transformer_tnet_spec.rb +1 -1
  193. data/spec/moneta/proxies/transformer/transformer_yaml_spec.rb +2 -2
  194. data/spec/moneta/proxies/weak_each_key/weak_each_key_spec.rb +0 -2
  195. data/spec/restserver.rb +55 -0
  196. metadata +136 -32
  197. data/lib/moneta/adapters/mongo/base.rb +0 -103
  198. data/lib/moneta/adapters/mongo/moped.rb +0 -164
  199. data/lib/moneta/adapters/mongo/official.rb +0 -157
  200. data/script/install-kyotocabinet +0 -17
  201. data/spec/moneta/adapters/mongo/adapter_mongo_moped_spec.rb +0 -25
  202. data/spec/moneta/adapters/mongo/adapter_mongo_moped_with_default_expires_spec.rb +0 -12
  203. data/spec/moneta/adapters/mongo/adapter_mongo_official_spec.rb +0 -25
  204. data/spec/moneta/adapters/mongo/adapter_mongo_official_with_default_expires_spec.rb +0 -12
  205. data/spec/moneta/adapters/mongo/standard_mongo_moped_spec.rb +0 -7
  206. data/spec/moneta/adapters/mongo/standard_mongo_official_spec.rb +0 -7
  207. data/spec/quality_spec.rb +0 -51
@@ -6,8 +6,7 @@ module Moneta
6
6
  include Defaults
7
7
 
8
8
  # @param [Hash] options Options hash
9
- def initialize(options = {})
10
- end
9
+ def initialize(options = {}); end
11
10
 
12
11
  # (see Proxy#key?)
13
12
  def key?(key, options = {})
@@ -48,7 +48,7 @@ module Moneta
48
48
 
49
49
  # (see Proxy#store)
50
50
  def store(key, value, options = {})
51
- transaction {@backend[key] = value }
51
+ transaction { @backend[key] = value }
52
52
  end
53
53
 
54
54
  # (see Proxy#delete)
@@ -59,7 +59,8 @@ module Moneta
59
59
  # (see Proxy#increment)
60
60
  def increment(key, amount = 1, options = {})
61
61
  transaction do
62
- value = Utils.to_int(@backend[key]) + amount
62
+ existing = @backend[key]
63
+ value = (existing == nil ? 0 : Integer(existing)) + amount
63
64
  @backend[key] = value.to_s
64
65
  value
65
66
  end
@@ -26,7 +26,11 @@ module Moneta
26
26
  # number as a time to live in seconds.
27
27
  def key?(key, options = {})
28
28
  with_expiry_update(key, default: nil, **options) do
29
- @backend.exists(key)
29
+ if @backend.respond_to?(:exists?)
30
+ @backend.exists?(key)
31
+ else
32
+ @backend.exists(key)
33
+ end
30
34
  end
31
35
  end
32
36
 
@@ -100,7 +104,7 @@ module Moneta
100
104
  # (see Defaults#values_at)
101
105
  def values_at(*keys, **options)
102
106
  with_expiry_update(*keys, default: nil, **options) do
103
- @backend.mget *keys
107
+ @backend.mget(*keys)
104
108
  end
105
109
  end
106
110
 
@@ -112,7 +116,7 @@ module Moneta
112
116
  old_values = @backend.mget(*keys)
113
117
  updates = pairs.each_with_index.with_object({}) do |(pair, i), updates|
114
118
  old_value = old_values[i]
115
- if !old_value.nil?
119
+ if old_value != nil
116
120
  key, new_value = pair
117
121
  updates[key] = yield(key, old_value, new_value)
118
122
  end
@@ -146,7 +150,7 @@ module Moneta
146
150
 
147
151
  def with_expiry_update(*keys, default: @default_expires, **options)
148
152
  expires = expires_value(options, default)
149
- if expires.nil?
153
+ if expires == nil
150
154
  yield
151
155
  else
152
156
  future = nil
@@ -11,10 +11,19 @@ module Moneta
11
11
 
12
12
  # @param [Hash] options
13
13
  # @option options [String] :url URL
14
- # @option options [Faraday connection] :backend Use existing backend instance
14
+ # @option options [Symbol] :adapter The adapter to tell Faraday to use
15
+ # @option options [Faraday::Connection] :backend Use existing backend instance
16
+ # @option options Other options passed to {Faraday::new} (unless
17
+ # :backend option is provided).
15
18
  def initialize(options = {})
16
- raise ArgumentError, 'Option :url is required' unless url = options[:url]
17
- @backend = options[:backend] || ::Faraday.new(url: url)
19
+ @backend = options.delete(:backend) ||
20
+ begin
21
+ raise ArgumentError, 'Option :url is required' unless url = options.delete(:url)
22
+ block = if faraday_adapter = options.delete(:adapter)
23
+ proc { |faraday| faraday.adapter faraday_adapter }
24
+ end
25
+ ::Faraday.new(url, options, &block)
26
+ end
18
27
  end
19
28
 
20
29
  # (see Proxy#key?)
@@ -30,7 +30,7 @@ module Moneta
30
30
  # (see Proxy#load)
31
31
  def load(key, options = {})
32
32
  @bucket.get(key, options.dup).raw_data
33
- rescue ::Riak::FailedRequest => ex
33
+ rescue ::Riak::FailedRequest
34
34
  nil
35
35
  end
36
36
 
@@ -53,7 +53,7 @@ module Moneta
53
53
  # (see Proxy#clear)
54
54
  def clear(options = {})
55
55
  @bucket.keys do |keys|
56
- keys.each{ |key| @bucket.delete(key) }
56
+ keys.each { |key| @bucket.delete(key) }
57
57
  end
58
58
  self
59
59
  end
@@ -7,9 +7,10 @@ module Moneta
7
7
  class Sequel
8
8
  include Defaults
9
9
 
10
- # Sequel::UniqueConstraintViolation is defined since sequel 3.44.0
11
- # older versions raise a Sequel::DatabaseError.
12
- UniqueConstraintViolation = defined?(::Sequel::UniqueConstraintViolation) ? ::Sequel::UniqueConstraintViolation : ::Sequel::DatabaseError
10
+ autoload :MySQL, 'moneta/adapters/sequel/mysql'
11
+ autoload :Postgres, 'moneta/adapters/sequel/postgres'
12
+ autoload :PostgresHStore, 'moneta/adapters/sequel/postgres_hstore'
13
+ autoload :SQLite, 'moneta/adapters/sequel/sqlite'
13
14
 
14
15
  supports :create, :increment, :each_key
15
16
  attr_reader :backend, :key_column, :value_column
@@ -35,58 +36,27 @@ module Moneta
35
36
  # possible to specify a separate connection to use for `#each_key`. Use
36
37
  # in conjunction with Sequel's `:servers` option
37
38
  # @option options All other options passed to `Sequel#connect`
38
- def self.new(options = {})
39
+ def initialize(options = {})
39
40
  extensions = options.delete(:extensions)
40
41
  connection_validation_timeout = options.delete(:connection_validation_timeout)
41
42
  optimize = options.delete(:optimize)
42
- backend = options.delete(:backend) ||
43
- begin
44
- raise ArgumentError, 'Option :db is required' unless db = options.delete(:db)
45
- other_cols = [:table, :create_table, :key_column, :value_column, :hstore]
46
- ::Sequel.connect(db, options.reject { |k,_| other_cols.member?(k) }).tap do |backend|
47
- if extensions
48
- raise ArgumentError, 'Option :extensions must be an Array' unless extensions.is_a?(Array)
49
- extensions.map(&:to_sym).each(&backend.method(:extension))
50
- end
51
-
52
- if connection_validation_timeout
53
- backend.pool.connection_validation_timeout = connection_validation_timeout
54
- end
55
- end
56
- end
57
-
58
- instance =
59
- if optimize.nil? || optimize
60
- case backend.database_type
61
- when :mysql
62
- MySQL.allocate
63
- when :postgres
64
- if options[:hstore]
65
- PostgresHStore.allocate
66
- elsif matches = backend.get(::Sequel[:version].function).match(/PostgreSQL (\d+)\.(\d+)/)
67
- # Our optimisations only work on Postgres 9.5+
68
- major, minor = matches[1..2].map(&:to_i)
69
- Postgres.allocate if major > 9 || (major == 9 && minor >= 5)
70
- end
71
- when :sqlite
72
- SQLite.allocate
73
- end
74
- end || allocate
43
+ @backend = options.delete(:backend) ||
44
+ connect(extensions: extensions, connection_validation_timeout: connection_validation_timeout, **options)
75
45
 
76
- instance.instance_variable_set(:@backend, backend)
77
- instance.send(:initialize, options)
78
- instance
79
- end
46
+ if hstore = options.delete(:hstore)
47
+ @row = hstore.to_s
48
+ extend Sequel::PostgresHStore
49
+ elsif optimize == nil || optimize
50
+ add_optimizations
51
+ end
80
52
 
81
- # @api private
82
- def initialize(options)
83
53
  @table_name = (options.delete(:table) || :moneta).to_sym
84
54
  @key_column = options.delete(:key_column) || :k
85
55
  @value_column = options.delete(:value_column) || :v
86
56
  @each_key_server = options.delete(:each_key_server)
87
57
 
88
58
  create_proc = options.delete(:create_table)
89
- if create_proc.nil?
59
+ if create_proc == nil
90
60
  create_table
91
61
  elsif create_proc
92
62
  create_proc.call(@backend)
@@ -124,7 +94,7 @@ module Moneta
124
94
  def create(key, value, options = {})
125
95
  @create.call(key: key, value: blob(value))
126
96
  true
127
- rescue UniqueConstraintViolation
97
+ rescue ::Sequel::ConstraintViolation
128
98
  false
129
99
  end
130
100
 
@@ -231,8 +201,39 @@ module Moneta
231
201
 
232
202
  protected
233
203
 
234
- def blob(s)
235
- ::Sequel.blob(s) unless s == nil
204
+ # @api private
205
+ def connect(db:, extensions: nil, connection_validation_timeout: nil, **options)
206
+ other_cols = [:table, :create_table, :key_column, :value_column, :hstore]
207
+ ::Sequel.connect(db, options.reject { |k,| other_cols.member?(k) }).tap do |backend|
208
+ if extensions
209
+ raise ArgumentError, 'Option :extensions must be an Array' unless extensions.is_a?(Array)
210
+ extensions.map(&:to_sym).each(&backend.method(:extension))
211
+ end
212
+
213
+ if connection_validation_timeout
214
+ backend.pool.connection_validation_timeout = connection_validation_timeout
215
+ end
216
+ end
217
+ end
218
+
219
+ # @api private
220
+ def add_optimizations
221
+ case backend.database_type
222
+ when :mysql
223
+ extend Sequel::MySQL
224
+ when :postgres
225
+ if matches = backend.get(::Sequel[:version].function).match(/PostgreSQL (\d+)\.(\d+)/)
226
+ # Our optimisations only work on Postgres 9.5+
227
+ major, minor = matches[1..2].map(&:to_i)
228
+ extend Sequel::Postgres if major > 9 || (major == 9 && minor >= 5)
229
+ end
230
+ when :sqlite
231
+ extend Sequel::SQLite
232
+ end
233
+ end
234
+
235
+ def blob(str)
236
+ ::Sequel.blob(str) unless str == nil
236
237
  end
237
238
 
238
239
  def blob_pairs(pairs)
@@ -279,41 +280,41 @@ module Moneta
279
280
  end
280
281
 
281
282
  def prepare_key
282
- @key = @table.
283
- where(key_column => :$key).select(1).
284
- prepare(:first, statement_id(:key))
283
+ @key = @table
284
+ .where(key_column => :$key).select(1)
285
+ .prepare(:first, statement_id(:key))
285
286
  end
286
287
 
287
288
  def prepare_load
288
- @load = @table.
289
- where(key_column => :$key).select(value_column).
290
- prepare(:first, statement_id(:load))
289
+ @load = @table
290
+ .where(key_column => :$key).select(value_column)
291
+ .prepare(:first, statement_id(:load))
291
292
  end
292
293
 
293
294
  def prepare_store
294
- @store_update = @table.
295
- where(key_column => :$key).
296
- prepare(:update, statement_id(:store_update), value_column => :$value)
295
+ @store_update = @table
296
+ .where(key_column => :$key)
297
+ .prepare(:update, statement_id(:store_update), value_column => :$value)
297
298
  end
298
299
 
299
300
  def prepare_create
300
- @create = @table.
301
- prepare(:insert, statement_id(:create), key_column => :$key, value_column => :$value)
301
+ @create = @table
302
+ .prepare(:insert, statement_id(:create), key_column => :$key, value_column => :$value)
302
303
  end
303
304
 
304
305
  def prepare_increment
305
- @load_for_update = @table.
306
- where(key_column => :$key).for_update.
307
- select(value_column).
308
- prepare(:first, statement_id(:load_for_update))
309
- @increment_update ||= @table.
310
- where(key_column => :$key, value_column => :$value).
311
- prepare(:update, statement_id(:increment_update), value_column => :$new_value)
306
+ @load_for_update = @table
307
+ .where(key_column => :$key).for_update
308
+ .select(value_column)
309
+ .prepare(:first, statement_id(:load_for_update))
310
+ @increment_update ||= @table
311
+ .where(key_column => :$key, value_column => :$value)
312
+ .prepare(:update, statement_id(:increment_update), value_column => :$new_value)
312
313
  end
313
314
 
314
315
  def prepare_delete
315
- @delete = @table.where(key_column => :$key).
316
- prepare(:delete, statement_id(:delete))
316
+ @delete = @table.where(key_column => :$key)
317
+ .prepare(:delete, statement_id(:delete))
317
318
  end
318
319
 
319
320
  def prepare_slice
@@ -328,433 +329,6 @@ module Moneta
328
329
 
329
330
  # @api private
330
331
  class IncrementError < ::Sequel::DatabaseError; end
331
-
332
- # @api private
333
- class MySQL < Sequel
334
- def store(key, value, options = {})
335
- @store.call(key: key, value: blob(value))
336
- value
337
- end
338
-
339
- def increment(key, amount = 1, options = {})
340
- @backend.transaction do
341
- # this creates a row-level lock even if there is no existing row (a
342
- # "gap lock").
343
- if row = @load_for_update.call(key: key)
344
- # Integer() will raise an exception if the existing value cannot be parsed
345
- amount += Integer(row[value_column])
346
- @increment_update.call(key: key, value: amount)
347
- else
348
- @create.call(key: key, value: amount)
349
- end
350
- amount
351
- end
352
- rescue ::Sequel::SerializationFailure # Thrown on deadlock
353
- tries ||= 0
354
- (tries += 1) <= 3 ? retry : raise
355
- end
356
-
357
- def merge!(pairs, options = {}, &block)
358
- @backend.transaction do
359
- pairs = yield_merge_pairs(pairs, &block) if block_given?
360
- @table.
361
- on_duplicate_key_update.
362
- import([key_column, value_column], blob_pairs(pairs).to_a)
363
- end
364
-
365
- self
366
- end
367
-
368
- def each_key
369
- return super unless block_given? && @each_key_server && @table.respond_to?(:stream)
370
- # Order is not required when streaming
371
- @table.server(@each_key_server).select(key_column).paged_each do |row|
372
- yield row[key_column]
373
- end
374
- self
375
- end
376
-
377
- protected
378
-
379
- def prepare_store
380
- @store = @table.
381
- on_duplicate_key_update.
382
- prepare(:insert, statement_id(:store), key_column => :$key, value_column => :$value)
383
- end
384
-
385
- def prepare_increment
386
- @increment_update = @table.
387
- where(key_column => :$key).
388
- prepare(:update, statement_id(:increment_update), value_column => :$value)
389
- super
390
- end
391
- end
392
-
393
- # @api private
394
- class Postgres < Sequel
395
- def store(key, value, options = {})
396
- @store.call(key: key, value: blob(value))
397
- value
398
- end
399
-
400
- def increment(key, amount = 1, options = {})
401
- result = @increment.call(key: key, value: blob(amount.to_s), amount: amount)
402
- if row = result.first
403
- row[value_column].to_i
404
- end
405
- end
406
-
407
- def delete(key, options = {})
408
- result = @delete.call(key: key)
409
- if row = result.first
410
- row[value_column]
411
- end
412
- end
413
-
414
- def merge!(pairs, options = {}, &block)
415
- @backend.transaction do
416
- pairs = yield_merge_pairs(pairs, &block) if block_given?
417
- @table.
418
- insert_conflict(
419
- target: key_column,
420
- update: {value_column => ::Sequel[:excluded][value_column]}).
421
- import([key_column, value_column], blob_pairs(pairs).to_a)
422
- end
423
-
424
- self
425
- end
426
-
427
- def each_key
428
- return super unless block_given? && !@each_key_server && @table.respond_to?(:use_cursor)
429
- # With a cursor, this will Just Work.
430
- @table.select(key_column).paged_each do |row|
431
- yield row[key_column]
432
- end
433
- self
434
- end
435
-
436
- protected
437
-
438
- def prepare_store
439
- @store = @table.
440
- insert_conflict(
441
- target: key_column,
442
- update: {value_column => ::Sequel[:excluded][value_column]}).
443
- prepare(:insert, statement_id(:store), key_column => :$key, value_column => :$value)
444
- end
445
-
446
- def prepare_increment
447
- update_expr = ::Sequel[:convert_to].function(
448
- (::Sequel[:convert_from].function(
449
- ::Sequel[@table_name][value_column],
450
- 'UTF8').cast(Integer) + :$amount).cast(String),
451
- 'UTF8')
452
-
453
- @increment = @table.
454
- returning(value_column).
455
- insert_conflict(target: key_column, update: {value_column => update_expr}).
456
- prepare(:insert, statement_id(:increment), key_column => :$key, value_column => :$value)
457
- end
458
-
459
- def prepare_delete
460
- @delete = @table.
461
- returning(value_column).
462
- where(key_column => :$key).
463
- prepare(:delete, statement_id(:delete))
464
- end
465
- end
466
-
467
- # @api private
468
- class PostgresHStore < Sequel
469
- def initialize(options)
470
- @row = options.delete(:hstore).to_s
471
- @backend.extension :pg_hstore
472
- ::Sequel.extension :pg_hstore_ops
473
- @backend.extension :pg_array
474
- super
475
- end
476
-
477
- def key?(key, options = {})
478
- if @key
479
- row = @key.call(row: @row, key: key) || false
480
- row && row[:present]
481
- else
482
- @key_pl.get(key)
483
- end
484
- end
485
-
486
- def store(key, value, options = {})
487
- @backend.transaction do
488
- create_row
489
- @store.call(row: @row, pair: ::Sequel.hstore(key => value))
490
- end
491
- value
492
- end
493
-
494
- def load(key, options = {})
495
- if row = @load.call(row: @row, key: key)
496
- row[:value]
497
- end
498
- end
499
-
500
- def delete(key, options = {})
501
- @backend.transaction do
502
- value = load(key, options)
503
- @delete.call(row: @row, key: key)
504
- value
505
- end
506
- end
507
-
508
- def increment(key, amount = 1, options = {})
509
- @backend.transaction do
510
- create_row
511
- if row = @increment.call(row: @row, key: key, amount: amount).first
512
- row[:value].to_i
513
- end
514
- end
515
- end
516
-
517
- def create(key, value, options = {})
518
- @backend.transaction do
519
- create_row
520
- 1 ==
521
- if @create
522
- @create.call(row: @row, key: key, pair: ::Sequel.hstore(key => value))
523
- else
524
- @table.
525
- where(key_column => @row).
526
- exclude(::Sequel[value_column].hstore.key?(key)).
527
- update(value_column => ::Sequel[value_column].hstore.merge(key => value))
528
- end
529
- end
530
- end
531
-
532
- def clear(options = {})
533
- @clear.call(row: @row)
534
- self
535
- end
536
-
537
- def values_at(*keys, **options)
538
- if row = @values_at.call(row: @row, keys: ::Sequel.pg_array(keys))
539
- row[:values].to_a
540
- else
541
- []
542
- end
543
- end
544
-
545
- def slice(*keys, **options)
546
- if row = @slice.call(row: @row, keys: ::Sequel.pg_array(keys))
547
- row[:pairs].to_h
548
- else
549
- []
550
- end
551
- end
552
-
553
- def merge!(pairs, options = {}, &block)
554
- @backend.transaction do
555
- create_row
556
- pairs = yield_merge_pairs(pairs, &block) if block_given?
557
- hash = Hash === pairs ? pairs : Hash[pairs.to_a]
558
- @store.call(row: @row, pair: ::Sequel.hstore(hash))
559
- end
560
-
561
- self
562
- end
563
-
564
- def each_key
565
- return enum_for(:each_key) { @size.call(row: @row)[:size] } unless block_given?
566
-
567
- ds =
568
- if @each_key_server
569
- @table.server(@each_key_server)
570
- else
571
- @table
572
- end
573
- ds = ds.order(:skeys) unless @table.respond_to?(:use_cursor)
574
- ds.where(key_column => @row).
575
- select(::Sequel[value_column].hstore.skeys).
576
- paged_each do |row|
577
- yield row[:skeys]
578
- end
579
- self
580
- end
581
-
582
- protected
583
-
584
- def create_row
585
- @create_row.call(row: @row)
586
- end
587
-
588
- def create_table
589
- key_column = self.key_column
590
- value_column = self.value_column
591
-
592
- @backend.create_table?(@table_name) do
593
- column key_column, String, null: false, primary_key: true
594
- column value_column, :hstore
595
- index value_column, type: :gin
596
- end
597
- end
598
-
599
- def slice_for_update(pairs)
600
- keys = pairs.map { |k, _| k }.to_a
601
- if row = @slice_for_update.call(row: @row, keys: ::Sequel.pg_array(keys))
602
- row[:pairs].to_h
603
- else
604
- {}
605
- end
606
- end
607
-
608
- def prepare_statements
609
- super
610
- prepare_create_row
611
- prepare_clear
612
- prepare_values_at
613
- prepare_size
614
- end
615
-
616
- def prepare_create_row
617
- @create_row = @table.
618
- insert_ignore.
619
- prepare(:insert, statement_id(:hstore_create_row), key_column => :$row, value_column => '')
620
- end
621
-
622
- def prepare_clear
623
- @clear = @table.where(key_column => :$row).prepare(:update, statement_id(:hstore_clear), value_column => '')
624
- end
625
-
626
- def prepare_key
627
- if defined?(JRUBY_VERSION)
628
- @key_pl = ::Sequel::Dataset::PlaceholderLiteralizer.loader(@table) do |pl, ds|
629
- ds.where(key_column => @row).select(::Sequel[value_column].hstore.key?(pl.arg))
630
- end
631
- else
632
- @key = @table.where(key_column => :$row).
633
- select(::Sequel[value_column].hstore.key?(:$key).as(:present)).
634
- prepare(:first, statement_id(:hstore_key))
635
- end
636
- end
637
-
638
- def prepare_store
639
- @store = @table.
640
- where(key_column => :$row).
641
- prepare(:update, statement_id(:hstore_store), value_column => ::Sequel[value_column].hstore.merge(:$pair))
642
- end
643
-
644
- def prepare_increment
645
- pair = ::Sequel[:hstore].function(
646
- :$key,
647
- (::Sequel[:coalesce].function(
648
- ::Sequel[value_column].hstore[:$key].cast(Integer),
649
- 0) + :$amount).cast(String))
650
-
651
- @increment = @table.
652
- returning(::Sequel[value_column].hstore[:$key].as(:value)).
653
- where(key_column => :$row).
654
- prepare(:update, statement_id(:hstore_increment), value_column => ::Sequel.join([value_column, pair]))
655
- end
656
-
657
- def prepare_load
658
- @load = @table.where(key_column => :$row).
659
- select(::Sequel[value_column].hstore[:$key].as(:value)).
660
- prepare(:first, statement_id(:hstore_load))
661
- end
662
-
663
- def prepare_delete
664
- @delete = @table.where(key_column => :$row).
665
- prepare(:update, statement_id(:hstore_delete), value_column => ::Sequel[value_column].hstore.delete(:$key))
666
- end
667
-
668
- def prepare_create
669
- # Under JRuby we can't use a prepared statement for queries involving
670
- # the hstore `?` (key?) operator. See
671
- # https://stackoverflow.com/questions/11940401/escaping-hstore-contains-operators-in-a-jdbc-prepared-statement
672
- return if defined?(JRUBY_VERSION)
673
- @create = @table.
674
- where(key_column => :$row).
675
- exclude(::Sequel[value_column].hstore.key?(:$key)).
676
- prepare(:update, statement_id(:hstore_create), value_column => ::Sequel[value_column].hstore.merge(:$pair))
677
- end
678
-
679
- def prepare_values_at
680
- @values_at = @table.
681
- where(key_column => :$row).
682
- select(::Sequel[value_column].hstore[::Sequel.cast(:$keys, :"text[]")].as(:values)).
683
- prepare(:first, statement_id(:hstore_values_at))
684
- end
685
-
686
- def prepare_slice
687
- slice = @table.
688
- where(key_column => :$row).
689
- select(::Sequel[value_column].hstore.slice(:$keys).as(:pairs))
690
- @slice = slice.prepare(:first, statement_id(:hstore_slice))
691
- @slice_for_update = slice.for_update.prepare(:first, statement_id(:hstore_slice_for_update))
692
- end
693
-
694
- def prepare_size
695
- @size =
696
- @backend.from(
697
- @table.
698
- where(key_column => :$row).
699
- select(::Sequel[value_column].hstore.each)).
700
- select { count.function.*.as(:size) }.
701
- prepare(:first, statement_id(:hstore_size))
702
- end
703
- end
704
-
705
- # @api private
706
- class SQLite < Sequel
707
- def initialize(options)
708
- @version = backend.get(::Sequel[:sqlite_version].function)
709
- # See https://sqlite.org/lang_UPSERT.html
710
- @can_upsert = ::Gem::Version.new(@version) >= ::Gem::Version.new('3.24.0')
711
- super
712
- end
713
-
714
- def store(key, value, options = {})
715
- @table.insert_conflict(:replace).insert(key_column => key, value_column => blob(value))
716
- value
717
- end
718
-
719
- def increment(key, amount = 1, options = {})
720
- return super unless @can_upsert
721
- @backend.transaction do
722
- @increment.call(key: key, value: amount.to_s, amount: amount)
723
- Integer(load(key))
724
- end
725
- end
726
-
727
- def merge!(pairs, options = {}, &block)
728
- @backend.transaction do
729
- pairs = yield_merge_pairs(pairs, &block) if block_given?
730
- @table.insert_conflict(:replace).import([key_column, value_column], blob_pairs(pairs).to_a)
731
- end
732
-
733
- self
734
- end
735
-
736
- protected
737
-
738
- def prepare_store
739
- @store = @table.
740
- insert_conflict(:replace).
741
- prepare(:insert, statement_id(:store), key_column => :$key, value_column => :$value)
742
- end
743
-
744
- def prepare_increment
745
- return super unless @can_upsert
746
- update_expr = (::Sequel[value_column].cast(Integer) + :$amount).cast(:blob)
747
- @increment = @table.
748
- insert_conflict(
749
- target: key_column,
750
- update: {value_column => update_expr},
751
- update_where:
752
- ::Sequel.|(
753
- {value_column => blob("0")},
754
- ::Sequel.~(::Sequel[value_column].cast(Integer)) => 0)).
755
- prepare(:insert, statement_id(:increment), key_column => :$key, value_column => :$value)
756
- end
757
- end
758
332
  end
759
333
  end
760
334
  end