Подтвердить что ты не робот

Почему мы помещаем модуль внутри класса в Ruby?

В Ruby я вижу, что полезно использовать классы внутри модулей ради пространства имен. Я также вижу, что можно поместить модули внутри классов. Но я не понимаю, почему вы это сделаете.

Модули обычно смешиваются с классами, правильно? Итак, какова была бы цель определения модуля внутри класса?

4b9b3361

Ответ 1

Мы могли бы использовать его при написании подобного обезьяне кода:

class DrugDealer
  module Drug
    def happy?; true; end
  end

  def approach(victim)
    victim.extend Drug
  end
end

o = Object.new
DrugDealer.new.approach(o)
o.happy? # => true

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

Это полезно, когда некоторые грани предмета применяются к некоторым подклассам, а другие грани применяются к другим подклассам, если не существует достаточного порядка в том, как эти аспекты применяются, чтобы освободить место для ясной иерархии классов (дерева). Подумайте о множественном наследовании! Упрощенный пример:

class Person
  def handshake
    :sloppy
  end

  def mind_contents
    :spam
  end

  module Proper
    def handshake
      :firm
    end
  end

  module Clever
    def mind_contents
      :theories
    end
  end
end

class Professor < Person
  include Proper
  include Clever

  # ...
end

И так далее. Вид приятный, когда используется разумно. Даже супер-вызовы и конструкторы (я не определял их здесь) протекают через все микшины и классы так, как я их хочу.

Ответ 2

class Image
    module Colors
        Red = ...
        Blue = ...
    end
    include Colors
end

include Image::Colors

Image.new.set_pixel x, y, Red 

Ответ 3

С тех пор я столкнулся с прецедентом в большом приложении Rails со сложным пространством имен. Упрощенный пример:

# app/models/invoice/dependents/item.rb
class Invoice
  module Dependents
    class Item
      # Define invoice item
    end
  end
end

Здесь Invoice - это собственный класс, но также является хорошим пространством имен для его зависимых элементов. Мы не можем сказать module Invoice, потому что эта константа уже определена как класс, но мы все равно можем использовать ее как пространство имен.

Гигантский оговорка

Если вы используете класс как пространство имен и используете Rails, убедитесь, что не объявляете этот класс в другом месте. Автозагрузка погубит ваш день. Например:

# app/helpers/invoice/dependents/items_helper.rb
class Invoice       # This line will cause you grief
  module Dependents
    module ItemsHelper
      # view helper methods
    end
  end
end

Тот факт, что class Invoice указан в этом файле, создает зависимость порядка загрузки; если эта строка class Invoice выполняется до вашего предполагаемого определения класса, определение предполагаемого класса может не работать должным образом. В этом примере я не могу объявить, что Invoice sublcasses ActiveRecord::Base, если Invoice уже объявлен без родительского класса.

Вы могли требовать, чтобы ваш "истинный" класс определял файл вверху другого файла, но, по крайней мере, в сценарии автозагрузки Rails, у вас будет меньше проблем, если вы сделаете это вместо этого:

# app/helpers/invoice/dependents/items_helper.rb
module Invoice:Dependents::ItemsHelper  
  # view helper methods
end

С помощью этого синтаксиса Rails увидит константу Invoice и будет использовать autoload, чтобы найти ее, найти ее в файле модели и определить ее так, как вы планировали.

Ответ 4

Я предполагаю, что это действительно просто использование класса в качестве пространства имен, что иногда просто более удобно, чем помещение всего в модуль. Ive никогда не видел это на практике, но его вполне корректный Ruby-код в любом случае.

Единственный реальный сценарий, о котором я могу думать, - использовать EventMachine в классе:

class Api
  def initialize
    EM.start_server "0.0.0.0", 8080, Server
  end

  module Server
    def receive_data (data)
      # do stuff
    end
  end
end