Когда вы определяете метод с помощью class << self
, вы определяете метод класса. Это то же самое, что и определение метэда следующим образом:
class Foo
def self.foo
puts 'foo'
end
# the above definition is the same as doing:
class << self
def foo
puts 'foo'
end
end
end
Выше показано 2 способа определения методов класса, которые вызываются непосредственно в классе, а не в экземплярах класса. Вы можете использовать второй синтаксис, если хотите определить только методы класса или несколько из них внутри блока class << self
. Но любой стиль имеет одинаковый результат.
Поскольку вы определили метод класса в модуле Direction
, include
или extend
не будут наследовать метод класса этого модуля. Это ожидаемое поведение.
Если вы хотите использовать наследование с методами класса из модуля, вы должны сделать это так, как описано ниже в статье, которую вы связали
module Direction
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def stop
puts 'stopping!'
end
end
def straight
puts "going straight!"
end
def turn
puts "turning!"
end
end
class Car
include Direction
end
Теперь вызывая методы классаCar
наследует, как определено в классе Direction
.
Car.stop
stopping!
=>nil # calling a method will return nil unless the method returns a value.
Однако всегда будьте осторожны при использовании любого рода наследования, поскольку Ruby является динамическим языком. Поэтому, если вы выполните приведенный выше код, а затем переопределите этот метод:
module Direction
module ClassMethods
def stop
puts 'go!'
end
end
end
Угадайте, что произойдет, если вы сделаете это:
Car.stop
Поскольку метод был определен внутри Direction
модуль, когда метод вызывается Car
, он будет вызывать метод из модуля Direction
.
Car.stop
go!
=>nil
Обновлен на основе комментариев:
Если вы предпочитаете использовать extend
против include
вместо этого вам нужно будет сделать следующее:
module Direction
def self.extended(base)
base.extend(ClassMethods)
end
module ClassMethods
def stop
puts 'stopping!'
end
end
end
class Car
extend Direction
end
В этом примере все методы, которые были унаследованы от модуля, «копируются» в класс, расширяющий их. Это позволяет избежать проблемы возможного результата переопределения метода модуля, о котором я предупреждал при использовании include
ранее в моем ответе.
Но вы можете посмотреть ответы на на этот вопрос , чтобы узнать, когда и зачем использовать тот или иной случай.