diff --git a/Gemfile.lock b/Gemfile.lock index 8b0e16c..123cc0b 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,9 +1,10 @@ PATH remote: . specs: - kredis (1.2.0) + kredis (1.4.0) + activemodel (>= 6.0.0) activesupport (>= 6.0.0) - redis (~> 4.2) + redis (>= 4.2, < 6) GEM remote: https://rubygems.org/ @@ -70,6 +71,7 @@ GEM builder (3.2.4) byebug (11.1.3) concurrent-ruby (1.1.8) + connection_pool (2.4.1) crass (1.0.6) erubi (1.10.0) globalid (0.4.2) @@ -84,17 +86,17 @@ GEM marcel (1.0.1) method_source (1.0.0) mini_mime (1.0.3) - mini_portile2 (2.5.0) + mini_portile2 (2.8.2) minitest (5.14.4) nio4r (2.5.7) - nokogiri (1.11.2) - mini_portile2 (~> 2.5.0) + nokogiri (1.15.2) + mini_portile2 (~> 2.8.2) racc (~> 1.4) - nokogiri (1.11.2-arm64-darwin) + nokogiri (1.15.2-arm64-darwin) racc (~> 1.4) - nokogiri (1.11.2-x86_64-darwin) + nokogiri (1.15.2-x86_64-darwin) racc (~> 1.4) - racc (1.5.2) + racc (1.7.1) rack (2.2.3) rack-test (1.1.0) rack (>= 1.0, < 3) @@ -125,7 +127,10 @@ GEM rake (>= 0.8.7) thor (~> 1.0) rake (13.0.3) - redis (4.6.0) + redis (5.0.6) + redis-client (>= 0.9.0) + redis-client (0.14.1) + connection_pool sprockets (4.0.2) concurrent-ruby (~> 1.0) rack (> 1, < 3) @@ -142,9 +147,9 @@ GEM zeitwerk (2.4.2) PLATFORMS - arm64-darwin-20 + arm64-darwin ruby - x86_64-darwin-20 + x86_64-darwin DEPENDENCIES byebug diff --git a/kredis.gemspec b/kredis.gemspec index d18c80b..8125e5d 100644 --- a/kredis.gemspec +++ b/kredis.gemspec @@ -11,7 +11,8 @@ Gem::Specification.new do |s| s.required_ruby_version = ">= 2.7.0" s.add_dependency "activesupport", ">= 6.0.0" - s.add_dependency "redis", "~> 4.2" + s.add_dependency "activemodel", ">= 6.0.0" + s.add_dependency "redis", ">= 4.2", "< 6" s.add_development_dependency "rails", ">= 6.0.0" s.files = Dir["lib/**/*", "MIT-LICENSE", "README.md"] diff --git a/lib/kredis.rb b/lib/kredis.rb index b65545b..03e8b0b 100644 --- a/lib/kredis.rb +++ b/lib/kredis.rb @@ -1,5 +1,6 @@ require "active_support" require "active_support/core_ext/module/attribute_accessors" +require "active_support/core_ext/module/attribute_accessors_per_thread" require "kredis/version" diff --git a/lib/kredis/log_subscriber.rb b/lib/kredis/log_subscriber.rb index 75f73f4..08a906b 100644 --- a/lib/kredis/log_subscriber.rb +++ b/lib/kredis/log_subscriber.rb @@ -15,7 +15,7 @@ def meta(event) private def formatted_in(color, event, type: nil) - color " Kredis #{type} (#{event.duration.round(1)}ms) #{event.payload[:message]}", color, true + color " Kredis #{type} (#{event.duration.round(1)}ms) #{event.payload[:message]}", color, bold: true end end diff --git a/lib/kredis/migration.rb b/lib/kredis/migration.rb index fa12e68..9b24a30 100644 --- a/lib/kredis/migration.rb +++ b/lib/kredis/migration.rb @@ -30,9 +30,15 @@ def migrate(from:, to:, pipeline: nil) end end - def delete_all(key_pattern) - each_key_batch_matching(key_pattern) do |keys, pipeline| - pipeline.del *keys + def delete_all(*key_patterns) + log_migration "DELETE ALL #{key_patterns.inspect}" do + if key_patterns.length > 1 + @redis.del *key_patterns + else + each_key_batch_matching(key_patterns.first) do |keys, pipeline| + pipeline.del *keys + end + end end end diff --git a/lib/kredis/type/datetime.rb b/lib/kredis/type/datetime.rb index 8841772..8d3b6e1 100644 --- a/lib/kredis/type/datetime.rb +++ b/lib/kredis/type/datetime.rb @@ -4,11 +4,11 @@ module Kredis module Type class DateTime < ActiveModel::Type::DateTime def serialize(value) - super&.iso8601(9) + super&.utc&.iso8601(9) end def cast_value(value) - super&.to_datetime + super&.to_time end end end diff --git a/lib/kredis/type/json.rb b/lib/kredis/type/json.rb index 4183d5f..d78b604 100644 --- a/lib/kredis/type/json.rb +++ b/lib/kredis/type/json.rb @@ -8,7 +8,7 @@ def type end def cast_value(value) - JSON.load(value) + JSON.parse(value) end def serialize(value) diff --git a/lib/kredis/types/counter.rb b/lib/kredis/types/counter.rb index bcfcf62..cacebb3 100644 --- a/lib/kredis/types/counter.rb +++ b/lib/kredis/types/counter.rb @@ -4,16 +4,16 @@ class Kredis::Types::Counter < Kredis::Types::Proxying attr_accessor :expires_in def increment(by: 1) - multi do |pipeline| - pipeline.set 0, ex: expires_in, nx: true - pipeline.incrby by + multi do + set 0, ex: expires_in, nx: true + incrby by end[-1] end def decrement(by: 1) - multi do |pipeline| - pipeline.set 0, ex: expires_in, nx: true - pipeline.decrby by + multi do + set 0, ex: expires_in, nx: true + decrby by end[-1] end diff --git a/lib/kredis/types/list.rb b/lib/kredis/types/list.rb index cc1867d..3987bfa 100644 --- a/lib/kredis/types/list.rb +++ b/lib/kredis/types/list.rb @@ -1,5 +1,5 @@ class Kredis::Types::List < Kredis::Types::Proxying - proxying :lrange, :lrem, :lpush, :rpush, :exists?, :del + proxying :lrange, :lrem, :lpush, :ltrim, :rpush, :exists?, :del attr_accessor :typed @@ -8,20 +8,24 @@ def elements end alias to_a elements - def remove(*elements, pipeline: nil) - types_to_strings(elements, typed).each { |element| (pipeline || proxy).lrem 0, element } + def remove(*elements) + types_to_strings(elements, typed).each { |element| lrem 0, element } end - def prepend(*elements, pipeline: nil) - (pipeline || proxy).lpush types_to_strings(elements, typed) if elements.flatten.any? + def prepend(*elements) + lpush types_to_strings(elements, typed) if elements.flatten.any? end - def append(*elements, pipeline: nil) - (pipeline || proxy).rpush types_to_strings(elements, typed) if elements.flatten.any? + def append(*elements) + rpush types_to_strings(elements, typed) if elements.flatten.any? end alias << append def clear del end + + def last(n = nil) + n ? lrange(-n, -1) : lrange(-1, -1).first + end end diff --git a/lib/kredis/types/proxy.rb b/lib/kredis/types/proxy.rb index 172ff2f..b609a6f 100644 --- a/lib/kredis/types/proxy.rb +++ b/lib/kredis/types/proxy.rb @@ -2,19 +2,21 @@ class Kredis::Types::Proxy require_relative "proxy/failsafe" include Failsafe - attr_accessor :redis, :key + attr_accessor :key + + thread_mattr_accessor :pipeline def initialize(redis, key, **options) @redis, @key = redis, key options.each { |key, value| send("#{key}=", value) } end - def multi(&block) - # NOTE: to be removed when Redis 4 compatibility gets dropped - return redis.multi unless block - - redis.multi do |pipeline| - block.call(Kredis::Types::Proxy.new(pipeline, key)) + def multi(*args, **kwargs, &block) + redis.multi(*args, **kwargs) do |pipeline| + self.pipeline = pipeline + block.call + ensure + self.pipeline = nil end end @@ -27,6 +29,10 @@ def method_missing(method, *args, **kwargs) end private + def redis + pipeline || @redis + end + def log_message(method, *args, **kwargs) args = args.flatten.reject(&:blank?).presence kwargs = kwargs.reject { |_k, v| v.blank? }.presence diff --git a/lib/kredis/types/proxying.rb b/lib/kredis/types/proxying.rb index a7c0e55..3607f0f 100644 --- a/lib/kredis/types/proxying.rb +++ b/lib/kredis/types/proxying.rb @@ -1,14 +1,14 @@ require "active_support/core_ext/module/delegation" class Kredis::Types::Proxying - attr_accessor :proxy, :redis, :key + attr_accessor :proxy, :key def self.proxying(*commands) delegate *commands, to: :proxy end def initialize(redis, key, **options) - @redis, @key = redis, key + @key = key @proxy = Kredis::Types::Proxy.new(redis, key) options.each { |key, value| send("#{key}=", value) } end diff --git a/lib/kredis/types/set.rb b/lib/kredis/types/set.rb index dcb79e1..dca921d 100644 --- a/lib/kredis/types/set.rb +++ b/lib/kredis/types/set.rb @@ -1,5 +1,5 @@ class Kredis::Types::Set < Kredis::Types::Proxying - proxying :smembers, :sadd, :srem, :multi, :del, :sismember, :scard, :spop, :exists? + proxying :smembers, :sadd, :srem, :multi, :del, :sismember, :scard, :spop, :exists?, :srandmember attr_accessor :typed @@ -8,19 +8,19 @@ def members end alias to_a members - def add(*members, pipeline: nil) - (pipeline || proxy).sadd types_to_strings(members, typed) if members.flatten.any? + def add(*members) + sadd types_to_strings(members, typed) if members.flatten.any? end alias << add - def remove(*members, pipeline: nil) - (pipeline || proxy).srem types_to_strings(members, typed) if members.flatten.any? + def remove(*members) + srem types_to_strings(members, typed) if members.flatten.any? end def replace(*members) - multi do |pipeline| - pipeline.del - add members, pipeline: pipeline + multi do + del + add members end end @@ -33,10 +33,18 @@ def size end def take - spop + string_to_type(spop, typed) end def clear del end + + def sample(count = nil) + if count.nil? + string_to_type(srandmember(count), typed) + else + strings_to_types(srandmember(count), typed) + end + end end diff --git a/lib/kredis/types/unique_list.rb b/lib/kredis/types/unique_list.rb index 2a85afc..eb4bbcc 100644 --- a/lib/kredis/types/unique_list.rb +++ b/lib/kredis/types/unique_list.rb @@ -8,10 +8,10 @@ def prepend(elements) elements = Array(elements).uniq return if elements.empty? - multi do |pipeline| - remove elements, pipeline: pipeline - super(elements, pipeline: pipeline) - pipeline.ltrim 0, (limit - 1) if limit + multi do + remove elements + super + ltrim 0, (limit - 1) if limit end end @@ -19,10 +19,10 @@ def append(elements) elements = Array(elements).uniq return if elements.empty? - multi do |pipeline| - remove elements, pipeline: pipeline - super(elements, pipeline: pipeline) - pipeline.ltrim -limit, -1 if limit + multi do + remove elements + super + ltrim -limit, -1 if limit end end alias << append diff --git a/lib/kredis/version.rb b/lib/kredis/version.rb index 01fec9a..3df29d9 100644 --- a/lib/kredis/version.rb +++ b/lib/kredis/version.rb @@ -1,3 +1,3 @@ module Kredis - VERSION = "1.2.0" + VERSION = "1.4.0" end diff --git a/test/migration_test.rb b/test/migration_test.rb index 032385a..40b9c5a 100644 --- a/test/migration_test.rb +++ b/test/migration_test.rb @@ -51,11 +51,19 @@ class MigrationTest < ActiveSupport::TestCase end end - test "delete_all" do + test "delete_all with pattern" do 3.times { |index| Kredis.proxy("mykey:#{index}").set "hello there #{index}" } Kredis::Migration.delete_all "mykey:*" 3.times { |index| assert_nil Kredis.proxy("mykey:#{index}").get } end + + test "delete_all with keys" do + 3.times { |index| Kredis.proxy("mykey:#{index}").set "hello there #{index}" } + + Kredis::Migration.delete_all *3.times.map { |index| "mykey:#{index}" } + + 3.times { |index| assert_nil Kredis.proxy("mykey:#{index}").get } + end end diff --git a/test/types/list_test.rb b/test/types/list_test.rb index 7e7aa5f..92cd1e2 100644 --- a/test/types/list_test.rb +++ b/test/types/list_test.rb @@ -41,10 +41,20 @@ class ListTest < ActiveSupport::TestCase assert_equal [], @list.elements end + test "last" do + @list.append(%w[ 1 2 3 ]) + assert_equal "3", @list.last + end + + test "last(n)" do + @list.append(%w[ 1 2 3 ]) + assert_equal %w[ 2 3 ], @list.last(2) + end + test "typed as datetime" do @list = Kredis.list "mylist", typed: :datetime - @list.append [ 1.day.from_now.midnight, 2.days.from_now.midnight ] + @list.append [ 1.day.from_now.midnight.in_time_zone("Pacific Time (US & Canada)"), 2.days.from_now.midnight.in_time_zone("UTC") ] assert_equal [ 1.day.from_now.midnight, 2.days.from_now.midnight ], @list.elements @list.remove(2.days.from_now.midnight) @@ -57,4 +67,10 @@ class ListTest < ActiveSupport::TestCase @list.append(%w[ 1 2 3 ]) assert @list.exists? end + + test "ltrim" do + @list.append(%w[ 1 2 3 4 ]) + @list.ltrim(-3, -2) + assert_equal %w[ 2 3 ], @list.elements + end end diff --git a/test/types/scalar_test.rb b/test/types/scalar_test.rb index 03a3ddc..29eacca 100644 --- a/test/types/scalar_test.rb +++ b/test/types/scalar_test.rb @@ -53,13 +53,16 @@ class ScalarTest < ActiveSupport::TestCase test "datetime casting Dates" do datetime = Kredis.datetime "myscalar" datetime.value = Date.current - assert_equal Date.current.to_datetime, datetime.value + assert_equal Date.current.to_time, datetime.value end test "json" do json = Kredis.json "myscalar" json.value = { "one" => 1, "string" => "hello" } assert_equal({ "one" => 1, "string" => "hello" }, json.value) + + json.value = {"json_class"=>"String", "raw"=>[97, 98, 99]} + assert_equal({"json_class"=>"String", "raw"=>[97, 98, 99]}, json.value) end test "invalid type" do diff --git a/test/types/set_test.rb b/test/types/set_test.rb index b536262..bd6bc08 100644 --- a/test/types/set_test.rb +++ b/test/types/set_test.rb @@ -72,6 +72,8 @@ class SetTest < ActiveSupport::TestCase @set.remove(2.7) assert_equal [ 1.5 ], @set.members + + assert_equal 1.5, @set.take end test "failing open" do @@ -88,4 +90,12 @@ class SetTest < ActiveSupport::TestCase @set.add(%w[ 1 2 3 ]) assert @set.exists? end + + test "srandmember" do + @set = Kredis.set "mylist", typed: :float + @set.add 1.5, 2.7 + + assert @set.sample.in?([ 1.5, 2.7 ]) + assert_equal [ 1.5, 2.7 ], @set.sample(2).sort + end end