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:

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

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

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.

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?

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