Current class

J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

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]
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

Concept of current class is well-explained in this article:
http://yugui.jp/articles/846
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")
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

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"
 
J

Josh Cheek

[Note: parts of this message were removed to make it a legal post.]

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"
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.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top