From 176cae009844b931fbafd4bc7604b16f642d138e Mon Sep 17 00:00:00 2001 From: Robert Pankowecki Date: Wed, 14 Nov 2018 14:05:46 +0100 Subject: [PATCH 1/8] Raises exception instead of emitting warning. The message tries to guide the developer on how to workaround the issue. It's better to fail rather than skip memoization which can lead to bugs. --- lib/memoist.rb | 5 +++-- test/memoist_test.rb | 30 ++++++++++++++++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/lib/memoist.rb b/lib/memoist.rb index 54b67e6..9d2775f 100644 --- a/lib/memoist.rb +++ b/lib/memoist.rb @@ -132,8 +132,9 @@ def memoize(*method_names) include InstanceMethods if method_defined?(unmemoized_method) - warn "Already memoized #{method_name}" - return + default = "AnIdentifier" + suggestion = respond_to?(:name) ? name || default : default + raise "Already memoized :#{method_name}. Try `memoize :#{method_name}, identifier: '#{suggestion}'` or use `@#{method_name} ||= compute` pattern instead." end alias_method unmemoized_method, method_name diff --git a/test/memoist_test.rb b/test/memoist_test.rb index 49a09d0..ad26fcd 100644 --- a/test/memoist_test.rb +++ b/test/memoist_test.rb @@ -547,4 +547,34 @@ def test_private_method_memoization assert_equal 'Yes', person.send(:is_developer?) assert_equal 1, person.is_developer_calls end + + def test_exception_when_subclass_without_identifier + err = assert_raises(StandardError) do + Class.new(Person) do + def self.name + "Staff" + end + memoize def name + 'Overwritten' + end + end + end + assert_equal( + "Already memoized :name. Try `memoize :name, identifier: 'Staff'` or use `@name ||= compute` pattern instead.", + err.message + ) + + err = assert_raises(StandardError) do + Class.new(Person) do + memoize def name + 'Overwritten' + end + end + end + assert_equal( + "Already memoized :name. Try `memoize :name, identifier: 'AnIdentifier'` or use `@name ||= compute` pattern instead.", + err.message + ) + end + end From 3feaacf998fc567e7168bdcfd723adb9bd27dd3f Mon Sep 17 00:00:00 2001 From: Phil Pirozhkov Date: Tue, 22 Jan 2019 17:29:45 +0700 Subject: [PATCH 2/8] Tweak syntax in tests to work in Ruby 2.1 --- test/memoist_test.rb | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/test/memoist_test.rb b/test/memoist_test.rb index ad26fcd..4f6638b 100644 --- a/test/memoist_test.rb +++ b/test/memoist_test.rb @@ -554,9 +554,10 @@ def test_exception_when_subclass_without_identifier def self.name "Staff" end - memoize def name + def name 'Overwritten' end + memoize :name end end assert_equal( @@ -566,9 +567,10 @@ def self.name err = assert_raises(StandardError) do Class.new(Person) do - memoize def name + def name 'Overwritten' end + memoize :name end end assert_equal( @@ -576,5 +578,4 @@ def self.name err.message ) end - end From c21c3a3b740773549cb114740761c3770e0e467c Mon Sep 17 00:00:00 2001 From: Jason Perrone Date: Fri, 10 Sep 2021 15:32:33 -0400 Subject: [PATCH 3/8] Fix for Ruby 3 keyword arg splatting change --- lib/memoist.rb | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/lib/memoist.rb b/lib/memoist.rb index 9d2775f..cce6ced 100644 --- a/lib/memoist.rb +++ b/lib/memoist.rb @@ -50,11 +50,11 @@ def self.escape_punctuation(string) string end - def self.memoist_eval(klass, *args, &block) + def self.memoist_eval(klass, *args, **kwargs, &block) if klass.respond_to?(:class_eval) - klass.class_eval(*args, &block) + klass.class_eval(*args, **kwargs, &block) else - klass.singleton_class.class_eval(*args, &block) + klass.singleton_class.class_eval(*args, **kwargs, &block) end end @@ -204,21 +204,21 @@ def #{method_name}(reload = false) # end module_eval <<-EOS, __FILE__, __LINE__ + 1 - def #{method_name}(*args) + def #{method_name}(*args, **kwargs) reload = Memoist.extract_reload!(method(#{unmemoized_method.inspect}), args) - skip_cache = reload || !(instance_variable_defined?(#{memoized_ivar.inspect}) && #{memoized_ivar} && #{memoized_ivar}.has_key?(args)) + skip_cache = reload || !(instance_variable_defined?(#{memoized_ivar.inspect}) && #{memoized_ivar} && #{memoized_ivar}.has_key?(args+kwargs.values)) set_cache = skip_cache && !frozen? if skip_cache - value = #{unmemoized_method}(*args) + value = #{unmemoized_method}(*args, **kwargs) else - value = #{memoized_ivar}[args] + value = #{memoized_ivar}[args+kwargs.values] end if set_cache #{memoized_ivar} ||= {} - #{memoized_ivar}[args] = value + #{memoized_ivar}[args+kwargs.values] = value end value From 89698281ecf5ee7b5e413cea0e1c6d2e17c89180 Mon Sep 17 00:00:00 2001 From: Jan Sterba Date: Sat, 8 Jan 2022 20:34:00 +0100 Subject: [PATCH 4/8] finalize ruby 3 support --- lib/memoist.rb | 6 +++--- test/memoist_test.rb | 29 ++++++++++++++++++++++++++++- 2 files changed, 31 insertions(+), 4 deletions(-) diff --git a/lib/memoist.rb b/lib/memoist.rb index cce6ced..6b561a4 100644 --- a/lib/memoist.rb +++ b/lib/memoist.rb @@ -207,18 +207,18 @@ def #{method_name}(reload = false) def #{method_name}(*args, **kwargs) reload = Memoist.extract_reload!(method(#{unmemoized_method.inspect}), args) - skip_cache = reload || !(instance_variable_defined?(#{memoized_ivar.inspect}) && #{memoized_ivar} && #{memoized_ivar}.has_key?(args+kwargs.values)) + skip_cache = reload || !(instance_variable_defined?(#{memoized_ivar.inspect}) && #{memoized_ivar} && #{memoized_ivar}.has_key?(args+kwargs.to_a)) set_cache = skip_cache && !frozen? if skip_cache value = #{unmemoized_method}(*args, **kwargs) else - value = #{memoized_ivar}[args+kwargs.values] + value = #{memoized_ivar}[args+kwargs.to_a] end if set_cache #{memoized_ivar} ||= {} - #{memoized_ivar}[args+kwargs.values] = value + #{memoized_ivar}[args+kwargs.to_a] = value end value diff --git a/test/memoist_test.rb b/test/memoist_test.rb index 4f6638b..95806d2 100644 --- a/test/memoist_test.rb +++ b/test/memoist_test.rb @@ -87,6 +87,16 @@ def update_attributes_calls @counter.count(:update_attributes) end + def do_with_special(_regular = 10, special_one: true, special_two: true) + @counter.call(:do_with_special) + true + end + memoize :do_with_special + + def do_with_special_calls + @counter.count(:do_with_special) + end + protected def memoize_protected_test @@ -265,6 +275,23 @@ def test_memoize_with_options_hash assert_equal 4, @person.update_attributes_calls end + def test_memoize_with_kwargs + assert_equal true, @person.do_with_special(1, special_one: true) + assert_equal 1, @person.do_with_special_calls + + 3.times { assert_equal true, @person.do_with_special(1, special_one: true) } + assert_equal 1, @person.do_with_special_calls + + assert_equal true, @person.do_with_special(2) + assert_equal 2, @person.do_with_special_calls + + assert_equal true, @person.do_with_special(1, special_one: false) + assert_equal 3, @person.do_with_special_calls + + assert_equal true, @person.do_with_special(1, special_two: false) + assert_equal 4, @person.do_with_special_calls + end + def test_memoization_with_punctuation assert_equal true, @person.name? @@ -348,7 +375,7 @@ def test_all_memoized_structs # Student < Person memoize :name, :identifier => :student # Teacher < Person memoize :seniority - expected = %w[age is_developer? memoize_protected_test name name? sleep update update_attributes] + expected = %w[age do_with_special is_developer? memoize_protected_test name name? sleep update update_attributes] structs = Person.all_memoized_structs assert_equal expected, structs.collect(&:memoized_method).collect(&:to_s).sort assert_equal '@_memoized_name', structs.detect { |s| s.memoized_method == :name }.ivar From 6e7dd9bbc7548b946baade8bf96b2c7f9c7812a5 Mon Sep 17 00:00:00 2001 From: Maciej Rzasa Date: Thu, 4 Aug 2022 09:42:22 +0200 Subject: [PATCH 5/8] Test hash argument with Ruby 3 --- test/memoist_test.rb | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/test/memoist_test.rb b/test/memoist_test.rb index 95806d2..9b18aa5 100644 --- a/test/memoist_test.rb +++ b/test/memoist_test.rb @@ -97,6 +97,16 @@ def do_with_special_calls @counter.count(:do_with_special) end + def format_metadata(metadata) + @counter.call(:format_metadata) + "#{metadata[:grade]}: #{metadata[:comment]}" + end + memoize :format_metadata + + def format_metadata_calls + @counter.count(:format_metadata) + end + protected def memoize_protected_test @@ -292,6 +302,18 @@ def test_memoize_with_kwargs assert_equal 4, @person.do_with_special_calls end + def test_memoize_with_hash_for_ruby3 + metadata = { grade: 42, comment: 'The meaning.' } + assert_equal '42: The meaning.', @person.format_metadata(metadata) + assert_equal 1, @person.format_metadata_calls + + assert_equal '42: The meaning.', @person.format_metadata(metadata) + assert_equal 1, @person.format_metadata_calls + + assert_equal '44: The name.', @person.format_metadata({ grade: 44, comment: 'The name.'}) + assert_equal 2, @person.format_metadata_calls + end + def test_memoization_with_punctuation assert_equal true, @person.name? @@ -375,7 +397,7 @@ def test_all_memoized_structs # Student < Person memoize :name, :identifier => :student # Teacher < Person memoize :seniority - expected = %w[age do_with_special is_developer? memoize_protected_test name name? sleep update update_attributes] + expected = %w[age do_with_special format_metadata is_developer? memoize_protected_test name name? sleep update update_attributes] structs = Person.all_memoized_structs assert_equal expected, structs.collect(&:memoized_method).collect(&:to_s).sort assert_equal '@_memoized_name', structs.detect { |s| s.memoized_method == :name }.ivar From 5d3332f6f6052fdee3b88becda9238f3b274ed85 Mon Sep 17 00:00:00 2001 From: Diego Guerra Date: Mon, 6 Mar 2023 14:41:38 +0100 Subject: [PATCH 6/8] Use GH & test ruby 3.2 --- .github/workflows/ci.yml | 29 +++++++++++++++++++++++++++++ .travis.yml | 21 --------------------- 2 files changed, 29 insertions(+), 21 deletions(-) create mode 100644 .github/workflows/ci.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..f301874 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + +jobs: + tests: + name: Tests + + runs-on: ubuntu-latest + strategy: + matrix: + ruby-version: ['3.0', '3.2.1'] + + steps: + - name: Check out repository + uses: actions/checkout@v2 + - name: Set up Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby-version }} + bundler: 2.3 + bundler-cache: true + env: + BUNDLE_GITHUB__COM: ${{ secrets.BUNDLE_GITHUB__COM }}:x-oauth-basic + - name: Run tests + run: bundle exec rake diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 2d505a8..0000000 --- a/.travis.yml +++ /dev/null @@ -1,21 +0,0 @@ -sudo: false -cache: bundler -language: ruby -rvm: - - 1.9.2 - - 1.9.3 - - 2.0.0 - - 2.1.10 - - 2.2.7 - - 2.3.4 - - 2.4.1 - - 2.5.1 - - ruby-head - - jruby-19mode - - jruby-9.1.5.0 - - jruby-head - -before_install: - - gem install bundler --no-document -v '~> 1.13' -before_script: - - unset JRUBY_OPTS From c6df421628d0d7a8f80ef1a76f49b176acf6197b Mon Sep 17 00:00:00 2001 From: Diego Guerra Date: Mon, 6 Mar 2023 15:42:44 +0100 Subject: [PATCH 7/8] Softer ruby requirement --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f301874..870ebe4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - ruby-version: ['3.0', '3.2.1'] + ruby-version: ['3.0', '3.2'] steps: - name: Check out repository From 88d0ade296c23dfa35dafcca99074291a7999da7 Mon Sep 17 00:00:00 2001 From: Diego Guerra Date: Mon, 6 Mar 2023 15:42:56 +0100 Subject: [PATCH 8/8] Don't ignore lock --- .gitignore | 1 - Gemfile.lock | 24 ++++++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) create mode 100644 Gemfile.lock diff --git a/.gitignore b/.gitignore index d87d4be..8f994bd 100644 --- a/.gitignore +++ b/.gitignore @@ -3,7 +3,6 @@ .bundle .config .yardoc -Gemfile.lock InstalledFiles _yardoc coverage diff --git a/Gemfile.lock b/Gemfile.lock new file mode 100644 index 0000000..e2bc09f --- /dev/null +++ b/Gemfile.lock @@ -0,0 +1,24 @@ +PATH + remote: . + specs: + memoist (0.16.0) + +GEM + remote: https://rubygems.org/ + specs: + benchmark-ips (2.11.0) + minitest (5.18.0) + rake (13.0.6) + +PLATFORMS + x86_64-linux + +DEPENDENCIES + benchmark-ips + bundler + memoist! + minitest (~> 5.10) + rake + +BUNDLED WITH + 2.4.3