kredis 1.3.0.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/MIT-LICENSE +2 -2
- data/README.md +97 -19
- data/lib/install/install.rb +2 -0
- data/lib/kredis/attributes.rb +46 -27
- data/lib/kredis/connections.rb +11 -2
- data/lib/kredis/default_values.rb +36 -0
- data/lib/kredis/log_subscriber.rb +3 -1
- data/lib/kredis/migration.rb +4 -2
- data/lib/kredis/namespace.rb +23 -4
- data/lib/kredis/railtie.rb +3 -1
- data/lib/kredis/type/boolean.rb +11 -0
- data/lib/kredis/type/datetime.rb +2 -2
- data/lib/kredis/type_casting.rb +5 -2
- data/lib/kredis/types/callbacks_proxy.rb +2 -0
- data/lib/kredis/types/counter.rb +11 -2
- data/lib/kredis/types/cycle.rb +2 -0
- data/lib/kredis/types/enum.rb +21 -4
- data/lib/kredis/types/flag.rb +9 -0
- data/lib/kredis/types/hash.rb +10 -1
- data/lib/kredis/types/limiter.rb +24 -0
- data/lib/kredis/types/list.rb +13 -0
- data/lib/kredis/types/ordered_set.rb +87 -0
- data/lib/kredis/types/proxy/failsafe.rb +2 -0
- data/lib/kredis/types/proxy.rb +12 -0
- data/lib/kredis/types/proxying.rb +10 -1
- data/lib/kredis/types/scalar.rb +10 -1
- data/lib/kredis/types/set.rb +23 -3
- data/lib/kredis/types/slots.rb +2 -0
- data/lib/kredis/types/unique_list.rb +3 -1
- data/lib/kredis/types.rb +24 -12
- data/lib/kredis/version.rb +1 -1
- data/lib/kredis.rb +3 -0
- data/lib/tasks/kredis/install.rake +2 -0
- metadata +21 -7
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c7b578a8b9d0b956e519c324d7ba9dd6036124637801366d9f1a2719a7d340d5
|
|
4
|
+
data.tar.gz: a4bc7d1a74540a7bd41691105833123bffd27ff32f07b01ce82986820d6068e3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5ca284918aa9079b5265f1603a9ff33c7fe6f562b4cf76a97ce571542a7dccb9265d2d94441b578570729e415d0ea1adf29786612a31635660cebe82917172df
|
|
7
|
+
data.tar.gz: 569e06a45f6696800372a6f11a953e12267faf009596767366a860c0f6f691c40668d5a0e94ea7cd0c9bf09370870bca209a3e9081d8f0489be79fce64864ae1
|
data/MIT-LICENSE
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
Copyright (c) 2020
|
|
1
|
+
Copyright (c) 2020-2025 37signals LLC
|
|
2
2
|
|
|
3
3
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
4
4
|
a copy of this software and associated documentation files (the
|
|
@@ -17,4 +17,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
17
17
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
18
18
|
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
19
19
|
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
20
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
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"
|
|
@@ -48,11 +48,13 @@ There are data structures for counters, enums, flags, lists, unique lists, sets,
|
|
|
48
48
|
list = Kredis.list "mylist"
|
|
49
49
|
list << "hello world!" # => RPUSH mylist "hello world!"
|
|
50
50
|
[ "hello world!" ] == list.elements # => LRANGE mylist 0, -1
|
|
51
|
+
list.clear # => DEL mylist
|
|
51
52
|
|
|
52
|
-
integer_list = Kredis.list "myintegerlist", typed: :integer
|
|
53
|
-
integer_list.append([
|
|
54
|
-
integer_list <<
|
|
55
|
-
[ 1, 2, 3, 4 ] == integer_list.elements
|
|
53
|
+
integer_list = Kredis.list "myintegerlist", typed: :integer, default: [ 1, 2, 3 ] # => EXISTS? myintegerlist, RPUSH myintegerlist "1" "2" "3"
|
|
54
|
+
integer_list.append([ 4, 5, 6 ]) # => RPUSH myintegerlist "4" "5" "6"
|
|
55
|
+
integer_list << 7 # => RPUSH myintegerlist "7"
|
|
56
|
+
[ 1, 2, 3, 4, 5, 6, 7 ] == integer_list.elements # => LRANGE myintegerlist 0 -1
|
|
57
|
+
integer_list.clear # => DEL myintegerlist
|
|
56
58
|
|
|
57
59
|
unique_list = Kredis.unique_list "myuniquelist"
|
|
58
60
|
unique_list.append(%w[ 2 3 4 ]) # => LREM myuniquelist 0, "2" + LREM myuniquelist 0, "3" + LREM myuniquelist 0, "4" + RPUSH myuniquelist "2", "3", "4"
|
|
@@ -61,12 +63,22 @@ unique_list.append([])
|
|
|
61
63
|
unique_list << "5" # => LREM myuniquelist 0, "5" + RPUSH myuniquelist "5"
|
|
62
64
|
unique_list.remove(3) # => LREM myuniquelist 0, "3"
|
|
63
65
|
[ "4", "2", "1", "5" ] == unique_list.elements # => LRANGE myuniquelist 0, -1
|
|
66
|
+
unique_list.clear # => DEL myuniquelist
|
|
67
|
+
|
|
68
|
+
ordered_set = Kredis.ordered_set "myorderedset"
|
|
69
|
+
ordered_set.append(%w[ 2 3 4 ]) # => ZADD myorderedset 1646131025.4953232 2 1646131025.495326 3 1646131025.4953272 4
|
|
70
|
+
ordered_set.prepend(%w[ 1 2 3 4 ]) # => ZADD myorderedset -1646131025.4957051 1 -1646131025.495707 2 -1646131025.4957082 3 -1646131025.4957092 4
|
|
71
|
+
ordered_set.append([])
|
|
72
|
+
ordered_set << "5" # => ZADD myorderedset 1646131025.4960442 5
|
|
73
|
+
ordered_set.remove(3) # => ZREM myorderedset 3
|
|
74
|
+
[ "4", "2", "1", "5" ] == ordered_set.elements # => ZRANGE myorderedset 0 -1
|
|
64
75
|
|
|
65
76
|
set = Kredis.set "myset", typed: :datetime
|
|
66
77
|
set.add(DateTime.tomorrow, DateTime.yesterday) # => SADD myset "2021-02-03 00:00:00 +0100" "2021-02-01 00:00:00 +0100"
|
|
67
78
|
set << DateTime.tomorrow # => SADD myset "2021-02-03 00:00:00 +0100"
|
|
68
79
|
2 == set.size # => SCARD myset
|
|
69
80
|
[ DateTime.tomorrow, DateTime.yesterday ] == set.members # => SMEMBERS myset
|
|
81
|
+
set.clear # => DEL myset
|
|
70
82
|
|
|
71
83
|
hash = Kredis.hash "myhash"
|
|
72
84
|
hash.update("key" => "value", "key2" => "value2") # => HSET myhash "key", "value", "key2", "value2"
|
|
@@ -94,6 +106,7 @@ counter.increment by: 2 # => SET mycounter 0 EX 5 NX + INCRBY "mycounter
|
|
|
94
106
|
2 == counter.value # => GET "mycounter"
|
|
95
107
|
sleep 6.seconds
|
|
96
108
|
0 == counter.value # => GET "mycounter"
|
|
109
|
+
counter.reset # => DEL mycounter
|
|
97
110
|
|
|
98
111
|
cycle = Kredis.cycle "mycycle", values: %i[ one two three ]
|
|
99
112
|
:one == cycle.value # => GET mycycle
|
|
@@ -103,6 +116,7 @@ cycle.next # => GET mycycle + SET mycycle 2
|
|
|
103
116
|
:three == cycle.value # => GET mycycle
|
|
104
117
|
cycle.next # => GET mycycle + SET mycycle 0
|
|
105
118
|
:one == cycle.value # => GET mycycle
|
|
119
|
+
cycle.reset # => DEL mycycle
|
|
106
120
|
|
|
107
121
|
enum = Kredis.enum "myenum", values: %w[ one two three ], default: "one"
|
|
108
122
|
"one" == enum.value # => GET myenum
|
|
@@ -153,18 +167,22 @@ sleep 0.5.seconds
|
|
|
153
167
|
true == flag.marked? #=> EXISTS myflag
|
|
154
168
|
sleep 0.6.seconds
|
|
155
169
|
false == flag.marked? #=> EXISTS myflag
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
And using structures on a different than the default `shared` redis instance, relying on `config/redis/secondary.yml`:
|
|
159
170
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
171
|
+
limiter = Kredis.limiter "mylimit", limit: 3, expires_in: 5.seconds
|
|
172
|
+
0 == limiter.value # => GET "limiter"
|
|
173
|
+
limiter.poke # => SET limiter 0 NX + INCRBY limiter 1
|
|
174
|
+
limiter.poke # => SET limiter 0 NX + INCRBY limiter 1
|
|
175
|
+
false == limiter.exceeded? # => GET "limiter"
|
|
176
|
+
limiter.poke # => SET limiter 0 NX + INCRBY limiter 1
|
|
177
|
+
true == limiter.exceeded? # => GET "limiter"
|
|
178
|
+
sleep 6
|
|
179
|
+
limiter.poke # => SET limiter 0 NX + INCRBY limiter 1
|
|
180
|
+
limiter.poke # => SET limiter 0 NX + INCRBY limiter 1
|
|
181
|
+
false == limiter.exceeded? # => GET "limiter"
|
|
166
182
|
```
|
|
167
183
|
|
|
184
|
+
### Models
|
|
185
|
+
|
|
168
186
|
You can use all these structures in models:
|
|
169
187
|
|
|
170
188
|
```ruby
|
|
@@ -189,6 +207,29 @@ person.morning.value = "blue" # => SET people:5:morning
|
|
|
189
207
|
true == person.morning.blue? # => GET people:5:morning
|
|
190
208
|
```
|
|
191
209
|
|
|
210
|
+
### Default values
|
|
211
|
+
|
|
212
|
+
You can set a default value for all types. For example:
|
|
213
|
+
|
|
214
|
+
```ruby
|
|
215
|
+
list = Kredis.list "favorite_colors", default: [ "red", "green", "blue" ]
|
|
216
|
+
|
|
217
|
+
# or, in a model
|
|
218
|
+
class Person < ApplicationRecord
|
|
219
|
+
kredis_string :name, default: "Unknown"
|
|
220
|
+
kredis_list :favorite_colors, default: [ "red", "green", "blue" ]
|
|
221
|
+
end
|
|
222
|
+
```
|
|
223
|
+
|
|
224
|
+
There's a performance overhead to consider though. When you first read or write an attribute in a model, Kredis will
|
|
225
|
+
check if the underlying Redis key exists, while watching for concurrent changes, and if it does not,
|
|
226
|
+
write the specified default value.
|
|
227
|
+
|
|
228
|
+
This means that using default values in a typical Rails app additional Redis calls (WATCH, EXISTS, UNWATCH) will be
|
|
229
|
+
executed for each Kredis attribute with a default value read or written during a request.
|
|
230
|
+
|
|
231
|
+
### Callbacks
|
|
232
|
+
|
|
192
233
|
You can also define `after_change` callbacks that trigger on mutations:
|
|
193
234
|
|
|
194
235
|
```ruby
|
|
@@ -201,6 +242,18 @@ class Person < ApplicationRecord
|
|
|
201
242
|
end
|
|
202
243
|
```
|
|
203
244
|
|
|
245
|
+
### Multiple Redis servers
|
|
246
|
+
|
|
247
|
+
And using structures on a different than the default `shared` redis instance, relying on `config/redis/secondary.yml`:
|
|
248
|
+
|
|
249
|
+
```ruby
|
|
250
|
+
one_string = Kredis.string "mystring"
|
|
251
|
+
two_string = Kredis.string "mystring", config: :secondary
|
|
252
|
+
|
|
253
|
+
one_string.value = "just on shared"
|
|
254
|
+
two_string.value != one_string.value
|
|
255
|
+
```
|
|
256
|
+
|
|
204
257
|
## Installation
|
|
205
258
|
|
|
206
259
|
1. Run `./bin/bundle add kredis`
|
|
@@ -210,9 +263,11 @@ Additional configurations can be added under `config/redis/*.yml` and referenced
|
|
|
210
263
|
|
|
211
264
|
Kredis passes the configuration to `Redis.new` to establish the connection. See the [Redis documentation](https://github.com/redis/redis-rb) for other configuration options.
|
|
212
265
|
|
|
266
|
+
If you don't have `config/redis/shared.yml` (or use another named configuration), Kredis will default to look in env for `REDIS_URL`, then fallback to a default URL of `redis://127.0.0.1:6379/0`.
|
|
267
|
+
|
|
213
268
|
### Redis support
|
|
214
269
|
|
|
215
|
-
Kredis works with Redis server 4.0+, with the [Redis Ruby](https://github.com/redis/redis-rb) client version 4.2+.
|
|
270
|
+
Kredis works with Redis server 4.0+, with the [Redis Ruby](https://github.com/redis/redis-rb) client version 4.2+. Redis Cluster is not supported.
|
|
216
271
|
|
|
217
272
|
### Setting SSL options on Redis Connections
|
|
218
273
|
|
|
@@ -220,14 +275,14 @@ If you need to connect to Redis with SSL, the recommended approach is to set you
|
|
|
220
275
|
|
|
221
276
|
```ruby
|
|
222
277
|
Kredis::Connections.connections[:shared] = Redis.new(
|
|
223
|
-
url: ENV[
|
|
278
|
+
url: ENV["REDIS_URL"],
|
|
224
279
|
ssl_params: {
|
|
225
280
|
cert_store: OpenSSL::X509::Store.new.tap { |store|
|
|
226
|
-
store.add_file(Rails.root.join(
|
|
281
|
+
store.add_file(Rails.root.join("config", "ca_cert.pem").to_s)
|
|
227
282
|
},
|
|
228
283
|
|
|
229
284
|
cert: OpenSSL::X509::Certificate.new(File.read(
|
|
230
|
-
Rails.root.join(
|
|
285
|
+
Rails.root.join("config", "client.crt")
|
|
231
286
|
)),
|
|
232
287
|
|
|
233
288
|
key: OpenSSL::PKey::RSA.new(
|
|
@@ -251,6 +306,29 @@ config.kredis.connector = ->(config) { SomeRedisProxy.new(config) }
|
|
|
251
306
|
|
|
252
307
|
By default Kredis will use `Redis.new(config)`.
|
|
253
308
|
|
|
309
|
+
## Development
|
|
310
|
+
|
|
311
|
+
A development console is available by running `bin/console`.
|
|
312
|
+
|
|
313
|
+
From there, you can experiment with Kredis. e.g.
|
|
314
|
+
|
|
315
|
+
```erb
|
|
316
|
+
>> str = Kredis.string "mystring"
|
|
317
|
+
Kredis (0.1ms) Connected to shared
|
|
318
|
+
=>
|
|
319
|
+
#<Kredis::Types::Scalar:0x0000000134c7d938
|
|
320
|
+
...
|
|
321
|
+
>> str.value = "hello, world"
|
|
322
|
+
Kredis Proxy (2.4ms) SET mystring ["hello, world"]
|
|
323
|
+
=> "hello, world"
|
|
324
|
+
>> str.value
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
Run tests with `bin/test`.
|
|
328
|
+
|
|
329
|
+
[`debug`](https://github.com/ruby/debug) can be used in the development console and in the test suite by inserting a
|
|
330
|
+
breakpoint, e.g. `debugger`.
|
|
331
|
+
|
|
254
332
|
## License
|
|
255
333
|
|
|
256
334
|
Kredis is released under the [MIT License](https://opensource.org/licenses/MIT).
|
data/lib/install/install.rb
CHANGED
data/lib/kredis/attributes.rb
CHANGED
|
@@ -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
|
|
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
|
|
50
|
-
kredis_connection_with __method__, name, key,
|
|
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
|
|
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,20 @@ 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
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def kredis_limiter(name, limit:, key: nil, config: :shared, after_change: nil, expires_in: nil)
|
|
76
|
+
kredis_connection_with __method__, name, key, limit: limit, config: config, after_change: after_change, expires_in: expires_in
|
|
67
77
|
end
|
|
68
78
|
|
|
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
|
|
79
|
+
def kredis_hash(name, key: nil, default: nil, typed: :string, config: :shared, after_change: nil)
|
|
80
|
+
kredis_connection_with __method__, name, key, default: default, typed: typed, config: config, after_change: after_change
|
|
71
81
|
end
|
|
72
82
|
|
|
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
|
|
83
|
+
def kredis_boolean(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil)
|
|
84
|
+
kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in
|
|
75
85
|
end
|
|
76
86
|
|
|
77
87
|
private
|
|
@@ -84,6 +94,7 @@ module Kredis::Attributes
|
|
|
84
94
|
if instance_variable_defined?(ivar_symbol)
|
|
85
95
|
instance_variable_get(ivar_symbol)
|
|
86
96
|
else
|
|
97
|
+
options[:default] = kredis_default_evaluated(options[:default]) if options[:default]
|
|
87
98
|
new_type = Kredis.send(type, kredis_key_evaluated(key) || kredis_key_for_attribute(name), **options)
|
|
88
99
|
instance_variable_set ivar_symbol,
|
|
89
100
|
after_change ? enrich_after_change_with_record_access(new_type, after_change) : new_type
|
|
@@ -102,7 +113,7 @@ module Kredis::Attributes
|
|
|
102
113
|
end
|
|
103
114
|
|
|
104
115
|
def kredis_key_for_attribute(name)
|
|
105
|
-
"#{self.class.name.tableize.
|
|
116
|
+
"#{self.class.name.tableize.tr("/", ":")}:#{extract_kredis_id}:#{name}"
|
|
106
117
|
end
|
|
107
118
|
|
|
108
119
|
def extract_kredis_id
|
|
@@ -115,4 +126,12 @@ module Kredis::Attributes
|
|
|
115
126
|
when Symbol then Kredis::Types::CallbacksProxy.new(type, ->(_) { send(original_after_change) })
|
|
116
127
|
end
|
|
117
128
|
end
|
|
129
|
+
|
|
130
|
+
def kredis_default_evaluated(default)
|
|
131
|
+
case default
|
|
132
|
+
when Proc then Proc.new { default.call(self) }
|
|
133
|
+
when Symbol then send(default)
|
|
134
|
+
else default
|
|
135
|
+
end
|
|
136
|
+
end
|
|
118
137
|
end
|
data/lib/kredis/connections.rb
CHANGED
|
@@ -1,14 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "redis"
|
|
2
4
|
|
|
3
5
|
module Kredis::Connections
|
|
6
|
+
DEFAULT_REDIS_URL = "redis://127.0.0.1:6379/0"
|
|
7
|
+
DEFAULT_REDIS_TIMEOUT = 1
|
|
8
|
+
|
|
4
9
|
mattr_accessor :connections, default: Hash.new
|
|
5
10
|
mattr_accessor :configurator
|
|
6
11
|
mattr_accessor :connector, default: ->(config) { Redis.new(config) }
|
|
7
12
|
|
|
8
13
|
def configured_for(name)
|
|
9
|
-
connections[name] ||=
|
|
10
|
-
|
|
14
|
+
connections[name] ||= Kredis.instrument :meta, message: "Connected to #{name}" do
|
|
15
|
+
if configurator.root.join("config/redis/#{name}.yml").exist?
|
|
11
16
|
connector.call configurator.config_for("redis/#{name}")
|
|
17
|
+
elsif name == :shared
|
|
18
|
+
Redis.new url: ENV.fetch("REDIS_URL", DEFAULT_REDIS_URL), timeout: DEFAULT_REDIS_TIMEOUT
|
|
19
|
+
else
|
|
20
|
+
raise "No configuration found for #{name}"
|
|
12
21
|
end
|
|
13
22
|
end
|
|
14
23
|
end
|
|
@@ -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
|
|
@@ -15,7 +17,7 @@ class Kredis::LogSubscriber < ActiveSupport::LogSubscriber
|
|
|
15
17
|
|
|
16
18
|
private
|
|
17
19
|
def formatted_in(color, event, type: nil)
|
|
18
|
-
color " Kredis #{type} (#{event.duration.round(1)}ms) #{event.payload[:message]}", color, true
|
|
20
|
+
color " Kredis #{type} (#{event.duration.round(1)}ms) #{event.payload[:message]}", color, bold: true
|
|
19
21
|
end
|
|
20
22
|
end
|
|
21
23
|
|
data/lib/kredis/migration.rb
CHANGED
|
@@ -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
|
|
38
|
+
@redis.del(*key_patterns)
|
|
37
39
|
else
|
|
38
40
|
each_key_batch_matching(key_patterns.first) do |keys, pipeline|
|
|
39
|
-
pipeline.del
|
|
41
|
+
pipeline.del(*keys)
|
|
40
42
|
end
|
|
41
43
|
end
|
|
42
44
|
end
|
data/lib/kredis/namespace.rb
CHANGED
|
@@ -1,12 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
module Kredis::Namespace
|
|
2
|
-
|
|
3
|
-
Thread.current[:kredis_namespace] = namespace
|
|
4
|
-
end
|
|
4
|
+
attr_accessor :global_namespace
|
|
5
5
|
|
|
6
6
|
def namespace
|
|
7
|
-
|
|
7
|
+
if global_namespace
|
|
8
|
+
if value = thread_namespace
|
|
9
|
+
"#{global_namespace}:#{value}"
|
|
10
|
+
else
|
|
11
|
+
global_namespace
|
|
12
|
+
end
|
|
13
|
+
else
|
|
14
|
+
thread_namespace
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def thread_namespace
|
|
19
|
+
Thread.current[:kredis_thread_namespace]
|
|
8
20
|
end
|
|
9
21
|
|
|
22
|
+
def thread_namespace=(value)
|
|
23
|
+
Thread.current[:kredis_thread_namespace] = value
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# Backward compatibility
|
|
27
|
+
alias_method :namespace=, :thread_namespace=
|
|
28
|
+
|
|
10
29
|
def namespaced_key(key)
|
|
11
30
|
namespace ? "#{namespace}:#{key}" : key
|
|
12
31
|
end
|
data/lib/kredis/railtie.rb
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
class Kredis::Railtie < ::Rails::Railtie
|
|
2
4
|
config.kredis = ActiveSupport::OrderedOptions.new
|
|
3
5
|
|
|
4
6
|
initializer "kredis.testing" do
|
|
5
7
|
ActiveSupport.on_load(:active_support_test_case) do
|
|
6
|
-
parallelize_setup { |worker| Kredis.
|
|
8
|
+
parallelize_setup { |worker| Kredis.global_namespace = [ Kredis.global_namespace, :test, worker ].compact.join("-") }
|
|
7
9
|
teardown { Kredis.clear_all }
|
|
8
10
|
end
|
|
9
11
|
end
|
data/lib/kredis/type/datetime.rb
CHANGED
data/lib/kredis/type_casting.rb
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require "json"
|
|
2
4
|
require "active_model/type"
|
|
3
|
-
require "kredis/type/
|
|
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:
|
|
17
|
+
boolean: Kredis::Type::Boolean.new,
|
|
15
18
|
datetime: Kredis::Type::DateTime.new,
|
|
16
19
|
json: Kredis::Type::Json.new
|
|
17
20
|
}
|
data/lib/kredis/types/counter.rb
CHANGED
|
@@ -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
|
data/lib/kredis/types/cycle.rb
CHANGED
data/lib/kredis/types/enum.rb
CHANGED
|
@@ -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
|
-
|
|
6
|
+
prepend Kredis::DefaultValues
|
|
7
|
+
|
|
8
|
+
InvalidDefault = Class.new(StandardError)
|
|
5
9
|
|
|
6
|
-
|
|
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
|
|
26
|
+
get
|
|
21
27
|
end
|
|
22
28
|
|
|
23
29
|
def reset
|
|
24
|
-
|
|
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
|
data/lib/kredis/types/flag.rb
CHANGED
|
@@ -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
|
data/lib/kredis/types/hash.rb
CHANGED
|
@@ -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) }.compact 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
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# A limiter is a specialized form of a counter that can be checked whether it has been exceeded and is provided fail safe. This means it can be used to guard login screens from brute force attacks without denying access in case Redis is offline.
|
|
4
|
+
#
|
|
5
|
+
# It will usually be used as an expiring limiter. Note that the limiter expires in total after the `expires_in` time used upon the first poke.
|
|
6
|
+
#
|
|
7
|
+
# It offers no guarentee that you can't poke yourself above the limit. You're responsible for checking `#exceeded?` yourself first, and this may produce a race condition. So only use this when the exact number of pokes is not critical.
|
|
8
|
+
class Kredis::Types::Limiter < Kredis::Types::Counter
|
|
9
|
+
class LimitExceeded < StandardError; end
|
|
10
|
+
|
|
11
|
+
attr_accessor :limit
|
|
12
|
+
|
|
13
|
+
def poke
|
|
14
|
+
failsafe returning: true do
|
|
15
|
+
increment
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def exceeded?
|
|
20
|
+
failsafe returning: false do
|
|
21
|
+
value >= limit
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/kredis/types/list.rb
CHANGED
|
@@ -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
|
|
@@ -24,4 +28,13 @@ class Kredis::Types::List < Kredis::Types::Proxying
|
|
|
24
28
|
def clear
|
|
25
29
|
del
|
|
26
30
|
end
|
|
31
|
+
|
|
32
|
+
def last(n = nil)
|
|
33
|
+
n ? lrange(-n, -1) : lrange(-1, -1).first
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
def set_default
|
|
38
|
+
append default
|
|
39
|
+
end
|
|
27
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
|
data/lib/kredis/types/proxy.rb
CHANGED
|
@@ -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
|
|
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
|
data/lib/kredis/types/scalar.rb
CHANGED
|
@@ -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, :
|
|
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
|
data/lib/kredis/types/set.rb
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
class Kredis::Types::Set < Kredis::Types::Proxying
|
|
2
|
-
|
|
4
|
+
prepend Kredis::DefaultValues
|
|
3
5
|
|
|
4
|
-
|
|
6
|
+
proxying :smembers, :sadd, :srem, :multi, :del, :sismember, :scard, :spop, :exists?, :srandmember, :smove
|
|
5
7
|
|
|
8
|
+
attr_accessor :typed
|
|
9
|
+
def move(set, member)
|
|
10
|
+
destination = set.respond_to?(:key) ? set.key : set
|
|
11
|
+
smove(destination, member)
|
|
12
|
+
end
|
|
6
13
|
def members
|
|
7
14
|
strings_to_types(smembers || [], typed).sort
|
|
8
15
|
end
|
|
@@ -33,10 +40,23 @@ class Kredis::Types::Set < Kredis::Types::Proxying
|
|
|
33
40
|
end
|
|
34
41
|
|
|
35
42
|
def take
|
|
36
|
-
spop
|
|
43
|
+
string_to_type(spop, typed)
|
|
37
44
|
end
|
|
38
45
|
|
|
39
46
|
def clear
|
|
40
47
|
del
|
|
41
48
|
end
|
|
49
|
+
|
|
50
|
+
def sample(count = nil)
|
|
51
|
+
if count.nil?
|
|
52
|
+
string_to_type(srandmember(count), typed)
|
|
53
|
+
else
|
|
54
|
+
strings_to_types(srandmember(count), typed)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
private
|
|
59
|
+
def set_default
|
|
60
|
+
add default
|
|
61
|
+
end
|
|
42
62
|
end
|
data/lib/kredis/types/slots.rb
CHANGED
|
@@ -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
|
|
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
|
|
63
|
-
type_from(
|
|
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
|
|
67
|
-
type_from(
|
|
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
|
|
71
|
-
type_from(
|
|
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)
|
|
@@ -79,6 +85,10 @@ module Kredis::Types
|
|
|
79
85
|
type_from(Slots, config, key, after_change: after_change, available: available)
|
|
80
86
|
end
|
|
81
87
|
|
|
88
|
+
def limiter(key, limit:, expires_in: nil, config: :shared, after_change: nil)
|
|
89
|
+
type_from(Limiter, config, key, after_change: after_change, expires_in: expires_in, limit: limit)
|
|
90
|
+
end
|
|
91
|
+
|
|
82
92
|
private
|
|
83
93
|
def type_from(type_klass, config, key, after_change: nil, **options)
|
|
84
94
|
type_klass.new(configured_for(config), namespaced_key(key), **options).then do |type|
|
|
@@ -99,4 +109,6 @@ require "kredis/types/hash"
|
|
|
99
109
|
require "kredis/types/list"
|
|
100
110
|
require "kredis/types/unique_list"
|
|
101
111
|
require "kredis/types/set"
|
|
112
|
+
require "kredis/types/ordered_set"
|
|
102
113
|
require "kredis/types/slots"
|
|
114
|
+
require "kredis/types/limiter"
|
data/lib/kredis/version.rb
CHANGED
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
|
|
metadata
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kredis
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 1.
|
|
4
|
+
version: 1.8.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Kasper Timm Hansen
|
|
8
8
|
- David Heinemeier Hansson
|
|
9
|
-
autorequire:
|
|
10
9
|
bindir: bin
|
|
11
10
|
cert_chain: []
|
|
12
|
-
date:
|
|
11
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
13
12
|
dependencies:
|
|
14
13
|
- !ruby/object:Gem::Dependency
|
|
15
14
|
name: activesupport
|
|
@@ -25,6 +24,20 @@ dependencies:
|
|
|
25
24
|
- - ">="
|
|
26
25
|
- !ruby/object:Gem::Version
|
|
27
26
|
version: 6.0.0
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: activemodel
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - ">="
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: 6.0.0
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - ">="
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: 6.0.0
|
|
28
41
|
- !ruby/object:Gem::Dependency
|
|
29
42
|
name: redis
|
|
30
43
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -59,7 +72,6 @@ dependencies:
|
|
|
59
72
|
- - ">="
|
|
60
73
|
- !ruby/object:Gem::Version
|
|
61
74
|
version: 6.0.0
|
|
62
|
-
description:
|
|
63
75
|
email: david@hey.com
|
|
64
76
|
executables: []
|
|
65
77
|
extensions: []
|
|
@@ -72,10 +84,12 @@ files:
|
|
|
72
84
|
- lib/kredis.rb
|
|
73
85
|
- lib/kredis/attributes.rb
|
|
74
86
|
- lib/kredis/connections.rb
|
|
87
|
+
- lib/kredis/default_values.rb
|
|
75
88
|
- lib/kredis/log_subscriber.rb
|
|
76
89
|
- lib/kredis/migration.rb
|
|
77
90
|
- lib/kredis/namespace.rb
|
|
78
91
|
- lib/kredis/railtie.rb
|
|
92
|
+
- lib/kredis/type/boolean.rb
|
|
79
93
|
- lib/kredis/type/datetime.rb
|
|
80
94
|
- lib/kredis/type/json.rb
|
|
81
95
|
- lib/kredis/type_casting.rb
|
|
@@ -86,7 +100,9 @@ files:
|
|
|
86
100
|
- lib/kredis/types/enum.rb
|
|
87
101
|
- lib/kredis/types/flag.rb
|
|
88
102
|
- lib/kredis/types/hash.rb
|
|
103
|
+
- lib/kredis/types/limiter.rb
|
|
89
104
|
- lib/kredis/types/list.rb
|
|
105
|
+
- lib/kredis/types/ordered_set.rb
|
|
90
106
|
- lib/kredis/types/proxy.rb
|
|
91
107
|
- lib/kredis/types/proxy/failsafe.rb
|
|
92
108
|
- lib/kredis/types/proxying.rb
|
|
@@ -100,7 +116,6 @@ homepage: https://github.com/rails/kredis
|
|
|
100
116
|
licenses:
|
|
101
117
|
- MIT
|
|
102
118
|
metadata: {}
|
|
103
|
-
post_install_message:
|
|
104
119
|
rdoc_options: []
|
|
105
120
|
require_paths:
|
|
106
121
|
- lib
|
|
@@ -115,8 +130,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
115
130
|
- !ruby/object:Gem::Version
|
|
116
131
|
version: '0'
|
|
117
132
|
requirements: []
|
|
118
|
-
rubygems_version: 3.
|
|
119
|
-
signing_key:
|
|
133
|
+
rubygems_version: 3.6.7
|
|
120
134
|
specification_version: 4
|
|
121
135
|
summary: Higher-level data structures built on Redis.
|
|
122
136
|
test_files: []
|