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