README.md in kredis-0.2.3 vs README.md in kredis-0.3.0
- old
+ new
@@ -18,100 +18,140 @@
integer = Kredis.integer "myinteger"
integer.value = 5 # => SET myinteger "5"
5 == integer.value # => GET myinteger
+decimal = Kredis.decimal "mydecimal" # accuracy!
+decimal.value = "%.47f" % (1.0/10) # => SET mydecimal "0.10000000000000000555111512312578270211815834045"
+BigDecimal("0.10000000000000000555111512312578270211815834045e0") == decimal.value # => GET mydecimal
+
+float = Kredis.float "myfloat" # speed!
+float.value = 1.0/10 # => SET myfloat "0.1"
+0.1 == float.value # => GET myfloat
+
+boolean = Kredis.boolean "myboolean"
+boolean.value = true # => SET myboolean "t"
+true == boolean.value # => GET myboolean
+
+datetime = Kredis.datetime "mydatetime"
+memoized_midnight = Time.zone.now.midnight
+datetime.value = memoized_midnight # SET mydatetime "2021-07-27T00:00:00.000000000Z"
+memoized_midnight == datetime.value # => GET mydatetime
+
json = Kredis.json "myjson"
json.value = { "one" => 1, "two" => "2" } # => SET myjson "{\"one\":1,\"two\":\"2\"}"
{ "one" => 1, "two" => "2" } == json.value # => GET myjson
```
There are data structures for counters, enums, flags, lists, unique lists, sets, and slots:
```ruby
list = Kredis.list "mylist"
-list << "hello world!"
-[ "hello world!" ] == list.elements
+list << "hello world!" # => RPUSH mylist "hello world!"
+[ "hello world!" ] == list.elements # => LRANGE mylist 0, -1
integer_list = Kredis.list "myintegerlist", typed: :integer
-integer_list.append([ 1, 2, 3 ]) # => LPUSH myintegerlist "1" "2" "3"
-integer_list << 4 # => LPUSH myintegerlist "4"
-[ 1, 2, 3, 4 ] == integer_list.elements # LRANGE 0 -1
+integer_list.append([ 1, 2, 3 ]) # => RPUSH myintegerlist "1" "2" "3"
+integer_list << 4 # => RPUSH myintegerlist "4"
+[ 1, 2, 3, 4 ] == integer_list.elements # => LRANGE myintegerlist 0 -1
unique_list = Kredis.unique_list "myuniquelist"
-unique_list.append(%w[ 2 3 4 ])
-unique_list.prepend(%w[ 1 2 3 4 ])
+unique_list.append(%w[ 2 3 4 ]) # => LREM myuniquelist 0, "2" + LREM myuniquelist 0, "3" + LREM myuniquelist 0, "4" + RPUSH myuniquelist "2", "3", "4"
+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"
unique_list.append([])
-unique_list << "5"
-unique_list.remove(3)
-[ "1", "2", "4", "5" ] == unique_list.elements
+unique_list << "5" # => LREM myuniquelist 0, "5" + RPUSH myuniquelist "5"
+unique_list.remove(3) # => LREM myuniquelist 0, "3"
+[ "4", "2", "1", "5" ] == unique_list.elements # => LRANGE myuniquelist 0, -1
set = Kredis.set "myset", typed: :datetime
-set.add(DateTime.tomorrow, DateTime.yesterday) # => SADD myset "2021-02-03 00:00:00 +0100" "2021-02-01 00:00:00 +0100"
-set << DateTime.tomorrow # => SADD myset "2021-02-03 00:00:00 +0100"
-2 == set.size # => SCARD myset
-[ DateTime.tomorrow, DateTime.yesterday ] == set.elements # => SMEMBERS myset
+set.add(DateTime.tomorrow, DateTime.yesterday) # => SADD myset "2021-02-03 00:00:00 +0100" "2021-02-01 00:00:00 +0100"
+set << DateTime.tomorrow # => SADD myset "2021-02-03 00:00:00 +0100"
+2 == set.size # => SCARD myset
+[ DateTime.tomorrow, DateTime.yesterday ] == set.members # => SMEMBERS myset
+hash = Kredis.hash "myhash"
+hash.update("key" => "value", "key2" => "value2") # => HSET myhash "key", "value", "key2", "value2"
+{ "key" => "value", "key2" => "value2" } == hash.to_h # => HGETALL myhash
+"value2" == hash["key2"] # => HMGET myhash "key2"
+%w[ key key2 ] == hash.keys # => HKEYS myhash
+%w[ value value2 ] == hash.values # => HVALS myhash
+hash.remove # => DEL myhash
+
+high_scores = Kredis.hash "high_scores", typed: :integer
+high_scores.update(space_invaders: 100, pong: 42) # HSET high_scores "space_invaders", "100", "pong", "42"
+%w[ space_invaders pong ] == high_scores.keys # HKEYS high_scores
+[ 100, 42 ] == high_scores.values # HVALS high_scores
+{ "space_invaders" => 100, "pong" => 42 } == high_scores.to_h # HGETALL high_scores
+
head_count = Kredis.counter "headcount"
0 == head_count.value # => GET "headcount"
-head_count.increment
-head_count.increment
-head_count.decrement
+head_count.increment # => SET headcount 0 NX + INCRBY headcount 1
+head_count.increment # => SET headcount 0 NX + INCRBY headcount 1
+head_count.decrement # => SET headcount 0 NX + DECRBY headcount 1
1 == head_count.value # => GET "headcount"
counter = Kredis.counter "mycounter", expires_in: 5.seconds
-counter.increment by: 2 # => SETEX "mycounter" 900 0 + INCR "mycounter" 2
+counter.increment by: 2 # => SET mycounter 0 EX 5 NX + INCRBY "mycounter" 2
2 == counter.value # => GET "mycounter"
sleep 6.seconds
0 == counter.value # => GET "mycounter"
cycle = Kredis.cycle "mycycle", values: %i[ one two three ]
-:one == cycle.value
-cycle.next
-:two == cycle.value
-cycle.next
-:three == cycle.value
-cycle.next
-:one == cycle.value
+:one == cycle.value # => GET mycycle
+cycle.next # => GET mycycle + SET mycycle 1
+:two == cycle.value # => GET mycycle
+cycle.next # => GET mycycle + SET mycycle 2
+:three == cycle.value # => GET mycycle
+cycle.next # => GET mycycle + SET mycycle 0
+:one == cycle.value # => GET mycycle
enum = Kredis.enum "myenum", values: %w[ one two three ], default: "one"
-"one" == enum.value
-true == enum.one?
-enum.value = "two"
-"two" == enum.value
+"one" == enum.value # => GET myenum
+true == enum.one? # => GET myenum
+enum.value = "two" # => SET myenum "two"
+"two" == enum.value # => GET myenum
enum.value = "four"
-"two" == enum.value
-enum.reset
-"one" == enum.value
+"two" == enum.value # => GET myenum
+enum.reset # => DEL myenum
+"one" == enum.value # => GET myenum
slots = Kredis.slots "myslots", available: 3
-true == slots.available?
-slots.reserve
-true == slots.available?
-slots.reserve
-true == slots.available?
-slots.reserve
-true == slots.available?
-slots.reserve
-false == slots.available?
-slots.release
-true == slots.available?
-slots.reset
+true == slots.available? # => GET myslots
+slots.reserve # => INCR myslots
+true == slots.available? # => GET myslots
+slots.reserve # => INCR myslots
+true == slots.available? # => GET myslots
+slots.reserve # => INCR myslots
+false == slots.available? # => GET myslots
+slots.reserve # => INCR myslots + DECR myslots
+false == slots.available? # => GET myslots
+slots.release # => DECR myslots
+true == slots.available? # => GET myslots
+slots.reset # => DEL myslots
+
+slot = Kredis.slot "myslot"
+true == slot.available? # => GET myslot
+slot.reserve # => INCR myslot
+false == slot.available? # => GET myslot
+slot.release # => DECR myslot
+true == slot.available? # => GET myslot
+slot.reset # => DEL myslot
+
flag = Kredis.flag "myflag"
-false == flag.marked?
-flag.mark
-true == flag.marked?
-flag.remove
-false == flag.marked?
+false == flag.marked? # => EXISTS myflag
+flag.mark # => SET myflag 1
+true == flag.marked? # => EXISTS myflag
+flag.remove # => DEL myflag
+false == flag.marked? # => EXISTS myflag
-flag.mark(expires_in: 1.second)
-true == flag.marked?
+flag.mark(expires_in: 1.second) #=> SET myflag 1 EX 1
+true == flag.marked? #=> EXISTS myflag
sleep 0.5.seconds
-true == flag.marked?
+true == flag.marked? #=> EXISTS myflag
sleep 0.6.seconds
-false == flag.marked?
+false == flag.marked? #=> EXISTS myflag
```
And using structures on a different than the default `shared` redis instance, relying on `config/redis/secondary.yml`:
```ruby
@@ -131,17 +171,28 @@
kredis_unique_list :skills, limit: 2
kredis_enum :morning, values: %w[ bright blue black ], default: "bright"
end
person = Person.find(5)
-person.names.append "David", "Heinemeier", "Hansson" # => SADD person:5:names "David" "Heinemeier" "Hansson"
-true == person.morning.bright?
-person.morning.value = "blue"
-true == person.morning.blue?
+person.names.append "David", "Heinemeier", "Hansson" # => RPUSH people:5:names "David" "Heinemeier" "Hansson"
+true == person.morning.bright? # => GET people:1:morning
+person.morning.value = "blue" # => SET people:1:morning
+true == person.morning.blue? # => GET people:1:morning
```
+You can also define `after_change` callbacks that trigger on mutations:
+```ruby
+class Person < ApplicationRecord
+ kredis_list :names, after_change: ->(p) { }
+ kredis_unique_list :skills, limit: 2, after_change: :skillset_changed
+
+ def skillset_changed
+ end
+end
+```
+
## Installation
1. Add the `kredis` gem to your Gemfile: `gem 'kredis'`
2. Run `./bin/bundle install`
3. Add a default configuration under `config/redis/shared.yml`
@@ -163,9 +214,35 @@
<<: *development
```
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`.
+### Setting SSL options on Redis Connections
+
+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:
+
+```ruby
+Kredis::Connections.connections[:shared] = Redis.new(
+ url: ENV['REDIS_URL'],
+ ssl_params: {
+ cert_store: OpenSSL::X509::Store.new.tap { |store|
+ store.add_file(Rails.root.join('config', 'ca_cert.pem').to_s)
+ },
+
+ cert: OpenSSL::X509::Certificate.new(File.read(
+ Rails.root.join('config', 'client.crt')
+ )),
+
+ key: OpenSSL::PKey::RSA.new(
+ Rails.application.credentials.redis[:client_key]
+ ),
+
+ verify_mode: OpenSSL::SSL::VERIFY_PEER
+ }
+)
+```
+
+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.
## License
Kredis is released under the [MIT License](https://opensource.org/licenses/MIT).