Unexpected results when extending methods to class Class and class Object

Hey all,

There’s a core Class class and core Object class in Ruby library:

http://www.ruby-doc.org/core/classes/Object.html

http://www.ruby-doc.org/core/classes/Class.html

First, let’s resolve the simple distinction between an Object and Class
as envisioned by Smalltalk but within the Ruby context:

#A class is a template used to define methods and properties
class Hello
attr_accessor :say_what

private
def say_what(txt)
puts txt + ‘my friend’
end
end

#Objects are the things actually created out of a class and can be used
in client code
@obj = Hello.new
@obj.say_what “hello”

@obj2 = Hello.new
@obj2.say_what “bye”

Both @obj and @obj2 instance variables that store an object in a memory
location, but both of the objects are part of the blueprint Hello class.

That above is the simple distinction. But you must realize that there is
both a class Class and a class Object that can be extended. Let’s extend
Class and Object:

class Class
def dual_accessor(*attributes)
end
end

class Object
def try_safe(method_chain)
self.send_method_chain(method_chain)
end
end

When I first look at the above, my understanding is since a class can
have a class (static) method, you can specify them as being independent
of the object instances of the class: def self.hey end. Hence, those
methods will always refer directly to the class itself and return a
value for the class. Now when you add methods to class Class, those
methods become available to any class that is created as well (since all
classes created using class construct inherit from Class) and you can
use those methods within any class:

ruby-1.8.7-p330 :020 > class Class
ruby-1.8.7-p330 :021?> def huh
ruby-1.8.7-p330 :022?> puts ‘hey’
ruby-1.8.7-p330 :023?> end
ruby-1.8.7-p330 :024?> end
=> nil
ruby-1.8.7-p330 :025 > class Pizza
ruby-1.8.7-p330 :026?> huh
ruby-1.8.7-p330 :027?> end
hey
=> nil

But when we try to call a class (static) method on an instance (an
object), since an instance is not a class, but an object that was
created, calling a class method (a method defined within class Class or
a method defined in class with self) on an instance, raises an
exception:

ruby-1.8.7-p330 :054 > class Peach
ruby-1.8.7-p330 :055?> def yummy
ruby-1.8.7-p330 :056?> puts ‘yum yum’
ruby-1.8.7-p330 :057?> end
ruby-1.8.7-p330 :058?> end
=> nil
ruby-1.8.7-p330 :059 > class Class
ruby-1.8.7-p330 :060?> def tasty
ruby-1.8.7-p330 :061?> puts ‘tasty tasty’
ruby-1.8.7-p330 :062?> end
ruby-1.8.7-p330 :063?> end
=> nil
ruby-1.8.7-p330 :064 > @yum = Peach.new
=> #Peach:0x105e4c2e8
ruby-1.8.7-p330 :065 > @yum.tasty
NoMethodError: undefined method `tasty’ for #Peach:0x105e4c2e8
from (irb):65
from :0

Now when you extend the Object core library with methods, since the
class Class is an object, any class created will inherit the method
extended to Object class. Also, since when we create an instance of a
class, that instance is an object, that instance will inherit the method
extended to Object class:

ruby-1.8.7-p330 :041 > class Apple
ruby-1.8.7-p330 :042?> def like_apples?
ruby-1.8.7-p330 :043?> puts ‘true’
ruby-1.8.7-p330 :044?> end
ruby-1.8.7-p330 :045?> end
=> nil
ruby-1.8.7-p330 :046 > class Object
ruby-1.8.7-p330 :047?> def like_apples!
ruby-1.8.7-p330 :048?> puts ‘hell yeah’
ruby-1.8.7-p330 :049?> end
ruby-1.8.7-p330 :050?> end
=> nil
ruby-1.8.7-p330 :051 > @apple = Apple.new
=> #Apple:0x105f15af8
ruby-1.8.7-p330 :052 > Apple.like_apples!
hell yeah
=> nil
ruby-1.8.7-p330 :053 > @apple.like_apples!
hell yeah
=> nil

If a class is an object and an object is a class:

ruby-1.8.7-p330 :039 > Class.kind_of? Object
=> true
ruby-1.8.7-p330 :040 > Object.kind_of? Class
=> true

Then why can we access an instance method (extended via Object) directly
as a class method (as shown above), but we cannot access a class method
with an instance of a class (as shown above)?

Thanks for response

On May 13, 10:43pm, John M. [email protected] wrote:

If a class is an object and an object is a class:

ruby-1.8.7-p330 :039 > Class.kind_of? Object
=> true
ruby-1.8.7-p330 :040 > Object.kind_of? Class
=> true

Wrong - not all objects are classes. Object is an instance of Class,
but instances of Object aren’t i.e. Object.kind_of? Class is the wrong
test - Object.new.kind_of?(Class) (or in your specific example,
Peach.new.kind_of?(Class)) is the relevant one.

Fred

but instances of Object aren’t i.e. Object.kind_of? Class is the wrong
test - Object.new.kind_of?(Class) (or in your specific example,
Peach.new.kind_of?(Class)) is the relevant one.

Fred

Thanks for response

I still dont think I am fully clear on the Class class role.

For example:

ruby-1.8.7-p330 :010 > class Class
ruby-1.8.7-p330 :011?> def is_a?
ruby-1.8.7-p330 :012?> puts ‘a’
ruby-1.8.7-p330 :013?> end
ruby-1.8.7-p330 :014?> end
=> nil
ruby-1.8.7-p330 :015 > A.is_a?
a
=> nil
ruby-1.8.7-p330 :016 > class A
ruby-1.8.7-p330 :017?> def a_is_a
ruby-1.8.7-p330 :018?> is_a?
ruby-1.8.7-p330 :019?> end
ruby-1.8.7-p330 :020?> end
=> nil
ruby-1.8.7-p330 :021 > @a = A.new
=> #<A:0x1069b8460>
ruby-1.8.7-p330 :022 > @a.a_is_a
ArgumentError: wrong number of arguments (0 for 1)

It says wrong number of arguments, despite there is nothing in argument
list in method definition. a_is_a is a getter method. It returns a value
returned by the class method is_a? defined in class Class. Since class A
is a class, it should have access to class methods of Class (and it
indeed works if you call the class method outside scope of instance
method definition and within the scope of the class A). Or is this an
issue of scope, where you cannot try to access a class method within an
instance method? But then why does it throw error it does?

Thanks for response.

On May 17, 7:43pm, John M. [email protected] wrote:

ruby-1.8.7-p330 :015 > A.is_a?
ruby-1.8.7-p330 :022 > @a.a_is_a

Your a_is_a is an instance method so is_a? is shorthand for self.is_a?
and in this case self is the instance of A, not A itself, so it’s
calling the instance method is_a? that A inherits from Object, not the
one you defined on Class. That is_a? method requires an argument so
you get the expected error.
You seem to be conflating a certain class object responding to a
method and instances of that class responding to it.

Fred