Current class

Going through Metaprogramming Ruby, and the author, Paolo Perrotta
points
out something I never paid attention to before.

class MyClass
def method_one
def method_two
“hello!”
end
end
end

obj = MyClass.new
obj.method_one
obj.method_two # => “Hello!”

my addition to show it is on MyClass, not obj’s singleton class

MyClass.new.method_two # => “hello!”

He uses this example to show that Ruby keeps track of the current class.
The
definition of method_two needs to know that it is being defined on, so
even
though it is being executed in an instance of MyClass, it is defined as
an
instance method of MyClass itself. This, understanding that Ruby keeps
track
of not only self, but also the current class, is then used to explain
the
difference between class_eval and instance_eval.

My questions are:

  1. Is any other situation where this information is relevant?
  2. Is there a way to access the current class? In the example below, you
    can
    see that both instance_eval and class_eval set self to the class, but I
    can’t ask for the current class (the place methods would be defined on),
    I
    only know how to ask for self’s class.

class MyClass
end

MyClass.instance_eval do
self # => MyClass
self.class # => Class
def current_class_is_singleton; end
end

MyClass.class_eval do
self # => MyClass
self.class # => Class
def current_class_is_self; end
end

MyClass.instance_methods(false) # => [:current_class_is_self]
MyClass.singleton_methods # => [:current_class_is_singleton]

Concept of current class is well-explained in this article:
http://yugui.jp/articles/846

On Sun, Jan 16, 2011 at 8:13 AM, Victor D.
[email protected]wrote:

Concept of current class is well-explained in this article:
Three implicit contexts in Ruby - 世界線航跡蔵

Thanks, Victor. From that article, it seems the answers to my questions
are:

  1. No (implicit in that no other use case was mentioned)
  2. No (explicit from the quote “there is no way to retrieve it
    directly”)

On Sun, Jan 16, 2011 at 7:59 AM, Josh C. [email protected]
wrote:

My questions are:

  1. Is any other situation where this information is relevant?

I found one more situation where this knowledge is relevant.

When you are in your main object, and you define methods (here, they
feel
like functions), they become available to all objects. The reason for
this
is because the current class is set to Object, so defining methods in
main
defines them as instance methods of Object, which is an ancestor of most
classes, so most objects can now invoke this method.

To test this, I went to Ruby 1.9, which has BasicObject that does not
inherit from Object, and showed that you can invoke f from an instance
of
Object, but not an instance of BasicObject.

RUBY_VERSION # => “1.9.1”

create a new function in the main object

self # => main
defined? f # => nil
def f
‘you see f’
end

current class is Object, so it defines the method on object

method :f # => #<Method: Object#f>
Object.instance_method :f # => #<UnboundMethod: Object#f>

Object inherits from BasicObject

BasicObject inherits from nothing

Object.ancestors # => [Object, Kernel, BasicObject]
BasicObject.ancestors # => [BasicObject]

so if f exists on instances of Object,

but not instances of BasicObject,

then the explanation of top-level “functions” is that

current class is Object, so def keyword defines methods in Object

and almost everything inherits from Object, so it is visible

everywhere
Array.new.instance_eval { f } # => “you see f”
begin
BasicObject.new.instance_eval { f } # =>
rescue
$!.to_s # => “undefined local variable or method `f’ for
#BasicObject:0x428a80
end

But the main object must be declared privately,

to which prevent it from showing up in objects’ methods lists

or being able to do stupid things like this

begin
Array.new.f # =>
rescue
$!.to_s # => “private method `f’ called for []:Array”
end

to test this, the above code should work if we make f public

public :f
Array.new.f # => “you see f”

On Tue, Jan 18, 2011 at 6:37 AM, Josh C. [email protected]
wrote:

is because the current class is set to Object, so defining methods in main

Object.instance_method :f # => #<UnboundMethod: Object#f>

then the explanation of top-level “functions” is that

to test this, the above code should work if we make f public

public :f
Array.new.f # => “you see f”

Okay, and this, then, explains, why you can include modules into the
general
namespace, and then the methods become available. Because you are
actually
including them on Object, so they become available everywhere. Which is
actually dangerous!

require ‘fileutils’
require ‘digest’

class User

def initialize(attributes)
@attributes = attributes
end

def method_missing(meth)
@attributes[meth]
end

def password_hash
Digest::MD5.hexdigest(pwd)
end

end

bill = User.new :name => ‘bill’ , :pwd => ‘a19532SDjkl’

store the password hash before and after including FileUtils

before = bill.password_hash
include FileUtils
after = bill.password_hash

compare them

before == after # => false
before # => “baa0396a6b57358e05e48e66be123288”
after # => “badfb5b273616983aa8bea8b099bf343”

so what happened?

str = ‘a19532SDjkl’ # => “a19532SDjkl”
Digest::MD5.hexdigest(str) # => “baa0396a6b57358e05e48e66be123288”
before # => “baa0396a6b57358e05e48e66be123288”

pwd # => “/Users/joshuacheek”
Digest::MD5.hexdigest(pwd) # => “badfb5b273616983aa8bea8b099bf343”
after # => “badfb5b273616983aa8bea8b099bf343”

So what you should actually do is extend FileUtils instead of include FileUtils, because Extend will put the module on the main object’s
singleton class, and it won’t be visible to any other objects.