Forum: Ruby Access module's anonymous parent

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Daa9d36e8d07746041fb5650aba957b1?d=identicon&s=25 Xavier Shay (xaviershay2)
on 2008-10-27 09:15
I want the following eval to output "Foo called", without having a named
constant, since in leaks in to the calling scope:

m = Module.new
m.instance_eval do
  def foo
    puts "Foo called"
  end
  ParentSelf = self
  class self::A
    ParentSelf.foo # How do I do this without a constant?
  end
end
a = m.const_get('A').new
puts ParentSelf # This should raise - constant should not exist


I have found given self and a global function, I can use the string
representation of the anon module to find it in ObjectSpace. Clearly
this is dodgy though:

def depend(instance, file)
  q = self.to_s.split('::').first.to_s
  found = nil
  ObjectSpace.each_object(Module) do |m|
    found = m if q == m.to_s
  end
  found.depend(file)
end

m = Module.new
m.instance_eval do
  def depend(file)
    puts "Depend called with file: #{file}"
  end

  class self::A
    depend(self, 'afile.rb')
  end
end
753dcb78b3a3651127665da4bed3c782?d=identicon&s=25 Brian Candler (candlerb)
on 2008-10-27 09:41
Is this what you're looking for?

m = Module.new
m.instance_eval do
  def foo
    puts "Foo called"
  end
  parent = self
  child = class self::A; self; end
  child.class_eval { parent.foo }
end
a = m.const_get('A').new
puts ParentSelf # This should raise - constant should not exist

(you can also use 'm.foo' instead of 'parent.foo', which means you can
remove the assignment 'parent = self')
Daa9d36e8d07746041fb5650aba957b1?d=identicon&s=25 Xavier Shay (xaviershay2)
on 2008-10-27 22:54
Brian Candler wrote:
> Is this what you're looking for?
>
> m = Module.new
> m.instance_eval do
>   def foo
>     puts "Foo called"
>   end
>   parent = self
>   child = class self::A; self; end
>   child.class_eval { parent.foo }
> end
> a = m.const_get('A').new
> puts ParentSelf # This should raise - constant should not exist
>
> (you can also use 'm.foo' instead of 'parent.foo', which means you can
> remove the assignment 'parent = self')

I would have liked to keep the more familiar class syntax without
resorting to class_eval

>   parent = self
>   child = class self::A; self; end
>   child.class_eval { parent.foo }

I'm autoloading a file, it contains this part, so it's basically the
public interface.

> class self::A
>   parent.foo

And for that reason this is a much nicer, I think. But if it can't be
done like this, it can't be done...
50b2daf0e7666574579b9edaf8f2b69a?d=identicon&s=25 Pit Capitain (Guest)
on 2008-10-28 08:06
(Received via mailing list)
2008/10/27 Xavier Shay <contact@rhnh.net>:
> I'm autoloading a file, it contains this part, so it's basically the
> public interface.
>
>> class self::A
>>   parent.foo
>
> And for that reason this is a much nicer, I think. But if it can't be
> done like this, it can't be done...

Xavier, I don't know why you want to have an anonymous module with a
local constant, but if you really need this and are willing to change
the public interface a little bit you could do

  m = Module.new
  m.module_eval do
    # methods for dependent classes
    def depend(file)
      puts "#{self} depends on #{file}"
    end
    # methods to define a dependent class
    def self.const_missing(name)
      const_set(name, Class.new)
    end
    def self.dependent_class(cls, &blk)
      parent_module = self
      cls.class_eval do extend(parent_module) end
      cls.class_eval(&blk)
    end
    # public interface for dependent classes
    dependent_class self::A do
      depend("afile.rb")
    end
  end

Regards,
Pit
Daa9d36e8d07746041fb5650aba957b1?d=identicon&s=25 Xavier Shay (xaviershay2)
on 2008-10-28 08:43
Pit Capitain wrote:
> 2008/10/27 Xavier Shay <contact@rhnh.net>:
>> I'm autoloading a file, it contains this part, so it's basically the
>> public interface.
>>
>>> class self::A
>>>   parent.foo
>>
>> And for that reason this is a much nicer, I think. But if it can't be
>> done like this, it can't be done...
>
> Xavier, I don't know why you want to have an anonymous module with a
> local constant
Hot loading of code. I'm being a bit anal about it, also I'm just
curious :)
Would be nice to not need the dependent_class bit, but it's not so bad.

I had to hack your code a bit to get what I want, which is this:

m = Module.new
m.instance_eval do
  self::Depend = []

  def self.const_missing(name)
    const_set(name, Class.new)
  end

  def self.dependent_class(cls, &blk)
    parent_module = self
    (class << cls; self; end).send(:define_method, :depend) do |file|
      parent_module::Depend << file
    end
    cls.class_eval(&blk)
  end

  dependent_class self::A do
    depend("afile.rb")

    def foo
      "foo"
    end
  end
end

a = m.const_get('A').new
puts a.foo # => foo
puts m::Depend.inspect # => ["afile.rb"]

Thanks!
Xavier
This topic is locked and can not be replied to.