lib/memo_wise.rb in memo_wise-1.8.0 vs lib/memo_wise.rb in memo_wise-1.9.0
- old
+ new
@@ -29,16 +29,16 @@
module MemoWise
# Constructor to set up memoization state before
# [calling the original](https://medium.com/@jeremy_96642/ruby-method-auditing-using-module-prepend-4f4e69aacd95)
# constructor.
#
- # - **Q:** Why is [Module#prepend](https://ruby-doc.org/3.2.1/Module.html#method-i-prepend)
+ # - **Q:** Why is [Module#prepend](https://ruby-doc.org/3.2.2/Module.html#method-i-prepend)
# important here
# ([more info](https://medium.com/@leo_hetsch/ruby-modules-include-vs-prepend-vs-extend-f09837a5b073))?
# - **A:** To set up *mutable state* inside the instance, even if the original
# constructor will then call
- # [Object#freeze](https://ruby-doc.org/3.2.1/Object.html#method-i-freeze).
+ # [Object#freeze](https://ruby-doc.org/3.2.2/Object.html#method-i-freeze).
#
# This approach supports memoization on frozen (immutable) objects -- for
# example, classes created by the
# [Values](https://github.com/tcrayford/Values)
# [gem](https://rubygems.org/gems/values).
@@ -76,18 +76,34 @@
MemoWise::InternalAPI.create_memo_wise_state!(self)
super
end
HEREDOC
+ module CreateMemoWiseStateOnExtended
+ def extended(base)
+ MemoWise::InternalAPI.create_memo_wise_state!(base)
+ super
+ end
+ end
+ private_constant(:CreateMemoWiseStateOnExtended)
+
+ module CreateMemoWiseStateOnInherited
+ def inherited(subclass)
+ MemoWise::InternalAPI.create_memo_wise_state!(subclass)
+ super
+ end
+ end
+ private_constant(:CreateMemoWiseStateOnInherited)
+
# @private
#
# Private setup method, called automatically by `prepend MemoWise` in a class.
#
# @param target [Class]
# The `Class` into to prepend the MemoWise methods e.g. `memo_wise`
#
- # @see https://ruby-doc.org/3.2.1/Module.html#method-i-prepend
+ # @see https://ruby-doc.org/3.2.2/Module.html#method-i-prepend
#
# @example
# class Example
# prepend MemoWise
# end
@@ -98,11 +114,11 @@
# [calling the original](https://medium.com/@jeremy_96642/ruby-method-auditing-using-module-prepend-4f4e69aacd95)
# allocator.
#
# This is necessary in addition to the `#initialize` method definition
# above because
- # [`Class#allocate`](https://ruby-doc.org/3.2.1/Class.html#method-i-allocate)
+ # [`Class#allocate`](https://ruby-doc.org/3.2.2/Class.html#method-i-allocate)
# bypasses `#initialize`, and when it's used (e.g.,
# [in ActiveRecord](https://github.com/rails/rails/blob/a395c3a6af1e079740e7a28994d77c8baadd2a9d/activerecord/lib/active_record/persistence.rb#L411))
# we still need to be able to access MemoWise's instance variable. Despite
# Ruby documentation indicating otherwise, `Class#new` does not call
# `Class#allocate`, so we need to override both.
@@ -134,13 +150,11 @@
# But a module/class extending another module with memo_wise
# would not call `.allocate`/`#initialize` before calling methods
#
# On method call `@_memo_wise` would still be `nil`
# causing error when fetching cache from `@_memo_wise`
- def klass.extended(base)
- MemoWise::InternalAPI.create_memo_wise_state!(base)
- end
+ klass.singleton_class.prepend(CreateMemoWiseStateOnExtended)
end
when Hash
unless method_name_or_hash.keys == [:self]
raise ArgumentError,
"`:self` is the only key allowed in memo_wise"
@@ -157,16 +171,15 @@
klass = klass.singleton_class
end
# This ensures that a memoized method defined on a parent class can
# still be used in a child class.
- klass.module_eval <<~HEREDOC, __FILE__, __LINE__ + 1
- def inherited(subclass)
- super
- MemoWise::InternalAPI.create_memo_wise_state!(subclass)
- end
- HEREDOC
+ if klass.is_a?(Class) && !klass.singleton_class?
+ klass.singleton_class.prepend(CreateMemoWiseStateOnInherited)
+ else
+ klass.prepend(CreateMemoWiseStateOnInherited)
+ end
raise ArgumentError, "#{method_name.inspect} must be a Symbol" unless method_name.is_a?(Symbol)
visibility = MemoWise::InternalAPI.method_visibility(klass, method_name)
original_memo_wised_name = MemoWise::InternalAPI.original_memo_wised_name(method_name)
@@ -253,10 +266,10 @@
method_name,
MemoWise.instance_method(method_name)
)
end
- # Override [Module#instance_method](https://ruby-doc.org/3.2.1/Module.html#method-i-instance_method)
+ # Override [Module#instance_method](https://ruby-doc.org/3.2.2/Module.html#method-i-instance_method)
# to proxy the original `UnboundMethod#parameters` results. We want the
# parameters to reflect the original method in order to support callers
# who want to use Ruby reflection to process the method parameters,
# because our overridden `#initialize` method, and in some cases the
# generated memoized methods, will have a generic set of parameters