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




module include (4)

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

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.





extend