Ruby: Можно ли определить метод класса в модуле?
Скажем, что есть три класса: A
, B
и C
. Я хочу, чтобы каждый класс имел метод класса, например self.foo
, который имеет точно такой же код для A
, B
и C
.
Можно ли определить self.foo
в модуле и включить этот модуль в A
, B
и C
? Я попытался сделать это и получил сообщение об ошибке, в котором говорилось, что foo
не распознается.
Ответы
Ответ 1
module Common
def foo
puts 'foo'
end
end
class A
extend Common
end
class B
extend Common
end
class C
extend Common
end
A.foo
Или вы можете расширить классы после этого:
class A
end
class B
end
class C
end
[A, B, C].each do |klass|
klass.extend Common
end
Ответ 2
Да
module Foo
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def some_method
# stuff
end
end
end
Одна из возможных замечаний, которые я должен добавить: если модуль будет использоваться всеми методами класса - лучше использовать только extend ModuleName
в модели и определять методы непосредственно в модуле вместо этого, вместо того, чтобы иметь модуль ClassMethods внутри модуля, a la
module ModuleName
def foo
# stuff
end
end
Ответ 3
Rails 3 представил модуль с именем ActiveSupport::Concern
, который имеет целью упростить синтаксис модулей.
module Foo
extend ActiveSupport::Concern
module ClassMethods
def some_method
# stuff
end
end
end
Это позволило нам сохранить несколько строк кода "шаблона" в модуле.
Ответ 4
Это базовая функциональность ruby mixin, которая делает рубин настолько особенным.
В то время как extend
превращает методы модуля в методы класса, include
превращает методы модуля в методы экземпляра в классе/модуле включения/расширения.
module SomeClassMethods
def a_class_method
'I´m a class method'
end
end
module SomeInstanceMethods
def an_instance_method
'I´m an instance method!'
end
end
class SomeClass
include SomeInstanceMethods
extend SomeClassMethods
end
instance = SomeClass.new
instance.an_instance_method => 'I´m an instance method!'
SomeClass.a_class_method => 'I´m a class method'
Ответ 5
Просто хотел расширить ответ Оливера Определить методы класса и методы экземпляра вместе в модуле.
module Foo
def self.included(base)
base.extend(ClassMethods)
end
module ClassMethods
def a_class_method
puts "ClassMethod Inside Module"
end
end
def not_a_class_method
puts "Instance method of foo module"
end
end
class FooBar
include Foo
end
FooBar.a_class_method
FooBar.methods.include?(:a_class_method)
FooBar.methods.include?(:not_a_class_method)
fb = FooBar.new
fb.not_a_class_method
fb.methods.include?(:not_a_class_method)
fb.methods.include?(:a_class_method)