Skip to content

Commit

Permalink
Resolve conflicts
Browse files Browse the repository at this point in the history
  • Loading branch information
heka1024 committed Feb 1, 2024
2 parents 5660bf1 + 4fbb2f5 commit fc2d019
Show file tree
Hide file tree
Showing 22 changed files with 212 additions and 52 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ jobs:
strategy:
matrix:
redis_server: ["4", "5", "6", "7"]
ruby: ["2.7", "3.0", "3.1", "3.2"]
ruby: ["3.1", "3.2", "3.3"]

name: Redis server ${{ matrix.redis_server }} - Ruby ${{ matrix.ruby }}

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,4 @@ jobs:
bundler-cache: true

- name: Run RuboCop
run: bundle exec rubocop --parallel
run: bin/rubocop --parallel
3 changes: 2 additions & 1 deletion .rubocop.yml
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
inherit_from: https://raw.githubusercontent.com/rails/rails/main/.rubocop.yml
# Omakase Ruby styling for Rails
inherit_gem: { rubocop-rails-omakase: rubocop.yml }
7 changes: 1 addition & 6 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,4 @@ gemspec

gem "rake"
gem "debug", ">= 1.0.0"
gem "rubocop"
gem "rubocop-minitest"
gem "rubocop-packaging"
gem "rubocop-performance"
gem "rubocop-rails"
gem "rubocop-md"
gem "rubocop-rails-omakase"
56 changes: 27 additions & 29 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
kredis (1.6.0)
kredis (1.7.0)
activemodel (>= 6.0.0)
activesupport (>= 6.0.0)
redis (>= 4.2, < 6)
Expand Down Expand Up @@ -84,7 +84,7 @@ GEM
io-console (0.6.0)
irb (1.7.1)
reline (>= 0.3.0)
json (2.6.3)
json (2.7.1)
language_server-protocol (3.17.0.3)
loofah (2.9.0)
crass (~> 1.0.2)
Expand All @@ -104,8 +104,8 @@ GEM
racc (~> 1.4)
nokogiri (1.15.2-x86_64-darwin)
racc (~> 1.4)
parallel (1.23.0)
parser (3.2.2.3)
parallel (1.24.0)
parser (3.2.2.4)
ast (~> 2.4.1)
racc
racc (1.7.1)
Expand Down Expand Up @@ -140,40 +140,43 @@ GEM
thor (~> 1.0)
rainbow (3.1.1)
rake (13.0.3)
redis (5.0.7)
redis-client (>= 0.9.0)
redis-client (0.17.0)
redis (5.0.8)
redis-client (>= 0.17.0)
redis-client (0.19.1)
connection_pool
regexp_parser (2.8.1)
regexp_parser (2.8.3)
reline (0.3.6)
io-console (~> 0.5)
rexml (3.2.5)
rubocop (1.54.1)
rexml (3.2.6)
rubocop (1.59.0)
json (~> 2.3)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.2.2.3)
parser (>= 3.2.2.4)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 1.8, < 3.0)
rexml (>= 3.2.5, < 4.0)
rubocop-ast (>= 1.28.0, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.29.0)
rubocop-ast (1.30.0)
parser (>= 3.2.1.0)
rubocop-md (1.2.0)
rubocop (>= 1.0)
rubocop-minitest (0.31.0)
rubocop-minitest (0.34.2)
rubocop (>= 1.39, < 2.0)
rubocop-packaging (0.5.2)
rubocop (>= 1.33, < 2.0)
rubocop-performance (1.18.0)
rubocop (>= 1.7.0, < 2.0)
rubocop-ast (>= 0.4.0)
rubocop-rails (2.20.2)
rubocop-ast (>= 1.30.0, < 2.0)
rubocop-performance (1.20.1)
rubocop (>= 1.48.1, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0)
rubocop-rails (2.23.1)
activesupport (>= 4.2.0)
rack (>= 1.1)
rubocop (>= 1.33.0, < 2.0)
rubocop-ast (>= 1.30.0, < 2.0)
rubocop-rails-omakase (1.0.0)
rubocop
rubocop-minitest
rubocop-performance
rubocop-rails
ruby-progressbar (1.13.0)
sprockets (4.0.2)
concurrent-ruby (~> 1.0)
Expand All @@ -185,7 +188,7 @@ GEM
thor (1.1.0)
tzinfo (2.0.4)
concurrent-ruby (~> 1.0)
unicode-display_width (2.4.2)
unicode-display_width (2.5.0)
websocket-driver (0.7.3)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
Expand All @@ -201,12 +204,7 @@ DEPENDENCIES
kredis!
rails (>= 6.0.0)
rake
rubocop
rubocop-md
rubocop-minitest
rubocop-packaging
rubocop-performance
rubocop-rails
rubocop-rails-omakase

