-
Notifications
You must be signed in to change notification settings - Fork 1
Rename Namespace#singleton_class to avoid conflicting with Kernel#singleton_class #685
Description
Rubydex::Namespace defines a singleton_class instance method, which shadows Kernel#singleton_class. This causes problems for tools (like IRB's ls command) that call singleton_class on arbitrary objects and expect it to return Ruby's actual singleton class.
Example
Running ls on a Rubydex object in IRB crashes because IRB calls singleton_class on the object, gets a Rubydex::SingletonClass instance back instead of a real Class, and then tries to call >= on it:
irb> ls graph.declarations.drop(10).first
undefined method '>=' for an instance of Rubydex::SingletonClass (NoMethodError)
singleton_ancestors = (singleton_class&.ancestors || []).reject { |c| c >= Class }
Analysis
An audit of all top-level Rubydex constants shows that Namespace#singleton_class is the only method that conflicts with a Kernel instance method:
Rubydex.constants.filter_map do |c|
m = Rubydex.const_get(c)
next unless m.is_a?(Module)
kernel_methods = m.instance_methods(false) & Kernel.instance_methods - [:to_s, :<=>]
[m, kernel_methods] unless kernel_methods.empty?
end
#=> [[Rubydex::Namespace, [:singleton_class]]]While individual tools like IRB can be fixed to be more defensive (e.g. using Class >= c instead of c >= Class), we should rename singleton_class on our side to avoid giving grief to any gem that reasonably expects standard Ruby semantics from that method name.