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

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 Sun, Jan 16, 2011 at 7:59 AM, Josh Cheek <josh.cheek@gmail.com> wrote:

My questions are:
1. Is any other situation where this information is relevant?

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 8:13 AM, Victor Deryagin <vderyagin@gmail.com>wrote:

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

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.

···

On Tue, Jan 18, 2011 at 6:37 AM, Josh Cheek <josh.cheek@gmail.com> wrote:

On Sun, Jan 16, 2011 at 7:59 AM, Josh Cheek <josh.cheek@gmail.com> 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"