ruby module include extend

¿Cuál es la diferencia entre incluir y extender en Ruby?



module include (6)

Solo metiendo la cabeza en Ruby metaprogramando. Los mixin / módulos siempre logran confundirme.

  • incluye : mezclas en métodos de módulo especificados como métodos de instancia en la clase objetivo
  • extend : mezcla en métodos de módulo especificados como métodos de clase en la clase objetivo

Entonces, ¿la principal diferencia es solo esto o acecha un dragón más grande? p.ej

module ReusableModule def module_method puts "Module Method: Hi there!" end end class ClassThatIncludes include ReusableModule end class ClassThatExtends extend ReusableModule end puts "Include" ClassThatIncludes.new.module_method # "Module Method: Hi there!" puts "Extend" ClassThatExtends.module_method # "Module Method: Hi there!"


Eso es correcto.

Detrás de escena, include es en realidad un alias para append_features , que (de la documentación):

La implementación predeterminada de Ruby es agregar las constantes, métodos y variables de módulo de este módulo a un Módulo si este módulo aún no se ha agregado a un Módulo o uno de sus ancestros.


Lo aprendí antes, pero lo aprecio cuando lo uso. Aquí está la diferencia:

Esto no funciona pero funcionaría si lo hubiera definido como def page_views(campaign) :

class UserAction include Calculations def self.page_views(campaign) overall_profit = calculate_campaign_profit(campaign) end end

Esto funciona:

class UserAction extend Calculations def self.page_views(campaign) overall_profit = calculate_campaign_profit(campaign) end end


Lo que has dicho es correcto. Sin embargo, hay más que eso.

Si tiene una clase Klazz y un módulo Mod , incluyendo Mod en Klazz le da instancias de acceso Klazz a los métodos de Mod . O puede extender Klazz con Mod dando a la clase Klazz acceso a los métodos de Mod . Pero también puede extender un objeto arbitrario con o.extend Mod . En este caso, el objeto individual obtiene los métodos de Mod , aunque no todos los demás objetos con la misma clase que o .


También me gustaría explicar el mecanismo como funciona. Si no estoy en lo correcto, por favor corrija.

Cuando usamos include estamos agregando un enlace de nuestra clase a un módulo que contiene algunos métodos.

class A include MyMOd end a = A.new a.some_method

Los objetos no tienen métodos, solo las clases y los módulos. Entonces, cuando a recibe a mensaje some_method , comienza a buscar el método some_method en la clase de eigen de a, luego en la clase A y luego en los módulos de clase A vinculados si hay algunos (en orden inverso, las últimas victorias incluidas).

Cuando usamos la extend , estamos agregando un enlace a un módulo en la clase eigen del objeto. Entonces, si usamos A.new.extend (MyMod) estamos agregando un enlace a nuestro módulo a la clase eigen de instancia de A o a a'' clase a'' . Y si usamos A.extend (MyMod) estamos agregando enlaces a A (objeto, las clases también son objetos) eigenclass A'' .

por lo tanto, la ruta de búsqueda de métodos para a es la siguiente: a => a ''=> módulos vinculados a'' class => A.

También hay un método prepend que cambia la ruta de búsqueda:

a => a ''=> modulesto prependido A => A => módulo incluido a A

Perdón por mi mal ingles.


Todas las demás respuestas son buenas, incluida la sugerencia para excavar en RubySpecs:

https://github.com/rubyspec/rubyspec/blob/master/core/module/include_spec.rb

https://github.com/rubyspec/rubyspec/blob/master/core/module/extend_object_spec.rb

En cuanto a los casos de uso:

Si incluye el módulo ReusableModule en la clase ClassThatIncludes, se hace referencia a los métodos, constantes, clases, submódulos y otras declaraciones.

Si extiende la clase ClassThatExtends con el módulo ReusableModule, los métodos y las constantes se copian . Obviamente, si no tiene cuidado, puede desperdiciar mucha memoria duplicando dinámicamente las definiciones.

Si usa ActiveSupport :: Concern, la funcionalidad .included () le permite reescribir la clase de inclusión directamente. El módulo ClassMethods dentro de una preocupación se extiende (copia) a la clase que incluye.


extender : agrega los métodos y constantes del módulo especificado a la metaclase del objetivo (es decir, la clase singleton), por ejemplo

  • si llama a Klazz.extend(Mod) , ahora Klazz tiene métodos de Mod (como métodos de clase)
  • si llama a obj.extend(Mod) , ahora obj tiene los métodos de Mod (como métodos de instancia), pero ninguna otra instancia de obj.class tiene esos métodos agregados.
  • extend es un método público

include - De forma predeterminada, se mezcla en los métodos del módulo especificado como métodos de instancia en el módulo / clase de destino. p.ej

  • si llamas class Klazz; include Mod; end; class Klazz; include Mod; end; , ahora todas las instancias de Klazz tienen acceso a los métodos de Mod (como métodos de instancia)
  • include es un método privado, porque está diseñado para ser llamado desde la clase / módulo contenedor.

Sin embargo , los módulos muy a menudo anulan el comportamiento de include mediante el parche de mono del método included . Esto es muy importante en el código de Rails heredado. más detalles de Yehuda Katz .

Más detalles sobre include , con su comportamiento predeterminado, asumiendo que ha ejecutado el siguiente código

class Klazz include Mod end

  • Si Mod ya está incluido en Klazz, o uno de sus ancestros, la declaración de inclusión no tiene efecto
  • También incluye las constantes de Mod en Klazz, siempre y cuando no entren en conflicto.
  • Le da a Klazz acceso a las variables del módulo de Mod, por ejemplo, @@foo o @@bar
  • plantea ArgumentError si hay inclusiones cíclicas
  • Agrega el módulo como el antepasado inmediato de la persona que llama (es decir, agrega Mod a Klazz.ancestors, pero Mod no se agrega a la cadena de Klazz.superclass.superclass.superclass. Por lo tanto, llamar a super en Klazz # foo verificará Mod # foo antes comprobando el método foo de la superclase real de Klazz. Consulte RubySpec para obtener más información.

Por supuesto, la documentación de ruby ​​core es siempre el mejor lugar para ir por estas cosas. El proyecto RubySpec también fue un recurso fantástico, ya que documentaron la funcionalidad con precisión.