kredis 1.4.0 → 1.6.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fdd8725b73c1bac9541b7b48ee62ebb46a8c51eaf78abe367fda75a12ee8c425
4
- data.tar.gz: fcee76b43f62b3bd0a99fe7d258cb1b5edab0a8d7f408f15f7e11be01a40b219
3
+ metadata.gz: 15ed6996059d28937ffa4f43a35e53d1e8fbc1894a30446575d9c00c390cd838
4
+ data.tar.gz: 296f80ea7719d69334d50240c9fce5969df4f8d976800013167c1fb0ebe6a0b3
5
5
  SHA512:
6
- metadata.gz: 1bd30168c4efb0e23f65f15eb0e0b34c0b3a62a6c871da6be82192187482f0fddc55405ffb52c12f8dad4397246b55f7d292aa3bbbd8d3dc75a4fdd2e94e009e
7
- data.tar.gz: 7dc32845d42347bcabe655447174d9cfb8b5e1d738fdf59af96557a455dc55ca52fced20fd76e514a21628dd725312120cbb6f0577e7925d6a6da585e77ffbf8
6
+ metadata.gz: cae34ebe3f21fe7c0e9cc0da5fc14bdc0d15931436bf7f07c552c14090489806990779ffd219be054f050468c809e23bf79302365f59f2dfced4b6a054ad352c
7
+ data.tar.gz: 96513897d1badc12f4fc6b2e51e017958f7e5eeaa5730d06bcb9d9a12d71ae09a4eb8ce2661e70a70647f4c4d7e5543a30451ab64d290b91de20d47602f30398
data/README.md CHANGED
@@ -21,11 +21,11 @@ integer.value = 5 # => SET myinteger "5"
21
21
  5 == integer.value # => GET myinteger
22
22
 
23
23
  decimal = Kredis.decimal "mydecimal" # accuracy!
24
- decimal.value = "%.47f" % (1.0/10) # => SET mydecimal "0.10000000000000000555111512312578270211815834045"
24
+ decimal.value = "%.47f" % (1.0 / 10) # => SET mydecimal "0.10000000000000000555111512312578270211815834045"
25
25
  BigDecimal("0.10000000000000000555111512312578270211815834045e0") == decimal.value # => GET mydecimal
26
26
 
27
27
  float = Kredis.float "myfloat" # speed!
28
- float.value = 1.0/10 # => SET myfloat "0.1"
28
+ float.value = 1.0 / 10 # => SET myfloat "0.1"
29
29
  0.1 == float.value # => GET myfloat
30
30
 
31
31
  boolean = Kredis.boolean "myboolean"
@@ -49,10 +49,10 @@ list = Kredis.list "mylist"
49
49
  list << "hello world!" # => RPUSH mylist "hello world!"
50
50
  [ "hello world!" ] == list.elements # => LRANGE mylist 0, -1
51
51
 
52
- integer_list = Kredis.list "myintegerlist", typed: :integer
53
- integer_list.append([ 1, 2, 3 ]) # => RPUSH myintegerlist "1" "2" "3"
54
- integer_list << 4 # => RPUSH myintegerlist "4"
55
- [ 1, 2, 3, 4 ] == integer_list.elements # => LRANGE myintegerlist 0 -1
52
+ integer_list = Kredis.list "myintegerlist", typed: :integer, default: [ 1, 2, 3 ] # => EXISTS? myintegerlist, RPUSH myintegerlist "1" "2" "3"
53
+ integer_list.append([ 4, 5, 6 ]) # => RPUSH myintegerlist "4" "5" "6"
54
+ integer_list << 7 # => RPUSH myintegerlist "7"
55
+ [ 1, 2, 3, 4, 5, 6, 7 ] == integer_list.elements # => LRANGE myintegerlist 0 -1
56
56
 
57
57
  unique_list = Kredis.unique_list "myuniquelist"
58
58
  unique_list.append(%w[ 2 3 4 ]) # => LREM myuniquelist 0, "2" + LREM myuniquelist 0, "3" + LREM myuniquelist 0, "4" + RPUSH myuniquelist "2", "3", "4"
@@ -62,6 +62,14 @@ unique_list << "5" # => LREM myuniquelist 0, "5" + R
62
62
  unique_list.remove(3) # => LREM myuniquelist 0, "3"
63
63
  [ "4", "2", "1", "5" ] == unique_list.elements # => LRANGE myuniquelist 0, -1
64
64
 
