Clarification needed between class << ClassName and class ClassName

I'm in the middle of Why's Poignent guide, and this one has me
stumped. Let's say MyClass exists. I want to add to it. So far, all
the examples have done:

class MyClass
   def new_method
      ...
   end
end

Now he adds:

class << MyClass
   def new_method
      ...
   end
end

He states the difference is the << operator allows you to alter the
definition of an object. Isn't that what I'm doing in the first
example as well? Won't my new_method be added to the class regardless?
He also states:

When you see class << obj, believe in your heart, I'm adding directly
to the definition of obj.

I'm failing to see what the practical difference is...

The << indicates you're adding class level methods rather than instance level.

Example:

irb(main):001:0> class MyClass
irb(main):002:1> def hello
irb(main):003:2> puts "hello"
irb(main):004:2> end
irb(main):005:1> end
=> nil
irb(main):006:0> y = MyClass.new
=> #<MyClass:0x2873ac8>
irb(main):007:0> y.hello
hello
=> nil

But with the << operator, you can't call it on an instance.

irb(main):008:0> class << MyClass
irb(main):009:1> def goodbye
irb(main):010:2> puts "bye!"
irb(main):011:2> end
irb(main):012:1> end
=> nil
irb(main):013:0> y = MyClass.new
=> #<MyClass:0x2856d60>
irb(main):015:0> y.goodbye
NoMethodError: undefined method `goodbye' for #<MyClass:0x2856d60>
        from (irb):15
irb(main):016:0> MyClass.goodbye
bye!
=> nil
irb(main):017:0>

--Jeremy

So you're essentially added a protected method? Would that be correct?
(or is it private?) So the method could only be used by other methods
withing the class?

Hi --

The << indicates you're adding class level methods rather than instance level.>
But with the << operator, you can't call it on an instance.

So you're essentially added a protected method? Would that be correct?
(or is it private?) So the method could only be used by other methods
withing the class?

Doing this:

   class << obj
     ...
   end

opens up a class definition block for the "singleton class" of obj.
Inside that singleton class, the methods you define are available only
to obj. They're not private methods (which would also be available to
other objects of the same class as obj). They're *singleton* methods,
which exist only to be called by that one specific object.

When obj is a class, as in class << SomeClass, the singleton methods
pertain to the class object, and are also called "class methods".
(They are also special-cased in one or two ways; but for the most
part, giving singleton methods to class objects is just one variant of
the general practice of giving singleton methods to objects.)

See: http://www.wobblini.net/singletons.html for more explanation and
details.

David

Right. Just to tie it back to _why's book, this is where the original
explanation's mention of "changing the object's definition" comes in.
When you define a class, you're creating a Class instance, and when
you use <<, you're changing the definition of that Class instance
(i.e., changing that object's definition).

It's confusing, I know, but if you read David's link and play with it
in irb, it'll sink in. :slight_smile:

--Jeremy

I read that as:

when you see foo << bar, believe in your heart, I'm adding directly to
the definition of bar.

But, I could be wrong!

-Thufir

See: http://www.wobblini.net/singletons.html for more explanation and
details.

Hmm, David, your Ruby for Rails book appears to be exactly what I'm looking for to fill in the gaps between the Ruby docs and the Rails docs. Sold!

Yes, +1 to that. His chapter on this sort of object behavior and
other dynamic stuff is bar none. Excellent.

--Jeremy

Hi --

[...]

Ah, I thought it was a general sort of thing, with class as a
placeholder.

-Thufir

So, the following ought to work? Cuz the modules are loaded but
ArInstance.find does not invoke the logger.

Thx

module ArSugar
   def self.included(base)
     RAILS_DEFAULT_LOGGER.debug "running included code"
     base.class_eval do
       base.extend(ClassMethods)
       class << self
         alias_method(:old_find, :find) unless method_defined?
(:old_find)
       end
     end
   end

   module ClassMethods
     RAILS_DEFAULT_LOGGER.debug "class methods"
     def find(*args)
       RAILS_DEFAULT_LOGGER.debug "my finder"
       old_find(args)
     end
   end
end