Skip to content

Commit

Permalink
Prepend ivar setup in included modules
Browse files Browse the repository at this point in the history
Fixes #302

Co-authored-by: alpaca-tc <[email protected]>
  • Loading branch information
JacobEvelyn and alpaca-tc committed Jan 30, 2024
1 parent 7cf8ed2 commit adbfd13
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 5 deletions.
23 changes: 19 additions & 4 deletions lib/memo_wise.rb
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,9 @@ module MemoWise
# [this article](https://www.ruby-lang.org/en/news/2019/12/12/separation-of-positional-and-keyword-arguments-in-ruby-3-0/)
# for more information.
#

# :nocov:
all_args = RUBY_VERSION < "2.7" ? "*" : "..."
# :nocov:
class_eval <<~HEREDOC, __FILE__, __LINE__ + 1
INITIALIZE_LITERAL = <<~HEREDOC
# On Ruby 2.7 or greater:
#
# def initialize(...)
Expand All @@ -72,11 +71,14 @@ module MemoWise
# super
# end
def initialize(#{all_args})
def initialize(#{RUBY_VERSION < "2.7" ? "*" : "..."})
MemoWise::InternalAPI.create_memo_wise_state!(self)
super
end
HEREDOC
# :nocov:

class_eval(INITIALIZE_LITERAL, __FILE__, __LINE__ + 1)

module CreateMemoWiseStateOnExtended
def extended(base)
Expand All @@ -94,6 +96,15 @@ def inherited(subclass)
end
private_constant(:CreateMemoWiseStateOnInherited)

module CreateMemoWiseStateOnIncluded
def included(base)
base.prepend(Module.new do
class_eval(INITIALIZE_LITERAL, __FILE__, __LINE__ + 1)
end)
end
end
private_constant(:CreateMemoWiseStateOnIncluded)

# @private
#
# Private setup method, called automatically by `prepend MemoWise` in a class.
Expand Down Expand Up @@ -176,6 +187,10 @@ def memo_wise(method_name_or_hash)
if klass.is_a?(Class) && !klass.singleton_class?
klass.singleton_class.prepend(CreateMemoWiseStateOnInherited)
else
if klass.is_a?(Module) && !klass.singleton_class?
klass.singleton_class.prepend(CreateMemoWiseStateOnIncluded)
end

klass.prepend(CreateMemoWiseStateOnInherited)
end

Expand Down
2 changes: 1 addition & 1 deletion spec/adding_methods_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def self.example; end
end
end

let(:expected_public_class_methods) { super() << :inherited }
let(:expected_public_class_methods) { super() + %i[inherited included] }

it "adds expected public *instance* methods only" do
expect { subject }.
Expand Down
42 changes: 42 additions & 0 deletions spec/memo_wise_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -351,11 +351,32 @@ def module2_method
end
end

let(:klass_with_initializer) do
Class.new do
include Module1
def initialize(*); end
end
end

let(:module_with_initializer) do
Module.new do
include Module1
def initialize(*); end
end
end

let(:klass_with_module_with_initializer) do
Class.new do
include Module3
end
end

let(:instance) { klass.new }

before(:each) do
stub_const("Module1", module1)
stub_const("Module2", module2)
stub_const("Module3", module_with_initializer)
end

it "memoizes inherited methods separately" do
Expand All @@ -364,6 +385,27 @@ def module2_method
expect(Array.new(4) { instance.module2_method }).to all eq("module2_method")
expect(instance.module2_method_counter).to eq(1)
end

it "can memoize klass with initializer" do
instance = klass_with_initializer.new(true)
expect { instance.module1_method }.not_to raise_error

expect(Array.new(4) { instance.module1_method }).to all eq("module1_method")
expect(instance.module1_method_counter).to eq(1)
end

it "can memoize klass with initializer" do
instance = klass_with_module_with_initializer.new(true)
expect { instance.module1_method }.not_to raise_error

expect(Array.new(4) { instance.module1_method }).to all eq("module1_method")
expect(instance.module1_method_counter).to eq(1)
end

it "can reset klass with initializer" do
instance = klass_with_initializer.new(true)
expect { instance.reset_memo_wise }.not_to raise_error
end
end

context "when the class, its superclass, and its module all memoize methods" do
Expand Down

0 comments on commit adbfd13

Please sign in to comment.