65
+ ordered_set = Kredis.ordered_set "myorderedset"
66
+ ordered_set.append(%w[ 2 3 4 ]) # => ZADD myorderedset 1646131025.4953232 2 1646131025.495326 3 1646131025.4953272 4
67
+ ordered_set.prepend(%w[ 1 2 3 4 ]) # => ZADD myorderedset -1646131025.4957051 1 -1646131025.495707 2 -1646131025.4957082 3 -1646131025.4957092 4
68
+ ordered_set.append([])
69
+ ordered_set << "5" # => ZADD myorderedset 1646131025.4960442 5
70
+ ordered_set.remove(3) # => ZREM myorderedset 3
71
+ [ "4", "2", "1", "5" ] == ordered_set.elements # => ZRANGE myorderedset 0 -1
72
+
65
73
  set = Kredis.set "myset", typed: :datetime
66
74
  set.add(DateTime.tomorrow, DateTime.yesterday) # => SADD myset "2021-02-03 00:00:00 +0100" "2021-02-01 00:00:00 +0100"
67
75
  set << DateTime.tomorrow # => SADD myset "2021-02-03 00:00:00 +0100"
@@ -155,15 +163,7 @@ sleep 0.6.seconds
155
163
  false == flag.marked? #=> EXISTS myflag
156
164
  ```
157
165
 
158
- And using structures on a different than the default `shared` redis instance, relying on `config/redis/secondary.yml`:
159
-
160
- ```ruby
161
- one_string = Kredis.string "mystring"
162
- two_string = Kredis.string "mystring", config: :secondary
163
-
164
- one_string.value = "just on shared"
165
- two_string.value != one_string.value
166
- ```
166
+ ### Models
167
167
 
168
168
  You can use all these structures in models:
169
169
 
@@ -189,6 +189,29 @@ person.morning.value = "blue" # => SET people:5:morning
189
189
  true == person.morning.blue? # => GET people:5:morning
190
190
  ```
191
191
 
192
+ ### Default values
193
+
194
+ You can set a default value for all types. For example:
195
+
196
+ ```ruby
197
+ list = Kredis.list "favorite_colors", default: [ "red", "green", "blue" ]
198
+
199
+ # or, in a model
200
+ class Person < ApplicationRecord
201
+ kredis_string :name, default: "Unknown"
202
+ kredis_list :favorite_colors, default: [ "red", "green", "blue" ]
203
+ end
204
+ ```
205
+
206
+ There's a performance overhead to consider though. When you first read or write an attribute in a model, Kredis will
207
+ check if the underlying Redis key exists, while watching for concurrent changes, and if it does not,
208
+ write the specified default value.
209
+
210
+ This means that using default values in a typical Rails app additional Redis calls (WATCH, EXISTS, UNWATCH) will be
211
+ executed for each Kredis attribute with a default value read or written during a request.
212
+
213
+ ### Callbacks
214
+
192
215
  You can also define `after_change` callbacks that trigger on mutations:
193
216
 
194
217
  ```ruby
@@ -201,6 +224,18 @@ class Person < ApplicationRecord
201
224
  end
202
225
  ```
203
226
 
227
+ ### Multiple Redis servers
228
+
229
+ And using structures on a different than the default `shared` redis instance, relying on `config/redis/secondary.yml`:
230
+
231
+ ```ruby
232
+ one_string = Kredis.string "mystring"
233
+ two_string = Kredis.string "mystring", config: :secondary
234
+
235
+ one_string.value = "just on shared"
236
+ two_string.value != one_string.value
237
+ ```
238
+
204
239
  ## Installation
205
240
 
206
241
  1. Run `./bin/bundle add kredis`
@@ -220,14 +255,14 @@ If you need to connect to Redis with SSL, the recommended approach is to set you
220
255
 
