Constants and metaclasses

Is there a way to define a constant in a metaclass and then have it used
in methods defined in the original class?

An example:

class Foo
  def foo
    p BAR
  end
end

f = Foo.new
class <<f
  BAR = "Hello"

  def bar
    p BAR
  end
end

f.bar
f.foo

The line "f.bar" will work fine, but "f.foo" will fail with "NameError:
uninitialized constant Foo::BAR". Is there some way to have the method
defined in the original class pick up on the constant in the metaclass
(without altering "p BAR")?

···

--
Andreas Launila

Andreas Launila wrote:

Is there a way to define a constant in a metaclass and then have it used
in methods defined in the original class?

An example:

class Foo
  def foo
    p BAR
  end
end

f = Foo.new
class <<f
  BAR = "Hello"

  def bar
    p BAR
  end
end

f.bar
f.foo

The line "f.bar" will work fine, but "f.foo" will fail with "NameError:
uninitialized constant Foo::BAR". Is there some way to have the method
defined in the original class pick up on the constant in the metaclass
(without altering "p BAR")?

Yes, with a little wrangling:

class Object
   def singleton_class
     class << self; self; end
   end
end

class Foo
   def foo
     p self.singleton_class::BAR
   end
end

f = Foo.new
class <<f
   BAR = "Hello"

   def bar
     p BAR
   end
end

f.bar
f.foo

···

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Hi --

···

On Tue, 12 Jun 2007, Andreas Launila wrote:

Is there a way to define a constant in a metaclass and then have it used
in methods defined in the original class?

An example:

class Foo
def foo
   p BAR
end
end

f = Foo.new
class <<f
BAR = "Hello"

def bar
   p BAR
end
end

f.bar
f.foo

The line "f.bar" will work fine, but "f.foo" will fail with "NameError:
uninitialized constant Foo::BAR". Is there some way to have the method
defined in the original class pick up on the constant in the metaclass
(without altering "p BAR")?

I don't think so (and I hope not, as it would be kind of strange :slight_smile:
but you could define BAR in the original class:

   class << f
      Foo::BAR = "Hello"

etc.

David

--
* Books:
   RAILS ROUTING (new! http://safari.awprofessional.com/9780321509246\)
   RUBY FOR RAILS (http://www.manning.com/black\)
* Ruby/Rails training
     & consulting: Ruby Power and Light, LLC (http://www.rubypal.com)

Joel VanderWerf wrote:

Andreas Launila wrote:

The line "f.bar" will work fine, but "f.foo" will fail with "NameError:
uninitialized constant Foo::BAR". Is there some way to have the method
defined in the original class pick up on the constant in the metaclass
(without altering "p BAR")?

Yes, with a little wrangling:

class Object
  def singleton_class
    class << self; self; end
  end
end

class Foo
  def foo
    p self.singleton_class::BAR
  end
end

Yes that works, but I'm specifically looking for something that does not
alter the line "p BAR". To get around that one would have to dynamically
set the constant BAR in the main class before executing "p BAR". A
problem with that comes when one has multiple instances and metaclasses,
one would end up changing the value of a constant each time the method
is invoked. Thanks for the reply though.

···

--
Andreas Launila

Andreas Launila wrote:

Joel VanderWerf wrote:

Andreas Launila wrote:

The line "f.bar" will work fine, but "f.foo" will fail with "NameError:
uninitialized constant Foo::BAR". Is there some way to have the method
defined in the original class pick up on the constant in the metaclass
(without altering "p BAR")?

Yes, with a little wrangling:

class Object
  def singleton_class
    class << self; self; end
  end
end

class Foo
  def foo
    p self.singleton_class::BAR
  end
end

Yes that works, but I'm specifically looking for something that does not
alter the line "p BAR". To get around that one would have to dynamically
set the constant BAR in the main class before executing "p BAR". A
problem with that comes when one has multiple instances and metaclasses,
one would end up changing the value of a constant each time the method
is invoked. Thanks for the reply though.

Oops, sorry.

It doesn't seem possible to use const_missing to do this, because const_missing is called on Foo (not on the instance or the singleton class) and there is no way to identify the instance of Foo in the const_missing method.

···

--
       vjoel : Joel VanderWerf : path berkeley edu : 510 665 3407

Joel VanderWerf wrote:

It doesn't seem possible to use const_missing to do this, because
const_missing is called on Foo (not on the instance or the singleton
class) and there is no way to identify the instance of Foo in the
const_missing method.

I didn't know that const_missing existed, that might be just enough to
not have to redefine constants if one accepts some dirtiness. Here is an
adaptation of your code with that thrown in.

class Object
  def singleton_class
    class << self; self; end
  end
end

class Foo
  def foo
    p BAR
  end

  private

  def self.const_missing(name)
    if !@caller.nil? and @caller.singleton_class.const_defined? name
      @caller.singleton_class.const_get(name)
    else
      super
    end
  end

  def self.next_caller(caller)
    @caller = caller
  end
end

f = Foo.new
class <<f
  BAR = "Hello"

  alias_method :old_foo, :foo
  def foo
    self.class.next_caller(self)
    old_foo
    self.class.next_caller(nil)
  end
end

g = Foo.new
class <<g
  BAR = "Goodbye"

  alias_method :old_foo, :foo
  def foo
    self.class.next_caller(self)
    old_foo
    self.class.next_caller(nil)
  end
end

f.foo
g.foo
f.foo

Outputs
"Hello"
"Goodbye"
"Hello"

It isn't pretty, it isn't thread safe, it will not work if Foo is called
directly, it could be shortened/generalized/altered/improved depending
on the purpose.

Thank you :slight_smile:

···

--
Andreas Launila