Kris
Kris

Reputation: 19948

How to define a const on the singleton class?

I want to define a constant for a single instance of an object, not for all instances of an object.

class MyTest
  def call
    Foo
  end
end

t = MyTest.new

t.call # => NameError (as expected)

t.singleton_class.class_eval do
  const_set 'Foo', 'foo'
end

t.singleton_class::Foo # => 'foo'
t.call # => NameError

Why does the const lookup not include the const defined in the objects singleton class?

Here is another attempt:

Dog = Class.new { def call; Bark; end }
d = Dog.new
d.call # => NameError (expected)
d.singleton_class.const_set('Bark', 'woof')
d.call # => NameError (not expected)

Upvotes: 3

Views: 328

Answers (4)

konsolebox
konsolebox

Reputation: 75488

The constant has been defined in the instance's singleton class but Foo doesn't include (singleton_class)::Foo in one of its possible evaluations so specify it explicitly:

class MyTest
  def call
    singleton_class::Foo
  end
end

Upvotes: 1

Cary Swoveland
Cary Swoveland

Reputation: 110675

Start be creating an arbitrary set and an instance of that set.

class A; end

a = A.new
  #=> #<A:0x00007ff0bbaa9f98>

For convenience, assign a's singleton class1 to a variable.

as = a.singleton_class
  #=> #<Class:#<A:0x00007ff0bbaa9f98>>

Create a constant in as whose value equals 3.

as.const_set(:A, 3)
  #=> 3

Check that the constant was defined.

as.constants
  #=> [:A]

Let's check its value also.

as.const_get(:A)
  #=> 3

Now let's create a method in as that references the constant we've created.

as.define_method(:cat) { puts "#{as.const_get(:A)} cats"}
  #=> :cat

Try it.

a.cat
  #=> "3 cats"

See Module#const_set and Module#const_get.

1. Every Ruby object has a singleton class. Singleton classes are like regular classes except they cannot be subclassed and one cannot create instances of subclasses.

Upvotes: 0

FriendFX
FriendFX

Reputation: 3079

I think this comment is correct - t.singleton_class.class_eval first gets the singleton_class of t and then evals something on that singleton classes' class.

Instead what you want is to define the constant on the instance of that singleton class, like so:

t.singleton_class.instance_eval do
  Foo = 'foo'
end

After that, t.call returns "foo".

Upvotes: 0

Thomas
Thomas

Reputation: 1633

I believe it is not possible:

  • const_set is defined on Module
  • Ruby looks for constant in nesting and ancestors

There is no place where to define the constant for a MyTest instance

class MyTest
  def call
    puts (Module.nesting + Module.ancestors).uniq.inspect
  end
end
MyTest.new.call
# => [MyTest, Module, Object, Kernel, BasicObject]

Upvotes: 0

Related Questions