221
256
  ```ruby
222
257
  Kredis::Connections.connections[:shared] = Redis.new(
223
- url: ENV['REDIS_URL'],
258
+ url: ENV["REDIS_URL"],
224
259
  ssl_params: {
225
260
  cert_store: OpenSSL::X509::Store.new.tap { |store|
226
- store.add_file(Rails.root.join('config', 'ca_cert.pem').to_s)
261
+ store.add_file(Rails.root.join("config", "ca_cert.pem").to_s)
227
262
  },
228
263
 
229
264
  cert: OpenSSL::X509::Certificate.new(File.read(
230
- Rails.root.join('config', 'client.crt')
265
+ Rails.root.join("config", "client.crt")
231
266
  )),
232
267
 
233
268
  key: OpenSSL::PKey::RSA.new(
@@ -251,6 +286,29 @@ config.kredis.connector = ->(config) { SomeRedisProxy.new(config) }
251
286
 
252
287
  By default Kredis will use `Redis.new(config)`.
253
288
 
289
+ ## Development
290
+
291
+ A development console is available by running `bin/console`.
292
+
293
+ From there, you can experiment with Kredis. e.g.
294
+
295
+ ```erb
296
+ >> str = Kredis.string "mystring"
297
+ Kredis (0.1ms) Connected to shared
298
+ =>
299
+ #<Kredis::Types::Scalar:0x0000000134c7d938
300
+ ...
301
+ >> str.value = "hello, world"
302
+ Kredis Proxy (2.4ms) SET mystring ["hello, world"]
303
+ => "hello, world"
304
+ >> str.value
305
+ ```
306
+
307
+ Run tests with `bin/test`.
308
+
309
+ [`debug`](https://github.com/ruby/debug) can be used in the development console and in the test suite by inserting a
310
+ breakpoint, e.g. `debugger`.
311
+
254
312
  ## License
255
313
 
256
314
  Kredis is released under the [MIT License](https://opensource.org/licenses/MIT).
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  yaml_path = Rails.root.join("config/redis/shared.yml")
2
4
  unless yaml_path.exist?
3
5
  say "Adding `config/redis/shared.yml`"
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Kredis::Attributes
2
4
  extend ActiveSupport::Concern
3
5
 
@@ -6,52 +8,56 @@ module Kredis::Attributes
6
8
  kredis_connection_with __method__, name, key, config: config, after_change: after_change
7
9
  end
8
10
 
9
- def kredis_string(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
10
- kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in
11
+ def kredis_string(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil)
12
+ kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in
11
13
  end
12
14
 
13
- def kredis_integer(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
14
- kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in
15
+ def kredis_integer(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil)
16
+ kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in
15
17
  end
16
18
 
17
- def kredis_decimal(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
18
- kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in
19
+ def kredis_decimal(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil)
20
+ kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in
19
21
  end
20
22
 
21
- def kredis_datetime(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
22
- kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in
23
+ def kredis_datetime(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil)
24
+ kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in
23
25
  end
24
26
 
25
- def kredis_flag(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
26
- kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in
27
+ def kredis_flag(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil)
28
+ kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in
27
29
 
28
30
  define_method("#{name}?") do
29
31
  send(name).marked?
30
32
  end
31
33
  end
32
34
 
33
- def kredis_float(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
34
- kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in
35
+ def kredis_float(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil)
36
+ kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in
35
37
  end
36
38
 
37
39
  def kredis_enum(name, key: nil, values:, default:, config: :shared, after_change: nil)
38
40
  kredis_connection_with __method__, name, key, values: values, default: default, config: config, after_change: after_change
39
41
  end
40
42
 
41
- def kredis_json(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
42
- kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in
43
+ def kredis_json(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil)
44
+ kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in
45
+ end
46
+
47
+ def kredis_list(name, key: nil, default: nil, typed: :string, config: :shared, after_change: nil)
48
+ kredis_connection_with __method__, name, key, default: default, typed: typed, config: config, after_change: after_change
43
49
  end
44
50
 
45
- def kredis_list(name, key: nil, typed: :string, config: :shared, after_change: nil)
46
- kredis_connection_with __method__, name, key, typed: typed, config: config, after_change: after_change
51
+ def kredis_unique_list(name, limit: nil, key: nil, default: nil, typed: :string, config: :shared, after_change: nil)
52
+ kredis_connection_with __method__, name, key, default: default, limit: limit, typed: typed, config: config, after_change: after_change
47
53
  end
48
54
 
49
- def kredis_unique_list(name, limit: nil, key: nil, typed: :string, config: :shared, after_change: nil)
50
- kredis_connection_with __method__, name, key, limit: limit, typed: typed, config: config, after_change: after_change
55
+ def kredis_set(name, key: nil, default: nil, typed: :string, config: :shared, after_change: nil)
56
+ kredis_connection_with __method__, name, key, default: default, typed: typed, config: config, after_change: after_change
51
57
  end
52
58
 
53
- def kredis_set(name, key: nil, typed: :string, config: :shared, after_change: nil)
54
- kredis_connection_with __method__, name, key, typed: typed, config: config, after_change: after_change
59
+ def kredis_ordered_set(name, limit: nil, default: nil, key: nil, typed: :string, config: :shared, after_change: nil)
60
+ kredis_connection_with __method__, name, key, default: default, limit: limit, typed: typed, config: config, after_change: after_change
55
61
  end
56
62
 
57
63
  def kredis_slot(name, key: nil, config: :shared, after_change: nil)
@@ -62,16 +68,16 @@ module Kredis::Attributes
62
68
  kredis_connection_with __method__, name, key, available: available, config: config, after_change: after_change
63
69
  end
64
70
 
65
- def kredis_counter(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
66
- kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in
71
+ def kredis_counter(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil)
72
+ kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in
67
73
  end
68
74
 
69
- def kredis_hash(name, key: nil, typed: :string, config: :shared, after_change: nil)
70
- kredis_connection_with __method__, name, key, typed: typed, config: config, after_change: after_change
75
+ def kredis_hash(name, key: nil, default: nil, typed: :string, config: :shared, after_change: nil)
76
+ kredis_connection_with __method__, name, key, default: default, typed: typed, config: config, after_change: after_change
71
77
  end
72
78
 
73
- def kredis_boolean(name, key: nil, config: :shared, after_change: nil, expires_in: nil)
74
- kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in
79
+ def kredis_boolean(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil)
80
+ kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in
75
81
  end
76
82
 
77
83
  private
@@ -84,6 +90,7 @@ module Kredis::Attributes
84
90
  if instance_variable_defined?(ivar_symbol)
85
91
  instance_variable_get(ivar_symbol)
86
92
  else
93
+ options[:default] = kredis_default_evaluated(options[:default]) if options[:default]
87
94
  new_type = Kredis.send(type, kredis_key_evaluated(key) || kredis_key_for_attribute(name), **options)
88
95
  instance_variable_set ivar_symbol,
89
96
  after_change ? enrich_after_change_with_record_access(new_type, after_change) : new_type
@@ -102,7 +109,7 @@ module Kredis::Attributes
102
109
  end
103
110
 
104
111
  def kredis_key_for_attribute(name)
105
- "#{self.class.name.tableize.gsub("/", ":")}:#{extract_kredis_id}:#{name}"
112
+ "#{self.class.name.tableize.tr("/", ":")}:#{extract_kredis_id}:#{name}"
106
113
  end
107
114
 
108
115
  def extract_kredis_id
@@ -115,4 +122,12 @@ module Kredis::Attributes
115
122
  when Symbol then Kredis::Types::CallbacksProxy.new(type, ->(_) { send(original_after_change) })
116
123
  end
117
124
  end
125
+
126
+ def kredis_default_evaluated(default)
127
+ case default
128
+ when Proc then Proc.new { default.call(self) }
129
+ when Symbol then send(default)
130
+ else default
131
+ end
132
+ end
118
133
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "redis"
2
4
 
3
5
  module Kredis::Connections
@@ -6,10 +8,8 @@ module Kredis::Connections
6
8
  mattr_accessor :connector, default: ->(config) { Redis.new(config) }
7
9
 
8
10
  def configured_for(name)
9
- connections[name] ||= begin
10
- Kredis.instrument :meta, message: "Connected to #{name}" do
11
- connector.call configurator.config_for("redis/#{name}")
12
- end
11
+ connections[name] ||= Kredis.instrument :meta, message: "Connected to #{name}" do
12
+ connector.call configurator.config_for("redis/#{name}")
13
13
  end
14
14
  end
15
15
 
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kredis::DefaultValues
4
+ extend ActiveSupport::Concern
5
+
6
+ prepended do
7
+ attr_writer :default
8
+
9
+ proxying :watch, :unwatch, :exists?
10
+
11
+ def default
12
+ case @default
13
+ when Proc then @default.call
14
+ when Symbol then send(@default)
15
+ else @default
16
+ end
17
+ end
18
+
19
+ private
20
+ def set_default
21
+ raise NotImplementedError, "Kredis type #{self.class} needs to define #set_default"
22
+ end
23
+ end
24
+
25
+ def initialize(...)
26
+ super
27
+
28
+ if default
29
+ watch do
30
+ set_default unless exists?
31
+
32
+ unwatch
33
+ end
34
+ end
35
+ end
36
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/log_subscriber"
2
4
 
3
5
  class Kredis::LogSubscriber < ActiveSupport::LogSubscriber
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/module/delegation"
2
4
 
3
5
  class Kredis::Migration
@@ -33,10 +35,10 @@ class Kredis::Migration
33
35
  def delete_all(*key_patterns)
34
36
  log_migration "DELETE ALL #{key_patterns.inspect}" do
35
37
  if key_patterns.length > 1
36
- @redis.del *key_patterns
38
+ @redis.del(*key_patterns)
37
39
  else
38
40
  each_key_batch_matching(key_patterns.first) do |keys, pipeline|
39
- pipeline.del *keys
41
+ pipeline.del(*keys)
40
42
  end
41
43
  end
42
44
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Kredis::Namespace
2
4
  def namespace=(namespace)
3
5
  Thread.current[:kredis_namespace] = namespace
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Kredis::Railtie < ::Rails::Railtie
2
4
  config.kredis = ActiveSupport::OrderedOptions.new
3
5
 
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kredis
4
+ module Type
5
+ class Boolean < ActiveModel::Type::Boolean
6
+ def serialize(value)
7
+ super ? 1 : 0
8
+ end
9
+ end
10
+ end
11
+ end
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "json"
2
4
  require "active_model/type"
3
- require "kredis/type/json"
5
+ require "kredis/type/boolean"
4
6
  require "kredis/type/datetime"
7
+ require "kredis/type/json"
5
8
 
6
9
  module Kredis::TypeCasting
7
10
  class InvalidType < StandardError; end
@@ -11,7 +14,7 @@ module Kredis::TypeCasting
11
14
  integer: ActiveModel::Type::Integer.new,
12
15
  decimal: ActiveModel::Type::Decimal.new,
13
16
  float: ActiveModel::Type::Float.new,
14
- boolean: ActiveModel::Type::Boolean.new,
17
+ boolean: Kredis::Type::Boolean.new,
15
18
  datetime: Kredis::Type::DateTime.new,
16
19
  json: Kredis::Type::Json.new
17
20
  }
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Kredis::Types::CallbacksProxy
2
4
  attr_reader :type
3
5
  delegate :to_s, to: :type
@@ -1,18 +1,22 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Kredis::Types::Counter < Kredis::Types::Proxying
4
+ prepend Kredis::DefaultValues
5
+
2
6
  proxying :multi, :set, :incrby, :decrby, :get, :del, :exists?
3
7
 
4
8
  attr_accessor :expires_in
5
9
 
6
10
  def increment(by: 1)
7
11
  multi do
8
- set 0, ex: expires_in, nx: true
12
+ set 0, ex: expires_in, nx: true if expires_in
9
13
  incrby by
10
14
  end[-1]
11
15
  end
12
16
 
13
17
  def decrement(by: 1)
14
18
  multi do
15
- set 0, ex: expires_in, nx: true
19
+ set 0, ex: expires_in, nx: true if expires_in
16
20
  decrby by
17
21
  end[-1]
18
22
  end
@@ -24,4 +28,9 @@ class Kredis::Types::Counter < Kredis::Types::Proxying
24
28
  def reset
25
29
  del
26
30
  end
31
+
32
+ private
33
+ def set_default
34
+ increment by: default
35
+ end
27
36
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Kredis::Types::Cycle < Kredis::Types::Counter
2
4
  attr_accessor :values
3
5
 
@@ -1,9 +1,15 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/object/inclusion"
2
4
 
3
5
  class Kredis::Types::Enum < Kredis::Types::Proxying
4
- proxying :set, :get, :del, :exists?
6
+ prepend Kredis::DefaultValues
7
+
8
+ InvalidDefault = Class.new(StandardError)
5
9
 
6
- attr_accessor :values, :default
10
+ proxying :set, :get, :del, :exists?, :multi
11
+
12
+ attr_accessor :values
7
13
 
8
14
  def initialize(...)
9
15
  super
@@ -17,11 +23,14 @@ class Kredis::Types::Enum < Kredis::Types::Proxying
17
23
  end
18
24
 
19
25
  def value
20
- get || default
26
+ get
21
27
  end
22
28
 
23
29
  def reset
24
- del
30
+ multi do
31
+ del
32
+ set_default
33
+ end
25
34
  end
26
35
 
27
36
  private
@@ -31,4 +40,12 @@ class Kredis::Types::Enum < Kredis::Types::Proxying
31
40
  define_singleton_method("#{defined_value}!") { self.value = defined_value }
32
41
  end
33
42
  end
43
+
44
+ def set_default
45
+ if default.in?(values) || default.nil?
46
+ set default
47
+ else
48
+ raise InvalidDefault, "Default value #{default.inspect} for #{key} is not a valid option (Valid values: #{values.join(", ")})"
49
+ end
50
+ end
34
51
  end
@@ -1,4 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Kredis::Types::Flag < Kredis::Types::Proxying
4
+ prepend Kredis::DefaultValues
5
+
2
6
  proxying :set, :exists?, :del
3
7
 
4
8
  attr_accessor :expires_in
@@ -14,4 +18,9 @@ class Kredis::Types::Flag < Kredis::Types::Proxying
14
18
  def remove
15
19
  del
16
20
  end
21
+
22
+ private
23
+ def set_default
24
+ mark if default
25
+ end
17
26
  end
@@ -1,6 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/hash"
2
4
 
3
5
  class Kredis::Types::Hash < Kredis::Types::Proxying
6
+ prepend Kredis::DefaultValues
7
+
4
8
  proxying :hget, :hset, :hmget, :hdel, :hgetall, :hkeys, :hvals, :del, :exists?
5
9
 
6
10
  attr_accessor :typed
@@ -14,7 +18,7 @@ class Kredis::Types::Hash < Kredis::Types::Proxying
14
18
  end
15
19
 
16
20
  def update(**entries)
17
- hset entries.transform_values{ |val| type_to_string(val, typed) } if entries.flatten.any?
21
+ hset entries.transform_values { |val| type_to_string(val, typed) } if entries.flatten.any?
18
22
  end
19
23
 
20
24
  def values_at(*keys)
@@ -42,4 +46,9 @@ class Kredis::Types::Hash < Kredis::Types::Proxying
42
46
  def values
43
47
  strings_to_types(hvals || [], typed)
44
48
  end
49
+
50
+ private
51
+ def set_default
52
+ update(**default)
53
+ end
45
54
  end
@@ -1,4 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Kredis::Types::List < Kredis::Types::Proxying
4
+ prepend Kredis::DefaultValues
5
+
2
6
  proxying :lrange, :lrem, :lpush, :ltrim, :rpush, :exists?, :del
3
7
 
4
8
  attr_accessor :typed
@@ -28,4 +32,9 @@ class Kredis::Types::List < Kredis::Types::Proxying
28
32
  def last(n = nil)
29
33
  n ? lrange(-n, -1) : lrange(-1, -1).first
30
34
  end
35
+
36
+ private
37
+ def set_default
38
+ append default
39
+ end
31
40
  end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ class Kredis::Types::OrderedSet < Kredis::Types::Proxying
4
+ prepend Kredis::DefaultValues
5
+
6
+ proxying :multi, :zrange, :zrem, :zadd, :zremrangebyrank, :zcard, :exists?, :del, :zscore
7
+
8
+ attr_accessor :typed
9
+ attr_reader :limit
10
+
11
+ def elements
12
+ strings_to_types(zrange(0, -1) || [], typed)
13
+ end
14
+ alias to_a elements
15
+
16
+ def remove(*elements)
17
+ zrem(types_to_strings(elements, typed))
18
+ end
19
+
20
+ def include?(element)
21
+ !!zscore(type_to_string(element, typed))
22
+ end
23
+
24
+ def prepend(elements)
25
+ insert(elements, prepending: true)
26
+ end
27
+
28
+ def append(elements)
29
+ insert(elements)
30
+ end
31
+ alias << append
32
+
33
+ def limit=(limit)
34
+ raise "Limit must be greater than 0" if limit && limit <= 0
35
+
36
+ @limit = limit
37
+ end
38
+
39
+ private
40
+ def insert(elements, prepending: false)
41
+ elements = Array(elements)
42
+ return if elements.empty?
43
+
44
+ elements_with_scores = types_to_strings(elements, typed).map.with_index do |element, index|
45
+ incremental_score = index * 0.000001
46
+
47
+ score = if prepending
48
+ -base_score - incremental_score
49
+ else
50
+ base_score + incremental_score
51
+ end
52
+
53
+ [ score, element ]
54
+ end
55
+
56
+ multi do
57
+ zadd(elements_with_scores)
58
+ trim(from_beginning: prepending)
59
+ end
60
+ end
61
+
62
+ def base_score
63
+ process_start_time + process_uptime
64
+ end
65
+
66
+ def process_start_time
67
+ @process_start_time ||= unproxied_redis.time.join(".").to_f - process_uptime
68
+ end
69
+
70
+ def process_uptime
71
+ Process.clock_gettime(Process::CLOCK_MONOTONIC)
72
+ end
73
+
74
+ def trim(from_beginning:)
75
+ return unless limit
76
+
77
+ if from_beginning
78
+ zremrangebyrank(limit, -1)
79
+ else
80
+ zremrangebyrank(0, -(limit + 1))
81
+ end
82
+ end
83
+
84
+ def set_default
85
+ append default
86
+ end
87
+ end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Kredis::Types::Proxy::Failsafe
2
4
  def initialize(*)
3
5
  super
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Kredis::Types::Proxy
2
4
  require_relative "proxy/failsafe"
3
5
  include Failsafe
@@ -20,6 +22,16 @@ class Kredis::Types::Proxy
20
22
  end
21
23
  end
22
24
 
25
+ def watch(&block)
26
+ redis.watch(key) do
27
+ block.call
28
+ end
29
+ end
30
+
31
+ def unwatch
32
+ redis.unwatch
33
+ end
34
+
23
35
  def method_missing(method, *args, **kwargs)
24
36
  Kredis.instrument :proxy, **log_message(method, *args, **kwargs) do
25
37
  failsafe do
@@ -1,13 +1,16 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support/core_ext/module/delegation"
2
4
 
3
5
  class Kredis::Types::Proxying
4
6
  attr_accessor :proxy, :key
5
7
 
6
8
  def self.proxying(*commands)
7
- delegate *commands, to: :proxy
9
+ delegate(*commands, to: :proxy)
8
10
  end
9
11
 
10
12
  def initialize(redis, key, **options)
13
+ @redis = redis
11
14
  @key = key
12
15
  @proxy = Kredis::Types::Proxy.new(redis, key)
13
16
  options.each { |key, value| send("#{key}=", value) }
@@ -17,6 +20,12 @@ class Kredis::Types::Proxying
17
20
  proxy.suppress_failsafe_with(returning: returning, &block)
18
21
  end
19
22
 
23
+ def unproxied_redis
24
+ # Generally, this should not be used. It's only here for the rare case where we need to
25
+ # call Redis commands that don't reference a key and don't want to be pipelined.
26
+ @redis
27
+ end
28
+
20
29
  private
21
30
  delegate :type_to_string, :string_to_type, :types_to_strings, :strings_to_types, to: :Kredis
22
31
  end
@@ -1,7 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Kredis::Types::Scalar < Kredis::Types::Proxying
4
+ prepend Kredis::DefaultValues
5
+
2
6
  proxying :set, :get, :exists?, :del, :expire, :expireat
3
7
 
4
- attr_accessor :typed, :default, :expires_in
8
+ attr_accessor :typed, :expires_in
5
9
 
6
10
  def value=(value)
7
11
  set type_to_string(value, typed), ex: expires_in
@@ -36,4 +40,9 @@ class Kredis::Types::Scalar < Kredis::Types::Proxying
36
40
  def expire_at(datetime)
37
41
  expireat datetime.to_i
38
42
  end
43
+
44
+ private
45
+ def set_default
46
+ self.value = default
47
+ end
39
48
  end
@@ -1,4 +1,8 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Kredis::Types::Set < Kredis::Types::Proxying
4
+ prepend Kredis::DefaultValues
5
+
2
6
  proxying :smembers, :sadd, :srem, :multi, :del, :sismember, :scard, :spop, :exists?, :srandmember
3
7
 
4
8
  attr_accessor :typed
@@ -47,4 +51,9 @@ class Kredis::Types::Set < Kredis::Types::Proxying
47
51
  strings_to_types(srandmember(count), typed)
48
52
  end
49
53
  end
54
+
55
+ private
56
+ def set_default
57
+ add default
58
+ end
50
59
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  class Kredis::Types::Slots < Kredis::Types::Proxying
2
4
  class NotAvailable < StandardError; end
3
5
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  # You'd normally call this a set, but Redis already has another data type for that
2
4
  class Kredis::Types::UniqueList < Kredis::Types::List
3
5
  proxying :multi, :ltrim, :exists?
@@ -22,7 +24,7 @@ class Kredis::Types::UniqueList < Kredis::Types::List
22
24
  multi do
23
25
  remove elements
24
26
  super
25
- ltrim -limit, -1 if limit
27
+ ltrim(-limit, -1) if limit
26
28
  end
27
29
  end
28
30
  alias << append
data/lib/kredis/types.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module Kredis::Types
2
4
  autoload :CallbacksProxy, "kredis/types/callbacks_proxy"
3
5
 
@@ -39,36 +41,40 @@ module Kredis::Types
39
41
  end
40
42
 
41
43
 
42
- def counter(key, expires_in: nil, config: :shared, after_change: nil)
43
- type_from(Counter, config, key, after_change: after_change, expires_in: expires_in)
44
+ def counter(key, expires_in: nil, default: nil, config: :shared, after_change: nil)
45
+ type_from(Counter, config, key, after_change: after_change, default: default, expires_in: expires_in)
44
46
  end
45
47
 
46
48
  def cycle(key, values:, expires_in: nil, config: :shared, after_change: nil)
47
49
  type_from(Cycle, config, key, after_change: after_change, values: values, expires_in: expires_in)
48
50
  end
49
51
 
50
- def flag(key, config: :shared, after_change: nil, expires_in: nil)
51
- type_from(Flag, config, key, after_change: after_change, expires_in: expires_in)
52
+ def flag(key, default: nil, config: :shared, after_change: nil, expires_in: nil)
53
+ type_from(Flag, config, key, after_change: after_change, default: default, expires_in: expires_in)
52
54
  end
53
55
 
54
56
  def enum(key, values:, default:, config: :shared, after_change: nil)
55
57
  type_from(Enum, config, key, after_change: after_change, values: values, default: default)
56
58
  end
57
59
 
58
- def hash(key, typed: :string, config: :shared, after_change: nil)
59
- type_from(Hash, config, key, after_change: after_change, typed: typed)
60
+ def hash(key, typed: :string, default: nil, config: :shared, after_change: nil)
61
+ type_from(Hash, config, key, after_change: after_change, default: default, typed: typed)
62
+ end
63
+
64
+ def list(key, default: nil, typed: :string, config: :shared, after_change: nil)
65
+ type_from(List, config, key, after_change: after_change, default: default, typed: typed)
60
66
  end
61
67
 
62
- def list(key, typed: :string, config: :shared, after_change: nil)
63
- type_from(List, config, key, after_change: after_change, typed: typed)
68
+ def unique_list(key, default: nil, typed: :string, limit: nil, config: :shared, after_change: nil)
69
+ type_from(UniqueList, config, key, after_change: after_change, default: default, typed: typed, limit: limit)
64
70
  end
65
71
 
66
- def unique_list(key, typed: :string, limit: nil, config: :shared, after_change: nil)
67
- type_from(UniqueList, config, key, after_change: after_change, typed: typed, limit: limit)
72
+ def set(key, default: nil, typed: :string, config: :shared, after_change: nil)
73
+ type_from(Set, config, key, after_change: after_change, default: default, typed: typed)
68
74
  end
69
75
 
70
- def set(key, typed: :string, config: :shared, after_change: nil)
71
- type_from(Set, config, key, after_change: after_change, typed: typed)
76
+ def ordered_set(key, default: nil, typed: :string, limit: nil, config: :shared, after_change: nil)
77
+ type_from(OrderedSet, config, key, after_change: after_change, default: default, typed: typed, limit: limit)
72
78
  end
73
79
 
74
80
  def slot(key, config: :shared, after_change: nil)
@@ -99,4 +105,5 @@ require "kredis/types/hash"
99
105
  require "kredis/types/list"
100
106
  require "kredis/types/unique_list"
101
107
  require "kredis/types/set"
108
+ require "kredis/types/ordered_set"
102
109
  require "kredis/types/slots"
@@ -1,3 +1,3 @@
1
1
  module Kredis
2
- VERSION = "1.4.0"
2
+ VERSION = "1.6.0"
3
3
  end
data/lib/kredis.rb CHANGED
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require "active_support"
2
4
  require "active_support/core_ext/module/attribute_accessors"
3
5
  require "active_support/core_ext/module/attribute_accessors_per_thread"
@@ -8,6 +10,7 @@ require "kredis/connections"
8
10
  require "kredis/log_subscriber"
9
11
  require "kredis/namespace"
10
12
  require "kredis/type_casting"
13
+ require "kredis/default_values"
11
14
  require "kredis/types"
12
15
  require "kredis/attributes"
13
16
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  namespace :kredis do
2
4
  desc "Install kredis"
3
5
  task :install do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kredis
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.4.0
4
+ version: 1.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kasper Timm Hansen
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2023-06-18 00:00:00.000000000 Z
12
+ date: 2023-10-19 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -86,10 +86,12 @@ files:
86
86
  - lib/kredis.rb
87
87
  - lib/kredis/attributes.rb
88
88
  - lib/kredis/connections.rb
89
+ - lib/kredis/default_values.rb
89
90
  - lib/kredis/log_subscriber.rb
90
91
  - lib/kredis/migration.rb
91
92
  - lib/kredis/namespace.rb
92
93
  - lib/kredis/railtie.rb
94
+ - lib/kredis/type/boolean.rb
93
95
  - lib/kredis/type/datetime.rb
94
96
  - lib/kredis/type/json.rb
95
97
  - lib/kredis/type_casting.rb
@@ -101,6 +103,7 @@ files:
101
103
  - lib/kredis/types/flag.rb
102
104
  - lib/kredis/types/hash.rb
103
105
  - lib/kredis/types/list.rb
106
+ - lib/kredis/types/ordered_set.rb
104
107
  - lib/kredis/types/proxy.rb
105
108
  - lib/kredis/types/proxy/failsafe.rb
106
109
  - lib/kredis/types/proxying.rb
@@ -129,7 +132,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
129
132
  - !ruby/object:Gem::Version
130
133
  version: '0'
131
134
  requirements: []
132
- rubygems_version: 3.4.10
135
+ rubygems_version: 3.4.20
133
136
  signing_key:
134
137
  specification_version: 4
135
138
  summary: Higher-level data structures built on Redis.