Skip to content

Rename Namespace#singleton_class to avoid conflicting with Kernel#singleton_class #685

@paracycle

Description

@paracycle

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions