From 133a0e071f87192a69d5c5519f85a667e44064a3 Mon Sep 17 00:00:00 2001 From: tleish Date: Thu, 9 Jun 2022 18:08:40 -0600 Subject: [PATCH 01/27] Add default proxy value --- lib/kredis/attributes.rb | 13 +++++++++++-- lib/kredis/types.rb | 4 ++-- lib/kredis/types/proxy.rb | 13 ++++++++++++- test/attributes_test.rb | 16 ++++++++++++++-- 4 files changed, 39 insertions(+), 7 deletions(-) diff --git a/lib/kredis/attributes.rb b/lib/kredis/attributes.rb index 21c3ce6..ac72eb1 100644 --- a/lib/kredis/attributes.rb +++ b/lib/kredis/attributes.rb @@ -2,8 +2,8 @@ module Kredis::Attributes extend ActiveSupport::Concern class_methods do - def kredis_proxy(name, key: nil, config: :shared, after_change: nil) - kredis_connection_with __method__, name, key, config: config, after_change: after_change + def kredis_proxy(name, key: nil, default: nil, config: :shared, after_change: nil) + kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change end def kredis_string(name, key: nil, config: :shared, after_change: nil, expires_in: nil) @@ -84,6 +84,7 @@ def kredis_connection_with(method, name, key, **options) if instance_variable_defined?(ivar_symbol) instance_variable_get(ivar_symbol) else + options.merge!(default: kredis_default_value_evaluated(options[:default])) if options[:default] new_type = Kredis.send(type, kredis_key_evaluated(key) || kredis_key_for_attribute(name), **options) instance_variable_set ivar_symbol, after_change ? enrich_after_change_with_record_access(new_type, after_change) : new_type @@ -115,4 +116,12 @@ def enrich_after_change_with_record_access(type, original_after_change) when Symbol then Kredis::Types::CallbacksProxy.new(type, ->(_) { send(original_after_change) }) end end + + def kredis_default_value_evaluated(default) + case default + when Proc then Proc.new { default.call(self) } + when Symbol then send(default) + else default + end + end end diff --git a/lib/kredis/types.rb b/lib/kredis/types.rb index 16ad906..dc1fa99 100644 --- a/lib/kredis/types.rb +++ b/lib/kredis/types.rb @@ -1,8 +1,8 @@ module Kredis::Types autoload :CallbacksProxy, "kredis/types/callbacks_proxy" - def proxy(key, config: :shared, after_change: nil) - type_from(Proxy, config, key, after_change: after_change) + def proxy(key, default: nil, config: :shared, after_change: nil) + type_from(Proxy, config, key, after_change: after_change, default: default) end diff --git a/lib/kredis/types/proxy.rb b/lib/kredis/types/proxy.rb index 172ff2f..b18cea9 100644 --- a/lib/kredis/types/proxy.rb +++ b/lib/kredis/types/proxy.rb @@ -2,7 +2,7 @@ class Kredis::Types::Proxy require_relative "proxy/failsafe" include Failsafe - attr_accessor :redis, :key + attr_accessor :redis, :key, :default def initialize(redis, key, **options) @redis, @key = redis, key @@ -18,6 +18,10 @@ def multi(&block) end end + def get + super || default_value + end + def method_missing(method, *args, **kwargs) Kredis.instrument :proxy, **log_message(method, *args, **kwargs) do failsafe do @@ -33,4 +37,11 @@ def log_message(method, *args, **kwargs) { message: "#{method.upcase} #{key} #{args&.inspect} #{kwargs&.inspect}".chomp } end + + def default_value + case default + when Proc then default.call.tap { |value| set(value) } + else default + end + end end diff --git a/test/attributes_test.rb b/test/attributes_test.rb index 5f4df6d..df5deca 100644 --- a/test/attributes_test.rb +++ b/test/attributes_test.rb @@ -4,9 +4,10 @@ class Person include Kredis::Attributes - kredis_proxy :anything + kredis_proxy :anything, default: "default" kredis_proxy :nothing, key: "something:else" - kredis_proxy :something, key: ->(p) { "person:#{p.id}:something" } + kredis_proxy :something, key: ->(p) { "person:#{p.id}:something" }, default: :name + kredis_proxy :something_with_default_via_lambda, default: ->(p) { p.name.downcase } 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 @@ -37,6 +38,10 @@ def id 8 end + def name + "Jason" + end + private def generate_key "some-generated-key" @@ -54,6 +59,7 @@ class AttributesTest < ActiveSupport::TestCase setup { @person = Person.new } test "proxy" do + assert_equal "default", @person.anything.get @person.anything.set "something" assert_equal "something", @person.anything.get end @@ -64,10 +70,16 @@ class AttributesTest < ActiveSupport::TestCase end test "proxy with custom proc key" do + assert_equal "Jason", @person.something.get @person.something.set "everything" assert_equal "everything", Kredis.redis.get("person:8:something") end + test "proxy with default value" do + assert_equal "jason", @person.something_with_default_via_lambda.get + assert_equal "jason", Kredis.redis.get("people:8:something_with_default_via_lambda") + end + test "list" do @person.names.append(%w[ david kasper ]) assert_equal %w[ david kasper ], @person.names.elements From 4eff947d71aeaea99378483896d8563a5c7ed1ca Mon Sep 17 00:00:00 2001 From: tleish Date: Thu, 9 Jun 2022 18:54:30 -0600 Subject: [PATCH 02/27] Add default list values --- lib/kredis/attributes.rb | 4 ++-- lib/kredis/types.rb | 4 ++-- lib/kredis/types/list.rb | 2 +- lib/kredis/types/proxying.rb | 9 ++++++++- test/attributes_test.rb | 6 ++++++ 5 files changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/kredis/attributes.rb b/lib/kredis/attributes.rb index ac72eb1..9cc3552 100644 --- a/lib/kredis/attributes.rb +++ b/lib/kredis/attributes.rb @@ -42,8 +42,8 @@ def kredis_json(name, key: nil, config: :shared, after_change: nil, expires_in: kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in end - def kredis_list(name, key: nil, typed: :string, config: :shared, after_change: nil) - kredis_connection_with __method__, name, key, typed: typed, config: config, after_change: after_change + def kredis_list(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 def kredis_unique_list(name, limit: nil, key: nil, typed: :string, config: :shared, after_change: nil) diff --git a/lib/kredis/types.rb b/lib/kredis/types.rb index dc1fa99..f147d1b 100644 --- a/lib/kredis/types.rb +++ b/lib/kredis/types.rb @@ -59,8 +59,8 @@ def hash(key, typed: :string, config: :shared, after_change: nil) type_from(Hash, config, key, after_change: after_change, typed: typed) end - def list(key, typed: :string, config: :shared, after_change: nil) - type_from(List, config, key, after_change: after_change, typed: typed) + def list(key, default: nil, typed: :string, config: :shared, after_change: nil) + type_from(List, config, key, after_change: after_change, default: default, typed: typed) end def unique_list(key, typed: :string, limit: nil, config: :shared, after_change: nil) diff --git a/lib/kredis/types/list.rb b/lib/kredis/types/list.rb index cc1867d..5ebecd0 100644 --- a/lib/kredis/types/list.rb +++ b/lib/kredis/types/list.rb @@ -4,7 +4,7 @@ class Kredis::Types::List < Kredis::Types::Proxying attr_accessor :typed def elements - strings_to_types(lrange(0, -1) || [], typed) + strings_to_types(exists? ? lrange(0, -1) : default_value(method: :append) || [], typed) end alias to_a elements diff --git a/lib/kredis/types/proxying.rb b/lib/kredis/types/proxying.rb index a7c0e55..8372b60 100644 --- a/lib/kredis/types/proxying.rb +++ b/lib/kredis/types/proxying.rb @@ -1,7 +1,7 @@ require "active_support/core_ext/module/delegation" class Kredis::Types::Proxying - attr_accessor :proxy, :redis, :key + attr_accessor :proxy, :redis, :key, :default def self.proxying(*commands) delegate *commands, to: :proxy @@ -19,4 +19,11 @@ def failsafe(returning: nil, &block) private delegate :type_to_string, :string_to_type, :types_to_strings, :strings_to_types, to: :Kredis + + def default_value(method: :set) + case default + when Proc then default.call.tap { |value| send(method, value) } + else default + end + end end diff --git a/test/attributes_test.rb b/test/attributes_test.rb index df5deca..8e33e6b 100644 --- a/test/attributes_test.rb +++ b/test/attributes_test.rb @@ -11,6 +11,7 @@ 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_unique_list :skills, limit: 2 kredis_flag :special kredis_flag :temporary_special, expires_in: 1.second @@ -95,6 +96,11 @@ class AttributesTest < ActiveSupport::TestCase assert_equal %w[ david kasper ], Kredis.redis.lrange("some-generated-key", 0, -1) end + test "list with default proc value" do + assert_equal %w[ Random Jason ], @person.names_with_default_via_lambda.elements + assert_equal %w[ Random Jason ], Kredis.redis.lrange("people:8:names_with_default_via_lambda", 0, -1) + end + test "unique list" do @person.skills.prepend(%w[ trolling photography ]) @person.skills.prepend("racing") From 903f2a9689dc1af333c0d2176d1995e9fd784aab Mon Sep 17 00:00:00 2001 From: tleish Date: Thu, 9 Jun 2022 19:09:10 -0600 Subject: [PATCH 03/27] Add default to unique_list --- lib/kredis/attributes.rb | 4 ++-- lib/kredis/types.rb | 4 ++-- lib/kredis/types/list.rb | 8 +++++++- lib/kredis/types/proxying.rb | 9 ++++----- test/attributes_test.rb | 6 ++++++ 5 files changed, 21 insertions(+), 10 deletions(-) diff --git a/lib/kredis/attributes.rb b/lib/kredis/attributes.rb index 9cc3552..8650b2d 100644 --- a/lib/kredis/attributes.rb +++ b/lib/kredis/attributes.rb @@ -46,8 +46,8 @@ def kredis_list(name, key: nil, default: nil, typed: :string, config: :shared, a kredis_connection_with __method__, name, key, default: default, typed: typed, config: config, after_change: after_change end - def kredis_unique_list(name, limit: nil, key: nil, typed: :string, config: :shared, after_change: nil) - kredis_connection_with __method__, name, key, limit: limit, typed: typed, config: config, after_change: after_change + def kredis_unique_list(name, limit: nil, key: nil, default: nil, typed: :string, config: :shared, after_change: nil) + kredis_connection_with __method__, name, key, default: default, limit: limit, typed: typed, config: config, after_change: after_change end def kredis_set(name, key: nil, typed: :string, config: :shared, after_change: nil) diff --git a/lib/kredis/types.rb b/lib/kredis/types.rb index f147d1b..877a268 100644 --- a/lib/kredis/types.rb +++ b/lib/kredis/types.rb @@ -63,8 +63,8 @@ def list(key, default: nil, typed: :string, config: :shared, after_change: nil) type_from(List, config, key, after_change: after_change, default: default, typed: typed) end - def unique_list(key, typed: :string, limit: nil, config: :shared, after_change: nil) - type_from(UniqueList, config, key, after_change: after_change, typed: typed, limit: limit) + def unique_list(key, default: nil, typed: :string, limit: nil, config: :shared, after_change: nil) + type_from(UniqueList, config, key, after_change: after_change, default: default, typed: typed, limit: limit) end def set(key, typed: :string, config: :shared, after_change: nil) diff --git a/lib/kredis/types/list.rb b/lib/kredis/types/list.rb index 5ebecd0..11f4e46 100644 --- a/lib/kredis/types/list.rb +++ b/lib/kredis/types/list.rb @@ -4,7 +4,8 @@ class Kredis::Types::List < Kredis::Types::Proxying attr_accessor :typed def elements - strings_to_types(exists? ? lrange(0, -1) : default_value(method: :append) || [], typed) + value = exists? ? lrange(0, -1) : default_value || [] + strings_to_types(value, typed) end alias to_a elements @@ -24,4 +25,9 @@ def append(*elements, pipeline: nil) def clear del end + + def set_and_get(value) + append(value) + elements + end end diff --git a/lib/kredis/types/proxying.rb b/lib/kredis/types/proxying.rb index 8372b60..aeaf3c0 100644 --- a/lib/kredis/types/proxying.rb +++ b/lib/kredis/types/proxying.rb @@ -20,10 +20,9 @@ def failsafe(returning: nil, &block) private delegate :type_to_string, :string_to_type, :types_to_strings, :strings_to_types, to: :Kredis - def default_value(method: :set) - case default - when Proc then default.call.tap { |value| send(method, value) } - else default - end + def default_value + return default unless default.is_a? Proc + + set_and_get(default.call) end end diff --git a/test/attributes_test.rb b/test/attributes_test.rb index 8e33e6b..7048ab0 100644 --- a/test/attributes_test.rb +++ b/test/attributes_test.rb @@ -13,6 +13,7 @@ class Person kredis_list :names_with_custom_key_via_method, key: :generate_key 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_flag :special kredis_flag :temporary_special, expires_in: 1.second kredis_string :address @@ -108,6 +109,11 @@ class AttributesTest < ActiveSupport::TestCase assert_equal %w[ racing photography ], @person.skills.elements end + test "unique list with default proc value" do + assert_equal %w[ Random Jason ], @person.skills_with_default_via_lambda.elements + assert_equal %w[ Random Jason ], Kredis.redis.lrange("people:8:skills_with_default_via_lambda", 0, -1) + end + test "flag" do assert_not @person.special? From a668f7837a9a7c4a4f156596c5dd6718c06c46da Mon Sep 17 00:00:00 2001 From: tleish Date: Thu, 9 Jun 2022 19:23:44 -0600 Subject: [PATCH 04/27] Add default to flag --- lib/kredis/attributes.rb | 4 ++-- lib/kredis/types.rb | 4 ++-- lib/kredis/types/flag.rb | 11 ++++++++++- test/attributes_test.rb | 5 +++++ 4 files changed, 19 insertions(+), 5 deletions(-) diff --git a/lib/kredis/attributes.rb b/lib/kredis/attributes.rb index 8650b2d..b99963c 100644 --- a/lib/kredis/attributes.rb +++ b/lib/kredis/attributes.rb @@ -22,8 +22,8 @@ def kredis_datetime(name, key: nil, config: :shared, after_change: nil, expires_ kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in end - def kredis_flag(name, key: nil, config: :shared, after_change: nil, expires_in: nil) - kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in + def kredis_flag(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil) + kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in define_method("#{name}?") do send(name).marked? diff --git a/lib/kredis/types.rb b/lib/kredis/types.rb index 877a268..172346d 100644 --- a/lib/kredis/types.rb +++ b/lib/kredis/types.rb @@ -47,8 +47,8 @@ def cycle(key, values:, expires_in: nil, config: :shared, after_change: nil) type_from(Cycle, config, key, after_change: after_change, values: values, expires_in: expires_in) end - def flag(key, config: :shared, after_change: nil, expires_in: nil) - type_from(Flag, config, key, after_change: after_change, expires_in: expires_in) + def flag(key, default: nil, config: :shared, after_change: nil, expires_in: nil) + type_from(Flag, config, key, after_change: after_change, default: default, expires_in: expires_in) end def enum(key, values:, default:, config: :shared, after_change: nil) diff --git a/lib/kredis/types/flag.rb b/lib/kredis/types/flag.rb index fef9c46..3d78787 100644 --- a/lib/kredis/types/flag.rb +++ b/lib/kredis/types/flag.rb @@ -8,10 +8,19 @@ def mark(expires_in: nil, force: true) end def marked? - exists? + exists? || default_value || false end def remove del end + + private + + def set_and_get(value) + return false unless value + + mark + true + end end diff --git a/test/attributes_test.rb b/test/attributes_test.rb index 7048ab0..2393052 100644 --- a/test/attributes_test.rb +++ b/test/attributes_test.rb @@ -16,6 +16,7 @@ class Person kredis_unique_list :skills_with_default_via_lambda, default: ->(p) { ["Random", "Random", p.name] } kredis_flag :special kredis_flag :temporary_special, expires_in: 1.second + kredis_flag :special_with_default_via_lambda, default: ->(p) { p.id == 8 } kredis_string :address kredis_integer :age kredis_decimal :salary @@ -124,6 +125,10 @@ class AttributesTest < ActiveSupport::TestCase assert_not @person.special? end + test "flag with default proc value" do + assert @person.special_with_default_via_lambda? + end + test "string" do assert_not @person.address.assigned? From 35dc9bfbebdf52371602eb5befd88df5b816bc90 Mon Sep 17 00:00:00 2001 From: tleish Date: Thu, 9 Jun 2022 20:04:20 -0600 Subject: [PATCH 05/27] Add default to string --- lib/kredis/attributes.rb | 4 ++-- lib/kredis/types/proxying.rb | 5 +++++ lib/kredis/types/scalar.rb | 4 ++-- test/attributes_test.rb | 8 ++++++++ 4 files changed, 17 insertions(+), 4 deletions(-) diff --git a/lib/kredis/attributes.rb b/lib/kredis/attributes.rb index b99963c..3ee73d8 100644 --- a/lib/kredis/attributes.rb +++ b/lib/kredis/attributes.rb @@ -6,8 +6,8 @@ def kredis_proxy(name, key: nil, default: nil, config: :shared, after_change: ni kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change end - def kredis_string(name, key: nil, config: :shared, after_change: nil, expires_in: nil) - kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in + def kredis_string(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil) + kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in end def kredis_integer(name, key: nil, config: :shared, after_change: nil, expires_in: nil) diff --git a/lib/kredis/types/proxying.rb b/lib/kredis/types/proxying.rb index aeaf3c0..65508e9 100644 --- a/lib/kredis/types/proxying.rb +++ b/lib/kredis/types/proxying.rb @@ -25,4 +25,9 @@ def default_value set_and_get(default.call) end + + def set_and_get(value) + set value + get + end end diff --git a/lib/kredis/types/scalar.rb b/lib/kredis/types/scalar.rb index 03ddf52..e17d8a1 100644 --- a/lib/kredis/types/scalar.rb +++ b/lib/kredis/types/scalar.rb @@ -11,14 +11,14 @@ def value value_after_casting = string_to_type(get, typed) if value_after_casting.nil? - default + default_value else value_after_casting end end def to_s - get || default&.to_s + get || default_value&.to_s end def assigned? diff --git a/test/attributes_test.rb b/test/attributes_test.rb index 2393052..7b4fc8f 100644 --- a/test/attributes_test.rb +++ b/test/attributes_test.rb @@ -18,6 +18,7 @@ class Person kredis_flag :temporary_special, expires_in: 1.second kredis_flag :special_with_default_via_lambda, default: ->(p) { p.id == 8 } kredis_string :address + kredis_string :address_with_default_via_lambda, default: ->(p) { p.name } kredis_integer :age kredis_decimal :salary kredis_datetime :last_seen_at @@ -140,6 +141,13 @@ class AttributesTest < ActiveSupport::TestCase assert_not @person.address.assigned? end + test "string with default proc value" do + assert_equal "Jason", @person.address_with_default_via_lambda.to_s + + @person.address.clear + assert_not @person.address.assigned? + end + test "integer" do @person.age.value = 41 assert_equal 41, @person.age.value From e6bcbbd4f8b6788a80ca19ccf85821d04ec8fbc7 Mon Sep 17 00:00:00 2001 From: tleish Date: Thu, 9 Jun 2022 23:00:49 -0600 Subject: [PATCH 06/27] Add default to integer --- lib/kredis/attributes.rb | 4 ++-- lib/kredis/types/scalar.rb | 2 +- test/attributes_test.rb | 10 ++++++++++ 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/lib/kredis/attributes.rb b/lib/kredis/attributes.rb index 3ee73d8..fe90f41 100644 --- a/lib/kredis/attributes.rb +++ b/lib/kredis/attributes.rb @@ -10,8 +10,8 @@ def kredis_string(name, key: nil, default: nil, config: :shared, after_change: n kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in end - def kredis_integer(name, key: nil, config: :shared, after_change: nil, expires_in: nil) - kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in + def kredis_integer(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil) + kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in end def kredis_decimal(name, key: nil, config: :shared, after_change: nil, expires_in: nil) diff --git a/lib/kredis/types/scalar.rb b/lib/kredis/types/scalar.rb index e17d8a1..d313a05 100644 --- a/lib/kredis/types/scalar.rb +++ b/lib/kredis/types/scalar.rb @@ -11,7 +11,7 @@ def value value_after_casting = string_to_type(get, typed) if value_after_casting.nil? - default_value + string_to_type(default_value, typed) else value_after_casting end diff --git a/test/attributes_test.rb b/test/attributes_test.rb index 7b4fc8f..92b77d2 100644 --- a/test/attributes_test.rb +++ b/test/attributes_test.rb @@ -20,6 +20,7 @@ class Person kredis_string :address kredis_string :address_with_default_via_lambda, default: ->(p) { p.name } kredis_integer :age + kredis_integer :age_with_default_via_lambda, default: ->(p) { Time.now.year - p.birthdate.year } kredis_decimal :salary kredis_datetime :last_seen_at kredis_float :height @@ -46,6 +47,10 @@ def name "Jason" end + def birthdate + Time.now - 25.years + end + private def generate_key "some-generated-key" @@ -154,6 +159,11 @@ class AttributesTest < ActiveSupport::TestCase assert_equal "41", @person.age.to_s end + test "integer with default proc value" do + assert_equal 25, @person.age_with_default_via_lambda.value + assert_equal "25", @person.age_with_default_via_lambda.to_s + end + test "decimal" do @person.salary.value = 10000.07 assert_equal 10000.07, @person.salary.value From ba0092c75b311f2304b32d6b803c775852e80511 Mon Sep 17 00:00:00 2001 From: tleish Date: Thu, 9 Jun 2022 23:15:58 -0600 Subject: [PATCH 07/27] Add default to decimal --- lib/kredis/attributes.rb | 4 ++-- test/attributes_test.rb | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/kredis/attributes.rb b/lib/kredis/attributes.rb index fe90f41..9db5d0c 100644 --- a/lib/kredis/attributes.rb +++ b/lib/kredis/attributes.rb @@ -14,8 +14,8 @@ def kredis_integer(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_decimal(name, key: nil, config: :shared, after_change: nil, expires_in: nil) - kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in + def kredis_decimal(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil) + kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in end def kredis_datetime(name, key: nil, config: :shared, after_change: nil, expires_in: nil) diff --git a/test/attributes_test.rb b/test/attributes_test.rb index 92b77d2..2c84906 100644 --- a/test/attributes_test.rb +++ b/test/attributes_test.rb @@ -22,6 +22,7 @@ class Person kredis_integer :age kredis_integer :age_with_default_via_lambda, default: ->(p) { Time.now.year - p.birthdate.year } kredis_decimal :salary + kredis_decimal :salary_with_default_via_lambda, default: ->(p) { p.hourly_wage * 40 * 52 } kredis_datetime :last_seen_at kredis_float :height kredis_enum :morning, values: %w[ bright blue black ], default: "bright" @@ -50,6 +51,10 @@ def name def birthdate Time.now - 25.years end + + def hourly_wage + 15.26 + end private def generate_key @@ -170,6 +175,11 @@ class AttributesTest < ActiveSupport::TestCase assert_equal "0.1000007e5", @person.salary.to_s end + test "decimal with default proc value" do + assert_equal 31_740.80.to_d, @person.salary_with_default_via_lambda.value + assert_equal "31740.8", @person.salary_with_default_via_lambda.to_s + end + test "float" do @person.height.value = 1.85 assert_equal 1.85, @person.height.value From 52b308b153ca3503d75825c50430017e368b2118 Mon Sep 17 00:00:00 2001 From: tleish Date: Thu, 9 Jun 2022 23:21:36 -0600 Subject: [PATCH 08/27] Add default to datetime --- lib/kredis/attributes.rb | 4 ++-- test/attributes_test.rb | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/kredis/attributes.rb b/lib/kredis/attributes.rb index 9db5d0c..ca36b82 100644 --- a/lib/kredis/attributes.rb +++ b/lib/kredis/attributes.rb @@ -18,8 +18,8 @@ def kredis_decimal(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_datetime(name, key: nil, config: :shared, after_change: nil, expires_in: nil) - kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in + def kredis_datetime(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil) + kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in end def kredis_flag(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil) diff --git a/test/attributes_test.rb b/test/attributes_test.rb index 2c84906..b017ef6 100644 --- a/test/attributes_test.rb +++ b/test/attributes_test.rb @@ -20,10 +20,11 @@ class Person kredis_string :address kredis_string :address_with_default_via_lambda, default: ->(p) { p.name } kredis_integer :age - kredis_integer :age_with_default_via_lambda, default: ->(p) { Time.now.year - p.birthdate.year } + kredis_integer :age_with_default_via_lambda, default: ->(p) { Date.today.year - p.birthdate.year } kredis_decimal :salary kredis_decimal :salary_with_default_via_lambda, default: ->(p) { p.hourly_wage * 40 * 52 } kredis_datetime :last_seen_at + kredis_datetime :last_seen_at_with_default_via_lambda, default: ->(p) { p.last_login } kredis_float :height kredis_enum :morning, values: %w[ bright blue black ], default: "bright" kredis_slot :attention @@ -49,13 +50,17 @@ def name end def birthdate - Time.now - 25.years + Date.today - 25.years end def hourly_wage 15.26 end + def last_login + Time.new(2002, 10, 31, 2, 2, 2, "+02:00") + end + private def generate_key "some-generated-key" @@ -186,12 +191,16 @@ class AttributesTest < ActiveSupport::TestCase assert_equal "1.85", @person.height.to_s end - test "datetime" do + test "datetime with default proc value" do freeze_time @person.last_seen_at.value = Time.now assert_equal Time.now, @person.last_seen_at.value end + test "datetime" do + assert_equal Time.new(2002, 10, 31, 2, 2, 2, "+02:00"), @person.last_seen_at_with_default_via_lambda.value + end + test "slot" do assert @person.attention.reserve assert_not @person.attention.available? From de0e5b18a3534d7e59e498fdd6fb1637a6f0cf65 Mon Sep 17 00:00:00 2001 From: tleish Date: Thu, 9 Jun 2022 23:27:04 -0600 Subject: [PATCH 09/27] Add default to float --- lib/kredis/attributes.rb | 4 ++-- test/attributes_test.rb | 10 ++++++++++ 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/lib/kredis/attributes.rb b/lib/kredis/attributes.rb index ca36b82..1fc0682 100644 --- a/lib/kredis/attributes.rb +++ b/lib/kredis/attributes.rb @@ -30,8 +30,8 @@ def kredis_flag(name, key: nil, default: nil, config: :shared, after_change: nil end end - def kredis_float(name, key: nil, config: :shared, after_change: nil, expires_in: nil) - kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in + def kredis_float(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil) + kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in end def kredis_enum(name, key: nil, values:, default:, config: :shared, after_change: nil) diff --git a/test/attributes_test.rb b/test/attributes_test.rb index b017ef6..c23401b 100644 --- a/test/attributes_test.rb +++ b/test/attributes_test.rb @@ -26,6 +26,7 @@ class Person kredis_datetime :last_seen_at kredis_datetime :last_seen_at_with_default_via_lambda, default: ->(p) { p.last_login } kredis_float :height + kredis_float :height_with_default_via_lambda, default: ->(p) { p.anthropometry[:height] } kredis_enum :morning, values: %w[ bright blue black ], default: "bright" kredis_slot :attention kredis_slots :meetings, available: 3 @@ -52,6 +53,10 @@ def name def birthdate Date.today - 25.years end + + def anthropometry + { height: 73.2, weight: 182.4 } + end def hourly_wage 15.26 @@ -191,6 +196,11 @@ class AttributesTest < ActiveSupport::TestCase assert_equal "1.85", @person.height.to_s end + test "float with default proc value" do + assert_equal 73.2, @person.height_with_default_via_lambda.value + assert_equal "73.2", @person.height_with_default_via_lambda.to_s + end + test "datetime with default proc value" do freeze_time @person.last_seen_at.value = Time.now From 77499eda7ac3850dd943ddaa831bec7ba8b55721 Mon Sep 17 00:00:00 2001 From: tleish Date: Thu, 9 Jun 2022 23:35:56 -0600 Subject: [PATCH 10/27] Add default proc to enum --- lib/kredis/types/enum.rb | 2 +- test/attributes_test.rb | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/lib/kredis/types/enum.rb b/lib/kredis/types/enum.rb index b14403f..504dc9f 100644 --- a/lib/kredis/types/enum.rb +++ b/lib/kredis/types/enum.rb @@ -17,7 +17,7 @@ def value=(value) end def value - get || default + get || default_value end def reset diff --git a/test/attributes_test.rb b/test/attributes_test.rb index c23401b..33fd418 100644 --- a/test/attributes_test.rb +++ b/test/attributes_test.rb @@ -28,6 +28,7 @@ class Person kredis_float :height kredis_float :height_with_default_via_lambda, default: ->(p) { p.anthropometry[:height] } kredis_enum :morning, values: %w[ bright blue black ], default: "bright" + kredis_enum :eye_color_with_default_via_lambda, values: %w[ hazel blue brown ], default: ->(p) { { ha: 'hazel', bl: 'blue', br: 'brown' }[p.eye_color.to_sym] } kredis_slot :attention kredis_slots :meetings, available: 3 kredis_set :vacations @@ -57,6 +58,10 @@ def birthdate def anthropometry { height: 73.2, weight: 182.4 } end + + def eye_color + 'ha' + end def hourly_wage 15.26 @@ -273,6 +278,11 @@ class AttributesTest < ActiveSupport::TestCase assert @person.morning.bright? end + test "enum with default proc value" do + assert @person.eye_color_with_default_via_lambda.hazel? + end + + test "set" do @person.vacations.add "paris" @person.vacations.add "paris" From 46fa83ace987528883fcbfabefbd505f09422fc8 Mon Sep 17 00:00:00 2001 From: tleish Date: Fri, 10 Jun 2022 10:17:20 -0600 Subject: [PATCH 11/27] rename default_value method to default --- lib/kredis/type/json.rb | 2 ++ lib/kredis/types/enum.rb | 6 +++--- lib/kredis/types/flag.rb | 9 ++++----- lib/kredis/types/list.rb | 10 ++++++---- lib/kredis/types/proxy.rb | 18 +++++++++--------- lib/kredis/types/proxying.rb | 16 +++------------- lib/kredis/types/scalar.rb | 18 ++++++++++-------- test/attributes_test.rb | 2 +- test/types/scalar_test.rb | 2 +- 9 files changed, 39 insertions(+), 44 deletions(-) diff --git a/lib/kredis/type/json.rb b/lib/kredis/type/json.rb index 4183d5f..a6f55f4 100644 --- a/lib/kredis/type/json.rb +++ b/lib/kredis/type/json.rb @@ -8,6 +8,8 @@ def type end def cast_value(value) + return value.stringify_keys if value.is_a? Hash + JSON.load(value) end diff --git a/lib/kredis/types/enum.rb b/lib/kredis/types/enum.rb index 504dc9f..0df41d6 100644 --- a/lib/kredis/types/enum.rb +++ b/lib/kredis/types/enum.rb @@ -1,9 +1,9 @@ require "active_support/core_ext/object/inclusion" class Kredis::Types::Enum < Kredis::Types::Proxying - proxying :set, :get, :del, :exists? + proxying :set, :get, :del, :exists?, :default - attr_accessor :values, :default + attr_accessor :values def initialize(...) super @@ -17,7 +17,7 @@ def value=(value) end def value - get || default_value + get end def reset diff --git a/lib/kredis/types/flag.rb b/lib/kredis/types/flag.rb index 3d78787..bb64b96 100644 --- a/lib/kredis/types/flag.rb +++ b/lib/kredis/types/flag.rb @@ -8,7 +8,7 @@ def mark(expires_in: nil, force: true) end def marked? - exists? || default_value || false + exists? || default? end def remove @@ -17,10 +17,9 @@ def remove private - def set_and_get(value) - return false unless value + def default? + return false unless @default && @default.is_a?(Proc) && @default.call - mark - true + mark && true end end diff --git a/lib/kredis/types/list.rb b/lib/kredis/types/list.rb index 11f4e46..63dc3eb 100644 --- a/lib/kredis/types/list.rb +++ b/lib/kredis/types/list.rb @@ -1,10 +1,10 @@ class Kredis::Types::List < Kredis::Types::Proxying - proxying :lrange, :lrem, :lpush, :rpush, :exists?, :del + proxying :lrange, :lrem, :lpush, :rpush, :exists?, :del, :default attr_accessor :typed def elements - value = exists? ? lrange(0, -1) : default_value || [] + value = exists? ? lrange(0, -1) : default || [] strings_to_types(value, typed) end alias to_a elements @@ -26,8 +26,10 @@ def clear del end - def set_and_get(value) - append(value) + def default + return @default unless @default.is_a? Proc + + append(@default.call) elements end end diff --git a/lib/kredis/types/proxy.rb b/lib/kredis/types/proxy.rb index b18cea9..a5abd79 100644 --- a/lib/kredis/types/proxy.rb +++ b/lib/kredis/types/proxy.rb @@ -2,10 +2,11 @@ class Kredis::Types::Proxy require_relative "proxy/failsafe" include Failsafe - attr_accessor :redis, :key, :default + attr_accessor :redis, :key def initialize(redis, key, **options) @redis, @key = redis, key + @default = options.delete(:default) options.each { |key, value| send("#{key}=", value) } end @@ -19,7 +20,13 @@ def multi(&block) end def get - super || default_value + super || default + end + + def default + return @default unless @default.is_a? Proc + + @default.call.tap { |value| set(value) } end def method_missing(method, *args, **kwargs) @@ -37,11 +44,4 @@ def log_message(method, *args, **kwargs) { message: "#{method.upcase} #{key} #{args&.inspect} #{kwargs&.inspect}".chomp } end - - def default_value - case default - when Proc then default.call.tap { |value| set(value) } - else default - end - end end diff --git a/lib/kredis/types/proxying.rb b/lib/kredis/types/proxying.rb index 65508e9..8cb85cc 100644 --- a/lib/kredis/types/proxying.rb +++ b/lib/kredis/types/proxying.rb @@ -1,7 +1,7 @@ require "active_support/core_ext/module/delegation" class Kredis::Types::Proxying - attr_accessor :proxy, :redis, :key, :default + attr_accessor :proxy, :redis, :key def self.proxying(*commands) delegate *commands, to: :proxy @@ -9,7 +9,8 @@ def self.proxying(*commands) def initialize(redis, key, **options) @redis, @key = redis, key - @proxy = Kredis::Types::Proxy.new(redis, key) + @default = options.delete(:default) + @proxy = Kredis::Types::Proxy.new(redis, key, default: @default) options.each { |key, value| send("#{key}=", value) } end @@ -19,15 +20,4 @@ def failsafe(returning: nil, &block) private delegate :type_to_string, :string_to_type, :types_to_strings, :strings_to_types, to: :Kredis - - def default_value - return default unless default.is_a? Proc - - set_and_get(default.call) - end - - def set_and_get(value) - set value - get - end end diff --git a/lib/kredis/types/scalar.rb b/lib/kredis/types/scalar.rb index d313a05..8eb4186 100644 --- a/lib/kredis/types/scalar.rb +++ b/lib/kredis/types/scalar.rb @@ -8,17 +8,11 @@ def value=(value) end def value - value_after_casting = string_to_type(get, typed) - - if value_after_casting.nil? - string_to_type(default_value, typed) - else - value_after_casting - end + string_to_type(get, typed) end def to_s - get || default_value&.to_s + value&.to_s end def assigned? @@ -36,4 +30,12 @@ def expire_in(seconds) def expire_at(datetime) expireat datetime.to_i end + + private + + def default + return @default unless @default.is_a? Proc + + @default.call.tap { |default_value| self.value = default_value } + end end diff --git a/test/attributes_test.rb b/test/attributes_test.rb index 33fd418..2b35731 100644 --- a/test/attributes_test.rb +++ b/test/attributes_test.rb @@ -192,7 +192,7 @@ class AttributesTest < ActiveSupport::TestCase test "decimal with default proc value" do assert_equal 31_740.80.to_d, @person.salary_with_default_via_lambda.value - assert_equal "31740.8", @person.salary_with_default_via_lambda.to_s + assert_equal "0.317408e5", @person.salary_with_default_via_lambda.to_s end test "float" do diff --git a/test/types/scalar_test.rb b/test/types/scalar_test.rb index 03a3ddc..3912ac0 100644 --- a/test/types/scalar_test.rb +++ b/test/types/scalar_test.rb @@ -96,7 +96,7 @@ class ScalarTest < ActiveSupport::TestCase assert_equal "8", integer.value.to_s - json = Kredis.json "myscalar", default: { "one" => 1, "string" => "hello" } + json = Kredis.json "myscalar", default: { one: 1, string: "hello" } assert_equal({ "one" => 1, "string" => "hello" }, json.value) end From abdf95adb873ded82648c393275892b44b6d91eb Mon Sep 17 00:00:00 2001 From: tleish Date: Fri, 10 Jun 2022 11:05:38 -0600 Subject: [PATCH 12/27] Add default proc to set --- lib/kredis/attributes.rb | 4 ++-- lib/kredis/types.rb | 4 ++-- lib/kredis/types/list.rb | 12 +++++++----- lib/kredis/types/set.rb | 12 +++++++++++- test/attributes_test.rb | 19 ++++++++++++++++--- test/types/set_test.rb | 10 ++++++++++ 6 files changed, 48 insertions(+), 13 deletions(-) diff --git a/lib/kredis/attributes.rb b/lib/kredis/attributes.rb index 1fc0682..77f25fd 100644 --- a/lib/kredis/attributes.rb +++ b/lib/kredis/attributes.rb @@ -50,8 +50,8 @@ def kredis_unique_list(name, limit: nil, key: nil, default: nil, typed: :string, kredis_connection_with __method__, name, key, default: default, limit: limit, typed: typed, config: config, after_change: after_change end - def kredis_set(name, key: nil, typed: :string, config: :shared, after_change: nil) - kredis_connection_with __method__, name, key, typed: typed, config: config, after_change: after_change + def kredis_set(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 def kredis_slot(name, key: nil, config: :shared, after_change: nil) diff --git a/lib/kredis/types.rb b/lib/kredis/types.rb index 172346d..94cca84 100644 --- a/lib/kredis/types.rb +++ b/lib/kredis/types.rb @@ -67,8 +67,8 @@ def unique_list(key, default: nil, typed: :string, limit: nil, config: :shared, type_from(UniqueList, config, key, after_change: after_change, default: default, typed: typed, limit: limit) end - def set(key, typed: :string, config: :shared, after_change: nil) - type_from(Set, config, key, after_change: after_change, typed: typed) + def set(key, default: nil, typed: :string, config: :shared, after_change: nil) + type_from(Set, config, key, after_change: after_change, default: default, typed: typed) end def slot(key, config: :shared, after_change: nil) diff --git a/lib/kredis/types/list.rb b/lib/kredis/types/list.rb index 63dc3eb..820a0ad 100644 --- a/lib/kredis/types/list.rb +++ b/lib/kredis/types/list.rb @@ -26,10 +26,12 @@ def clear del end - def default - return @default unless @default.is_a? Proc + private - append(@default.call) - elements - end + def default + return @default unless @default.is_a? Proc + + append(@default.call) + elements + end end diff --git a/lib/kredis/types/set.rb b/lib/kredis/types/set.rb index dcb79e1..4a15cc8 100644 --- a/lib/kredis/types/set.rb +++ b/lib/kredis/types/set.rb @@ -4,7 +4,8 @@ class Kredis::Types::Set < Kredis::Types::Proxying attr_accessor :typed def members - strings_to_types(smembers || [], typed).sort + value = exists? ? smembers : default || [] + strings_to_types(value, typed).sort end alias to_a members @@ -39,4 +40,13 @@ def take def clear del end + + private + + def default + return @default unless @default.is_a? Proc + + add(@default.call) + members + end end diff --git a/test/attributes_test.rb b/test/attributes_test.rb index 2b35731..0d7d376 100644 --- a/test/attributes_test.rb +++ b/test/attributes_test.rb @@ -26,12 +26,13 @@ class Person kredis_datetime :last_seen_at kredis_datetime :last_seen_at_with_default_via_lambda, default: ->(p) { p.last_login } kredis_float :height - kredis_float :height_with_default_via_lambda, default: ->(p) { p.anthropometry[:height] } + kredis_float :height_with_default_via_lambda, default: ->(p) { JSON.parse(p.anthropometry)[:height] } kredis_enum :morning, values: %w[ bright blue black ], default: "bright" kredis_enum :eye_color_with_default_via_lambda, values: %w[ hazel blue brown ], default: ->(p) { { ha: 'hazel', bl: 'blue', br: 'brown' }[p.eye_color.to_sym] } kredis_slot :attention kredis_slots :meetings, available: 3 kredis_set :vacations + kredis_set :vacations_with_default_via_lambda, default: ->(p) { JSON.parse(p.vacation_destinations).map{ |location| location['city'] } } kredis_json :settings kredis_counter :amount kredis_counter :expiring_amount, expires_in: 1.second @@ -56,13 +57,13 @@ def birthdate end def anthropometry - { height: 73.2, weight: 182.4 } + { height: 73.2, weight: 182.4 }.to_json end def eye_color 'ha' end - + def hourly_wage 15.26 end @@ -71,6 +72,13 @@ def last_login Time.new(2002, 10, 31, 2, 2, 2, "+02:00") end + def vacation_destinations + [ + { city: 'Paris', region: 'Île-de-France', country: 'FR' }, + { city: 'Paris', region: 'Texas', country: 'US' } + ].to_json + end + private def generate_key "some-generated-key" @@ -298,6 +306,11 @@ class AttributesTest < ActiveSupport::TestCase assert_equal "paris", @person.vacations.take end + test "set with default proc value" do + assert_equal [ "Paris" ], @person.vacations_with_default_via_lambda.members + assert_equal [ "Paris" ], Kredis.redis.smembers("people:8:vacations_with_default_via_lambda") + end + test "json" do @person.settings.value = { "color" => "red", "count" => 2 } assert_equal({ "color" => "red", "count" => 2 }, @person.settings.value) diff --git a/test/types/set_test.rb b/test/types/set_test.rb index b536262..30ed182 100644 --- a/test/types/set_test.rb +++ b/test/types/set_test.rb @@ -88,4 +88,14 @@ class SetTest < ActiveSupport::TestCase @set.add(%w[ 1 2 3 ]) assert @set.exists? end + + test "default" do + @set = Kredis.set "mylist", default: %w[ 1 2 3 ] + assert_equal %w[ 1 2 3 ], @set.members + end + + test "default via proc" do + @set = Kredis.set "mylist", default: -> () { %w[ 3 3 1 2 ] } + assert_equal %w[ 1 2 3 ], @set.members + end end From 4933f6ca90239e0b026a2fddeab7b4a31f60e463 Mon Sep 17 00:00:00 2001 From: tleish Date: Fri, 10 Jun 2022 12:32:40 -0600 Subject: [PATCH 13/27] Add additional unit tests --- lib/kredis/attributes.rb | 4 ++-- lib/kredis/types/flag.rb | 2 +- test/attributes_test.rb | 3 ++- test/proxy_test.rb | 12 ++++++++++++ test/types/enum_test.rb | 5 +++++ test/types/flag_test.rb | 12 ++++++++++++ test/types/list_test.rb | 12 ++++++++++++ test/types/scalar_test.rb | 16 ++++++++++++++++ test/types/unique_list_test.rb | 12 ++++++++++++ 9 files changed, 74 insertions(+), 4 deletions(-) diff --git a/lib/kredis/attributes.rb b/lib/kredis/attributes.rb index 77f25fd..4947148 100644 --- a/lib/kredis/attributes.rb +++ b/lib/kredis/attributes.rb @@ -84,7 +84,7 @@ def kredis_connection_with(method, name, key, **options) if instance_variable_defined?(ivar_symbol) instance_variable_get(ivar_symbol) else - options.merge!(default: kredis_default_value_evaluated(options[:default])) if options[:default] + options.merge!(default: kredis_default_evaluated(options[:default])) if options[:default] new_type = Kredis.send(type, kredis_key_evaluated(key) || kredis_key_for_attribute(name), **options) instance_variable_set ivar_symbol, after_change ? enrich_after_change_with_record_access(new_type, after_change) : new_type @@ -117,7 +117,7 @@ def enrich_after_change_with_record_access(type, original_after_change) end end - def kredis_default_value_evaluated(default) + def kredis_default_evaluated(default) case default when Proc then Proc.new { default.call(self) } when Symbol then send(default) diff --git a/lib/kredis/types/flag.rb b/lib/kredis/types/flag.rb index bb64b96..1cc5c44 100644 --- a/lib/kredis/types/flag.rb +++ b/lib/kredis/types/flag.rb @@ -18,7 +18,7 @@ def remove private def default? - return false unless @default && @default.is_a?(Proc) && @default.call + return !!@default unless @default.is_a?(Proc) && @default.call mark && true end diff --git a/test/attributes_test.rb b/test/attributes_test.rb index 0d7d376..6de4448 100644 --- a/test/attributes_test.rb +++ b/test/attributes_test.rb @@ -26,7 +26,7 @@ class Person kredis_datetime :last_seen_at kredis_datetime :last_seen_at_with_default_via_lambda, default: ->(p) { p.last_login } kredis_float :height - kredis_float :height_with_default_via_lambda, default: ->(p) { JSON.parse(p.anthropometry)[:height] } + kredis_float :height_with_default_via_lambda, default: ->(p) { JSON.parse(p.anthropometry)['height'] } kredis_enum :morning, values: %w[ bright blue black ], default: "bright" kredis_enum :eye_color_with_default_via_lambda, values: %w[ hazel blue brown ], default: ->(p) { { ha: 'hazel', bl: 'blue', br: 'brown' }[p.eye_color.to_sym] } kredis_slot :attention @@ -210,6 +210,7 @@ class AttributesTest < ActiveSupport::TestCase end test "float with default proc value" do + assert_not_equal 73.2, Kredis.redis.get("people:8:height_with_default_via_lambda") assert_equal 73.2, @person.height_with_default_via_lambda.value assert_equal "73.2", @person.height_with_default_via_lambda.to_s end diff --git a/test/proxy_test.rb b/test/proxy_test.rb index 94c8a1f..faf7164 100644 --- a/test/proxy_test.rb +++ b/test/proxy_test.rb @@ -19,4 +19,16 @@ class ProxyTest < ActiveSupport::TestCase assert @proxy.set("two") stub_redis_down(@proxy) { assert_nil @proxy.set("two") } end + + test "default" do + @proxy = Kredis.proxy "something", default: "one" + + assert_equal "one", @proxy.get + end + + test "default via proc" do + @proxy = Kredis.proxy "something", default: ->() { "one" } + + assert_equal "one", @proxy.get + end end diff --git a/test/types/enum_test.rb b/test/types/enum_test.rb index 84c587b..29c26d8 100644 --- a/test/types/enum_test.rb +++ b/test/types/enum_test.rb @@ -7,6 +7,11 @@ class EnumTest < ActiveSupport::TestCase assert_equal "one", @enum.value end + test "default via proc" do + @enum = Kredis.enum "myenum", values: %w[ one two three ], default: ->() { "two" } + assert_equal "two", @enum.value + end + test "predicates" do assert @enum.one? diff --git a/test/types/flag_test.rb b/test/types/flag_test.rb index b1df179..fb07d2a 100644 --- a/test/types/flag_test.rb +++ b/test/types/flag_test.rb @@ -38,4 +38,16 @@ class FlagTest < ActiveSupport::TestCase sleep 0.6.seconds assert_not @flag.marked? end + + test "default" do + @flag = Kredis.flag "myflag", default: true + + assert @flag.marked? + end + + test "default via proc" do + @flag = Kredis.flag "myflag", default: ->() { true } + + assert @flag.marked? + end end diff --git a/test/types/list_test.rb b/test/types/list_test.rb index 7e7aa5f..fee3aed 100644 --- a/test/types/list_test.rb +++ b/test/types/list_test.rb @@ -57,4 +57,16 @@ class ListTest < ActiveSupport::TestCase @list.append(%w[ 1 2 3 ]) assert @list.exists? end + + test "default" do + @list = Kredis.list "mylist", default: %w[ 1 2 3 ] + + assert_equal %w[ 1 2 3 ], @list.elements + end + + test "default via proc" do + @list = Kredis.list "mylist", default: ->() { %w[ 1 2 3 ] } + + assert_equal %w[ 1 2 3 ], @list.elements + end end diff --git a/test/types/scalar_test.rb b/test/types/scalar_test.rb index 3912ac0..8a526c3 100644 --- a/test/types/scalar_test.rb +++ b/test/types/scalar_test.rb @@ -100,6 +100,22 @@ class ScalarTest < ActiveSupport::TestCase assert_equal({ "one" => 1, "string" => "hello" }, json.value) end + test "default via proc" do + integer = Kredis.scalar "myscalar", typed: :integer, default: ->() { 8 } + assert_equal 8, integer.value + + integer.value = 5 + assert_equal 5, integer.value + + integer.clear + assert_equal 8, integer.value + + integer.clear + + json = Kredis.json "myscalar", default: ->() { { one: 1, string: "hello" } } + assert_equal({ "one" => 1, "string" => "hello" }, json.value) + end + test "returns default when failing open" do integer = Kredis.scalar "myscalar", typed: :integer, default: 8 integer.value = 42 diff --git a/test/types/unique_list_test.rb b/test/types/unique_list_test.rb index 17762db..aa94193 100644 --- a/test/types/unique_list_test.rb +++ b/test/types/unique_list_test.rb @@ -73,4 +73,16 @@ class UniqueListTest < ActiveSupport::TestCase @list.prepend(%w[ 1 1 1 ]) assert_equal %w[ 1 ], @list.elements end + + test "default" do + @list = Kredis.unique_list "myuniquelist", default: %w[ 1 2 3 ] + + assert_equal %w[ 1 2 3 ], @list.elements + end + + test "default via proc" do + @list = Kredis.unique_list "myuniquelist", default: ->() { %w[ 1 2 3 3 ] } + + assert_equal %w[ 1 2 3 ], @list.elements + end end From 321d33c9ef34d7bf645fb6c73bab6ca1c17e584c Mon Sep 17 00:00:00 2001 From: tleish Date: Fri, 10 Jun 2022 17:14:37 -0600 Subject: [PATCH 14/27] Add default proc to json and counter --- lib/kredis/attributes.rb | 8 ++++---- lib/kredis/types.rb | 4 ++-- lib/kredis/types/counter.rb | 26 ++++++++++++++++++-------- lib/kredis/types/enum.rb | 2 +- lib/kredis/types/proxy.rb | 2 +- lib/kredis/types/proxying.rb | 8 +++++++- lib/kredis/types/scalar.rb | 2 +- test/attributes_test.rb | 18 ++++++++++++++++++ test/types/counter_test.rb | 28 ++++++++++++++++++++++++++++ test/types/scalar_test.rb | 5 +++-- 10 files changed, 83 insertions(+), 20 deletions(-) diff --git a/lib/kredis/attributes.rb b/lib/kredis/attributes.rb index 4947148..02d69b1 100644 --- a/lib/kredis/attributes.rb +++ b/lib/kredis/attributes.rb @@ -38,8 +38,8 @@ def kredis_enum(name, key: nil, values:, default:, config: :shared, after_change kredis_connection_with __method__, name, key, values: values, default: default, config: config, after_change: after_change end - def kredis_json(name, key: nil, config: :shared, after_change: nil, expires_in: nil) - kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in + def kredis_json(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil) + kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in end def kredis_list(name, key: nil, default: nil, typed: :string, config: :shared, after_change: nil) @@ -62,8 +62,8 @@ def kredis_slots(name, available:, key: nil, config: :shared, after_change: nil) kredis_connection_with __method__, name, key, available: available, config: config, after_change: after_change end - def kredis_counter(name, key: nil, config: :shared, after_change: nil, expires_in: nil) - kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in + def kredis_counter(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil) + kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in end def kredis_hash(name, key: nil, typed: :string, config: :shared, after_change: nil) diff --git a/lib/kredis/types.rb b/lib/kredis/types.rb index 94cca84..a39dd5e 100644 --- a/lib/kredis/types.rb +++ b/lib/kredis/types.rb @@ -39,8 +39,8 @@ def json(key, default: nil, config: :shared, after_change: nil, expires_in: nil) end - def counter(key, expires_in: nil, config: :shared, after_change: nil) - type_from(Counter, config, key, after_change: after_change, expires_in: expires_in) + def counter(key, expires_in: nil, default: nil, config: :shared, after_change: nil) + type_from(Counter, config, key, after_change: after_change, default: default, expires_in: expires_in) end def cycle(key, values:, expires_in: nil, config: :shared, after_change: nil) diff --git a/lib/kredis/types/counter.rb b/lib/kredis/types/counter.rb index bcfcf62..652ccfb 100644 --- a/lib/kredis/types/counter.rb +++ b/lib/kredis/types/counter.rb @@ -4,17 +4,13 @@ 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 - end[-1] + set_default unless exists? + incrby by end def decrement(by: 1) - multi do |pipeline| - pipeline.set 0, ex: expires_in, nx: true - pipeline.decrby by - end[-1] + set_default unless exists? + decrby by end def value @@ -24,4 +20,18 @@ def value def reset del end + + private + + def value=(new_value) + set(new_value.to_i, ex: expires_in, nx: true) + value + end + + def default + return self.value = @default unless @default.is_a? Proc + + @default.call.tap { |value| self.value = value } + end + alias set_default default end diff --git a/lib/kredis/types/enum.rb b/lib/kredis/types/enum.rb index 0df41d6..6951d48 100644 --- a/lib/kredis/types/enum.rb +++ b/lib/kredis/types/enum.rb @@ -1,7 +1,7 @@ require "active_support/core_ext/object/inclusion" class Kredis::Types::Enum < Kredis::Types::Proxying - proxying :set, :get, :del, :exists?, :default + proxying :set, :get, :del, :exists? attr_accessor :values diff --git a/lib/kredis/types/proxy.rb b/lib/kredis/types/proxy.rb index a5abd79..a3f06f8 100644 --- a/lib/kredis/types/proxy.rb +++ b/lib/kredis/types/proxy.rb @@ -26,7 +26,7 @@ def get def default return @default unless @default.is_a? Proc - @default.call.tap { |value| set(value) } + @default.call.tap { |value| set(value) unless value.nil? } end def method_missing(method, *args, **kwargs) diff --git a/lib/kredis/types/proxying.rb b/lib/kredis/types/proxying.rb index 8cb85cc..2fb466f 100644 --- a/lib/kredis/types/proxying.rb +++ b/lib/kredis/types/proxying.rb @@ -10,7 +10,7 @@ def self.proxying(*commands) def initialize(redis, key, **options) @redis, @key = redis, key @default = options.delete(:default) - @proxy = Kredis::Types::Proxy.new(redis, key, default: @default) + @proxy = Kredis::Types::Proxy.new(redis, key, default: ->() { default }) options.each { |key, value| send("#{key}=", value) } end @@ -20,4 +20,10 @@ def failsafe(returning: nil, &block) private delegate :type_to_string, :string_to_type, :types_to_strings, :strings_to_types, to: :Kredis + + def default + return @default unless @default.is_a? Proc + + @default.call.tap { |value| set(value) unless value.nil? } + end end diff --git a/lib/kredis/types/scalar.rb b/lib/kredis/types/scalar.rb index 8eb4186..f012fd4 100644 --- a/lib/kredis/types/scalar.rb +++ b/lib/kredis/types/scalar.rb @@ -36,6 +36,6 @@ def expire_at(datetime) def default return @default unless @default.is_a? Proc - @default.call.tap { |default_value| self.value = default_value } + string_to_type(@default.call, typed).tap { |default_value| self.value = default_value } end end diff --git a/test/attributes_test.rb b/test/attributes_test.rb index 6de4448..ff8dae6 100644 --- a/test/attributes_test.rb +++ b/test/attributes_test.rb @@ -34,7 +34,9 @@ class Person kredis_set :vacations kredis_set :vacations_with_default_via_lambda, default: ->(p) { JSON.parse(p.vacation_destinations).map{ |location| location['city'] } } kredis_json :settings + kredis_json :settings_with_default_via_lambda, default: ->(p) { JSON.parse(p.anthropometry).merge(eye_color: p.eye_color) } kredis_counter :amount + kredis_counter :amount_with_default_via_lambda, default: ->(p) { Date.today.year - p.birthdate.year } kredis_counter :expiring_amount, expires_in: 1.second kredis_string :temporary_password, expires_in: 1.second kredis_hash :high_scores, typed: :integer @@ -317,6 +319,13 @@ class AttributesTest < ActiveSupport::TestCase assert_equal({ "color" => "red", "count" => 2 }, @person.settings.value) end + test "json with default proc value" do + expect = {"height"=>73.2, "weight"=>182.4, "eye_color"=>"ha"} + assert_equal expect, @person.settings_with_default_via_lambda.value + assert_equal expect.to_s, Kredis.redis.get("people:8:settings_with_default_via_lambda") + end + + test "counter" do @person.amount.increment assert_equal 1, @person.amount.value @@ -331,6 +340,15 @@ class AttributesTest < ActiveSupport::TestCase end end + test "counter with default proc value" do + @person.amount_with_default_via_lambda.increment + assert_equal 26, @person.amount_with_default_via_lambda.value + @person.amount_with_default_via_lambda.decrement + assert_equal 25, @person.amount_with_default_via_lambda.value + end + + + test "hash" do @person.high_scores.update(space_invaders: 100, pong: 42) assert_equal({ "space_invaders" => 100, "pong" => 42 }, @person.high_scores.to_h) diff --git a/test/types/counter_test.rb b/test/types/counter_test.rb index 821b7b5..85e90a1 100644 --- a/test/types/counter_test.rb +++ b/test/types/counter_test.rb @@ -78,4 +78,32 @@ class CounterTest < ActiveSupport::TestCase @counter.increment assert @counter.exists? end + + test "default value" do + @counter = Kredis.counter "mycounter", default: 10 + assert_equal 10, @counter.value + end + + test "expiring counter with default" do + @counter = Kredis.counter "mycounter", default: ->() { 10 }, expires_in: 1.second + + @counter.increment + assert_equal 11, @counter.value + + sleep 0.5.seconds + + @counter.increment + assert_equal 12, @counter.value + + sleep 0.6.seconds + + assert_equal 10, @counter.value + end + + test "default via proc" do + @counter = Kredis.counter "mycounter", default: ->() { 10 } + assert_equal 10, @counter.value + @counter.decrement + assert_equal 9, @counter.value + end end diff --git a/test/types/scalar_test.rb b/test/types/scalar_test.rb index 8a526c3..f0e4c77 100644 --- a/test/types/scalar_test.rb +++ b/test/types/scalar_test.rb @@ -95,12 +95,13 @@ class ScalarTest < ActiveSupport::TestCase assert_equal 8, integer.value assert_equal "8", integer.value.to_s - + integer.clear + json = Kredis.json "myscalar", default: { one: 1, string: "hello" } assert_equal({ "one" => 1, "string" => "hello" }, json.value) end - test "default via proc" do + test "default via proc for nil" do integer = Kredis.scalar "myscalar", typed: :integer, default: ->() { 8 } assert_equal 8, integer.value From 411d04aeab389ac6252b13f6f17d212041dbcd57 Mon Sep 17 00:00:00 2001 From: tleish Date: Fri, 10 Jun 2022 18:36:32 -0600 Subject: [PATCH 15/27] Add default proc to hash and boolean --- lib/kredis/attributes.rb | 8 ++++---- lib/kredis/types.rb | 4 ++-- lib/kredis/types/counter.rb | 13 ++++++------- lib/kredis/types/hash.rb | 11 +++++++++++ lib/kredis/types/proxying.rb | 3 +++ test/attributes_test.rb | 16 ++++++++++++++-- test/types/hash_test.rb | 11 +++++++++++ 7 files changed, 51 insertions(+), 15 deletions(-) diff --git a/lib/kredis/attributes.rb b/lib/kredis/attributes.rb index 02d69b1..a4d7128 100644 --- a/lib/kredis/attributes.rb +++ b/lib/kredis/attributes.rb @@ -66,12 +66,12 @@ 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_hash(name, key: nil, typed: :string, config: :shared, after_change: nil) - kredis_connection_with __method__, name, key, typed: typed, config: config, after_change: after_change + 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 - def kredis_boolean(name, key: nil, config: :shared, after_change: nil, expires_in: nil) - kredis_connection_with __method__, name, key, config: config, after_change: after_change, expires_in: expires_in + def kredis_boolean(name, key: nil, default: nil, config: :shared, after_change: nil, expires_in: nil) + kredis_connection_with __method__, name, key, default: default, config: config, after_change: after_change, expires_in: expires_in end private diff --git a/lib/kredis/types.rb b/lib/kredis/types.rb index a39dd5e..385c31f 100644 --- a/lib/kredis/types.rb +++ b/lib/kredis/types.rb @@ -55,8 +55,8 @@ def enum(key, values:, default:, config: :shared, after_change: nil) type_from(Enum, config, key, after_change: after_change, values: values, default: default) end - def hash(key, typed: :string, config: :shared, after_change: nil) - type_from(Hash, config, key, after_change: after_change, typed: typed) + def hash(key, typed: :string, default: nil, config: :shared, after_change: nil) + type_from(Hash, config, key, after_change: after_change, default: default, typed: typed) end def list(key, default: nil, typed: :string, config: :shared, after_change: nil) diff --git a/lib/kredis/types/counter.rb b/lib/kredis/types/counter.rb index 652ccfb..8a4900c 100644 --- a/lib/kredis/types/counter.rb +++ b/lib/kredis/types/counter.rb @@ -1,15 +1,15 @@ class Kredis::Types::Counter < Kredis::Types::Proxying proxying :multi, :set, :incrby, :decrby, :get, :del, :exists? + before_method :set_default, :value, :increment, :decrement + attr_accessor :expires_in def increment(by: 1) - set_default unless exists? incrby by end def decrement(by: 1) - set_default unless exists? decrby by end @@ -25,13 +25,12 @@ def reset def value=(new_value) set(new_value.to_i, ex: expires_in, nx: true) - value end - def default - return self.value = @default unless @default.is_a? Proc + def set_default + return if exists? - @default.call.tap { |value| self.value = value } + value = @default.is_a?(Proc) ? @default.call : @default + self.value = value end - alias set_default default end diff --git a/lib/kredis/types/hash.rb b/lib/kredis/types/hash.rb index bc1b990..18b7e50 100644 --- a/lib/kredis/types/hash.rb +++ b/lib/kredis/types/hash.rb @@ -3,6 +3,8 @@ class Kredis::Types::Hash < Kredis::Types::Proxying proxying :hget, :hset, :hmget, :hdel, :hgetall, :hkeys, :hvals, :del, :exists? + before_method :set_default, :[], :[]=, :entries, :keys, :values, :values_at, :delete + attr_accessor :typed def [](key) @@ -42,4 +44,13 @@ def keys def values strings_to_types(hvals || [], typed) end + + private + + def set_default + return if exists? + + value = @default.is_a?(Proc) ? @default.call : @default + update(value) unless value.nil? + end end diff --git a/lib/kredis/types/proxying.rb b/lib/kredis/types/proxying.rb index 2fb466f..1a8f325 100644 --- a/lib/kredis/types/proxying.rb +++ b/lib/kredis/types/proxying.rb @@ -1,6 +1,9 @@ require "active_support/core_ext/module/delegation" +require "kredis/types/before_method_hook" class Kredis::Types::Proxying + extend Kredis::Types::BeforeMethodHook + attr_accessor :proxy, :redis, :key def self.proxying(*commands) diff --git a/test/attributes_test.rb b/test/attributes_test.rb index ff8dae6..d8e789f 100644 --- a/test/attributes_test.rb +++ b/test/attributes_test.rb @@ -40,7 +40,9 @@ class Person kredis_counter :expiring_amount, expires_in: 1.second kredis_string :temporary_password, expires_in: 1.second kredis_hash :high_scores, typed: :integer + 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 } def self.name "Person" @@ -66,6 +68,10 @@ def eye_color 'ha' end + def scores + [10, 28, 2, 7].to_json + end + def hourly_wage 15.26 end @@ -347,8 +353,6 @@ class AttributesTest < ActiveSupport::TestCase assert_equal 25, @person.amount_with_default_via_lambda.value end - - test "hash" do @person.high_scores.update(space_invaders: 100, pong: 42) assert_equal({ "space_invaders" => 100, "pong" => 42 }, @person.high_scores.to_h) @@ -356,6 +360,10 @@ class AttributesTest < ActiveSupport::TestCase assert_equal([ 100, 42 ], @person.high_scores.values) end + test "hash with default proc value" do + assert_equal({ "high_score" => 28 }, @person.high_scores_with_default_via_lambda.to_h) + end + test "boolean" do @person.onboarded.value = true assert @person.onboarded.value @@ -364,6 +372,10 @@ class AttributesTest < ActiveSupport::TestCase refute @person.onboarded.value end + test "boolean with default proc value" do + assert @person.adult_with_default_via_lambda.value + end + test "missing id to constrain key" do assert_raise NotImplementedError do MissingIdPerson.new.anything diff --git a/test/types/hash_test.rb b/test/types/hash_test.rb index 3f607f9..26c5e84 100644 --- a/test/types/hash_test.rb +++ b/test/types/hash_test.rb @@ -90,4 +90,15 @@ class HashTest < ActiveSupport::TestCase @hash[:key] = :value assert @hash.exists? end + + test "default value" do + @hash = Kredis.hash "myhash", typed: :integer, default: { space_invaders: "100", pong: "42" } + assert_equal(%w[ space_invaders pong ], @hash.keys) + assert_equal({ "space_invaders" => 100, "pong" => 42 }, @hash.to_h) + end + + test "default via proc" do + @hash = Kredis.hash "myhash", typed: :integer, default: ->() { { space_invaders: "100", pong: "42" } } + assert_equal({ "space_invaders" => 100, "pong" => 42 }, @hash.to_h) + end end From ed3cbd8ea385ff317da84028536b6b22fc2b2d36 Mon Sep 17 00:00:00 2001 From: tleish Date: Fri, 10 Jun 2022 18:36:50 -0600 Subject: [PATCH 16/27] Add before_method_hook --- lib/kredis/types/before_method_hook.rb | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 lib/kredis/types/before_method_hook.rb diff --git a/lib/kredis/types/before_method_hook.rb b/lib/kredis/types/before_method_hook.rb new file mode 100644 index 0000000..fb593be --- /dev/null +++ b/lib/kredis/types/before_method_hook.rb @@ -0,0 +1,11 @@ +module Kredis::Types::BeforeMethodHook + def before_method(wrapper_method, *methods) + prepend(@method_wrapper = Module.new) unless @method_wrapper + methods.each do |method_name| + @method_wrapper.send(:define_method, method_name) do |*args, &block| + send wrapper_method + super(*args, &block) + end + end + end +end From 10308363b073827b14d1c82c528fdb3610577466 Mon Sep 17 00:00:00 2001 From: tleish Date: Fri, 10 Jun 2022 21:56:59 -0600 Subject: [PATCH 17/27] Fix for ruby 3 --- lib/kredis/types/before_method_hook.rb | 4 ++-- lib/kredis/types/hash.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/kredis/types/before_method_hook.rb b/lib/kredis/types/before_method_hook.rb index fb593be..1bdf5de 100644 --- a/lib/kredis/types/before_method_hook.rb +++ b/lib/kredis/types/before_method_hook.rb @@ -2,9 +2,9 @@ module Kredis::Types::BeforeMethodHook def before_method(wrapper_method, *methods) prepend(@method_wrapper = Module.new) unless @method_wrapper methods.each do |method_name| - @method_wrapper.send(:define_method, method_name) do |*args, &block| + @method_wrapper.send(:define_method, method_name) do |*args, **kwargs, &block| send wrapper_method - super(*args, &block) + super(*args, **kwargs, &block) end end end diff --git a/lib/kredis/types/hash.rb b/lib/kredis/types/hash.rb index 18b7e50..4b64118 100644 --- a/lib/kredis/types/hash.rb +++ b/lib/kredis/types/hash.rb @@ -51,6 +51,6 @@ def set_default return if exists? value = @default.is_a?(Proc) ? @default.call : @default - update(value) unless value.nil? + update(**value) unless value.nil? end end From 1bb9288fc9915391229621393f32684db61e6a9f Mon Sep 17 00:00:00 2001 From: tleish Date: Sun, 19 Jun 2022 22:39:32 -0600 Subject: [PATCH 18/27] refactor Kredis::Types::BeforeMethodsHook#before_methods --- .../types/{before_method_hook.rb => before_methods_hook.rb} | 6 +++--- lib/kredis/types/counter.rb | 2 +- lib/kredis/types/hash.rb | 2 +- lib/kredis/types/proxying.rb | 4 ++-- 4 files changed, 7 insertions(+), 7 deletions(-) rename lib/kredis/types/{before_method_hook.rb => before_methods_hook.rb} (68%) diff --git a/lib/kredis/types/before_method_hook.rb b/lib/kredis/types/before_methods_hook.rb similarity index 68% rename from lib/kredis/types/before_method_hook.rb rename to lib/kredis/types/before_methods_hook.rb index 1bdf5de..0315163 100644 --- a/lib/kredis/types/before_method_hook.rb +++ b/lib/kredis/types/before_methods_hook.rb @@ -1,9 +1,9 @@ -module Kredis::Types::BeforeMethodHook - def before_method(wrapper_method, *methods) +module Kredis::Types::BeforeMethodsHook + def before_methods(*methods, invoke:) prepend(@method_wrapper = Module.new) unless @method_wrapper methods.each do |method_name| @method_wrapper.send(:define_method, method_name) do |*args, **kwargs, &block| - send wrapper_method + send invoke super(*args, **kwargs, &block) end end diff --git a/lib/kredis/types/counter.rb b/lib/kredis/types/counter.rb index 8a4900c..3d5a915 100644 --- a/lib/kredis/types/counter.rb +++ b/lib/kredis/types/counter.rb @@ -1,7 +1,7 @@ class Kredis::Types::Counter < Kredis::Types::Proxying proxying :multi, :set, :incrby, :decrby, :get, :del, :exists? - before_method :set_default, :value, :increment, :decrement + before_methods :value, :increment, :decrement, invoke: :set_default attr_accessor :expires_in diff --git a/lib/kredis/types/hash.rb b/lib/kredis/types/hash.rb index 4b64118..4ad256c 100644 --- a/lib/kredis/types/hash.rb +++ b/lib/kredis/types/hash.rb @@ -3,7 +3,7 @@ class Kredis::Types::Hash < Kredis::Types::Proxying proxying :hget, :hset, :hmget, :hdel, :hgetall, :hkeys, :hvals, :del, :exists? - before_method :set_default, :[], :[]=, :entries, :keys, :values, :values_at, :delete + before_methods :[], :[]=, :entries, :keys, :values, :values_at, :delete, invoke: :set_default attr_accessor :typed diff --git a/lib/kredis/types/proxying.rb b/lib/kredis/types/proxying.rb index 1a8f325..017bb45 100644 --- a/lib/kredis/types/proxying.rb +++ b/lib/kredis/types/proxying.rb @@ -1,8 +1,8 @@ require "active_support/core_ext/module/delegation" -require "kredis/types/before_method_hook" +require "kredis/types/before_methods_hook" class Kredis::Types::Proxying - extend Kredis::Types::BeforeMethodHook + extend Kredis::Types::BeforeMethodsHook attr_accessor :proxy, :redis, :key From 10ceac9567b897dc5670bc803d6ee1d7fc66124f Mon Sep 17 00:00:00 2001 From: tleish Date: Mon, 20 Jun 2022 10:00:39 -0600 Subject: [PATCH 19/27] code review updates --- lib/kredis/type/json.rb | 8 +++++--- lib/kredis/types/counter.rb | 10 +--------- lib/kredis/types/enum.rb | 6 +++++- lib/kredis/types/flag.rb | 12 ++++++------ lib/kredis/types/hash.rb | 6 +----- lib/kredis/types/list.rb | 15 +++++++-------- lib/kredis/types/proxy.rb | 11 ----------- lib/kredis/types/proxying.rb | 12 ++++++++---- lib/kredis/types/scalar.rb | 11 ++++------- lib/kredis/types/set.rb | 13 ++++++------- test/attributes_test.rb | 14 +++----------- test/proxy_test.rb | 12 ------------ test/types/flag_test.rb | 17 +++++++++++++++-- test/types/scalar_test.rb | 2 +- 14 files changed, 62 insertions(+), 87 deletions(-) diff --git a/lib/kredis/type/json.rb b/lib/kredis/type/json.rb index a6f55f4..77b8a8c 100644 --- a/lib/kredis/type/json.rb +++ b/lib/kredis/type/json.rb @@ -8,9 +8,11 @@ def type end def cast_value(value) - return value.stringify_keys if value.is_a? Hash - - JSON.load(value) + if value.is_a? Hash + value.stringify_keys + else + JSON.load(value) + end end def serialize(value) diff --git a/lib/kredis/types/counter.rb b/lib/kredis/types/counter.rb index 3d5a915..07fe5fc 100644 --- a/lib/kredis/types/counter.rb +++ b/lib/kredis/types/counter.rb @@ -22,15 +22,7 @@ def reset end private - - def value=(new_value) - set(new_value.to_i, ex: expires_in, nx: true) - end - def set_default - return if exists? - - value = @default.is_a?(Proc) ? @default.call : @default - self.value = value + set(default.to_i, ex: expires_in, nx: true) unless exists? end end diff --git a/lib/kredis/types/enum.rb b/lib/kredis/types/enum.rb index 6951d48..c253139 100644 --- a/lib/kredis/types/enum.rb +++ b/lib/kredis/types/enum.rb @@ -17,7 +17,7 @@ def value=(value) end def value - get + get || initialize_with_default end def reset @@ -31,4 +31,8 @@ def define_predicates_for_values define_singleton_method("#{defined_value}!") { self.value = defined_value } end end + + def initialize_with_default + default { |default_value| self.value = default_value } + end end diff --git a/lib/kredis/types/flag.rb b/lib/kredis/types/flag.rb index 1cc5c44..d38e87a 100644 --- a/lib/kredis/types/flag.rb +++ b/lib/kredis/types/flag.rb @@ -8,7 +8,7 @@ def mark(expires_in: nil, force: true) end def marked? - exists? || default? + exists? || exists_after_default_value? end def remove @@ -16,10 +16,10 @@ def remove end private - - def default? - return !!@default unless @default.is_a?(Proc) && @default.call - - mark && true + def exists_after_default_value? + !!default do |default_value| + mark if default_value + !!default_value + end end end diff --git a/lib/kredis/types/hash.rb b/lib/kredis/types/hash.rb index 4ad256c..90d3bc4 100644 --- a/lib/kredis/types/hash.rb +++ b/lib/kredis/types/hash.rb @@ -46,11 +46,7 @@ def values end private - def set_default - return if exists? - - value = @default.is_a?(Proc) ? @default.call : @default - update(**value) unless value.nil? + update(**default) unless exists? || default.nil? end end diff --git a/lib/kredis/types/list.rb b/lib/kredis/types/list.rb index 820a0ad..50d1539 100644 --- a/lib/kredis/types/list.rb +++ b/lib/kredis/types/list.rb @@ -1,10 +1,10 @@ class Kredis::Types::List < Kredis::Types::Proxying - proxying :lrange, :lrem, :lpush, :rpush, :exists?, :del, :default + proxying :lrange, :lrem, :lpush, :rpush, :exists?, :del attr_accessor :typed def elements - value = exists? ? lrange(0, -1) : default || [] + value = exists? ? lrange(0, -1) : initialize_with_default || [] strings_to_types(value, typed) end alias to_a elements @@ -27,11 +27,10 @@ def clear end private - - def default - return @default unless @default.is_a? Proc - - append(@default.call) - elements + def initialize_with_default + default do |default_value| + append(default_value) + elements + end end end diff --git a/lib/kredis/types/proxy.rb b/lib/kredis/types/proxy.rb index a3f06f8..172ff2f 100644 --- a/lib/kredis/types/proxy.rb +++ b/lib/kredis/types/proxy.rb @@ -6,7 +6,6 @@ class Kredis::Types::Proxy def initialize(redis, key, **options) @redis, @key = redis, key - @default = options.delete(:default) options.each { |key, value| send("#{key}=", value) } end @@ -19,16 +18,6 @@ def multi(&block) end end - def get - super || default - end - - def default - return @default unless @default.is_a? Proc - - @default.call.tap { |value| set(value) unless value.nil? } - end - def method_missing(method, *args, **kwargs) Kredis.instrument :proxy, **log_message(method, *args, **kwargs) do failsafe do diff --git a/lib/kredis/types/proxying.rb b/lib/kredis/types/proxying.rb index 017bb45..8fa3474 100644 --- a/lib/kredis/types/proxying.rb +++ b/lib/kredis/types/proxying.rb @@ -13,7 +13,7 @@ def self.proxying(*commands) def initialize(redis, key, **options) @redis, @key = redis, key @default = options.delete(:default) - @proxy = Kredis::Types::Proxy.new(redis, key, default: ->() { default }) + @proxy = Kredis::Types::Proxy.new(redis, key) options.each { |key, value| send("#{key}=", value) } end @@ -25,8 +25,12 @@ def failsafe(returning: nil, &block) delegate :type_to_string, :string_to_type, :types_to_strings, :strings_to_types, to: :Kredis def default - return @default unless @default.is_a? Proc - - @default.call.tap { |value| set(value) unless value.nil? } + if @default.is_a?(Proc) && block_given? + yield(@default.call) + elsif @default.is_a?(Proc) + @default.call + else + @default + end end end diff --git a/lib/kredis/types/scalar.rb b/lib/kredis/types/scalar.rb index f012fd4..c8a0658 100644 --- a/lib/kredis/types/scalar.rb +++ b/lib/kredis/types/scalar.rb @@ -1,14 +1,14 @@ class Kredis::Types::Scalar < Kredis::Types::Proxying proxying :set, :get, :exists?, :del, :expire, :expireat - attr_accessor :typed, :default, :expires_in + attr_accessor :typed, :expires_in def value=(value) set type_to_string(value, typed), ex: expires_in end def value - string_to_type(get, typed) + string_to_type(get || initialize_with_default, typed) end def to_s @@ -32,10 +32,7 @@ def expire_at(datetime) end private - - def default - return @default unless @default.is_a? Proc - - string_to_type(@default.call, typed).tap { |default_value| self.value = default_value } + def initialize_with_default + default { |default_value| self.value = default_value } end end diff --git a/lib/kredis/types/set.rb b/lib/kredis/types/set.rb index 4a15cc8..bac48b0 100644 --- a/lib/kredis/types/set.rb +++ b/lib/kredis/types/set.rb @@ -4,7 +4,7 @@ class Kredis::Types::Set < Kredis::Types::Proxying attr_accessor :typed def members - value = exists? ? smembers : default || [] + value = exists? ? smembers : initialize_with_default || [] strings_to_types(value, typed).sort end alias to_a members @@ -42,11 +42,10 @@ def clear end private - - def default - return @default unless @default.is_a? Proc - - add(@default.call) - members + def initialize_with_default + default do |default_value| + add(default_value) + members + end end end diff --git a/test/attributes_test.rb b/test/attributes_test.rb index d8e789f..4465d75 100644 --- a/test/attributes_test.rb +++ b/test/attributes_test.rb @@ -4,10 +4,9 @@ class Person include Kredis::Attributes - kredis_proxy :anything, default: "default" + kredis_proxy :anything kredis_proxy :nothing, key: "something:else" - kredis_proxy :something, key: ->(p) { "person:#{p.id}:something" }, default: :name - kredis_proxy :something_with_default_via_lambda, default: ->(p) { p.name.downcase } + kredis_proxy :something, key: ->(p) { "person:#{p.id}:something" } 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 @@ -104,7 +103,6 @@ class AttributesTest < ActiveSupport::TestCase setup { @person = Person.new } test "proxy" do - assert_equal "default", @person.anything.get @person.anything.set "something" assert_equal "something", @person.anything.get end @@ -115,16 +113,10 @@ class AttributesTest < ActiveSupport::TestCase end test "proxy with custom proc key" do - assert_equal "Jason", @person.something.get @person.something.set "everything" assert_equal "everything", Kredis.redis.get("person:8:something") end - test "proxy with default value" do - assert_equal "jason", @person.something_with_default_via_lambda.get - assert_equal "jason", Kredis.redis.get("people:8:something_with_default_via_lambda") - end - test "list" do @person.names.append(%w[ david kasper ]) assert_equal %w[ david kasper ], @person.names.elements @@ -328,7 +320,7 @@ class AttributesTest < ActiveSupport::TestCase test "json with default proc value" do expect = {"height"=>73.2, "weight"=>182.4, "eye_color"=>"ha"} assert_equal expect, @person.settings_with_default_via_lambda.value - assert_equal expect.to_s, Kredis.redis.get("people:8:settings_with_default_via_lambda") + assert_equal expect.to_json, Kredis.redis.get("people:8:settings_with_default_via_lambda") end diff --git a/test/proxy_test.rb b/test/proxy_test.rb index faf7164..94c8a1f 100644 --- a/test/proxy_test.rb +++ b/test/proxy_test.rb @@ -19,16 +19,4 @@ class ProxyTest < ActiveSupport::TestCase assert @proxy.set("two") stub_redis_down(@proxy) { assert_nil @proxy.set("two") } end - - test "default" do - @proxy = Kredis.proxy "something", default: "one" - - assert_equal "one", @proxy.get - end - - test "default via proc" do - @proxy = Kredis.proxy "something", default: ->() { "one" } - - assert_equal "one", @proxy.get - end end diff --git a/test/types/flag_test.rb b/test/types/flag_test.rb index fb07d2a..8b3846c 100644 --- a/test/types/flag_test.rb +++ b/test/types/flag_test.rb @@ -1,4 +1,5 @@ require "test_helper" +require "active_support/core_ext/numeric/time" class FlagTest < ActiveSupport::TestCase setup { @flag = Kredis.flag "myflag" } @@ -39,15 +40,27 @@ class FlagTest < ActiveSupport::TestCase assert_not @flag.marked? end - test "default" do + test "default true" do @flag = Kredis.flag "myflag", default: true assert @flag.marked? end - test "default via proc" do + test "default false" do + @flag = Kredis.flag "myflag", default: false + + assert_not @flag.marked? + end + + test "default true via proc" do @flag = Kredis.flag "myflag", default: ->() { true } assert @flag.marked? end + + test "default false via proc" do + @flag = Kredis.flag "myflag", default: ->() { false } + + assert_not @flag.marked? + end end diff --git a/test/types/scalar_test.rb b/test/types/scalar_test.rb index f0e4c77..2144205 100644 --- a/test/types/scalar_test.rb +++ b/test/types/scalar_test.rb @@ -101,7 +101,7 @@ class ScalarTest < ActiveSupport::TestCase assert_equal({ "one" => 1, "string" => "hello" }, json.value) end - test "default via proc for nil" do + test "default via proc" do integer = Kredis.scalar "myscalar", typed: :integer, default: ->() { 8 } assert_equal 8, integer.value From 7e85720b6e473d623463141b6391e4fe85c43fc5 Mon Sep 17 00:00:00 2001 From: tleish Date: Thu, 14 Jul 2022 22:13:17 -0600 Subject: [PATCH 20/27] fix code review feedback --- lib/kredis/types/counter.rb | 2 +- lib/kredis/types/flag.rb | 7 +++---- lib/kredis/types/proxying.rb | 16 ++++++++-------- 3 files changed, 12 insertions(+), 13 deletions(-) diff --git a/lib/kredis/types/counter.rb b/lib/kredis/types/counter.rb index 07fe5fc..34a5f0a 100644 --- a/lib/kredis/types/counter.rb +++ b/lib/kredis/types/counter.rb @@ -23,6 +23,6 @@ def reset private def set_default - set(default.to_i, ex: expires_in, nx: true) unless exists? + set(default.to_i, ex: expires_in, nx: true) end end diff --git a/lib/kredis/types/flag.rb b/lib/kredis/types/flag.rb index d38e87a..95085b5 100644 --- a/lib/kredis/types/flag.rb +++ b/lib/kredis/types/flag.rb @@ -17,9 +17,8 @@ def remove private def exists_after_default_value? - !!default do |default_value| - mark if default_value - !!default_value - end + default_boolean = !!default + mark if default_boolean + default_boolean end end diff --git a/lib/kredis/types/proxying.rb b/lib/kredis/types/proxying.rb index 8fa3474..09786ce 100644 --- a/lib/kredis/types/proxying.rb +++ b/lib/kredis/types/proxying.rb @@ -12,7 +12,7 @@ def self.proxying(*commands) def initialize(redis, key, **options) @redis, @key = redis, key - @default = options.delete(:default) + @default_value = options.delete(:default) @proxy = Kredis::Types::Proxy.new(redis, key) options.each { |key, value| send("#{key}=", value) } end @@ -25,12 +25,12 @@ def failsafe(returning: nil, &block) delegate :type_to_string, :string_to_type, :types_to_strings, :strings_to_types, to: :Kredis def default - if @default.is_a?(Proc) && block_given? - yield(@default.call) - elsif @default.is_a?(Proc) - @default.call - else - @default - end + @default ||= if @default_value.is_a?(Proc) && block_given? + yield(@default_value.call) + elsif @default_value.is_a?(Proc) + @default_value.call + else + @default_value + end end end From 180f57cb498287e62a6445aa0de389ad03db749a Mon Sep 17 00:00:00 2001 From: David Heinemeier Hansson Date: Fri, 15 Jul 2022 15:33:56 -0700 Subject: [PATCH 21/27] Match indentation --- lib/kredis/attributes.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/kredis/attributes.rb b/lib/kredis/attributes.rb index a4d7128..001a349 100644 --- a/lib/kredis/attributes.rb +++ b/lib/kredis/attributes.rb @@ -117,11 +117,11 @@ def enrich_after_change_with_record_access(type, original_after_change) end end - def kredis_default_evaluated(default) - case default - when Proc then Proc.new { default.call(self) } - when Symbol then send(default) - else default + def kredis_default_evaluated(default) + case default + when Proc then Proc.new { default.call(self) } + when Symbol then send(default) + else default + end end - end end From bf2ba58eb404750579ea18f83ad31de8710eb2dc Mon Sep 17 00:00:00 2001 From: tleish Date: Tue, 19 Jul 2022 18:39:30 -0600 Subject: [PATCH 22/27] create custom callnx method and refactor --- lib/kredis/types/before_methods_hook.rb | 11 -------- lib/kredis/types/counter.rb | 18 +++++++----- lib/kredis/types/enum.rb | 13 ++++++--- lib/kredis/types/flag.rb | 9 +----- lib/kredis/types/hash.rb | 29 +++++++++++-------- lib/kredis/types/list.rb | 37 +++++++++++++++++-------- lib/kredis/types/proxy.rb | 17 ++++++++++++ lib/kredis/types/proxying.rb | 22 +++++++-------- lib/kredis/types/scalar.rb | 14 ++++++---- lib/kredis/types/set.rb | 26 ++++++++++------- lib/kredis/types/unique_list.rb | 5 ++++ test/attributes_test.rb | 5 ---- test/types/enum_test.rb | 5 ++++ test/types/flag_test.rb | 25 ----------------- test/types/hash_test.rb | 26 ++++++++++++++++- test/types/list_test.rb | 33 ++++++++++++++++++++++ test/types/scalar_test.rb | 8 ++++++ test/types/set_test.rb | 30 ++++++++++++++++++++ test/types/unique_list_test.rb | 8 +++++- 19 files changed, 230 insertions(+), 111 deletions(-) delete mode 100644 lib/kredis/types/before_methods_hook.rb diff --git a/lib/kredis/types/before_methods_hook.rb b/lib/kredis/types/before_methods_hook.rb deleted file mode 100644 index 0315163..0000000 --- a/lib/kredis/types/before_methods_hook.rb +++ /dev/null @@ -1,11 +0,0 @@ -module Kredis::Types::BeforeMethodsHook - def before_methods(*methods, invoke:) - prepend(@method_wrapper = Module.new) unless @method_wrapper - methods.each do |method_name| - @method_wrapper.send(:define_method, method_name) do |*args, **kwargs, &block| - send invoke - super(*args, **kwargs, &block) - end - end - end -end diff --git a/lib/kredis/types/counter.rb b/lib/kredis/types/counter.rb index 34a5f0a..8990ace 100644 --- a/lib/kredis/types/counter.rb +++ b/lib/kredis/types/counter.rb @@ -1,20 +1,24 @@ class Kredis::Types::Counter < Kredis::Types::Proxying proxying :multi, :set, :incrby, :decrby, :get, :del, :exists? - before_methods :value, :increment, :decrement, invoke: :set_default - attr_accessor :expires_in def increment(by: 1) - incrby by + multi do + initialize_with_default + incrby by + end[-1] end def decrement(by: 1) - decrby by + multi do + initialize_with_default + decrby by + end[-1] end def value - get.to_i + (get || default).to_i end def reset @@ -22,7 +26,7 @@ def reset end private - def set_default - set(default.to_i, ex: expires_in, nx: true) + def initialize_with_default + set default.to_i, ex: expires_in, nx: true end end diff --git a/lib/kredis/types/enum.rb b/lib/kredis/types/enum.rb index c253139..8d27ee1 100644 --- a/lib/kredis/types/enum.rb +++ b/lib/kredis/types/enum.rb @@ -1,7 +1,7 @@ require "active_support/core_ext/object/inclusion" class Kredis::Types::Enum < Kredis::Types::Proxying - proxying :set, :get, :del, :exists? + proxying :multi, :set, :get, :del, :exists? attr_accessor :values @@ -17,7 +17,10 @@ def value=(value) end def value - get || initialize_with_default + multi do + initialize_with_default + get + end.last end def reset @@ -32,7 +35,9 @@ def define_predicates_for_values end end - def initialize_with_default - default { |default_value| self.value = default_value } + def set_default(value) + if validated_choice = value.presence_in(values) + set validated_choice, nx: true + end end end diff --git a/lib/kredis/types/flag.rb b/lib/kredis/types/flag.rb index 95085b5..fef9c46 100644 --- a/lib/kredis/types/flag.rb +++ b/lib/kredis/types/flag.rb @@ -8,17 +8,10 @@ def mark(expires_in: nil, force: true) end def marked? - exists? || exists_after_default_value? + exists? end def remove del end - - private - def exists_after_default_value? - default_boolean = !!default - mark if default_boolean - default_boolean - end end diff --git a/lib/kredis/types/hash.rb b/lib/kredis/types/hash.rb index 90d3bc4..bc2859c 100644 --- a/lib/kredis/types/hash.rb +++ b/lib/kredis/types/hash.rb @@ -1,30 +1,35 @@ require "active_support/core_ext/hash" class Kredis::Types::Hash < Kredis::Types::Proxying - proxying :hget, :hset, :hmget, :hdel, :hgetall, :hkeys, :hvals, :del, :exists? - - before_methods :[], :[]=, :entries, :keys, :values, :values_at, :delete, invoke: :set_default + proxying :hset, :hdel, :hgetall, :del, :exists?, :multi, :callnx attr_accessor :typed def [](key) - string_to_type(hget(key), typed) + string_to_type(entries[key], typed) end def []=(key, value) update key => value end + def update(**entries) - hset entries.transform_values{ |val| type_to_string(val, typed) } if entries.flatten.any? + multi do + initialize_with_default + hset entries.transform_values{ |val| type_to_string(val, typed) } if entries.flatten.any? + end end def values_at(*keys) - strings_to_types(hmget(keys) || [], typed) + strings_to_types(entries.values_at(*keys) || [], typed) end def delete(*keys) - hdel keys if keys.flatten.any? + multi do + initialize_with_default + hdel keys if keys.flatten.any? + end end def remove @@ -33,20 +38,20 @@ def remove alias clear remove def entries - (hgetall || {}).transform_values { |val| string_to_type(val, typed) }.with_indifferent_access + (hgetall.presence || default || {}).transform_values { |val| string_to_type(val, typed) }.with_indifferent_access end alias to_h entries def keys - hkeys || [] + entries.keys || [] end def values - strings_to_types(hvals || [], typed) + strings_to_types(entries.values || [], typed) end private - def set_default - update(**default) unless exists? || default.nil? + def set_default(entries) + callnx(:hset, entries.transform_values{ |val| type_to_string(val, typed) }) end end diff --git a/lib/kredis/types/list.rb b/lib/kredis/types/list.rb index c80e118..60e12f2 100644 --- a/lib/kredis/types/list.rb +++ b/lib/kredis/types/list.rb @@ -1,24 +1,42 @@ class Kredis::Types::List < Kredis::Types::Proxying - proxying :lrange, :lrem, :lpush, :rpush, :exists?, :del + proxying :lrange, :lrem, :lpush, :rpush, :exists?, :del, :multi, :callnx attr_accessor :typed def elements - value = exists? ? lrange(0, -1) : initialize_with_default || [] - strings_to_types(value, typed) + values = multi do + initialize_with_default + lrange(0, -1) + end[-1] + strings_to_types(values || [], typed) end alias to_a elements def remove(*elements) - types_to_strings(elements, typed).each { |element| lrem 0, element } + return if elements.empty? + + multi do + initialize_with_default + types_to_strings(elements, typed).each { |element| lrem 0, element } + end end def prepend(*elements) - lpush types_to_strings(elements, typed) if elements.flatten.any? + return if elements.empty? + + multi do + initialize_with_default + lpush types_to_strings(elements, typed) if elements.flatten.any? + end end def append(*elements) - rpush types_to_strings(elements, typed) if elements.flatten.any? + return if elements.empty? + + multi do + initialize_with_default + rpush types_to_strings(elements, typed) if elements.flatten.any? + end end alias << append @@ -27,10 +45,7 @@ def clear end private - def initialize_with_default - default do |default_value| - append(default_value) - elements - end + def set_default(elements) + callnx(:rpush, types_to_strings(Array(elements), typed)) end end diff --git a/lib/kredis/types/proxy.rb b/lib/kredis/types/proxy.rb index b609a6f..9f3cd80 100644 --- a/lib/kredis/types/proxy.rb +++ b/lib/kredis/types/proxy.rb @@ -12,6 +12,8 @@ def initialize(redis, key, **options) end def multi(*args, **kwargs, &block) + return block.call if self.pipeline # return and execute block for nested multi pipeline + redis.multi(*args, **kwargs) do |pipeline| self.pipeline = pipeline block.call @@ -28,6 +30,21 @@ def method_missing(method, *args, **kwargs) end end + CALLNX = <<~LUA + if redis.call("exists", KEYS[1]) == 0 then + redis.call("%{method}", KEYS[1], unpack(ARGV)) + end + LUA + def callnx(method, values) + safe_method_name = method.to_s.gsub(/[^a-z_]/,'_') + cmd = CALLNX % { method: safe_method_name } + Kredis.instrument :proxy, **log_message(:callnx, *([safe_method_name] + Array(values))) do + failsafe do + redis.eval cmd, Array(key), Array(values).flatten + end + end + end + private def redis pipeline || @redis diff --git a/lib/kredis/types/proxying.rb b/lib/kredis/types/proxying.rb index 09786ce..ea5ef19 100644 --- a/lib/kredis/types/proxying.rb +++ b/lib/kredis/types/proxying.rb @@ -1,9 +1,6 @@ require "active_support/core_ext/module/delegation" -require "kredis/types/before_methods_hook" class Kredis::Types::Proxying - extend Kredis::Types::BeforeMethodsHook - attr_accessor :proxy, :redis, :key def self.proxying(*commands) @@ -12,7 +9,7 @@ def self.proxying(*commands) def initialize(redis, key, **options) @redis, @key = redis, key - @default_value = options.delete(:default) + @default = options.delete(:default) @proxy = Kredis::Types::Proxy.new(redis, key) options.each { |key, value| send("#{key}=", value) } end @@ -25,12 +22,15 @@ def failsafe(returning: nil, &block) delegate :type_to_string, :string_to_type, :types_to_strings, :strings_to_types, to: :Kredis def default - @default ||= if @default_value.is_a?(Proc) && block_given? - yield(@default_value.call) - elsif @default_value.is_a?(Proc) - @default_value.call - else - @default_value - end + if @default.is_a?(Proc) + @default.call + else + @default + end + end + + def initialize_with_default + default_value = default + set_default(default_value) if default_value.present? end end diff --git a/lib/kredis/types/scalar.rb b/lib/kredis/types/scalar.rb index c8a0658..d48211a 100644 --- a/lib/kredis/types/scalar.rb +++ b/lib/kredis/types/scalar.rb @@ -1,5 +1,5 @@ class Kredis::Types::Scalar < Kredis::Types::Proxying - proxying :set, :get, :exists?, :del, :expire, :expireat + proxying :set, :get, :exists?, :del, :expire, :expireat, :multi attr_accessor :typed, :expires_in @@ -8,11 +8,15 @@ def value=(value) end def value - string_to_type(get || initialize_with_default, typed) + get_value = multi do + initialize_with_default + get + end[-1] + string_to_type(get_value, typed) end def to_s - value&.to_s + value.to_s end def assigned? @@ -32,7 +36,7 @@ def expire_at(datetime) end private - def initialize_with_default - default { |default_value| self.value = default_value } + def set_default(value) + set type_to_string(value, typed), ex: expires_in, nx: true end end diff --git a/lib/kredis/types/set.rb b/lib/kredis/types/set.rb index 6a20bdb..74e23b5 100644 --- a/lib/kredis/types/set.rb +++ b/lib/kredis/types/set.rb @@ -1,21 +1,30 @@ 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?, :callnx attr_accessor :typed def members - value = exists? ? smembers : initialize_with_default || [] - strings_to_types(value, typed).sort + values = multi do + initialize_with_default + smembers + end[-1] + strings_to_types(values || [], typed).sort end alias to_a members def add(*members) - sadd types_to_strings(members, typed) if members.flatten.any? + multi do + initialize_with_default + sadd types_to_strings(members, typed) if members.flatten.any? + end end alias << add def remove(*members) - srem types_to_strings(members, typed) if members.flatten.any? + multi do + initialize_with_default + srem types_to_strings(members, typed) if members.flatten.any? + end end def replace(*members) @@ -42,10 +51,7 @@ def clear end private - def initialize_with_default - default do |default_value| - add(default_value) - members - end + def set_default(members) + callnx(:sadd, types_to_strings(Array(members), typed)) end end diff --git a/lib/kredis/types/unique_list.rb b/lib/kredis/types/unique_list.rb index eb4bbcc..1b9f775 100644 --- a/lib/kredis/types/unique_list.rb +++ b/lib/kredis/types/unique_list.rb @@ -26,4 +26,9 @@ def append(elements) end end alias << append + + private + def set_default(elements) + callnx(:rpush, types_to_strings(Array(elements).uniq, typed)) + end end diff --git a/test/attributes_test.rb b/test/attributes_test.rb index 4465d75..4aaaa05 100644 --- a/test/attributes_test.rb +++ b/test/attributes_test.rb @@ -15,7 +15,6 @@ class Person kredis_unique_list :skills_with_default_via_lambda, default: ->(p) { ["Random", "Random", p.name] } kredis_flag :special kredis_flag :temporary_special, expires_in: 1.second - kredis_flag :special_with_default_via_lambda, default: ->(p) { p.id == 8 } kredis_string :address kredis_string :address_with_default_via_lambda, default: ->(p) { p.name } kredis_integer :age @@ -159,10 +158,6 @@ class AttributesTest < ActiveSupport::TestCase assert_not @person.special? end - test "flag with default proc value" do - assert @person.special_with_default_via_lambda? - end - test "string" do assert_not @person.address.assigned? diff --git a/test/types/enum_test.rb b/test/types/enum_test.rb index 29c26d8..bf7bedb 100644 --- a/test/types/enum_test.rb +++ b/test/types/enum_test.rb @@ -12,6 +12,11 @@ class EnumTest < ActiveSupport::TestCase assert_equal "two", @enum.value end + test "does not set default for invalid option" do + @enum = Kredis.enum "myenum", values: %w[ one two three ], default: ->() { "four" } + assert_nil @enum.value + end + test "predicates" do assert @enum.one? diff --git a/test/types/flag_test.rb b/test/types/flag_test.rb index 8b3846c..b1df179 100644 --- a/test/types/flag_test.rb +++ b/test/types/flag_test.rb @@ -1,5 +1,4 @@ require "test_helper" -require "active_support/core_ext/numeric/time" class FlagTest < ActiveSupport::TestCase setup { @flag = Kredis.flag "myflag" } @@ -39,28 +38,4 @@ class FlagTest < ActiveSupport::TestCase sleep 0.6.seconds assert_not @flag.marked? end - - test "default true" do - @flag = Kredis.flag "myflag", default: true - - assert @flag.marked? - end - - test "default false" do - @flag = Kredis.flag "myflag", default: false - - assert_not @flag.marked? - end - - test "default true via proc" do - @flag = Kredis.flag "myflag", default: ->() { true } - - assert @flag.marked? - end - - test "default false via proc" do - @flag = Kredis.flag "myflag", default: ->() { false } - - assert_not @flag.marked? - end end diff --git a/test/types/hash_test.rb b/test/types/hash_test.rb index 26c5e84..0ea05b4 100644 --- a/test/types/hash_test.rb +++ b/test/types/hash_test.rb @@ -36,6 +36,9 @@ class HashTest < ActiveSupport::TestCase @hash.delete("key") assert_equal({ "key2" => "value2", "key3" => "value3" }, @hash.to_h) + @hash.delete("bogus") + assert_equal({ "key2" => "value2", "key3" => "value3" }, @hash.to_h) + @hash.delete("key2", "key3") assert_equal({}, @hash.to_h) end @@ -93,8 +96,29 @@ class HashTest < ActiveSupport::TestCase test "default value" do @hash = Kredis.hash "myhash", typed: :integer, default: { space_invaders: "100", pong: "42" } - assert_equal(%w[ space_invaders pong ], @hash.keys) assert_equal({ "space_invaders" => 100, "pong" => 42 }, @hash.to_h) + assert_equal(%w[ space_invaders pong ], @hash.keys) + assert_equal([100, 42], @hash.values) + assert_equal(100, @hash['space_invaders']) + assert_equal([100, 42], @hash.values_at('space_invaders', 'pong')) + end + + test "update with default" do + @hash = Kredis.hash "myhash", typed: :integer, default: { space_invaders: "100", pong: '42' } + @hash.update(ping: '54') + assert_equal(%w[ space_invaders pong ping ], @hash.keys) + end + + test "[]= with default" do + @hash = Kredis.hash "myhash", typed: :integer, default: { space_invaders: "100", pong: '42' } + @hash[:ping] = '54' + assert_equal(%w[ space_invaders pong ping ], @hash.keys) + end + + test "delete with default" do + @hash = Kredis.hash "myhash", typed: :integer, default: { space_invaders: "100", pong: '42' } + @hash.delete(:pong) + assert_equal(%w[ space_invaders ], @hash.keys) end test "default via proc" do diff --git a/test/types/list_test.rb b/test/types/list_test.rb index fee3aed..d18c9bf 100644 --- a/test/types/list_test.rb +++ b/test/types/list_test.rb @@ -64,9 +64,42 @@ class ListTest < ActiveSupport::TestCase assert_equal %w[ 1 2 3 ], @list.elements end + test "default empty array" do + @list = Kredis.list "mylist", default: [] + + assert_equal [], @list.elements + end + + test "default with nil" do + @list = Kredis.list "mylist", default: nil + + assert_equal [], @list.elements + end + test "default via proc" do @list = Kredis.list "mylist", default: ->() { %w[ 1 2 3 ] } assert_equal %w[ 1 2 3 ], @list.elements end + + test "append with default" do + @list = Kredis.list "mylist", default: ->() { %w[ 1 ] } + @list.append(%w[ 2 3 ]) + @list.append(4) + assert_equal %w[ 1 2 3 4 ], @list.elements + end + + test "prepend with default" do + @list = Kredis.list "mylist", default: ->() { %w[ 1 ] } + @list.prepend(%w[ 2 3 ]) + @list.prepend(4) + assert_equal %w[ 4 3 2 1 ], @list.elements + end + + test "remove with default" do + @list = Kredis.list "mylist", default: ->() { %w[ 1 2 3 4 ] } + @list.remove(%w[ 1 2 ]) + @list.remove(3) + assert_equal %w[ 4 ], @list.elements + end end diff --git a/test/types/scalar_test.rb b/test/types/scalar_test.rb index 2144205..edf91f9 100644 --- a/test/types/scalar_test.rb +++ b/test/types/scalar_test.rb @@ -117,6 +117,14 @@ class ScalarTest < ActiveSupport::TestCase assert_equal({ "one" => 1, "string" => "hello" }, json.value) end + test "does not cache proc results after clear" do + hex = Kredis.scalar "myscalar", default: ->() { SecureRandom.hex } + original_default_value = hex.value + assert_equal original_default_value, hex.value + hex.clear + assert_not_equal original_default_value, hex.value + end + test "returns default when failing open" do integer = Kredis.scalar "myscalar", typed: :integer, default: 8 integer.value = 42 diff --git a/test/types/set_test.rb b/test/types/set_test.rb index 30ed182..0a88958 100644 --- a/test/types/set_test.rb +++ b/test/types/set_test.rb @@ -94,8 +94,38 @@ class SetTest < ActiveSupport::TestCase assert_equal %w[ 1 2 3 ], @set.members end + test "default is an empty array" do + @set = Kredis.set "mylist", default: [] + assert_equal [], @set.members + end + + test "default is nil" do + @set = Kredis.set "mylist", default: nil + assert_equal [], @set.members + end + test "default via proc" do @set = Kredis.set "mylist", default: -> () { %w[ 3 3 1 2 ] } assert_equal %w[ 1 2 3 ], @set.members end + + test "add with default" do + @set = Kredis.set "mylist", typed: :integer, default: -> () { %w[ 1 2 3 ] } + @set.add(%w[ 5 6 7 ]) + assert_equal [1, 2, 3, 5, 6, 7], @set.members + end + + test "remove with default" do + @set = Kredis.set "mylist", default: -> () { %w[ 1 2 3 4 ] } + @set.remove(%w[ 2 3 ]) + @set.remove("1") + assert_equal %w[ 4 ], @set.members + end + + test "replace with default" do + @set = Kredis.set "mylist", typed: :integer, default: -> () { %w[ 1 2 3 ] } + @set.add(%w[ 5 6 7 ]) + @set.replace(%w[ 8 9 10 ]) + assert_equal [1, 2, 3, 8, 9, 10], @set.members + end end diff --git a/test/types/unique_list_test.rb b/test/types/unique_list_test.rb index aa94193..fc03b2f 100644 --- a/test/types/unique_list_test.rb +++ b/test/types/unique_list_test.rb @@ -81,8 +81,14 @@ class UniqueListTest < ActiveSupport::TestCase end test "default via proc" do - @list = Kredis.unique_list "myuniquelist", default: ->() { %w[ 1 2 3 3 ] } + @list = Kredis.unique_list "myuniquelist", default: ->() { %w[ 1 2 3 3 ] } assert_equal %w[ 1 2 3 ], @list.elements end + + test "prepend with default" do + @list = Kredis.unique_list "myuniquelist", default: %w[ 1 2 3 ] + @list.prepend(%w[ 6 7 8 ]) + assert_equal %w[ 8 7 6 1 2 3 ], @list.elements + end end From d0146ee86c75868c9bbeb0a0c04a9c5c880361a2 Mon Sep 17 00:00:00 2001 From: tleish Date: Tue, 19 Jul 2022 18:54:18 -0600 Subject: [PATCH 23/27] add additional default options to set --- lib/kredis/types/proxy.rb | 4 ++-- lib/kredis/types/set.rb | 15 ++++++++++++--- 2 files changed, 14 insertions(+), 5 deletions(-) diff --git a/lib/kredis/types/proxy.rb b/lib/kredis/types/proxy.rb index 9f3cd80..8d61c51 100644 --- a/lib/kredis/types/proxy.rb +++ b/lib/kredis/types/proxy.rb @@ -36,8 +36,8 @@ def method_missing(method, *args, **kwargs) end LUA def callnx(method, values) - safe_method_name = method.to_s.gsub(/[^a-z_]/,'_') - cmd = CALLNX % { method: safe_method_name } + safe_method_name = method.to_s.gsub(/[^a-z_]/, '_') + cmd = format(CALLNX, method: safe_method_name) Kredis.instrument :proxy, **log_message(:callnx, *([safe_method_name] + Array(values))) do failsafe do redis.eval cmd, Array(key), Array(values).flatten diff --git a/lib/kredis/types/set.rb b/lib/kredis/types/set.rb index 74e23b5..c6289d9 100644 --- a/lib/kredis/types/set.rb +++ b/lib/kredis/types/set.rb @@ -35,15 +35,24 @@ def replace(*members) end def include?(member) - sismember type_to_string(member, typed) + multi do + initialize_with_default + sismember type_to_string(member, typed) + end[-1] end def size - scard.to_i + multi do + initialize_with_default + scard + end[-1].to_i end def take - spop + multi do + initialize_with_default + spop + end[-1] end def clear From 90d9208fe09afa5b25121a746d752dab12d95b52 Mon Sep 17 00:00:00 2001 From: tleish Date: Tue, 19 Jul 2022 19:00:51 -0600 Subject: [PATCH 24/27] move primary #set_default method to Kredis::Types::Proxying --- lib/kredis/types/proxying.rb | 4 ++++ lib/kredis/types/scalar.rb | 5 ----- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/lib/kredis/types/proxying.rb b/lib/kredis/types/proxying.rb index ea5ef19..900b632 100644 --- a/lib/kredis/types/proxying.rb +++ b/lib/kredis/types/proxying.rb @@ -33,4 +33,8 @@ def initialize_with_default default_value = default set_default(default_value) if default_value.present? end + + def set_default(value) + set type_to_string(value, typed), ex: expires_in, nx: true + end end diff --git a/lib/kredis/types/scalar.rb b/lib/kredis/types/scalar.rb index d48211a..a716913 100644 --- a/lib/kredis/types/scalar.rb +++ b/lib/kredis/types/scalar.rb @@ -34,9 +34,4 @@ def expire_in(seconds) def expire_at(datetime) expireat datetime.to_i end - - private - def set_default(value) - set type_to_string(value, typed), ex: expires_in, nx: true - end end From 5c4664b1e767c73453d5d860e57c5dad6840aa38 Mon Sep 17 00:00:00 2001 From: tleish Date: Tue, 19 Jul 2022 19:02:31 -0600 Subject: [PATCH 25/27] updated enum multi block to use standar [-1] pattern instead of .last --- lib/kredis/types/enum.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/kredis/types/enum.rb b/lib/kredis/types/enum.rb index 8d27ee1..16ed492 100644 --- a/lib/kredis/types/enum.rb +++ b/lib/kredis/types/enum.rb @@ -20,7 +20,7 @@ def value multi do initialize_with_default get - end.last + end[-1] end def reset From ddf555f3f6cd5cdc4291d4bc674e3a6a05dddf05 Mon Sep 17 00:00:00 2001 From: tleish Date: Wed, 20 Jul 2022 17:39:46 -0600 Subject: [PATCH 26/27] refactor init_default_in_multi to not use multi if default not defined --- lib/kredis/types/counter.rb | 18 ++++++++--------- lib/kredis/types/enum.rb | 13 ++---------- lib/kredis/types/hash.rb | 16 ++++++++------- lib/kredis/types/list.rb | 24 +++++++++-------------- lib/kredis/types/proxying.rb | 15 ++++++++++---- lib/kredis/types/scalar.rb | 11 ++++++----- lib/kredis/types/set.rb | 38 ++++++++++++++---------------------- test/types/enum_test.rb | 6 ++++-- test/types/hash_test.rb | 19 +++++++++++++++--- test/types/list_test.rb | 22 +++++++++++++++++---- test/types/set_test.rb | 21 ++++++++++++++++---- 11 files changed, 115 insertions(+), 88 deletions(-) diff --git a/lib/kredis/types/counter.rb b/lib/kredis/types/counter.rb index 8990ace..dd2befb 100644 --- a/lib/kredis/types/counter.rb +++ b/lib/kredis/types/counter.rb @@ -4,17 +4,11 @@ class Kredis::Types::Counter < Kredis::Types::Proxying attr_accessor :expires_in def increment(by: 1) - multi do - initialize_with_default - incrby by - end[-1] + init_default_in_multi { incrby by } end def decrement(by: 1) - multi do - initialize_with_default - decrby by - end[-1] + init_default_in_multi { decrby by } end def value @@ -26,7 +20,11 @@ def reset end private - def initialize_with_default - set default.to_i, ex: expires_in, nx: true + def set_default(value) + set value.to_i, ex: expires_in, nx: true + end + + def default + super.to_i end end diff --git a/lib/kredis/types/enum.rb b/lib/kredis/types/enum.rb index 16ed492..046a808 100644 --- a/lib/kredis/types/enum.rb +++ b/lib/kredis/types/enum.rb @@ -1,7 +1,7 @@ require "active_support/core_ext/object/inclusion" class Kredis::Types::Enum < Kredis::Types::Proxying - proxying :multi, :set, :get, :del, :exists? + proxying :set, :get, :del, :exists? attr_accessor :values @@ -17,10 +17,7 @@ def value=(value) end def value - multi do - initialize_with_default - get - end[-1] + get || default.presence_in(values) end def reset @@ -34,10 +31,4 @@ def define_predicates_for_values define_singleton_method("#{defined_value}!") { self.value = defined_value } end end - - def set_default(value) - if validated_choice = value.presence_in(values) - set validated_choice, nx: true - end - end end diff --git a/lib/kredis/types/hash.rb b/lib/kredis/types/hash.rb index bc2859c..08e4fa1 100644 --- a/lib/kredis/types/hash.rb +++ b/lib/kredis/types/hash.rb @@ -1,6 +1,8 @@ require "active_support/core_ext/hash" class Kredis::Types::Hash < Kredis::Types::Proxying + ZERO_FIELDS_ADDED = 0 + proxying :hset, :hdel, :hgetall, :del, :exists?, :multi, :callnx attr_accessor :typed @@ -15,9 +17,10 @@ def []=(key, value) def update(**entries) - multi do - initialize_with_default - hset entries.transform_values{ |val| type_to_string(val, typed) } if entries.flatten.any? + return ZERO_FIELDS_ADDED if entries.flatten.blank? + + init_default_in_multi do + hset entries.transform_values{ |val| type_to_string(val, typed) } end end @@ -26,10 +29,9 @@ def values_at(*keys) end def delete(*keys) - multi do - initialize_with_default - hdel keys if keys.flatten.any? - end + return ZERO_FIELDS_ADDED if keys.flatten.blank? + + init_default_in_multi { hdel keys } end def remove diff --git a/lib/kredis/types/list.rb b/lib/kredis/types/list.rb index 60e12f2..d57c846 100644 --- a/lib/kredis/types/list.rb +++ b/lib/kredis/types/list.rb @@ -4,38 +4,32 @@ class Kredis::Types::List < Kredis::Types::Proxying attr_accessor :typed def elements - values = multi do - initialize_with_default - lrange(0, -1) - end[-1] + values = init_default_in_multi { lrange(0, -1) } strings_to_types(values || [], typed) end alias to_a elements def remove(*elements) - return if elements.empty? + return [] if elements.flatten.blank? - multi do - initialize_with_default + init_default_in_multi do types_to_strings(elements, typed).each { |element| lrem 0, element } end end def prepend(*elements) - return if elements.empty? + return self.elements.count if elements.flatten.blank? - multi do - initialize_with_default - lpush types_to_strings(elements, typed) if elements.flatten.any? + init_default_in_multi do + lpush types_to_strings(elements, typed) end end def append(*elements) - return if elements.empty? + return self.elements.count if elements.flatten.blank? - multi do - initialize_with_default - rpush types_to_strings(elements, typed) if elements.flatten.any? + init_default_in_multi do + rpush types_to_strings(elements, typed) end end alias << append diff --git a/lib/kredis/types/proxying.rb b/lib/kredis/types/proxying.rb index 900b632..cc2b989 100644 --- a/lib/kredis/types/proxying.rb +++ b/lib/kredis/types/proxying.rb @@ -29,12 +29,19 @@ def default end end - def initialize_with_default - default_value = default - set_default(default_value) if default_value.present? + def init_default_in_multi(&block) + if (default_value = default).blank? + block.call + else + multi_results = multi do + set_default(default_value) + block.call + end + Array(multi_results)[-1] # convert to array in case in the middle of nested multi + end end def set_default(value) - set type_to_string(value, typed), ex: expires_in, nx: true + raise NotImplementedError, "kredis type needs to define #set_default" end end diff --git a/lib/kredis/types/scalar.rb b/lib/kredis/types/scalar.rb index a716913..b60cb30 100644 --- a/lib/kredis/types/scalar.rb +++ b/lib/kredis/types/scalar.rb @@ -8,11 +8,7 @@ def value=(value) end def value - get_value = multi do - initialize_with_default - get - end[-1] - string_to_type(get_value, typed) + string_to_type(init_default_in_multi{ get }, typed) end def to_s @@ -34,4 +30,9 @@ def expire_in(seconds) def expire_at(datetime) expireat datetime.to_i end + + private + def set_default(value) + set type_to_string(value, typed), ex: expires_in, nx: true + end end diff --git a/lib/kredis/types/set.rb b/lib/kredis/types/set.rb index c6289d9..0ff514a 100644 --- a/lib/kredis/types/set.rb +++ b/lib/kredis/types/set.rb @@ -4,55 +4,47 @@ class Kredis::Types::Set < Kredis::Types::Proxying attr_accessor :typed def members - values = multi do - initialize_with_default - smembers - end[-1] + values = init_default_in_multi { smembers } strings_to_types(values || [], typed).sort end alias to_a members def add(*members) - multi do - initialize_with_default - sadd types_to_strings(members, typed) if members.flatten.any? + return size if members.flatten.blank? + + init_default_in_multi do + sadd types_to_strings(members, typed) end end alias << add def remove(*members) - multi do - initialize_with_default - srem types_to_strings(members, typed) if members.flatten.any? + return size if members.flatten.blank? + + init_default_in_multi do + srem types_to_strings(members, typed) end end def replace(*members) + return size if members.flatten.blank? + multi do del add members - end + end[-1] end def include?(member) - multi do - initialize_with_default - sismember type_to_string(member, typed) - end[-1] + init_default_in_multi { sismember type_to_string(member, typed) } end def size - multi do - initialize_with_default - scard - end[-1].to_i + init_default_in_multi { scard }.to_i end def take - multi do - initialize_with_default - spop - end[-1] + init_default_in_multi { spop } end def clear diff --git a/test/types/enum_test.rb b/test/types/enum_test.rb index bf7bedb..7224065 100644 --- a/test/types/enum_test.rb +++ b/test/types/enum_test.rb @@ -13,8 +13,10 @@ class EnumTest < ActiveSupport::TestCase end test "does not set default for invalid option" do - @enum = Kredis.enum "myenum", values: %w[ one two three ], default: ->() { "four" } - assert_nil @enum.value + enum = Kredis.enum "myenum1", values: [ 1, 2, 3 ], default: ->() { nil } + assert_nil enum.value + enum = Kredis.enum "myenum2", values: [ 1, 2, 3 ], default: ->() { 4 } + assert_nil enum.value end test "predicates" do diff --git a/test/types/hash_test.rb b/test/types/hash_test.rb index 0ea05b4..5a5b1b8 100644 --- a/test/types/hash_test.rb +++ b/test/types/hash_test.rb @@ -18,7 +18,15 @@ class HashTest < ActiveSupport::TestCase end test "update" do - @hash.update(key: :value) + update_count = @hash.update(key: :value) + assert_equal(1, update_count) + + update_count = @hash.update(key: :value) + assert_equal(0, update_count) + + update_count = @hash.update + assert_equal(0, update_count) + @hash.update("key2" => "value2", "key3" => "value3") assert_equal({ "key" => "value", "key2" => "value2", "key3" => "value3" }, @hash.to_h) end @@ -33,12 +41,17 @@ class HashTest < ActiveSupport::TestCase @hash.update("key2" => "value2", "key3" => "value3") assert_equal({ "key" => "value", "key2" => "value2", "key3" => "value3" }, @hash.to_h) - @hash.delete("key") + delete_count = @hash.delete("key") + assert_equal(1, delete_count) assert_equal({ "key2" => "value2", "key3" => "value3" }, @hash.to_h) - @hash.delete("bogus") + delete_count = @hash.delete("bogus") + assert_equal(0, delete_count) assert_equal({ "key2" => "value2", "key3" => "value3" }, @hash.to_h) + delete_count = @hash.delete + assert_equal(0, delete_count) + @hash.delete("key2", "key3") assert_equal({}, @hash.to_h) end diff --git a/test/types/list_test.rb b/test/types/list_test.rb index d18c9bf..f8b83eb 100644 --- a/test/types/list_test.rb +++ b/test/types/list_test.rb @@ -11,8 +11,10 @@ class ListTest < ActiveSupport::TestCase end test "append nothing" do - @list.append(%w[ 1 2 3 ]) - @list.append([]) + list_length = @list.append(%w[ 1 2 3 ]) + assert_equal 3, list_length + list_length = @list.append([]) + assert_equal 3, list_length assert_equal %w[ 1 2 3 ], @list.to_a end @@ -23,8 +25,10 @@ class ListTest < ActiveSupport::TestCase end test "prepend nothing" do - @list.prepend("1", "2", "3") - @list.prepend([]) + list_length = @list.prepend("1", "2", "3") + assert_equal 3, list_length + list_length = @list.prepend([]) + assert_equal 3, list_length assert_equal %w[ 3 2 1 ], @list.elements end @@ -35,6 +39,16 @@ class ListTest < ActiveSupport::TestCase assert_equal %w[ 4 ], @list.elements end + test "remove nothing" do + @list.append(%w[ 1 2 3 4 ]) + @list.remove(%w[ 1 2 ]) + removed_items = @list.remove(3) + assert_equal %w[ 3 ], removed_items + removed_items = @list.remove + assert_equal [], removed_items + assert_equal %w[ 4 ], @list.elements + end + test "clear" do @list.append(%w[ 1 2 3 4 ]) @list.clear diff --git a/test/types/set_test.rb b/test/types/set_test.rb index 0a88958..ba665be 100644 --- a/test/types/set_test.rb +++ b/test/types/set_test.rb @@ -12,8 +12,10 @@ class SetTest < ActiveSupport::TestCase end test "add nothing" do - @set.add(%w[ 1 2 3 ]) - @set.add([]) + set_size = @set.add(%w[ 1 2 3 ]) + assert_equal 3, set_size + set_size = @set.add([]) + assert_equal 3, set_size assert_equal %w[ 1 2 3 ], @set.to_a end @@ -25,8 +27,10 @@ class SetTest < ActiveSupport::TestCase end test "remove nothing" do - @set.add(%w[ 1 2 3 4 ]) - @set.remove([]) + set_size = @set.add(%w[ 1 2 3 4 ]) + assert_equal 4, set_size + set_size = @set.remove([]) + assert_equal 4, set_size assert_equal %w[ 1 2 3 4 ], @set.members end @@ -36,6 +40,15 @@ class SetTest < ActiveSupport::TestCase assert_equal %w[ 5 6 ], @set.members end + test "replace nothing" do + @set.add(%w[ 1 2 3 4 ]) + set_size = @set.replace(%w[ 5 6 ]) + assert_equal 2, set_size + set_size = @set.replace + assert_equal 2, set_size + assert_equal %w[ 5 6 ], @set.members + end + test "include" do @set.add("1", "2", "3", "4") assert @set.include?("1") From ef2eacbd1159b6704c422b56b5975ad199780010 Mon Sep 17 00:00:00 2001 From: tleish Date: Wed, 20 Jul 2022 18:01:08 -0600 Subject: [PATCH 27/27] refactor multi in Kredis::Types::Proxying --- lib/kredis/types/list.rb | 2 +- lib/kredis/types/proxying.rb | 4 ++-- lib/kredis/types/scalar.rb | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/kredis/types/list.rb b/lib/kredis/types/list.rb index d57c846..7d31a19 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, :multi, :callnx + proxying :lrange, :lrem, :lpush, :rpush, :exists?, :del, :callnx attr_accessor :typed diff --git a/lib/kredis/types/proxying.rb b/lib/kredis/types/proxying.rb index cc2b989..89979bc 100644 --- a/lib/kredis/types/proxying.rb +++ b/lib/kredis/types/proxying.rb @@ -33,11 +33,11 @@ def init_default_in_multi(&block) if (default_value = default).blank? block.call else - multi_results = multi do + multi_results = proxy.multi do set_default(default_value) block.call end - Array(multi_results)[-1] # convert to array in case in the middle of nested multi + Array(multi_results)[-1] end end diff --git a/lib/kredis/types/scalar.rb b/lib/kredis/types/scalar.rb index b60cb30..8ad44f0 100644 --- a/lib/kredis/types/scalar.rb +++ b/lib/kredis/types/scalar.rb @@ -1,5 +1,5 @@ class Kredis::Types::Scalar < Kredis::Types::Proxying - proxying :set, :get, :exists?, :del, :expire, :expireat, :multi + proxying :set, :get, :exists?, :del, :expire, :expireat attr_accessor :typed, :expires_in