Clarify intentions around method redefinitions
Don't use remove_method or remove_possible_method just before a new definition: at best the purpose is unclear, and at worst it creates a race condition. Instead, prefer redefine_method when practical, and silence_redefinition_of_method otherwise.
This commit is contained in:
parent
2cd8ac1b68
commit
2e6658ae51
|
@ -1,5 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require "active_support/core_ext/module/redefine_method"
|
||||||
|
|
||||||
module ActionCable
|
module ActionCable
|
||||||
# If you need to disconnect a given connection, you can go through the
|
# If you need to disconnect a given connection, you can go through the
|
||||||
# RemoteConnections. You can find the connections you're looking for by
|
# RemoteConnections. You can find the connections you're looking for by
|
||||||
|
|
|
@ -85,7 +85,7 @@ module ActionController
|
||||||
def self.remove(key)
|
def self.remove(key)
|
||||||
RENDERERS.delete(key.to_sym)
|
RENDERERS.delete(key.to_sym)
|
||||||
method_name = _render_with_renderer_method_name(key)
|
method_name = _render_with_renderer_method_name(key)
|
||||||
remove_method(method_name) if method_defined?(method_name)
|
remove_possible_method(method_name)
|
||||||
end
|
end
|
||||||
|
|
||||||
def self._render_with_renderer_method_name(key)
|
def self._render_with_renderer_method_name(key)
|
||||||
|
|
|
@ -4,6 +4,7 @@ require "rack/session/abstract/id"
|
||||||
require "active_support/core_ext/hash/conversions"
|
require "active_support/core_ext/hash/conversions"
|
||||||
require "active_support/core_ext/object/to_query"
|
require "active_support/core_ext/object/to_query"
|
||||||
require "active_support/core_ext/module/anonymous"
|
require "active_support/core_ext/module/anonymous"
|
||||||
|
require "active_support/core_ext/module/redefine_method"
|
||||||
require "active_support/core_ext/hash/keys"
|
require "active_support/core_ext/hash/keys"
|
||||||
require "active_support/testing/constant_lookup"
|
require "active_support/testing/constant_lookup"
|
||||||
require_relative "template_assertions"
|
require_relative "template_assertions"
|
||||||
|
@ -19,7 +20,7 @@ module ActionController
|
||||||
# the database on the main thread, so they could open a txn, then the
|
# the database on the main thread, so they could open a txn, then the
|
||||||
# controller thread will open a new connection and try to access data
|
# controller thread will open a new connection and try to access data
|
||||||
# that's only visible to the main thread's txn. This is the problem in #23483.
|
# that's only visible to the main thread's txn. This is the problem in #23483.
|
||||||
remove_method :new_controller_thread
|
silence_redefinition_of_method :new_controller_thread
|
||||||
def new_controller_thread # :nodoc:
|
def new_controller_thread # :nodoc:
|
||||||
yield
|
yield
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
require_relative "../journey"
|
require_relative "../journey"
|
||||||
require "active_support/core_ext/object/to_query"
|
require "active_support/core_ext/object/to_query"
|
||||||
require "active_support/core_ext/hash/slice"
|
require "active_support/core_ext/hash/slice"
|
||||||
|
require "active_support/core_ext/module/redefine_method"
|
||||||
require "active_support/core_ext/module/remove_method"
|
require "active_support/core_ext/module/remove_method"
|
||||||
require "active_support/core_ext/array/extract_options"
|
require "active_support/core_ext/array/extract_options"
|
||||||
require "action_controller/metal/exceptions"
|
require "action_controller/metal/exceptions"
|
||||||
|
@ -546,7 +547,7 @@ module ActionDispatch
|
||||||
|
|
||||||
# plus a singleton class method called _routes ...
|
# plus a singleton class method called _routes ...
|
||||||
included do
|
included do
|
||||||
singleton_class.send(:redefine_method, :_routes) { routes }
|
redefine_singleton_method(:_routes) { routes }
|
||||||
end
|
end
|
||||||
|
|
||||||
# And an instance method _routes. Note that
|
# And an instance method _routes. Note that
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "rendering"
|
require_relative "rendering"
|
||||||
require "active_support/core_ext/module/remove_method"
|
require "active_support/core_ext/module/redefine_method"
|
||||||
|
|
||||||
module ActionView
|
module ActionView
|
||||||
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
|
# Layouts reverse the common pattern of including shared headers and footers in many templates to isolate changes in
|
||||||
|
@ -279,7 +279,7 @@ module ActionView
|
||||||
# If a layout is not explicitly mentioned then look for a layout with the controller's name.
|
# If a layout is not explicitly mentioned then look for a layout with the controller's name.
|
||||||
# if nothing is found then try same procedure to find super class's layout.
|
# if nothing is found then try same procedure to find super class's layout.
|
||||||
def _write_layout_method # :nodoc:
|
def _write_layout_method # :nodoc:
|
||||||
remove_possible_method(:_layout)
|
silence_redefinition_of_method(:_layout)
|
||||||
|
|
||||||
prefixes = /\blayouts/.match?(_implied_layout_name) ? [] : ["layouts"]
|
prefixes = /\blayouts/.match?(_implied_layout_name) ? [] : ["layouts"]
|
||||||
default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}, false, [], { formats: formats }).first || super"
|
default_behavior = "lookup_context.find_all('#{_implied_layout_name}', #{prefixes.inspect}, false, [], { formats: formats }).first || super"
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "active_support/core_ext/module/remove_method"
|
require "active_support/core_ext/module/redefine_method"
|
||||||
require "action_controller"
|
require "action_controller"
|
||||||
require "action_controller/test_case"
|
require "action_controller/test_case"
|
||||||
require "action_view"
|
require "action_view"
|
||||||
|
@ -171,7 +171,7 @@ module ActionView
|
||||||
|
|
||||||
def say_no_to_protect_against_forgery!
|
def say_no_to_protect_against_forgery!
|
||||||
_helpers.module_eval do
|
_helpers.module_eval do
|
||||||
remove_possible_method :protect_against_forgery?
|
silence_redefinition_of_method :protect_against_forgery?
|
||||||
def protect_against_forgery?
|
def protect_against_forgery?
|
||||||
false
|
false
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
require "active_support/core_ext/hash/except"
|
require "active_support/core_ext/hash/except"
|
||||||
require "active_support/core_ext/module/introspection"
|
require "active_support/core_ext/module/introspection"
|
||||||
require "active_support/core_ext/module/remove_method"
|
require "active_support/core_ext/module/redefine_method"
|
||||||
|
|
||||||
module ActiveModel
|
module ActiveModel
|
||||||
class Name
|
class Name
|
||||||
|
@ -218,7 +218,7 @@ module ActiveModel
|
||||||
# provided method below, or rolling your own is required.
|
# provided method below, or rolling your own is required.
|
||||||
module Naming
|
module Naming
|
||||||
def self.extended(base) #:nodoc:
|
def self.extended(base) #:nodoc:
|
||||||
base.remove_possible_method :model_name
|
base.silence_redefinition_of_method :model_name
|
||||||
base.delegate :model_name, to: :class
|
base.delegate :model_name, to: :class
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require "active_support/core_ext/hash/except"
|
require "active_support/core_ext/hash/except"
|
||||||
|
require "active_support/core_ext/module/redefine_method"
|
||||||
require "active_support/core_ext/object/try"
|
require "active_support/core_ext/object/try"
|
||||||
require "active_support/core_ext/hash/indifferent_access"
|
require "active_support/core_ext/hash/indifferent_access"
|
||||||
|
|
||||||
|
@ -355,9 +356,7 @@ module ActiveRecord
|
||||||
# associations are just regular associations.
|
# associations are just regular associations.
|
||||||
def generate_association_writer(association_name, type)
|
def generate_association_writer(association_name, type)
|
||||||
generated_association_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1
|
generated_association_methods.module_eval <<-eoruby, __FILE__, __LINE__ + 1
|
||||||
if method_defined?(:#{association_name}_attributes=)
|
silence_redefinition_of_method :#{association_name}_attributes=
|
||||||
remove_method(:#{association_name}_attributes=)
|
|
||||||
end
|
|
||||||
def #{association_name}_attributes=(attributes)
|
def #{association_name}_attributes=(attributes)
|
||||||
assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
|
assign_nested_attributes_for_#{type}_association(:#{association_name}, attributes)
|
||||||
end
|
end
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "../kernel/singleton_class"
|
require_relative "../kernel/singleton_class"
|
||||||
require_relative "../module/remove_method"
|
require_relative "../module/redefine_method"
|
||||||
require_relative "../array/extract_options"
|
require_relative "../array/extract_options"
|
||||||
|
|
||||||
class Class
|
class Class
|
||||||
|
@ -92,25 +92,23 @@ class Class
|
||||||
default_value = options.fetch(:default, nil)
|
default_value = options.fetch(:default, nil)
|
||||||
|
|
||||||
attrs.each do |name|
|
attrs.each do |name|
|
||||||
remove_possible_singleton_method(name)
|
singleton_class.silence_redefinition_of_method(name)
|
||||||
define_singleton_method(name) { nil }
|
define_singleton_method(name) { nil }
|
||||||
|
|
||||||
remove_possible_singleton_method("#{name}?")
|
singleton_class.silence_redefinition_of_method("#{name}?")
|
||||||
define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
|
define_singleton_method("#{name}?") { !!public_send(name) } if instance_predicate
|
||||||
|
|
||||||
ivar = "@#{name}"
|
ivar = "@#{name}"
|
||||||
|
|
||||||
remove_possible_singleton_method("#{name}=")
|
singleton_class.silence_redefinition_of_method("#{name}=")
|
||||||
define_singleton_method("#{name}=") do |val|
|
define_singleton_method("#{name}=") do |val|
|
||||||
singleton_class.class_eval do
|
singleton_class.class_eval do
|
||||||
remove_possible_method(name)
|
redefine_method(name) { val }
|
||||||
define_method(name) { val }
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if singleton_class?
|
if singleton_class?
|
||||||
class_eval do
|
class_eval do
|
||||||
remove_possible_method(name)
|
redefine_method(name) do
|
||||||
define_method(name) do
|
|
||||||
if instance_variable_defined? ivar
|
if instance_variable_defined? ivar
|
||||||
instance_variable_get ivar
|
instance_variable_get ivar
|
||||||
else
|
else
|
||||||
|
@ -123,8 +121,7 @@ class Class
|
||||||
end
|
end
|
||||||
|
|
||||||
if instance_reader
|
if instance_reader
|
||||||
remove_possible_method name
|
redefine_method(name) do
|
||||||
define_method(name) do
|
|
||||||
if instance_variable_defined?(ivar)
|
if instance_variable_defined?(ivar)
|
||||||
instance_variable_get ivar
|
instance_variable_get ivar
|
||||||
else
|
else
|
||||||
|
@ -132,13 +129,13 @@ class Class
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
remove_possible_method "#{name}?"
|
redefine_method("#{name}?") { !!public_send(name) } if instance_predicate
|
||||||
define_method("#{name}?") { !!public_send(name) } if instance_predicate
|
|
||||||
end
|
end
|
||||||
|
|
||||||
if instance_writer
|
if instance_writer
|
||||||
remove_possible_method "#{name}="
|
redefine_method("#{name}=") do |val|
|
||||||
attr_writer name
|
instance_variable_set ivar, val
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
unless default_value.nil?
|
unless default_value.nil?
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
require "date"
|
require "date"
|
||||||
require_relative "../../inflector/methods"
|
require_relative "../../inflector/methods"
|
||||||
require_relative "zones"
|
require_relative "zones"
|
||||||
require_relative "../module/remove_method"
|
require_relative "../module/redefine_method"
|
||||||
|
|
||||||
class Date
|
class Date
|
||||||
DATE_FORMATS = {
|
DATE_FORMATS = {
|
||||||
|
@ -19,14 +19,6 @@ class Date
|
||||||
iso8601: lambda { |date| date.iso8601 }
|
iso8601: lambda { |date| date.iso8601 }
|
||||||
}
|
}
|
||||||
|
|
||||||
# Ruby 1.9 has Date#to_time which converts to localtime only.
|
|
||||||
remove_method :to_time
|
|
||||||
|
|
||||||
# Ruby 1.9 has Date#xmlschema which converts to a string without the time
|
|
||||||
# component. This removal may generate an issue on FreeBSD, that's why we
|
|
||||||
# need to use remove_possible_method here
|
|
||||||
remove_possible_method :xmlschema
|
|
||||||
|
|
||||||
# Convert to a formatted string. See DATE_FORMATS for predefined formats.
|
# Convert to a formatted string. See DATE_FORMATS for predefined formats.
|
||||||
#
|
#
|
||||||
# This method is aliased to <tt>to_s</tt>.
|
# This method is aliased to <tt>to_s</tt>.
|
||||||
|
@ -72,6 +64,8 @@ class Date
|
||||||
alias_method :default_inspect, :inspect
|
alias_method :default_inspect, :inspect
|
||||||
alias_method :inspect, :readable_inspect
|
alias_method :inspect, :readable_inspect
|
||||||
|
|
||||||
|
silence_redefinition_of_method :to_time
|
||||||
|
|
||||||
# Converts a Date instance to a Time, where the time is set to the beginning of the day.
|
# Converts a Date instance to a Time, where the time is set to the beginning of the day.
|
||||||
# The timezone can be either :local or :utc (default :local).
|
# The timezone can be either :local or :utc (default :local).
|
||||||
#
|
#
|
||||||
|
@ -89,6 +83,8 @@ class Date
|
||||||
::Time.send(form, year, month, day)
|
::Time.send(form, year, month, day)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
silence_redefinition_of_method :xmlschema
|
||||||
|
|
||||||
# Returns a string which represents the time in used time zone as DateTime
|
# Returns a string which represents the time in used time zone as DateTime
|
||||||
# defined by XML Schema:
|
# defined by XML Schema:
|
||||||
#
|
#
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "../date_and_time/compatibility"
|
require_relative "../date_and_time/compatibility"
|
||||||
require_relative "../module/remove_method"
|
require_relative "../module/redefine_method"
|
||||||
|
|
||||||
class DateTime
|
class DateTime
|
||||||
include DateAndTime::Compatibility
|
include DateAndTime::Compatibility
|
||||||
|
|
||||||
remove_possible_method :to_time
|
silence_redefinition_of_method :to_time
|
||||||
|
|
||||||
# Either return an instance of `Time` with the same UTC offset
|
# Either return an instance of `Time` with the same UTC offset
|
||||||
# as +self+ or an instance of `Time` representing the same time
|
# as +self+ or an instance of `Time` representing the same time
|
||||||
|
|
|
@ -10,4 +10,5 @@ require_relative "module/attr_internal"
|
||||||
require_relative "module/concerning"
|
require_relative "module/concerning"
|
||||||
require_relative "module/delegation"
|
require_relative "module/delegation"
|
||||||
require_relative "module/deprecation"
|
require_relative "module/deprecation"
|
||||||
|
require_relative "module/redefine_method"
|
||||||
require_relative "module/remove_method"
|
require_relative "module/remove_method"
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Module
|
||||||
|
# Marks the named method as intended to be redefined, if it exists.
|
||||||
|
# Suppresses the Ruby method redefinition warning. Prefer
|
||||||
|
# #redefine_method where possible.
|
||||||
|
def silence_redefinition_of_method(method)
|
||||||
|
if method_defined?(method) || private_method_defined?(method)
|
||||||
|
# This suppresses the "method redefined" warning; the self-alias
|
||||||
|
# looks odd, but means we don't need to generate a unique name
|
||||||
|
alias_method method, method
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Replaces the existing method definition, if there is one, with the passed
|
||||||
|
# block as its body.
|
||||||
|
def redefine_method(method, &block)
|
||||||
|
visibility = method_visibility(method)
|
||||||
|
silence_redefinition_of_method(method)
|
||||||
|
define_method(method, &block)
|
||||||
|
send(visibility, method)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Replaces the existing singleton method definition, if there is one, with
|
||||||
|
# the passed block as its body.
|
||||||
|
def redefine_singleton_method(method, &block)
|
||||||
|
singleton_class.redefine_method(method, &block)
|
||||||
|
end
|
||||||
|
|
||||||
|
def method_visibility(method) # :nodoc:
|
||||||
|
case
|
||||||
|
when private_method_defined?(method)
|
||||||
|
:private
|
||||||
|
when protected_method_defined?(method)
|
||||||
|
:protected
|
||||||
|
else
|
||||||
|
:public
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,5 +1,7 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
require_relative "redefine_method"
|
||||||
|
|
||||||
class Module
|
class Module
|
||||||
# Removes the named method, if it exists.
|
# Removes the named method, if it exists.
|
||||||
def remove_possible_method(method)
|
def remove_possible_method(method)
|
||||||
|
@ -10,28 +12,6 @@ class Module
|
||||||
|
|
||||||
# Removes the named singleton method, if it exists.
|
# Removes the named singleton method, if it exists.
|
||||||
def remove_possible_singleton_method(method)
|
def remove_possible_singleton_method(method)
|
||||||
singleton_class.instance_eval do
|
singleton_class.remove_possible_method(method)
|
||||||
remove_possible_method(method)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Replaces the existing method definition, if there is one, with the passed
|
|
||||||
# block as its body.
|
|
||||||
def redefine_method(method, &block)
|
|
||||||
visibility = method_visibility(method)
|
|
||||||
remove_possible_method(method)
|
|
||||||
define_method(method, &block)
|
|
||||||
send(visibility, method)
|
|
||||||
end
|
|
||||||
|
|
||||||
def method_visibility(method) # :nodoc:
|
|
||||||
case
|
|
||||||
when private_method_defined?(method)
|
|
||||||
:private
|
|
||||||
when protected_method_defined?(method)
|
|
||||||
:protected
|
|
||||||
else
|
|
||||||
:public
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
require "erb"
|
require "erb"
|
||||||
require_relative "../kernel/singleton_class"
|
require_relative "../kernel/singleton_class"
|
||||||
|
require_relative "../module/redefine_method"
|
||||||
require_relative "../../multibyte/unicode"
|
require_relative "../../multibyte/unicode"
|
||||||
|
|
||||||
class ERB
|
class ERB
|
||||||
|
@ -23,13 +24,12 @@ class ERB
|
||||||
unwrapped_html_escape(s).html_safe
|
unwrapped_html_escape(s).html_safe
|
||||||
end
|
end
|
||||||
|
|
||||||
# Aliasing twice issues a warning "discarding old...". Remove first to avoid it.
|
silence_redefinition_of_method :h
|
||||||
remove_method(:h)
|
|
||||||
alias h html_escape
|
alias h html_escape
|
||||||
|
|
||||||
module_function :h
|
module_function :h
|
||||||
|
|
||||||
singleton_class.send(:remove_method, :html_escape)
|
singleton_class.silence_redefinition_of_method :html_escape
|
||||||
module_function :html_escape
|
module_function :html_escape
|
||||||
|
|
||||||
# HTML escapes strings but doesn't wrap them with an ActiveSupport::SafeBuffer.
|
# HTML escapes strings but doesn't wrap them with an ActiveSupport::SafeBuffer.
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
require_relative "../date_and_time/compatibility"
|
require_relative "../date_and_time/compatibility"
|
||||||
require_relative "../module/remove_method"
|
require_relative "../module/redefine_method"
|
||||||
|
|
||||||
class Time
|
class Time
|
||||||
include DateAndTime::Compatibility
|
include DateAndTime::Compatibility
|
||||||
|
|
||||||
remove_possible_method :to_time
|
silence_redefinition_of_method :to_time
|
||||||
|
|
||||||
# Either return +self+ or the time in the local system timezone depending
|
# Either return +self+ or the time in the local system timezone depending
|
||||||
# on the setting of +ActiveSupport.to_time_preserves_timezone+.
|
# on the setting of +ActiveSupport.to_time_preserves_timezone+.
|
||||||
|
|
|
@ -5,8 +5,9 @@ str = "\xE6\x97\xA5\xE6\x9C\xAC\xE8\xAA\x9E" # Ni-ho-nn-go in UTF-8, means Japan
|
||||||
parser = URI::Parser.new
|
parser = URI::Parser.new
|
||||||
|
|
||||||
unless str == parser.unescape(parser.escape(str))
|
unless str == parser.unescape(parser.escape(str))
|
||||||
|
require "active_support/core_ext/module/redefine_method"
|
||||||
URI::Parser.class_eval do
|
URI::Parser.class_eval do
|
||||||
remove_method :unescape
|
silence_redefinition_of_method :unescape
|
||||||
def unescape(str, escaped = /%[a-fA-F\d]{2}/)
|
def unescape(str, escaped = /%[a-fA-F\d]{2}/)
|
||||||
# TODO: Are we actually sure that ASCII == UTF-8?
|
# TODO: Are we actually sure that ASCII == UTF-8?
|
||||||
# YK: My initial experiments say yes, but let's be sure please
|
# YK: My initial experiments say yes, but let's be sure please
|
||||||
|
|
|
@ -864,7 +864,11 @@ There are cases where you need to define a method with `define_method`, but don'
|
||||||
|
|
||||||
The method `redefine_method` prevents such a potential warning, removing the existing method before if needed.
|
The method `redefine_method` prevents such a potential warning, removing the existing method before if needed.
|
||||||
|
|
||||||
NOTE: Defined in `active_support/core_ext/module/remove_method.rb`.
|
You can also use `silence_redefinition_of_method` if you need to define
|
||||||
|
the replacement method yourself (because you're using `delegate`, for
|
||||||
|
example).
|
||||||
|
|
||||||
|
NOTE: Defined in `active_support/core_ext/module/redefine_method.rb`.
|
||||||
|
|
||||||
Extensions to `Class`
|
Extensions to `Class`
|
||||||
---------------------
|
---------------------
|
||||||
|
|
Loading…
Reference in New Issue