kredis 0.2.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of kredis might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +177 -24
- data/lib/kredis/attributes.rb +52 -26
- data/lib/kredis/connections.rb +13 -4
- data/lib/kredis/log_subscriber.rb +22 -0
- data/lib/kredis/migration.rb +22 -16
- data/lib/kredis/railtie.rb +20 -24
- data/lib/kredis/type_casting.rb +5 -3
- data/lib/kredis/types/callbacks_proxy.rb +31 -0
- data/lib/kredis/types/counter.rb +12 -1
- data/lib/kredis/types/cycle.rb +13 -0
- data/lib/kredis/types/hash.rb +45 -0
- data/lib/kredis/types/proxy/failsafe.rb +26 -0
- data/lib/kredis/types/proxy.rb +11 -6
- data/lib/kredis/types/proxying.rb +4 -0
- data/lib/kredis/types/scalar.rb +18 -4
- data/lib/kredis/types/set.rb +1 -1
- data/lib/kredis/types/slots.rb +18 -14
- data/lib/kredis/types.rb +53 -34
- data/lib/kredis/version.rb +1 -1
- data/lib/kredis.rb +14 -4
- metadata +26 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 678d0983bf3cb50abb7732dc3c3e4da93764173dbdf03ce367254dbdb9297ef2
|
4
|
+
data.tar.gz: 7209773f020b0d1055171f30f6ff204786f3edf84e5938cd09f55789fd86e934
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2aa0b22042910eaa558f1bf8e5fed47aa7385d16ba46f63024f9311760a4f2ee74c42dadb3f333388755d1862a04999ca83cc016bf023a3e1a48491f089c7e03
|
7
|
+
data.tar.gz: ff37e6f461fc3117fd7d02116da43931550683581c6327a176f58d3f09ba06b2da538cd27976f01d7922a7ce7bf9768a8c03445b25e3dc62e635f3ac953093e0
|
data/README.md
CHANGED
@@ -2,48 +2,164 @@
|
|
2
2
|
|
3
3
|
Kredis (Keyed Redis) encapsulates higher-level types and data structures around a single key, so you can interact with them as coherent objects rather than isolated procedural commands. These higher-level structures can be configured as attributes within Active Models and Active Records using a declarative DSL.
|
4
4
|
|
5
|
-
Kredis is configured using env-aware
|
5
|
+
Kredis is configured using env-aware YAML files, using `Rails.application.config_for`, so you can locate the data structures on separate Redis instances, if you've reached a scale where a single shared instance is no longer sufficient.
|
6
6
|
|
7
7
|
Kredis provides namespacing support for keys such that you can safely run parallel testing against the data structures without different tests trampling each others data.
|
8
8
|
|
9
9
|
|
10
10
|
## Examples
|
11
11
|
|
12
|
-
Kredis provides typed scalars for strings, integers, decimals, floats, booleans, datetimes, and
|
12
|
+
Kredis provides typed scalars for strings, integers, decimals, floats, booleans, datetimes, and JSON hashes:
|
13
13
|
|
14
14
|
```ruby
|
15
15
|
string = Kredis.string "mystring"
|
16
16
|
string.value = "hello world!" # => SET mystring "hello world"
|
17
17
|
"hello world!" == string.value # => GET mystring
|
18
18
|
|
19
|
-
integer = Kredis.
|
19
|
+
integer = Kredis.integer "myinteger"
|
20
20
|
integer.value = 5 # => SET myinteger "5"
|
21
|
-
5 ==
|
21
|
+
5 == integer.value # => GET myinteger
|
22
|
+
|
23
|
+
decimal = Kredis.decimal "mydecimal" # accuracy!
|
24
|
+
decimal.value = "%.47f" % (1.0/10) # => SET mydecimal "0.10000000000000000555111512312578270211815834045"
|
25
|
+
BigDecimal("0.10000000000000000555111512312578270211815834045e0") == decimal.value # => GET mydecimal
|
26
|
+
|
27
|
+
float = Kredis.float "myfloat" # speed!
|
28
|
+
float.value = 1.0/10 # => SET myfloat "0.1"
|
29
|
+
0.1 == float.value # => GET myfloat
|
30
|
+
|
31
|
+
boolean = Kredis.boolean "myboolean"
|
32
|
+
boolean.value = true # => SET myboolean "t"
|
33
|
+
true == boolean.value # => GET myboolean
|
34
|
+
|
35
|
+
datetime = Kredis.datetime "mydatetime"
|
36
|
+
memoized_midnight = Time.zone.now.midnight
|
37
|
+
datetime.value = memoized_midnight # SET mydatetime "2021-07-27T00:00:00.000000000Z"
|
38
|
+
memoized_midnight == datetime.value # => GET mydatetime
|
22
39
|
|
23
40
|
json = Kredis.json "myjson"
|
24
|
-
|
25
|
-
{ "one" => 1, "two" => "2" } ==
|
41
|
+
json.value = { "one" => 1, "two" => "2" } # => SET myjson "{\"one\":1,\"two\":\"2\"}"
|
42
|
+
{ "one" => 1, "two" => "2" } == json.value # => GET myjson
|
26
43
|
```
|
27
44
|
|
28
|
-
There are data structures for counters, enums, flags, lists,
|
45
|
+
There are data structures for counters, enums, flags, lists, unique lists, sets, and slots:
|
29
46
|
|
30
47
|
```ruby
|
31
|
-
list = Kredis.list "mylist"
|
32
|
-
list
|
33
|
-
|
34
|
-
|
48
|
+
list = Kredis.list "mylist"
|
49
|
+
list << "hello world!" # => RPUSH mylist "hello world!"
|
50
|
+
[ "hello world!" ] == list.elements # => LRANGE mylist 0, -1
|
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
|
56
|
+
|
57
|
+
unique_list = Kredis.unique_list "myuniquelist"
|
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"
|
59
|
+
unique_list.prepend(%w[ 1 2 3 4 ]) # => LREM myuniquelist 0, "1" + LREM myuniquelist 0, "2" + LREM myuniquelist 0, "3" + LREM myuniquelist 0, "4" + LPUSH myuniquelist "1", "2", "3", "4"
|
60
|
+
unique_list.append([])
|
61
|
+
unique_list << "5" # => LREM myuniquelist 0, "5" + RPUSH myuniquelist "5"
|
62
|
+
unique_list.remove(3) # => LREM myuniquelist 0, "3"
|
63
|
+
[ "4", "2", "1", "5" ] == unique_list.elements # => LRANGE myuniquelist 0, -1
|
35
64
|
|
36
65
|
set = Kredis.set "myset", typed: :datetime
|
37
|
-
set.add(DateTime.tomorrow, DateTime.yesterday)
|
38
|
-
set << DateTime.tomorrow
|
39
|
-
2 == set.size
|
40
|
-
[ DateTime.tomorrow, DateTime.yesterday ] == set.
|
41
|
-
|
42
|
-
|
43
|
-
|
66
|
+
set.add(DateTime.tomorrow, DateTime.yesterday) # => SADD myset "2021-02-03 00:00:00 +0100" "2021-02-01 00:00:00 +0100"
|
67
|
+
set << DateTime.tomorrow # => SADD myset "2021-02-03 00:00:00 +0100"
|
68
|
+
2 == set.size # => SCARD myset
|
69
|
+
[ DateTime.tomorrow, DateTime.yesterday ] == set.members # => SMEMBERS myset
|
70
|
+
|
71
|
+
hash = Kredis.hash "myhash"
|
72
|
+
hash.update("key" => "value", "key2" => "value2") # => HSET myhash "key", "value", "key2", "value2"
|
73
|
+
{ "key" => "value", "key2" => "value2" } == hash.to_h # => HGETALL myhash
|
74
|
+
"value2" == hash["key2"] # => HMGET myhash "key2"
|
75
|
+
%w[ key key2 ] == hash.keys # => HKEYS myhash
|
76
|
+
%w[ value value2 ] == hash.values # => HVALS myhash
|
77
|
+
hash.remove # => DEL myhash
|
78
|
+
|
79
|
+
high_scores = Kredis.hash "high_scores", typed: :integer
|
80
|
+
high_scores.update(space_invaders: 100, pong: 42) # HSET high_scores "space_invaders", "100", "pong", "42"
|
81
|
+
%w[ space_invaders pong ] == high_scores.keys # HKEYS high_scores
|
82
|
+
[ 100, 42 ] == high_scores.values # HVALS high_scores
|
83
|
+
{ "space_invaders" => 100, "pong" => 42 } == high_scores.to_h # HGETALL high_scores
|
84
|
+
|
85
|
+
head_count = Kredis.counter "headcount"
|
86
|
+
0 == head_count.value # => GET "headcount"
|
87
|
+
head_count.increment # => SET headcount 0 NX + INCRBY headcount 1
|
88
|
+
head_count.increment # => SET headcount 0 NX + INCRBY headcount 1
|
89
|
+
head_count.decrement # => SET headcount 0 NX + DECRBY headcount 1
|
90
|
+
1 == head_count.value # => GET "headcount"
|
91
|
+
|
92
|
+
counter = Kredis.counter "mycounter", expires_in: 5.seconds
|
93
|
+
counter.increment by: 2 # => SET mycounter 0 EX 5 NX + INCRBY "mycounter" 2
|
44
94
|
2 == counter.value # => GET "mycounter"
|
45
|
-
|
95
|
+
sleep 6.seconds
|
46
96
|
0 == counter.value # => GET "mycounter"
|
97
|
+
|
98
|
+
cycle = Kredis.cycle "mycycle", values: %i[ one two three ]
|
99
|
+
:one == cycle.value # => GET mycycle
|
100
|
+
cycle.next # => GET mycycle + SET mycycle 1
|
101
|
+
:two == cycle.value # => GET mycycle
|
102
|
+
cycle.next # => GET mycycle + SET mycycle 2
|
103
|
+
:three == cycle.value # => GET mycycle
|
104
|
+
cycle.next # => GET mycycle + SET mycycle 0
|
105
|
+
:one == cycle.value # => GET mycycle
|
106
|
+
|
107
|
+
enum = Kredis.enum "myenum", values: %w[ one two three ], default: "one"
|
108
|
+
"one" == enum.value # => GET myenum
|
109
|
+
true == enum.one? # => GET myenum
|
110
|
+
enum.value = "two" # => SET myenum "two"
|
111
|
+
"two" == enum.value # => GET myenum
|
112
|
+
enum.value = "four"
|
113
|
+
"two" == enum.value # => GET myenum
|
114
|
+
enum.reset # => DEL myenum
|
115
|
+
"one" == enum.value # => GET myenum
|
116
|
+
|
117
|
+
slots = Kredis.slots "myslots", available: 3
|
118
|
+
true == slots.available? # => GET myslots
|
119
|
+
slots.reserve # => INCR myslots
|
120
|
+
true == slots.available? # => GET myslots
|
121
|
+
slots.reserve # => INCR myslots
|
122
|
+
true == slots.available? # => GET myslots
|
123
|
+
slots.reserve # => INCR myslots
|
124
|
+
false == slots.available? # => GET myslots
|
125
|
+
slots.reserve # => INCR myslots + DECR myslots
|
126
|
+
false == slots.available? # => GET myslots
|
127
|
+
slots.release # => DECR myslots
|
128
|
+
true == slots.available? # => GET myslots
|
129
|
+
slots.reset # => DEL myslots
|
130
|
+
|
131
|
+
|
132
|
+
slot = Kredis.slot "myslot"
|
133
|
+
true == slot.available? # => GET myslot
|
134
|
+
slot.reserve # => INCR myslot
|
135
|
+
false == slot.available? # => GET myslot
|
136
|
+
slot.release # => DECR myslot
|
137
|
+
true == slot.available? # => GET myslot
|
138
|
+
slot.reset # => DEL myslot
|
139
|
+
|
140
|
+
flag = Kredis.flag "myflag"
|
141
|
+
false == flag.marked? # => EXISTS myflag
|
142
|
+
flag.mark # => SET myflag 1
|
143
|
+
true == flag.marked? # => EXISTS myflag
|
144
|
+
flag.remove # => DEL myflag
|
145
|
+
false == flag.marked? # => EXISTS myflag
|
146
|
+
|
147
|
+
flag.mark(expires_in: 1.second) #=> SET myflag 1 EX 1
|
148
|
+
true == flag.marked? #=> EXISTS myflag
|
149
|
+
sleep 0.5.seconds
|
150
|
+
true == flag.marked? #=> EXISTS myflag
|
151
|
+
sleep 0.6.seconds
|
152
|
+
false == flag.marked? #=> EXISTS myflag
|
153
|
+
```
|
154
|
+
|
155
|
+
And using structures on a different than the default `shared` redis instance, relying on `config/redis/secondary.yml`:
|
156
|
+
|
157
|
+
```ruby
|
158
|
+
one_string = Kredis.string "mystring"
|
159
|
+
two_string = Kredis.string "mystring", config: :secondary
|
160
|
+
|
161
|
+
one_string.value = "just on shared"
|
162
|
+
two_string.value != one_string.value
|
47
163
|
```
|
48
164
|
|
49
165
|
You can use all these structures in models:
|
@@ -57,12 +173,23 @@ class Person < ApplicationRecord
|
|
57
173
|
end
|
58
174
|
|
59
175
|
person = Person.find(5)
|
60
|
-
person.names.append "David", "Heinemeier", "Hansson" # =>
|
61
|
-
true == person.morning.bright?
|
62
|
-
person.morning.value = "blue"
|
63
|
-
true == person.morning.blue?
|
176
|
+
person.names.append "David", "Heinemeier", "Hansson" # => RPUSH people:5:names "David" "Heinemeier" "Hansson"
|
177
|
+
true == person.morning.bright? # => GET people:1:morning
|
178
|
+
person.morning.value = "blue" # => SET people:1:morning
|
179
|
+
true == person.morning.blue? # => GET people:1:morning
|
64
180
|
```
|
65
181
|
|
182
|
+
You can also define `after_change` callbacks that trigger on mutations:
|
183
|
+
|
184
|
+
```ruby
|
185
|
+
class Person < ApplicationRecord
|
186
|
+
kredis_list :names, after_change: ->(p) { }
|
187
|
+
kredis_unique_list :skills, limit: 2, after_change: :skillset_changed
|
188
|
+
|
189
|
+
def skillset_changed
|
190
|
+
end
|
191
|
+
end
|
192
|
+
```
|
66
193
|
|
67
194
|
## Installation
|
68
195
|
|
@@ -87,8 +214,34 @@ test:
|
|
87
214
|
<<: *development
|
88
215
|
```
|
89
216
|
|
90
|
-
Additional configurations can be added under `config/redis/*.yml` and referenced when a type is created.
|
217
|
+
Additional configurations can be added under `config/redis/*.yml` and referenced when a type is created, e.g. `Kredis.string("mystring", config: :strings)` would lookup `config/redis/strings.yml`. Under the hood `Kredis.configured_for` is called which'll pass the configuration on to `Redis.new`.
|
218
|
+
|
219
|
+
### Setting SSL options on Redis Connections
|
220
|
+
|
221
|
+
If you need to connect to Redis with SSL, the recommended approach is to set your Redis instance manually by adding an entry to the `Kredis::Connections.connections` hash. Below an example showing how to connect to Redis using Client Authentication:
|
222
|
+
|
223
|
+
```ruby
|
224
|
+
Kredis::Connections.connections[:shared] = Redis.new(
|
225
|
+
url: ENV['REDIS_URL'],
|
226
|
+
ssl_params: {
|
227
|
+
cert_store: OpenSSL::X509::Store.new.tap { |store|
|
228
|
+
store.add_file(Rails.root.join('config', 'ca_cert.pem').to_s)
|
229
|
+
},
|
230
|
+
|
231
|
+
cert: OpenSSL::X509::Certificate.new(File.read(
|
232
|
+
Rails.root.join('config', 'client.crt')
|
233
|
+
)),
|
234
|
+
|
235
|
+
key: OpenSSL::PKey::RSA.new(
|
236
|
+
Rails.application.credentials.redis[:client_key]
|
237
|
+
),
|
238
|
+
|
239
|
+
verify_mode: OpenSSL::SSL::VERIFY_PEER
|
240
|
+
}
|
241
|
+
)
|
242
|
+
```
|
91
243
|
|
244
|
+
The above code could be added to either `config/environments/production.rb` or an initializer. Please ensure that your client private key, if used, is stored your credentials file or another secure location.
|
92
245
|
|
93
246
|
## License
|
94
247
|
|
data/lib/kredis/attributes.rb
CHANGED
@@ -2,68 +2,83 @@ module Kredis::Attributes
|
|
2
2
|
extend ActiveSupport::Concern
|
3
3
|
|
4
4
|
class_methods do
|
5
|
-
def kredis_proxy(name, key: nil, config: :shared)
|
6
|
-
kredis_connection_with __method__, name, key, config: config
|
5
|
+
def kredis_proxy(name, key: nil, config: :shared, after_change: nil)
|
6
|
+
kredis_connection_with __method__, name, key, config: config, after_change: after_change
|
7
7
|
end
|
8
8
|
|
9
|
-
def kredis_string(name, key: nil, config: :shared)
|
10
|
-
kredis_connection_with __method__, name, key, config: config
|
9
|
+
def kredis_string(name, key: nil, config: :shared, after_change: nil)
|
10
|
+
kredis_connection_with __method__, name, key, config: config, after_change: after_change
|
11
11
|
end
|
12
12
|
|
13
|
-
def kredis_integer(name, key: nil, config: :shared)
|
14
|
-
kredis_connection_with __method__, name, key, config: config
|
13
|
+
def kredis_integer(name, key: nil, config: :shared, after_change: nil)
|
14
|
+
kredis_connection_with __method__, name, key, config: config, after_change: after_change
|
15
15
|
end
|
16
16
|
|
17
|
-
def
|
18
|
-
kredis_connection_with __method__, name, key, config: config
|
17
|
+
def kredis_decimal(name, key: nil, config: :shared, after_change: nil)
|
18
|
+
kredis_connection_with __method__, name, key, config: config, after_change: after_change
|
19
19
|
end
|
20
20
|
|
21
|
-
def
|
22
|
-
kredis_connection_with __method__, name, key, config: config
|
21
|
+
def kredis_datetime(name, key: nil, config: :shared, after_change: nil)
|
22
|
+
kredis_connection_with __method__, name, key, config: config, after_change: after_change
|
23
|
+
end
|
24
|
+
|
25
|
+
def kredis_flag(name, key: nil, config: :shared, after_change: nil)
|
26
|
+
kredis_connection_with __method__, name, key, config: config, after_change: after_change
|
23
27
|
|
24
28
|
define_method("#{name}?") do
|
25
29
|
send(name).marked?
|
26
30
|
end
|
27
31
|
end
|
28
32
|
|
29
|
-
def kredis_enum(name, key: nil, values:, default:, config: :shared)
|
30
|
-
kredis_connection_with __method__, name, key, values: values, default: default, config: config
|
33
|
+
def kredis_enum(name, key: nil, values:, default:, config: :shared, after_change: nil)
|
34
|
+
kredis_connection_with __method__, name, key, values: values, default: default, config: config, after_change: after_change
|
35
|
+
end
|
36
|
+
|
37
|
+
def kredis_json(name, key: nil, config: :shared, after_change: nil)
|
38
|
+
kredis_connection_with __method__, name, key, config: config, after_change: after_change
|
39
|
+
end
|
40
|
+
|
41
|
+
def kredis_list(name, key: nil, typed: :string, config: :shared, after_change: nil)
|
42
|
+
kredis_connection_with __method__, name, key, typed: typed, config: config, after_change: after_change
|
31
43
|
end
|
32
44
|
|
33
|
-
def
|
34
|
-
kredis_connection_with __method__, name, key, config: config
|
45
|
+
def kredis_unique_list(name, limit: nil, key: nil, typed: :string, config: :shared, after_change: nil)
|
46
|
+
kredis_connection_with __method__, name, key, limit: limit, typed: typed, config: config, after_change: after_change
|
35
47
|
end
|
36
48
|
|
37
|
-
def
|
38
|
-
kredis_connection_with __method__, name, key, typed: typed, config: config
|
49
|
+
def kredis_set(name, key: nil, typed: :string, config: :shared, after_change: nil)
|
50
|
+
kredis_connection_with __method__, name, key, typed: typed, config: config, after_change: after_change
|
39
51
|
end
|
40
52
|
|
41
|
-
def
|
42
|
-
kredis_connection_with __method__, name, key,
|
53
|
+
def kredis_slot(name, key: nil, config: :shared, after_change: nil)
|
54
|
+
kredis_connection_with __method__, name, key, config: config, after_change: after_change
|
43
55
|
end
|
44
56
|
|
45
|
-
def
|
46
|
-
kredis_connection_with __method__, name, key,
|
57
|
+
def kredis_slots(name, available:, key: nil, config: :shared, after_change: nil)
|
58
|
+
kredis_connection_with __method__, name, key, available: available, config: config, after_change: after_change
|
47
59
|
end
|
48
60
|
|
49
|
-
def
|
50
|
-
kredis_connection_with __method__, name, key, config: config
|
61
|
+
def kredis_counter(name, key: nil, config: :shared, after_change: nil)
|
62
|
+
kredis_connection_with __method__, name, key, config: config, after_change: after_change
|
51
63
|
end
|
52
64
|
|
53
|
-
def
|
54
|
-
kredis_connection_with __method__, name, key,
|
65
|
+
def kredis_hash(name, key: nil, typed: :string, config: :shared, after_change: nil)
|
66
|
+
kredis_connection_with __method__, name, key, typed: typed, config: config, after_change: after_change
|
55
67
|
end
|
56
68
|
|
57
69
|
private
|
58
70
|
def kredis_connection_with(method, name, key, **options)
|
59
71
|
ivar_symbol = :"@#{name}_#{method}"
|
60
72
|
type = method.to_s.sub("kredis_", "")
|
73
|
+
after_change = options.delete(:after_change)
|
61
74
|
|
62
75
|
define_method(name) do
|
63
76
|
if instance_variable_defined?(ivar_symbol)
|
64
77
|
instance_variable_get(ivar_symbol)
|
65
78
|
else
|
66
|
-
|
79
|
+
new_type = Kredis.send(type, kredis_key_evaluated(key) || kredis_key_for_attribute(name), **options)
|
80
|
+
instance_variable_set ivar_symbol,
|
81
|
+
after_change ? enrich_after_change_with_record_access(new_type, after_change) : new_type
|
67
82
|
end
|
68
83
|
end
|
69
84
|
end
|
@@ -78,6 +93,17 @@ module Kredis::Attributes
|
|
78
93
|
end
|
79
94
|
|
80
95
|
def kredis_key_for_attribute(name)
|
81
|
-
"#{self.class.name.tableize.gsub("/", ":")}:#{
|
96
|
+
"#{self.class.name.tableize.gsub("/", ":")}:#{extract_kredis_id}:#{name}"
|
97
|
+
end
|
98
|
+
|
99
|
+
def extract_kredis_id
|
100
|
+
try(:id) or raise NotImplementedError, "kredis needs a unique id, either implement an id method or pass a custom key."
|
101
|
+
end
|
102
|
+
|
103
|
+
def enrich_after_change_with_record_access(type, original_after_change)
|
104
|
+
case original_after_change
|
105
|
+
when Proc then Kredis::Types::CallbacksProxy.new(type, ->(_) { original_after_change.call(self) })
|
106
|
+
when Symbol then Kredis::Types::CallbacksProxy.new(type, ->(_) { send(original_after_change) })
|
107
|
+
end
|
82
108
|
end
|
83
109
|
end
|
data/lib/kredis/connections.rb
CHANGED
@@ -6,13 +6,22 @@ module Kredis::Connections
|
|
6
6
|
|
7
7
|
def configured_for(name)
|
8
8
|
connections[name] ||= begin
|
9
|
-
|
10
|
-
|
9
|
+
Kredis.instrument :meta, message: "Connected to #{name}" do
|
10
|
+
Redis.new configurator.config_for("redis/#{name}")
|
11
|
+
end
|
11
12
|
end
|
12
13
|
end
|
13
14
|
|
14
15
|
def clear_all
|
15
|
-
|
16
|
-
|
16
|
+
Kredis.instrument :meta, message: "Connections all cleared" do
|
17
|
+
connections.each_value do |connection|
|
18
|
+
if Kredis.namespace
|
19
|
+
keys = connection.keys("#{Kredis.namespace}:*")
|
20
|
+
connection.del keys if keys.any?
|
21
|
+
else
|
22
|
+
connection.flushdb
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
17
26
|
end
|
18
27
|
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require "active_support/log_subscriber"
|
2
|
+
|
3
|
+
class Kredis::LogSubscriber < ActiveSupport::LogSubscriber
|
4
|
+
def proxy(event)
|
5
|
+
debug formatted_in(YELLOW, event, type: "Proxy")
|
6
|
+
end
|
7
|
+
|
8
|
+
def migration(event)
|
9
|
+
debug formatted_in(YELLOW, event, type: "Migration")
|
10
|
+
end
|
11
|
+
|
12
|
+
def meta(event)
|
13
|
+
info formatted_in(MAGENTA, event)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def formatted_in(color, event, type: nil)
|
18
|
+
color " Kredis #{type} (#{event.duration.round(1)}ms) #{event.payload[:message]}", color, true
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
Kredis::LogSubscriber.attach_to :kredis
|
data/lib/kredis/migration.rb
CHANGED
@@ -1,47 +1,53 @@
|
|
1
1
|
require "active_support/core_ext/module/delegation"
|
2
2
|
|
3
3
|
class Kredis::Migration
|
4
|
-
singleton_class.delegate :migrate_all, :migrate, to: :new
|
4
|
+
singleton_class.delegate :migrate_all, :migrate, :delete_all, to: :new
|
5
5
|
|
6
6
|
def initialize(config = :shared)
|
7
7
|
@redis = Kredis.configured_for config
|
8
|
+
# TODO: Replace script loading with `copy` command once Redis 6.2+ is the minimum supported version.
|
8
9
|
@copy_sha = @redis.script "load", "redis.call('SETNX', KEYS[2], redis.call('GET', KEYS[1])); return 1;"
|
9
10
|
end
|
10
11
|
|
11
12
|
def migrate_all(key_pattern)
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
migrate from: key, to: yield(key, *ids)
|
17
|
-
end
|
13
|
+
each_key_batch_matching(key_pattern) do |keys|
|
14
|
+
keys.each do |key|
|
15
|
+
ids = key.scan(/\d+/).map(&:to_i)
|
16
|
+
migrate from: key, to: yield(key, *ids)
|
18
17
|
end
|
19
18
|
end
|
20
19
|
end
|
21
20
|
|
22
21
|
def migrate(from:, to:)
|
23
|
-
|
22
|
+
namespaced_to = Kredis.namespaced_key(to)
|
24
23
|
|
25
|
-
if from !=
|
26
|
-
log_migration "Migrating key #{from} to #{
|
27
|
-
|
24
|
+
if to.present? && from != namespaced_to
|
25
|
+
log_migration "Migrating key #{from} to #{namespaced_to}" do
|
26
|
+
@redis.evalsha @copy_sha, keys: [ from, namespaced_to ]
|
27
|
+
end
|
28
28
|
else
|
29
|
-
log_migration "Skipping unaltered migration key #{from}"
|
29
|
+
log_migration "Skipping blank/unaltered migration key #{from} → #{to}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete_all(key_pattern)
|
34
|
+
each_key_batch_matching(key_pattern) do |keys|
|
35
|
+
@redis.del *keys
|
30
36
|
end
|
31
37
|
end
|
32
38
|
|
33
39
|
private
|
34
40
|
SCAN_BATCH_SIZE = 1_000
|
35
41
|
|
36
|
-
def
|
42
|
+
def each_key_batch_matching(key_pattern, &block)
|
37
43
|
cursor = "0"
|
38
44
|
begin
|
39
45
|
cursor, keys = @redis.scan(cursor, match: key_pattern, count: SCAN_BATCH_SIZE)
|
40
|
-
yield keys
|
46
|
+
@redis.pipelined { yield keys }
|
41
47
|
end until cursor == "0"
|
42
48
|
end
|
43
49
|
|
44
|
-
def log_migration(message)
|
45
|
-
Kredis.
|
50
|
+
def log_migration(message, &block)
|
51
|
+
Kredis.instrument :migration, message: message, &block
|
46
52
|
end
|
47
53
|
end
|
data/lib/kredis/railtie.rb
CHANGED
@@ -1,33 +1,29 @@
|
|
1
|
-
|
1
|
+
class Kredis::Railtie < ::Rails::Railtie
|
2
|
+
config.kredis = ActiveSupport::OrderedOptions.new
|
2
3
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
initializer "kredis.testing" do
|
8
|
-
ActiveSupport.on_load(:active_support_test_case) do
|
9
|
-
parallelize_setup { |worker| Kredis.namespace = "test-#{worker}" }
|
10
|
-
parallelize_teardown { Kredis.clear_all }
|
11
|
-
end
|
4
|
+
initializer "kredis.testing" do
|
5
|
+
ActiveSupport.on_load(:active_support_test_case) do
|
6
|
+
parallelize_setup { |worker| Kredis.namespace = "test-#{worker}" }
|
7
|
+
teardown { Kredis.clear_all }
|
12
8
|
end
|
9
|
+
end
|
13
10
|
|
14
|
-
|
15
|
-
|
16
|
-
|
11
|
+
initializer "kredis.logger" do
|
12
|
+
Kredis::LogSubscriber.logger = config.kredis.logger || Rails.logger
|
13
|
+
end
|
17
14
|
|
18
|
-
|
19
|
-
|
20
|
-
|
15
|
+
initializer "kredis.configurator" do
|
16
|
+
Kredis.configurator = Rails.application
|
17
|
+
end
|
21
18
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
19
|
+
initializer "kredis.attributes" do
|
20
|
+
# No load hook for Active Model, just defer until after initialization.
|
21
|
+
config.after_initialize do
|
22
|
+
ActiveModel::Model.include Kredis::Attributes if defined?(ActiveModel::Model)
|
23
|
+
end
|
27
24
|
|
28
|
-
|
29
|
-
|
30
|
-
end
|
25
|
+
ActiveSupport.on_load(:active_record) do
|
26
|
+
include Kredis::Attributes
|
31
27
|
end
|
32
28
|
end
|
33
29
|
end
|
data/lib/kredis/type_casting.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
require "json"
|
2
|
+
|
1
3
|
module Kredis::TypeCasting
|
2
4
|
class InvalidType < StandardError; end
|
3
5
|
|
@@ -16,7 +18,7 @@ module Kredis::TypeCasting
|
|
16
18
|
when TrueClass, FalseClass
|
17
19
|
value ? "t" : "f"
|
18
20
|
when Time, DateTime, ActiveSupport::TimeWithZone
|
19
|
-
value.
|
21
|
+
value.iso8601(9)
|
20
22
|
when Hash
|
21
23
|
JSON.dump(value)
|
22
24
|
else
|
@@ -25,7 +27,7 @@ module Kredis::TypeCasting
|
|
25
27
|
end
|
26
28
|
|
27
29
|
def string_to_type(value, type)
|
28
|
-
raise InvalidType if type && !
|
30
|
+
raise InvalidType if type && !VALID_TYPES.include?(type)
|
29
31
|
|
30
32
|
case type
|
31
33
|
when nil, :string then value
|
@@ -33,7 +35,7 @@ module Kredis::TypeCasting
|
|
33
35
|
when :decimal then value.to_d
|
34
36
|
when :float then value.to_f
|
35
37
|
when :boolean then value == "t" ? true : false
|
36
|
-
when :datetime then Time.
|
38
|
+
when :datetime then Time.iso8601(value)
|
37
39
|
when :json then JSON.load(value)
|
38
40
|
end if value.present?
|
39
41
|
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Kredis::Types::CallbacksProxy
|
2
|
+
attr_reader :type
|
3
|
+
delegate :to_s, to: :type
|
4
|
+
|
5
|
+
AFTER_CHANGE_OPERATIONS = {
|
6
|
+
Kredis::Types::Counter => %i[ increment decrement reset ],
|
7
|
+
Kredis::Types::Cycle => %i[ next ],
|
8
|
+
Kredis::Types::Enum => %i[ value= reset ],
|
9
|
+
Kredis::Types::Flag => %i[ mark remove ],
|
10
|
+
Kredis::Types::Hash => %i[ update delete ],
|
11
|
+
Kredis::Types::List => %i[ remove prepend append << ],
|
12
|
+
Kredis::Types::Scalar => %i[ value= clear ],
|
13
|
+
Kredis::Types::Set => %i[ add << remove replace take clear ],
|
14
|
+
Kredis::Types::Slots => %i[ reserve release reset ]
|
15
|
+
}
|
16
|
+
|
17
|
+
def initialize(type, callback)
|
18
|
+
@type, @callback = type, callback
|
19
|
+
end
|
20
|
+
|
21
|
+
def method_missing(method, *args, **kwargs, &block)
|
22
|
+
result = type.send(method, *args, **kwargs, &block)
|
23
|
+
invoke_suitable_after_change_callback_for method
|
24
|
+
result
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def invoke_suitable_after_change_callback_for(method)
|
29
|
+
@callback.call(type) if AFTER_CHANGE_OPERATIONS[type.class]&.include? method
|
30
|
+
end
|
31
|
+
end
|
data/lib/kredis/types/counter.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
class Kredis::Types::Counter < Kredis::Types::Proxying
|
2
|
-
proxying :multi, :set, :incrby, :get
|
2
|
+
proxying :multi, :set, :incrby, :decrby, :get, :del
|
3
3
|
|
4
4
|
attr_accessor :expires_in
|
5
5
|
|
@@ -10,7 +10,18 @@ class Kredis::Types::Counter < Kredis::Types::Proxying
|
|
10
10
|
end
|
11
11
|
end
|
12
12
|
|
13
|
+
def decrement(by: 1)
|
14
|
+
multi do
|
15
|
+
set 0, ex: expires_in, nx: true
|
16
|
+
decrby by
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
13
20
|
def value
|
14
21
|
get.to_i
|
15
22
|
end
|
23
|
+
|
24
|
+
def reset
|
25
|
+
del
|
26
|
+
end
|
16
27
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require "active_support/core_ext/hash"
|
2
|
+
|
3
|
+
class Kredis::Types::Hash < Kredis::Types::Proxying
|
4
|
+
proxying :hget, :hset, :hmget, :hdel, :hgetall, :hkeys, :hvals, :del
|
5
|
+
|
6
|
+
attr_accessor :typed
|
7
|
+
|
8
|
+
def [](key)
|
9
|
+
string_to_type(hget(key), typed)
|
10
|
+
end
|
11
|
+
|
12
|
+
def []=(key, value)
|
13
|
+
update key => value
|
14
|
+
end
|
15
|
+
|
16
|
+
|
17
|
+
def update(**entries)
|
18
|
+
hset types_to_strings(entries) if entries.flatten.any?
|
19
|
+
end
|
20
|
+
|
21
|
+
def values_at(*keys)
|
22
|
+
strings_to_types(hmget(keys) || [], typed)
|
23
|
+
end
|
24
|
+
|
25
|
+
def delete(*keys)
|
26
|
+
hdel types_to_strings(keys) if keys.flatten.any?
|
27
|
+
end
|
28
|
+
|
29
|
+
def remove
|
30
|
+
del
|
31
|
+
end
|
32
|
+
|
33
|
+
def entries
|
34
|
+
(hgetall || {}).transform_values { |val| string_to_type(val, typed) }.with_indifferent_access
|
35
|
+
end
|
36
|
+
alias to_h entries
|
37
|
+
|
38
|
+
def keys
|
39
|
+
hkeys || []
|
40
|
+
end
|
41
|
+
|
42
|
+
def values
|
43
|
+
strings_to_types(hvals || [], typed)
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Kredis::Types::Proxy::Failsafe
|
2
|
+
def initialize(*)
|
3
|
+
super
|
4
|
+
@fail_safe_suppressed = false
|
5
|
+
end
|
6
|
+
|
7
|
+
def failsafe
|
8
|
+
yield
|
9
|
+
rescue Redis::BaseError
|
10
|
+
raise if fail_safe_suppressed?
|
11
|
+
end
|
12
|
+
|
13
|
+
def suppress_failsafe_with(returning: nil)
|
14
|
+
old_fail_safe_suppressed, @fail_safe_suppressed = @fail_safe_suppressed, true
|
15
|
+
yield
|
16
|
+
rescue Redis::BaseError
|
17
|
+
returning
|
18
|
+
ensure
|
19
|
+
@fail_safe_suppressed = old_fail_safe_suppressed
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def fail_safe_suppressed?
|
24
|
+
@fail_safe_suppressed
|
25
|
+
end
|
26
|
+
end
|
data/lib/kredis/types/proxy.rb
CHANGED
@@ -1,4 +1,7 @@
|
|
1
1
|
class Kredis::Types::Proxy
|
2
|
+
require_relative "proxy/failsafe"
|
3
|
+
include Failsafe
|
4
|
+
|
2
5
|
attr_accessor :redis, :key
|
3
6
|
|
4
7
|
def initialize(redis, key, **options)
|
@@ -11,16 +14,18 @@ class Kredis::Types::Proxy
|
|
11
14
|
end
|
12
15
|
|
13
16
|
def method_missing(method, *args, **kwargs)
|
14
|
-
Kredis.
|
15
|
-
|
17
|
+
Kredis.instrument :proxy, **log_message(method, *args, **kwargs) do
|
18
|
+
failsafe do
|
19
|
+
redis.public_send method, key, *args, **kwargs
|
20
|
+
end
|
21
|
+
end
|
16
22
|
end
|
17
23
|
|
18
24
|
private
|
19
25
|
def log_message(method, *args, **kwargs)
|
20
|
-
args = args.flatten.
|
21
|
-
kwargs = kwargs.
|
22
|
-
type_name = self.class.name.split("::").last
|
26
|
+
args = args.flatten.reject(&:blank?).presence
|
27
|
+
kwargs = kwargs.reject { |_k, v| v.blank? }.presence
|
23
28
|
|
24
|
-
|
29
|
+
{ message: "#{method.upcase} #{key} #{args&.inspect} #{kwargs&.inspect}".chomp }
|
25
30
|
end
|
26
31
|
end
|
@@ -13,6 +13,10 @@ class Kredis::Types::Proxying
|
|
13
13
|
options.each { |key, value| send("#{key}=", value) }
|
14
14
|
end
|
15
15
|
|
16
|
+
def failsafe(returning: nil, &block)
|
17
|
+
proxy.suppress_failsafe_with(returning: returning, &block)
|
18
|
+
end
|
19
|
+
|
16
20
|
private
|
17
21
|
delegate :type_to_string, :string_to_type, :types_to_strings, :strings_to_types, to: :Kredis
|
18
22
|
end
|
data/lib/kredis/types/scalar.rb
CHANGED
@@ -1,14 +1,20 @@
|
|
1
1
|
class Kredis::Types::Scalar < Kredis::Types::Proxying
|
2
|
-
proxying :set, :get, :exists?, :del
|
2
|
+
proxying :set, :get, :exists?, :del, :expire, :expireat
|
3
3
|
|
4
|
-
attr_accessor :typed, :default
|
4
|
+
attr_accessor :typed, :default, :expires_in
|
5
5
|
|
6
6
|
def value=(value)
|
7
|
-
set type_to_string(value)
|
7
|
+
set type_to_string(value), ex: expires_in
|
8
8
|
end
|
9
9
|
|
10
10
|
def value
|
11
|
-
string_to_type(get, typed)
|
11
|
+
value_after_casting = string_to_type(get, typed)
|
12
|
+
|
13
|
+
if value_after_casting.nil?
|
14
|
+
default
|
15
|
+
else
|
16
|
+
value_after_casting
|
17
|
+
end
|
12
18
|
end
|
13
19
|
|
14
20
|
def to_s
|
@@ -22,4 +28,12 @@ class Kredis::Types::Scalar < Kredis::Types::Proxying
|
|
22
28
|
def clear
|
23
29
|
del
|
24
30
|
end
|
31
|
+
|
32
|
+
def expire_in(seconds)
|
33
|
+
expire seconds.to_i
|
34
|
+
end
|
35
|
+
|
36
|
+
def expire_at(datetime)
|
37
|
+
expireat datetime.to_i
|
38
|
+
end
|
25
39
|
end
|
data/lib/kredis/types/set.rb
CHANGED
data/lib/kredis/types/slots.rb
CHANGED
@@ -6,23 +6,25 @@ class Kredis::Types::Slots < Kredis::Types::Proxying
|
|
6
6
|
attr_accessor :available
|
7
7
|
|
8
8
|
def reserve
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
9
|
+
failsafe returning: false do
|
10
|
+
if block_given?
|
11
|
+
begin
|
12
|
+
if reserve
|
13
|
+
yield
|
14
|
+
true
|
15
|
+
else
|
16
|
+
false
|
17
|
+
end
|
18
|
+
ensure
|
19
|
+
release
|
20
|
+
end
|
21
|
+
else
|
22
|
+
if incr <= available
|
13
23
|
true
|
14
24
|
else
|
25
|
+
release
|
15
26
|
false
|
16
27
|
end
|
17
|
-
ensure
|
18
|
-
release
|
19
|
-
end
|
20
|
-
else
|
21
|
-
if incr <= available
|
22
|
-
true
|
23
|
-
else
|
24
|
-
release
|
25
|
-
false
|
26
28
|
end
|
27
29
|
end
|
28
30
|
end
|
@@ -32,7 +34,9 @@ class Kredis::Types::Slots < Kredis::Types::Proxying
|
|
32
34
|
end
|
33
35
|
|
34
36
|
def available?
|
35
|
-
|
37
|
+
failsafe returning: false do
|
38
|
+
get.to_i < available
|
39
|
+
end
|
36
40
|
end
|
37
41
|
|
38
42
|
def reset
|
data/lib/kredis/types.rb
CHANGED
@@ -1,73 +1,90 @@
|
|
1
1
|
module Kredis::Types
|
2
|
-
|
3
|
-
|
2
|
+
autoload :CallbacksProxy, "kredis/types/callbacks_proxy"
|
3
|
+
|
4
|
+
def proxy(key, config: :shared, after_change: nil)
|
5
|
+
type_from(Proxy, config, key, after_change: after_change)
|
4
6
|
end
|
5
7
|
|
6
8
|
|
7
|
-
def scalar(key, typed: :string, default: nil, config: :shared)
|
8
|
-
Scalar
|
9
|
+
def scalar(key, typed: :string, default: nil, config: :shared, after_change: nil, expires_in: nil)
|
10
|
+
type_from(Scalar, config, key, after_change: after_change, typed: typed, default: default, expires_in: expires_in)
|
11
|
+
end
|
12
|
+
|
13
|
+
def string(key, default: nil, config: :shared, after_change: nil, expires_in: nil)
|
14
|
+
type_from(Scalar, config, key, after_change: after_change, typed: :string, default: default, expires_in: expires_in)
|
9
15
|
end
|
10
16
|
|
11
|
-
def
|
12
|
-
Scalar
|
17
|
+
def integer(key, default: nil, config: :shared, after_change: nil, expires_in: nil)
|
18
|
+
type_from(Scalar, config, key, after_change: after_change, typed: :integer, default: default, expires_in: expires_in)
|
13
19
|
end
|
14
20
|
|
15
|
-
def
|
16
|
-
Scalar
|
21
|
+
def decimal(key, default: nil, config: :shared, after_change: nil, expires_in: nil)
|
22
|
+
type_from(Scalar, config, key, after_change: after_change, typed: :decimal, default: default, expires_in: expires_in)
|
17
23
|
end
|
18
24
|
|
19
|
-
def
|
20
|
-
Scalar
|
25
|
+
def float(key, default: nil, config: :shared, after_change: nil, expires_in: nil)
|
26
|
+
type_from(Scalar, config, key, after_change: after_change, typed: :float, default: default, expires_in: expires_in)
|
21
27
|
end
|
22
28
|
|
23
|
-
def
|
24
|
-
Scalar
|
29
|
+
def boolean(key, default: nil, config: :shared, after_change: nil, expires_in: nil)
|
30
|
+
type_from(Scalar, config, key, after_change: after_change, typed: :boolean, default: default, expires_in: expires_in)
|
25
31
|
end
|
26
32
|
|
27
|
-
def
|
28
|
-
Scalar
|
33
|
+
def datetime(key, default: nil, config: :shared, after_change: nil, expires_in: nil)
|
34
|
+
type_from(Scalar, config, key, after_change: after_change, typed: :datetime, default: default, expires_in: expires_in)
|
29
35
|
end
|
30
36
|
|
31
|
-
def
|
32
|
-
Scalar
|
37
|
+
def json(key, default: nil, config: :shared, after_change: nil, expires_in: nil)
|
38
|
+
type_from(Scalar, config, key, after_change: after_change, typed: :json, default: default, expires_in: expires_in)
|
33
39
|
end
|
34
40
|
|
35
|
-
|
36
|
-
|
41
|
+
|
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)
|
37
44
|
end
|
38
45
|
|
46
|
+
def cycle(key, values:, expires_in: nil, config: :shared, after_change: nil)
|
47
|
+
type_from(Cycle, config, key, after_change: after_change, values: values, expires_in: expires_in)
|
48
|
+
end
|
39
49
|
|
40
|
-
def
|
41
|
-
|
50
|
+
def flag(key, config: :shared, after_change: nil)
|
51
|
+
type_from(Flag, config, key, after_change: after_change)
|
42
52
|
end
|
43
53
|
|
44
|
-
def
|
45
|
-
|
54
|
+
def enum(key, values:, default:, config: :shared, after_change: nil)
|
55
|
+
type_from(Enum, config, key, after_change: after_change, values: values, default: default)
|
46
56
|
end
|
47
57
|
|
48
|
-
def
|
49
|
-
|
58
|
+
def hash(key, typed: :string, config: :shared, after_change: nil)
|
59
|
+
type_from(Hash, config, key, after_change: after_change, typed: typed)
|
50
60
|
end
|
51
61
|
|
52
|
-
def list(key, typed: :string, config: :shared)
|
53
|
-
List
|
62
|
+
def list(key, typed: :string, config: :shared, after_change: nil)
|
63
|
+
type_from(List, config, key, after_change: after_change, typed: typed)
|
54
64
|
end
|
55
65
|
|
56
|
-
def unique_list(key, typed: :string, limit: nil, config: :shared)
|
57
|
-
UniqueList
|
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)
|
58
68
|
end
|
59
69
|
|
60
|
-
def set(key, typed: :string, config: :shared)
|
61
|
-
Set
|
70
|
+
def set(key, typed: :string, config: :shared, after_change: nil)
|
71
|
+
type_from(Set, config, key, after_change: after_change, typed: typed)
|
62
72
|
end
|
63
73
|
|
64
|
-
def slot(key, config: :shared)
|
65
|
-
Slots
|
74
|
+
def slot(key, config: :shared, after_change: nil)
|
75
|
+
type_from(Slots, config, key, after_change: after_change, available: 1)
|
66
76
|
end
|
67
77
|
|
68
|
-
def slots(key, available:, config: :shared)
|
69
|
-
Slots
|
78
|
+
def slots(key, available:, config: :shared, after_change: nil)
|
79
|
+
type_from(Slots, config, key, after_change: after_change, available: available)
|
70
80
|
end
|
81
|
+
|
82
|
+
private
|
83
|
+
def type_from(type_klass, config, key, after_change: nil, **options)
|
84
|
+
type_klass.new(configured_for(config), namespaced_key(key), **options).then do |type|
|
85
|
+
after_change ? CallbacksProxy.new(type, after_change) : type
|
86
|
+
end
|
87
|
+
end
|
71
88
|
end
|
72
89
|
|
73
90
|
require "kredis/types/proxy"
|
@@ -75,8 +92,10 @@ require "kredis/types/proxying"
|
|
75
92
|
|
76
93
|
require "kredis/types/scalar"
|
77
94
|
require "kredis/types/counter"
|
95
|
+
require "kredis/types/cycle"
|
78
96
|
require "kredis/types/flag"
|
79
97
|
require "kredis/types/enum"
|
98
|
+
require "kredis/types/hash"
|
80
99
|
require "kredis/types/list"
|
81
100
|
require "kredis/types/unique_list"
|
82
101
|
require "kredis/types/set"
|
data/lib/kredis/version.rb
CHANGED
data/lib/kredis.rb
CHANGED
@@ -1,20 +1,30 @@
|
|
1
|
-
require "
|
1
|
+
require "active_support"
|
2
|
+
require "active_support/core_ext/module/attribute_accessors"
|
3
|
+
|
2
4
|
require "kredis/version"
|
3
5
|
|
4
6
|
require "kredis/connections"
|
7
|
+
require "kredis/log_subscriber"
|
5
8
|
require "kredis/namespace"
|
9
|
+
require "kredis/type_casting"
|
6
10
|
require "kredis/types"
|
7
11
|
require "kredis/attributes"
|
8
|
-
require "kredis/type_casting"
|
9
12
|
|
10
|
-
|
11
|
-
include Connections, Namespace, Types, TypeCasting
|
13
|
+
require "kredis/railtie" if defined?(Rails::Railtie)
|
12
14
|
|
15
|
+
module Kredis
|
16
|
+
include Connections, Namespace, TypeCasting, Types
|
13
17
|
extend self
|
14
18
|
|
19
|
+
autoload :Migration, "kredis/migration"
|
20
|
+
|
15
21
|
mattr_accessor :logger
|
16
22
|
|
17
23
|
def redis(config: :shared)
|
18
24
|
configured_for(config)
|
19
25
|
end
|
26
|
+
|
27
|
+
def instrument(channel, **options, &block)
|
28
|
+
ActiveSupport::Notifications.instrument("#{channel}.kredis", **options, &block)
|
29
|
+
end
|
20
30
|
end
|
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: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Kasper Timm Hansen
|
@@ -9,10 +9,10 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2021-
|
12
|
+
date: 2021-09-04 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
15
|
+
name: activesupport
|
16
16
|
requirement: !ruby/object:Gem::Requirement
|
17
17
|
requirements:
|
18
18
|
- - ">="
|
@@ -31,14 +31,28 @@ dependencies:
|
|
31
31
|
requirements:
|
32
32
|
- - "~>"
|
33
33
|
- !ruby/object:Gem::Version
|
34
|
-
version: '4.
|
34
|
+
version: '4.2'
|
35
35
|
type: :runtime
|
36
36
|
prerelease: false
|
37
37
|
version_requirements: !ruby/object:Gem::Requirement
|
38
38
|
requirements:
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
|
-
version: '4.
|
41
|
+
version: '4.2'
|
42
|
+
- !ruby/object:Gem::Dependency
|
43
|
+
name: rails
|
44
|
+
requirement: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: 6.0.0
|
49
|
+
type: :development
|
50
|
+
prerelease: false
|
51
|
+
version_requirements: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: 6.0.0
|
42
56
|
description:
|
43
57
|
email: david@hey.com
|
44
58
|
executables: []
|
@@ -50,16 +64,21 @@ files:
|
|
50
64
|
- lib/kredis.rb
|
51
65
|
- lib/kredis/attributes.rb
|
52
66
|
- lib/kredis/connections.rb
|
67
|
+
- lib/kredis/log_subscriber.rb
|
53
68
|
- lib/kredis/migration.rb
|
54
69
|
- lib/kredis/namespace.rb
|
55
70
|
- lib/kredis/railtie.rb
|
56
71
|
- lib/kredis/type_casting.rb
|
57
72
|
- lib/kredis/types.rb
|
73
|
+
- lib/kredis/types/callbacks_proxy.rb
|
58
74
|
- lib/kredis/types/counter.rb
|
75
|
+
- lib/kredis/types/cycle.rb
|
59
76
|
- lib/kredis/types/enum.rb
|
60
77
|
- lib/kredis/types/flag.rb
|
78
|
+
- lib/kredis/types/hash.rb
|
61
79
|
- lib/kredis/types/list.rb
|
62
80
|
- lib/kredis/types/proxy.rb
|
81
|
+
- lib/kredis/types/proxy/failsafe.rb
|
63
82
|
- lib/kredis/types/proxying.rb
|
64
83
|
- lib/kredis/types/scalar.rb
|
65
84
|
- lib/kredis/types/set.rb
|
@@ -78,14 +97,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
78
97
|
requirements:
|
79
98
|
- - ">="
|
80
99
|
- !ruby/object:Gem::Version
|
81
|
-
version: 2.
|
100
|
+
version: 2.7.0
|
82
101
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
83
102
|
requirements:
|
84
103
|
- - ">="
|
85
104
|
- !ruby/object:Gem::Version
|
86
105
|
version: '0'
|
87
106
|
requirements: []
|
88
|
-
rubygems_version: 3.1.
|
107
|
+
rubygems_version: 3.1.4
|
89
108
|
signing_key:
|
90
109
|
specification_version: 4
|
91
110
|
summary: Higher-level data structures built on Redis.
|