Subsecciones


El Hook included

Cuando un módulo es incluído en una clase, el método de clase included es invocado con argumento el objeto clase o módulo en el cual es incluído.

Veamos un ejemplo de uso de included .

Creando declaraciones de métodos abstractos

En Ruby no existe una palabra abstract que indique que un método es abstracto. Creemos una:

[~/Chapter7ClassesAndModules/inheritance]$ cat abstract.rb 
module Abstract
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def abstract_methods(*args)
      args.each do |name|
        class_eval(<<-CHUCHU)
          def #{name}(*args)
            raise NotImplementedError.new("You must implement #{name}.")
          end
        CHUCHU
      end   # args.each
    end     # def abstract_methods
  end     # module ClassMethods
end

class Tutu
  include Abstract

  abstract_methods :foo, :bar
end

[~/Chapter7ClassesAndModules/inheritance]$ pry
[1] pry(main)> require './abstract'
=> true
[2] pry(main)> z = Tutu.new
=> #<Tutu:0x007f9bde11ac70>
[3] pry(main)> z.bar
NotImplementedError: You must implement bar.
from (eval):2:in `bar'
[4] pry(main)>

Incluyendo Métodos de Instancia y de Clase de un Módulo

El Problema

Ejercicio 15.5.1   Encuentre alguna forma de hacer que un módulo pueda proveer métodos de clase y métodos de instancia

Primeras Solución

[~/src/ruby/ruby_best_practices/chapter3_mastering_the_dynamic_toolkit/tracking_mixins(master)]$ cat including_class_and_instance_methods.rb 
module MyFeatures

  module ClassMethods
    def say_hello
      "Hello"
    end

    def say_goodbye
      "Goodbye"
    end
  end

  def say_hello
    "Hello from #{self}!"
  end

  def say_goodbye
    "Goodbye from #{self}"
  end
end

class A
  include MyFeatures
  extend MyFeatures::ClassMethods
end

# List instance methods
puts A.public_instance_methods.select { |m| m =~/say/ }.inspect # [:say_hello, :say_goodbye] 
# List class methods
puts A.methods.select { |m| m =~/say/ }.inspect                 # [:say_hello, :say_goodbye] 

puts A.say_hello     # Hello
puts A.new.say_hello # Hello from #<A:0x007fb8c38880f0>!


Segunda Solución: Usando el Gancho included

included is a callback invoked whenever the receiver is included in another module or class. Por ejemplo:

[~/chapter8ReflectionandMetaprogramming]$ pry
[1] pry(main)> module A
[1] pry(main)*   def A.included(mod)  
[1] pry(main)*     puts "#{self} included in #{mod}"    
[1] pry(main)*   end    
[1] pry(main)* end  
=> :included
[2] pry(main)> module Enumerable
[2] pry(main)*   include A  
[2] pry(main)* end  
A included in Enumerable

Sigue la solución:

[~/src/ruby/ruby_best_practices/chapter3_mastering_the_dynamic_toolkit/tracking_mixins(master)]$ cat including_class_and_instance_methods2.rb 
module MyFeatures

  module ClassMethods
    def say_hello
      "Hello"
    end

    def say_goodbye
      "Goodbye"
    end
  end

  def self.included(base) # set the hook
    base.extend(ClassMethods)
  end

  def say_hello
    "Hello from #{self}!"
  end

  def say_goodbye
    "Goodbye from #{self}"
  end
end

class A
  include MyFeatures # Now only one include is needed
end

# List instance methods
puts A.public_instance_methods.select { |m| m =~/say/ }.inspect # [:say_hello, :say_goodbye] 
# List class methods
puts A.methods.select { |m| m =~/say/ }.inspect                 # [:say_hello, :say_goodbye] 

puts A.say_hello     # Hello
puts A.new.say_hello # Hello from #<A:0x007fb8c38880f0>!

Casiano Rodriguez León 2015-06-18