kredis 0.2.3 → 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 +135 -58
- data/lib/kredis/attributes.rb +47 -25
- data/lib/kredis/connections.rb +11 -9
- data/lib/kredis/log_subscriber.rb +22 -0
- data/lib/kredis/migration.rb +5 -4
- data/lib/kredis/railtie.rb +1 -1
- data/lib/kredis/type_casting.rb +3 -1
- data/lib/kredis/types/callbacks_proxy.rb +31 -0
- data/lib/kredis/types/hash.rb +45 -0
- data/lib/kredis/types/proxy.rb +5 -5
- data/lib/kredis/types/scalar.rb +18 -4
- data/lib/kredis/types.rb +50 -36
- data/lib/kredis/version.rb +1 -1
- data/lib/kredis.rb +7 -1
- metadata +25 -8
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
@@ -20,6 +20,23 @@ integer = Kredis.integer "myinteger"
|
|
20
20
|
integer.value = 5 # => SET myinteger "5"
|
21
21
|
5 == integer.value # => GET myinteger
|
22
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
|
39
|
+
|
23
40
|
json = Kredis.json "myjson"
|
24
41
|
json.value = { "one" => 1, "two" => "2" } # => SET myjson "{\"one\":1,\"two\":\"2\"}"
|
25
42
|
{ "one" => 1, "two" => "2" } == json.value # => GET myjson
|
@@ -29,87 +46,110 @@ There are data structures for counters, enums, flags, lists, unique lists, sets,
|
|
29
46
|
|
30
47
|
```ruby
|
31
48
|
list = Kredis.list "mylist"
|
32
|
-
list << "hello world!"
|
33
|
-
[ "hello world!" ] == list.elements
|
49
|
+
list << "hello world!" # => RPUSH mylist "hello world!"
|
50
|
+
[ "hello world!" ] == list.elements # => LRANGE mylist 0, -1
|
34
51
|
|
35
52
|
integer_list = Kredis.list "myintegerlist", typed: :integer
|
36
|
-
integer_list.append([ 1, 2, 3 ]) # =>
|
37
|
-
integer_list << 4 # =>
|
38
|
-
[ 1, 2, 3, 4 ] == integer_list.elements # LRANGE 0 -1
|
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
|
39
56
|
|
40
57
|
unique_list = Kredis.unique_list "myuniquelist"
|
41
|
-
unique_list.append(%w[ 2 3 4 ])
|
42
|
-
unique_list.prepend(%w[ 1 2 3 4 ])
|
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"
|
43
60
|
unique_list.append([])
|
44
|
-
unique_list << "5"
|
45
|
-
unique_list.remove(3)
|
46
|
-
[ "
|
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
|
47
64
|
|
48
65
|
set = Kredis.set "myset", typed: :datetime
|
49
|
-
set.add(DateTime.tomorrow, DateTime.yesterday)
|
50
|
-
set << DateTime.tomorrow
|
51
|
-
2 == set.size
|
52
|
-
[ DateTime.tomorrow, DateTime.yesterday ] == set.
|
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
|
53
84
|
|
54
85
|
head_count = Kredis.counter "headcount"
|
55
86
|
0 == head_count.value # => GET "headcount"
|
56
|
-
head_count.increment
|
57
|
-
head_count.increment
|
58
|
-
head_count.decrement
|
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
|
59
90
|
1 == head_count.value # => GET "headcount"
|
60
91
|
|
61
92
|
counter = Kredis.counter "mycounter", expires_in: 5.seconds
|
62
|
-
counter.increment by: 2 # =>
|
93
|
+
counter.increment by: 2 # => SET mycounter 0 EX 5 NX + INCRBY "mycounter" 2
|
63
94
|
2 == counter.value # => GET "mycounter"
|
64
95
|
sleep 6.seconds
|
65
96
|
0 == counter.value # => GET "mycounter"
|
66
97
|
|
67
98
|
cycle = Kredis.cycle "mycycle", values: %i[ one two three ]
|
68
|
-
:one == cycle.value
|
69
|
-
cycle.next
|
70
|
-
:two == cycle.value
|
71
|
-
cycle.next
|
72
|
-
:three == cycle.value
|
73
|
-
cycle.next
|
74
|
-
:one == cycle.value
|
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
|
75
106
|
|
76
107
|
enum = Kredis.enum "myenum", values: %w[ one two three ], default: "one"
|
77
|
-
"one" == enum.value
|
78
|
-
true == enum.one?
|
79
|
-
enum.value = "two"
|
80
|
-
"two" == enum.value
|
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
|
81
112
|
enum.value = "four"
|
82
|
-
"two" == enum.value
|
83
|
-
enum.reset
|
84
|
-
"one" == enum.value
|
113
|
+
"two" == enum.value # => GET myenum
|
114
|
+
enum.reset # => DEL myenum
|
115
|
+
"one" == enum.value # => GET myenum
|
85
116
|
|
86
117
|
slots = Kredis.slots "myslots", available: 3
|
87
|
-
true == slots.available?
|
88
|
-
slots.reserve
|
89
|
-
true == slots.available?
|
90
|
-
slots.reserve
|
91
|
-
true == slots.available?
|
92
|
-
slots.reserve
|
93
|
-
|
94
|
-
slots.reserve
|
95
|
-
false == slots.available?
|
96
|
-
slots.release
|
97
|
-
true == slots.available?
|
98
|
-
slots.reset
|
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
|
99
139
|
|
100
140
|
flag = Kredis.flag "myflag"
|
101
|
-
false == flag.marked?
|
102
|
-
flag.mark
|
103
|
-
true == flag.marked?
|
104
|
-
flag.remove
|
105
|
-
false == flag.marked?
|
106
|
-
|
107
|
-
flag.mark(expires_in: 1.second)
|
108
|
-
true == flag.marked?
|
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
|
109
149
|
sleep 0.5.seconds
|
110
|
-
true == flag.marked?
|
150
|
+
true == flag.marked? #=> EXISTS myflag
|
111
151
|
sleep 0.6.seconds
|
112
|
-
false == flag.marked?
|
152
|
+
false == flag.marked? #=> EXISTS myflag
|
113
153
|
```
|
114
154
|
|
115
155
|
And using structures on a different than the default `shared` redis instance, relying on `config/redis/secondary.yml`:
|
@@ -133,12 +173,23 @@ class Person < ApplicationRecord
|
|
133
173
|
end
|
134
174
|
|
135
175
|
person = Person.find(5)
|
136
|
-
person.names.append "David", "Heinemeier", "Hansson" # =>
|
137
|
-
true == person.morning.bright?
|
138
|
-
person.morning.value = "blue"
|
139
|
-
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
|
140
180
|
```
|
141
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
|
+
```
|
142
193
|
|
143
194
|
## Installation
|
144
195
|
|
@@ -165,6 +216,32 @@ test:
|
|
165
216
|
|
166
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`.
|
167
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
|
+
```
|
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.
|
168
245
|
|
169
246
|
## License
|
170
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
|
31
35
|
end
|
32
36
|
|
33
|
-
def kredis_json(name, key: nil, config: :shared)
|
34
|
-
kredis_connection_with __method__, name, key, config: config
|
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
|
35
39
|
end
|
36
40
|
|
37
|
-
def kredis_list(name, key: nil, typed: :string, config: :shared)
|
38
|
-
kredis_connection_with __method__, name, key, typed: typed, config: config
|
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
|
39
43
|
end
|
40
44
|
|
41
|
-
def kredis_unique_list(name, limit: nil, key: nil, typed: :string, config: :shared)
|
42
|
-
kredis_connection_with __method__, name, key, limit: limit, typed: typed, 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
|
43
47
|
end
|
44
48
|
|
45
|
-
def kredis_set(name, key: nil, typed: :string, config: :shared)
|
46
|
-
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
|
47
51
|
end
|
48
52
|
|
49
|
-
def kredis_slot(name, key: nil, config: :shared)
|
50
|
-
kredis_connection_with __method__, name, key, config: config
|
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
|
51
55
|
end
|
52
56
|
|
53
|
-
def kredis_slots(name, available:, key: nil, config: :shared)
|
54
|
-
kredis_connection_with __method__, name, key, available: available, config: config
|
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
|
59
|
+
end
|
60
|
+
|
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
|
63
|
+
end
|
64
|
+
|
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
|
@@ -84,4 +99,11 @@ module Kredis::Attributes
|
|
84
99
|
def extract_kredis_id
|
85
100
|
try(:id) or raise NotImplementedError, "kredis needs a unique id, either implement an id method or pass a custom key."
|
86
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
|
108
|
+
end
|
87
109
|
end
|
data/lib/kredis/connections.rb
CHANGED
@@ -6,19 +6,21 @@ 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
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
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
|
22
24
|
end
|
23
25
|
end
|
24
26
|
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
@@ -22,8 +22,9 @@ class Kredis::Migration
|
|
22
22
|
namespaced_to = Kredis.namespaced_key(to)
|
23
23
|
|
24
24
|
if to.present? && from != namespaced_to
|
25
|
-
log_migration "Migrating key #{from} to #{namespaced_to}"
|
26
|
-
|
25
|
+
log_migration "Migrating key #{from} to #{namespaced_to}" do
|
26
|
+
@redis.evalsha @copy_sha, keys: [ from, namespaced_to ]
|
27
|
+
end
|
27
28
|
else
|
28
29
|
log_migration "Skipping blank/unaltered migration key #{from} → #{to}"
|
29
30
|
end
|
@@ -46,7 +47,7 @@ class Kredis::Migration
|
|
46
47
|
end until cursor == "0"
|
47
48
|
end
|
48
49
|
|
49
|
-
def log_migration(message)
|
50
|
-
Kredis.
|
50
|
+
def log_migration(message, &block)
|
51
|
+
Kredis.instrument :migration, message: message, &block
|
51
52
|
end
|
52
53
|
end
|
data/lib/kredis/railtie.rb
CHANGED
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
|
|
@@ -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
|
@@ -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
|
@@ -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
|
data/lib/kredis/types/proxy.rb
CHANGED
@@ -14,9 +14,10 @@ class Kredis::Types::Proxy
|
|
14
14
|
end
|
15
15
|
|
16
16
|
def method_missing(method, *args, **kwargs)
|
17
|
-
|
18
|
-
|
19
|
-
|
17
|
+
Kredis.instrument :proxy, **log_message(method, *args, **kwargs) do
|
18
|
+
failsafe do
|
19
|
+
redis.public_send method, key, *args, **kwargs
|
20
|
+
end
|
20
21
|
end
|
21
22
|
end
|
22
23
|
|
@@ -24,8 +25,7 @@ class Kredis::Types::Proxy
|
|
24
25
|
def log_message(method, *args, **kwargs)
|
25
26
|
args = args.flatten.reject(&:blank?).presence
|
26
27
|
kwargs = kwargs.reject { |_k, v| v.blank? }.presence
|
27
|
-
type_name = self.class.name.split("::").last
|
28
28
|
|
29
|
-
|
29
|
+
{ message: "#{method.upcase} #{key} #{args&.inspect} #{kwargs&.inspect}".chomp }
|
30
30
|
end
|
31
31
|
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.rb
CHANGED
@@ -1,77 +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)
|
9
11
|
end
|
10
12
|
|
11
|
-
def string(key, default: nil, config: :shared)
|
12
|
-
Scalar
|
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)
|
13
15
|
end
|
14
16
|
|
15
|
-
def integer(key, default: nil, config: :shared)
|
16
|
-
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)
|
17
19
|
end
|
18
20
|
|
19
|
-
def decimal(key, default: nil, config: :shared)
|
20
|
-
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)
|
21
23
|
end
|
22
24
|
|
23
|
-
def float(key, default: nil, config: :shared)
|
24
|
-
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)
|
25
27
|
end
|
26
28
|
|
27
|
-
def boolean(key, default: nil, config: :shared)
|
28
|
-
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)
|
29
31
|
end
|
30
32
|
|
31
|
-
def datetime(key, default: nil, config: :shared)
|
32
|
-
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)
|
33
35
|
end
|
34
36
|
|
35
|
-
def json(key, default: nil, config: :shared)
|
36
|
-
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)
|
37
39
|
end
|
38
40
|
|
39
41
|
|
40
|
-
def counter(key, expires_in: nil, config: :shared)
|
41
|
-
Counter
|
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
|
+
end
|
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)
|
42
48
|
end
|
43
49
|
|
44
|
-
def
|
45
|
-
|
50
|
+
def flag(key, config: :shared, after_change: nil)
|
51
|
+
type_from(Flag, config, key, after_change: after_change)
|
46
52
|
end
|
47
53
|
|
48
|
-
def
|
49
|
-
|
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)
|
50
56
|
end
|
51
57
|
|
52
|
-
def
|
53
|
-
|
58
|
+
def hash(key, typed: :string, config: :shared, after_change: nil)
|
59
|
+
type_from(Hash, config, key, after_change: after_change, typed: typed)
|
54
60
|
end
|
55
61
|
|
56
|
-
def list(key, typed: :string, config: :shared)
|
57
|
-
List
|
62
|
+
def list(key, typed: :string, config: :shared, after_change: nil)
|
63
|
+
type_from(List, config, key, after_change: after_change, typed: typed)
|
58
64
|
end
|
59
65
|
|
60
|
-
def unique_list(key, typed: :string, limit: nil, config: :shared)
|
61
|
-
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)
|
62
68
|
end
|
63
69
|
|
64
|
-
def set(key, typed: :string, config: :shared)
|
65
|
-
Set
|
70
|
+
def set(key, typed: :string, config: :shared, after_change: nil)
|
71
|
+
type_from(Set, config, key, after_change: after_change, typed: typed)
|
66
72
|
end
|
67
73
|
|
68
|
-
def slot(key, config: :shared)
|
69
|
-
Slots
|
74
|
+
def slot(key, config: :shared, after_change: nil)
|
75
|
+
type_from(Slots, config, key, after_change: after_change, available: 1)
|
70
76
|
end
|
71
77
|
|
72
|
-
def slots(key, available:, config: :shared)
|
73
|
-
Slots
|
78
|
+
def slots(key, available:, config: :shared, after_change: nil)
|
79
|
+
type_from(Slots, config, key, after_change: after_change, available: available)
|
74
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
|
75
88
|
end
|
76
89
|
|
77
90
|
require "kredis/types/proxy"
|
@@ -82,6 +95,7 @@ require "kredis/types/counter"
|
|
82
95
|
require "kredis/types/cycle"
|
83
96
|
require "kredis/types/flag"
|
84
97
|
require "kredis/types/enum"
|
98
|
+
require "kredis/types/hash"
|
85
99
|
require "kredis/types/list"
|
86
100
|
require "kredis/types/unique_list"
|
87
101
|
require "kredis/types/set"
|
data/lib/kredis/version.rb
CHANGED
data/lib/kredis.rb
CHANGED
@@ -4,6 +4,7 @@ require "active_support/core_ext/module/attribute_accessors"
|
|
4
4
|
require "kredis/version"
|
5
5
|
|
6
6
|
require "kredis/connections"
|
7
|
+
require "kredis/log_subscriber"
|
7
8
|
require "kredis/namespace"
|
8
9
|
require "kredis/type_casting"
|
9
10
|
require "kredis/types"
|
@@ -13,12 +14,17 @@ require "kredis/railtie" if defined?(Rails::Railtie)
|
|
13
14
|
|
14
15
|
module Kredis
|
15
16
|
include Connections, Namespace, TypeCasting, Types
|
16
|
-
|
17
17
|
extend self
|
18
18
|
|
19
|
+
autoload :Migration, "kredis/migration"
|
20
|
+
|
19
21
|
mattr_accessor :logger
|
20
22
|
|
21
23
|
def redis(config: :shared)
|
22
24
|
configured_for(config)
|
23
25
|
end
|
26
|
+
|
27
|
+
def instrument(channel, **options, &block)
|
28
|
+
ActiveSupport::Notifications.instrument("#{channel}.kredis", **options, &block)
|
29
|
+
end
|
24
30
|
end
|
metadata
CHANGED
@@ -1,18 +1,18 @@
|
|
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
|
8
8
|
- David Heinemeier Hansson
|
9
|
-
autorequire:
|
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
|
- - ">="
|
@@ -39,7 +39,21 @@ dependencies:
|
|
39
39
|
- - "~>"
|
40
40
|
- !ruby/object:Gem::Version
|
41
41
|
version: '4.2'
|
42
|
-
|
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
|
56
|
+
description:
|
43
57
|
email: david@hey.com
|
44
58
|
executables: []
|
45
59
|
extensions: []
|
@@ -50,15 +64,18 @@ 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
|
59
75
|
- lib/kredis/types/cycle.rb
|
60
76
|
- lib/kredis/types/enum.rb
|
61
77
|
- lib/kredis/types/flag.rb
|
78
|
+
- lib/kredis/types/hash.rb
|
62
79
|
- lib/kredis/types/list.rb
|
63
80
|
- lib/kredis/types/proxy.rb
|
64
81
|
- lib/kredis/types/proxy/failsafe.rb
|
@@ -72,7 +89,7 @@ homepage: https://github.com/rails/kredis
|
|
72
89
|
licenses:
|
73
90
|
- MIT
|
74
91
|
metadata: {}
|
75
|
-
post_install_message:
|
92
|
+
post_install_message:
|
76
93
|
rdoc_options: []
|
77
94
|
require_paths:
|
78
95
|
- lib
|
@@ -87,8 +104,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
87
104
|
- !ruby/object:Gem::Version
|
88
105
|
version: '0'
|
89
106
|
requirements: []
|
90
|
-
rubygems_version: 3.
|
91
|
-
signing_key:
|
107
|
+
rubygems_version: 3.1.4
|
108
|
+
signing_key:
|
92
109
|
specification_version: 4
|
93
110
|
summary: Higher-level data structures built on Redis.
|
94
111
|
test_files: []
|