BUNDLED WITH
2.3.12
18 changes: 17 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,20 @@ sleep 0.5.seconds
true == flag.marked? #=> EXISTS myflag
sleep 0.6.seconds
false == flag.marked? #=> EXISTS myflag

limiter = Kredis.limiter "mylimit", limit: 3, expires_in: 5.seconds
0 == limiter.value # => GET "limiter"
limiter.poke # => SET limiter 0 NX + INCRBY limiter 1
limiter.poke # => SET limiter 0 NX + INCRBY limiter 1
limiter.poke # => SET limiter 0 NX + INCRBY limiter 1
false == limiter.exceeded? # => GET "limiter"
limiter.poke # => SET limiter 0 NX + INCRBY limiter 1
true == limiter.exceeded? # => GET "limiter"
sleep 6
limiter.poke # => SET limiter 0 NX + INCRBY limiter 1
limiter.poke # => SET limiter 0 NX + INCRBY limiter 1
limiter.poke # => SET limiter 0 NX + INCRBY limiter 1
false == limiter.exceeded? # => GET "limiter"
```

### Models
Expand Down Expand Up @@ -245,9 +259,11 @@ Additional configurations can be added under `config/redis/*.yml` and referenced

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.

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`.

### Redis support

Kredis works with Redis server 4.0+, with the [Redis Ruby](https://github.com/redis/redis-rb) client version 4.2+.
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.

### Setting SSL options on Redis Connections

Expand Down
27 changes: 27 additions & 0 deletions bin/rubocop
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env ruby
# frozen_string_literal: true

#
# This file was generated by Bundler.
#
# The application 'rubocop' is installed as part of a gem, and
# this file is here to facilitate running it.
#

ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)

bundle_binstub = File.expand_path("bundle", __dir__)

if File.file?(bundle_binstub)
if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
load(bundle_binstub)
else
abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
end
end

require "rubygems"
require "bundler/setup"

load Gem.bin_path("rubocop", "rubocop")
4 changes: 4 additions & 0 deletions lib/kredis/attributes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ def kredis_counter(name, key: nil, default: nil, config: :shared, after_change:
kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in
end

def kredis_limiter(name, limit:, key: nil, config: :shared, after_change: nil, expires_in: nil)
kredis_connection_with __method__, name, key, limit: limit, config: config, after_change: after_change, expires_in: expires_in
end

def kredis_hash(name, key: nil, default: nil, typed: :string, config: :shared, after_change: nil)
kredis_connection_with __method__, name, key, default: default, typed: typed, config: config, after_change: after_change
end
Expand Down
11 changes: 10 additions & 1 deletion lib/kredis/connections.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,22 @@
require "redis"

module Kredis::Connections
DEFAULT_REDIS_URL = "redis://127.0.0.1:6379/0"
DEFAULT_REDIS_TIMEOUT = 1

mattr_accessor :connections, default: Hash.new
mattr_accessor :configurator
mattr_accessor :connector, default: ->(config) { Redis.new(config) }

def configured_for(name)
connections[name] ||= Kredis.instrument :meta, message: "Connected to #{name}" do
connector.call configurator.config_for("redis/#{name}")
if configurator.root.join("config/redis/#{name}.yml").exist?
connector.call configurator.config_for("redis/#{name}")
elsif name == :shared
Redis.new url: ENV.fetch("REDIS_URL", DEFAULT_REDIS_URL), timeout: DEFAULT_REDIS_TIMEOUT
else
raise "No configuration found for #{name}"
end
end
end

Expand Down
5 changes: 5 additions & 0 deletions lib/kredis/types.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ def slots(key, available:, config: :shared, after_change: nil)
type_from(Slots, config, key, after_change: after_change, available: available)
end

def limiter(key, limit:, expires_in: nil, config: :shared, after_change: nil)
type_from(Limiter, config, key, after_change: after_change, expires_in: expires_in, limit: limit)
end

private
def type_from(type_klass, config, key, after_change: nil, **options)
type_klass.new(configured_for(config), namespaced_key(key), **options).then do |type|
Expand All @@ -107,3 +111,4 @@ def type_from(type_klass, config, key, after_change: nil, **options)
require "kredis/types/set"
require "kredis/types/ordered_set"
require "kredis/types/slots"
require "kredis/types/limiter"
24 changes: 24 additions & 0 deletions lib/kredis/types/limiter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# frozen_string_literal: true

# 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.
#
# 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.
#
# 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.
class Kredis::Types::Limiter < Kredis::Types::Counter
class LimitExceeded < StandardError; end

attr_accessor :limit

def poke
failsafe returning: true do
increment
end
end

def exceeded?
failsafe returning: false do
value >= limit
end
end
end
2 changes: 1 addition & 1 deletion lib/kredis/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module Kredis
VERSION = "1.6.0"
VERSION = "1.7.0"
end
26 changes: 23 additions & 3 deletions test/attributes_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ class Person
kredis_list :names
kredis_list :names_with_custom_key_via_lambda, key: ->(p) { "person:#{p.id}:names_customized" }
kredis_list :names_with_custom_key_via_method, key: :generate_key
kredis_list :names_with_default_via_lambda, default: ->(p) { ["Random", p.name] }
kredis_list :names_with_default_via_lambda, default: ->(p) { [ "Random", p.name ] }
kredis_unique_list :skills, limit: 2
kredis_unique_list :skills_with_default_via_lambda, default: ->(p) { ["Random", "Random", p.name] }
kredis_unique_list :skills_with_default_via_lambda, default: ->(p) { [ "Random", "Random", p.name ] }
kredis_ordered_set :reading_list, limit: 2
kredis_flag :special
kredis_flag :temporary_special, expires_in: 1.second
Expand Down Expand Up @@ -44,6 +44,7 @@ class Person
kredis_hash :high_scores_with_default_via_lambda, typed: :integer, default: ->(p) { { high_score: JSON.parse(p.scores).max } }
kredis_boolean :onboarded
kredis_boolean :adult_with_default_via_lambda, default: ->(p) { Date.today.year - p.birthdate.year >= 18 }
kredis_limiter :update_limit, limit: 3, expires_in: 1.second

def self.name
"Person"
Expand All @@ -70,7 +71,7 @@ def eye_color
end

def scores
[10, 28, 2, 7].to_json
[ 10, 28, 2, 7 ].to_json
end

def hourly_wage
Expand All @@ -88,6 +89,14 @@ def vacation_destinations
].to_json
end

def update!
if update_limit.exceeded?
raise "Limiter exceeded"
else
update_limit.poke
end
end

private
def generate_key
"some-generated-key"
Expand Down Expand Up @@ -408,4 +417,15 @@ def suddenly_implemented_person.id; 8; end
sleep 0.6.seconds
end
end

test "limiter exceeded" do
3.times { @person.update! }
assert_raises { @person.update! }
end

test "expiring limiter" do
3.times { @person.update! }
sleep 1.1
assert_nothing_raised { 3.times { @person.update! } }
end
end
2 changes: 1 addition & 1 deletion test/callbacks_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ class CallbacksTest < ActiveSupport::TestCase
vacations = Kredis.set "vacations", after_change: ->(set) { @callback_check = set.members }
vacations.add "paris"

assert_equal ["paris"], @callback_check
assert_equal [ "paris" ], @callback_check
end

test "hash with after_change proc callback" do
Expand Down
29 changes: 29 additions & 0 deletions test/connections_test.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# frozen_string_literal: true

require "test_helper"
require "yaml"

class ConnectionsTest < ActiveSupport::TestCase
setup { Kredis.connections = {} }
teardown { Kredis.namespace = nil }

test "clear all" do
Expand All @@ -26,4 +28,31 @@ class ConnectionsTest < ActiveSupport::TestCase
assert_nil integer.value
assert_equal "don't remove me", Kredis.configured_for(:shared).get("mykey")
end

test "config from file" do
fixture_config = YAML.load_file(Pathname.new(Dir.pwd).join("test/fixtures/config/redis/shared.yml"))["test"].symbolize_keys

Kredis.configurator.stub(:config_for, fixture_config) do
Kredis.configurator.stub(:root, Pathname.new(Dir.pwd).join("test/fixtures")) do
assert_match %r{redis://127.0.0.1:6379/4}, Kredis.redis.inspect
end
end
end

test "default config in env" do
ENV["REDIS_URL"] = "redis://127.0.0.1:6379/3"
assert_match %r{redis://127.0.0.1:6379/3}, Kredis.redis.inspect
ensure
ENV.delete("REDIS_URL")
end

test "default config without env" do
assert_match %r{redis://127.0.0.1:6379/0}, Kredis.redis.inspect
end

test "custom config is missing" do
assert_raises do
Kredis.configured_for(:missing).set "mykey", "won't get set"
end
end
end
Loading

0 comments on commit fc2d019

Please sign in to comment.