Никогда не встречали class << obj;self;end? Я сегодня узнал разницу между такой хитрой конструкцией и obj.class. Она тонкая, но есть.
> s = "new string"
> puts s.class
String
> def s.cool
> "cool"
> end
> puts s.class
String
Стоп! Нас жёстко обманывают! Никакой на самом деле s не String!!! Дело в том, что объекты в руби не имеют места, куда им запихнуть собственные (singleton) методы, попросту некуда! Вопрос, где же тогда хранится метод cool? В singleton-классе, обернутом вокруг String. В момент объявления singleton-метода cool происходит создание нового класса, в который вкладывается обычный класс String. Объект t превращается из String-а в этот виртуальный класс, сохраняя все признаки String-а.
Но что бы выглядеть на 100%, как String, этот хитрый класс перекрывает на себе метод class, который возвращает вложенный класс.
Именно поэтому он и выглядит как String. Как же добраться до этого мерзавца?
> class << s
> self
> end
#<Class:#<String:0xe498>>
> s.class.object_id
105500
> class << s; self; end.object_id
286700
Это очень такой ай-ай-айный механизм, но он есть. Перекрыть его невозможно, потому что self не перекрывается.
Соответственно, дальше уже смело можно определять ещё методы на s, они все будут создаваться на singleton классе, обёрнутом вокруг String.