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 dcbeceb
Show file tree
Hide file tree
Showing 3 changed files with 41 additions and 5 deletions.
27 changes: 23 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,17 @@ def inherited(subclass)
end
private_constant(:CreateMemoWiseStateOnInherited)

module CreateMemoWiseStateOnIncluded
def included(base)
return unless base.is_a?(Class) && !base.singleton_class?

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 +189,12 @@ 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)
elsif klass.is_a?(Module)
klass.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
17 changes: 17 additions & 0 deletions spec/memo_wise_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,13 @@ def module2_method
end
end

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

let(:instance) { klass.new }

before(:each) do
Expand All @@ -364,6 +371,16 @@ 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
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 dcbeceb

Please sign in to comment.