Ruby self.extended вызывается как метод экземпляра

module Country
  def location
    puts "location"
  end

  def self.included(base)
    def cities
      puts "cities"
    end
  end

  def self.extended(base)
    def animals
      puts "animals"
    end
  end
end

class Test
  include Country
end

class Test2
  extend Country
end

Насколько я понимаю, self.included будет вызываться, когда модуль включается как метод экземпляра, тогда как self.extended будет вызываться, когда модуль расширяется как метод статического класса.

Но когда у меня есть два класса в одном файле, почему он не выдает ошибку

Тест.новых.животных

=>животные

И если бы я удалил класс Test 2,

 # class Test2
  # extend Country
# end

Тест.новых.животных

=> Нет ошибки метода


person noobyz    schedule 07.05.2020    source источник
comment
Вы можете определить произвольные методы с extended, но не с def в этом блоке. Вы должны сделать base.define_method(...), если вам нужно сделать это динамически.   -  person tadman    schedule 08.05.2020


Ответы (2)


def bar без явного definee (т. е. def foo.bar) определяет bar в ближайшем лексически охватывающем модуле. Ближайшим лексически охватывающим модулем для всех трех ваших def всегда является Country, поэтому все три метода определены в модуле Country.

Если вы хотите определить одноэлементный метод, вы можете использовать

module Country
  def self.extended(base)
    def base.animals
      puts "animals"
    end
  end
end

Дополнительные сведения см. в разделе Ruby, какой класс получает метод, когда нет явного получателя?.

person Jörg W Mittag    schedule 08.05.2020
comment
как насчет определения self.include? мне нужно сделать то же самое, что и def base.cities? - person noobyz; 11.05.2020

Возможно, я прямо проясню, что, по моему мнению, не полностью и явно адресовано красивому ответу Йорга (с максимальным уважением) тем, кто не очень хорошо знаком с Ruby "Объектная модель":

module Country
  def location
    puts "location"
  end

  def self.included(base)
    def cities
      puts "cities"
    end
  end

  def self.extended(base)

    puts "#{self}"  ## NOTICE THIS NEW LINE! NEW LINE

    def animals
      puts "animals"
    end
  end
end


class Test
  include Country
end

class Test2
  extend Country
end

Test.new.animals

В чем проблема?

Мы расширяем Test2, не так ли? Как же тогда определяется метод животных в Test1?

Ключевым моментом является добавление строки puts "#{self} над методом животных.

Здесь мы видим, что метод животных определен в модуле Country. Так что на самом деле, когда вы думаете, что расширяете, вы на самом деле убедитесь, что он добавлен как метод экземпляра, а не как «метод статического класса» (если вы исходите из фона С#/java). Строго говоря, это не совсем точно: когда вы «расширяете» таким образом — и если вы делаете это правильно — вы фактически добавляете метод в одноэлементный класс Test2. В этом отношении объектная модель Ruby немного сложна. Метод статического класса — это метод, ДОБАВЛЕННЫЙ в одноэлементный класс класса. Что такое одноэлементный класс? Теперь вы попадаете в объектную модель ruby. Это сложно и немного отнимает мозги, но как только вы это освоите, вы сможете делать довольно мощные (и опасные?) вещи, такие как: исправление обезьян.

Решение:

Йорг говорит лучше, чем я. вам нужно определить животных следующим образом: def base.animals.

person BKSpurgeon    schedule 08.05.2020
comment
Вы делаете это, если хотите создать метод статического класса - это, строго говоря, не совсем точно, я пишу это только потому, что вы исходите из фона java/c# - person BKSpurgeon; 11.05.2020
comment
Итак, я верю в def self.included(base), мне не нужно указывать def base.cities правильно? - person noobyz; 12.05.2020
comment
@noobyz это неправильно: если вы хотите создать метод статического класса, вы ДОЛЖНЫ поместить: def base.cities внутри def self.included(base) - person BKSpurgeon; 12.05.2020
comment
но я подумал, что когда мы включаем модуль, вся функция становится экземпляром класса, в который она включается, и наоборот для исключения модуля. Может быть, я упускаю из виду цель использования def self.included/self.excluded? - person noobyz; 13.05.2020
comment
Да, но вы допустили ошибку в коде. вы забыли слово base при определении метода статического класса, и это слово имеет значение. - person BKSpurgeon; 13.05.2020