como o método de delegação do rails funciona?

Depois de ler a resposta por jvans abaixo e ver o código-fonte mais um pouco, entendi agora :). E caso alguém ainda esteja se perguntando como exatamente os delegados do Rails funcionam. Tudo o que os rails estão fazendo é criar um novo método com (module_eval) no arquivo / classe a partir do qual você executou o método delegado.

Então, por exemplo:

  class A
    delegate :hello, :to => :b
  end

  class B
    def hello
     p hello
    end
  end

No momento em que o delegado é chamado de rails, criará um método hello com (* args, & block) na classe A (tecnicamente no arquivo em que a classe A está escrita) e nesse método todos os trilhos usam o valor ": to" (que deve ser um objeto ou uma classe que já esteja definida na classe A) e atribuí-lo a uma variável local _, em seguida, basta chamar o método nesse objeto ou classe que passa nos parâmetros.

Portanto, para que o delegado trabalhe sem gerar uma exceção ... com o exemplo anterior. Uma instância de A já deve ter uma variável de instância referente a uma instância da classe B.

  class A
    attr_accessor :b

    def b
      @b ||= B.new
    end

    delegate :hello, :to => :b
  end

  class B
    def hello
     p hello
    end
  end

Esta não é uma pergunta sobre "como usar o método delegado em trilhos", que eu já sei. Eu estou querendo saber como exatamente "delegar" delega métodos: D. No código-fonte do Rails 4, delegado é definido na classe principal do Ruby Module, que o torna disponível como método de classe em todos os aplicativos rails.

Na verdade, minha primeira pergunta seria como está incluída a classe Ruby Module? Quero dizer, toda classe Ruby tem ancestrais de> Object> Kernel> BasicObject e qualquer módulo em ruby tem os mesmos ancestrais. Então, como exatamente o ruby adiciona métodos a todas as classes / módulos de ruby quando alguém reabre a classe Module?

Minha segunda pergunta é .. Entendo que o método delegate no rails usa module_eval para a delegação real, mas não entendo realmente como o module_eval funciona.

def delegate(*methods)
 options = methods.pop
 unless options.is_a?(Hash) && to = options[:to]
  raise ArgumentError, 'Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, to: :greeter).'
end

prefix, allow_nil = options.values_at(:prefix, :allow_nil)

if prefix == true && to =~ /^[^a-z_]/
  raise ArgumentError, 'Can only automatically set the delegation prefix when delegating to a method.'
end

method_prefix = \
  if prefix
    "#{prefix == true ? to : prefix}_"
  else
    ''
  end

file, line = caller.first.split(':', 2)
line = line.to_i

to = to.to_s
to = 'self.class' if to == 'class'

methods.each do |method|
  # Attribute writer methods only accept one argument. Makes sure []=
  # methods still accept two arguments.
  definition = (method =~ /[^\]]=$/) ? 'arg' : '*args, &block'

  # The following generated methods call the target exactly once, storing
  # the returned value in a dummy variable.
  #
  # Reason is twofold: On one hand doing less calls is in general better.
  # On the other hand it could be that the target has side-effects,
  # whereas conceptually, from the user point of view, the delegator should
  # be doing one call.
  if allow_nil
    module_eval(<<-EOS, file, line - 3)
      def #{method_prefix}#{method}(#{definition})        # def customer_name(*args, &block)
        _ = #{to}                                         #   _ = client
        if !_.nil? || nil.respond_to?(:#{method})         #   if !_.nil? || nil.respond_to?(:name)
          _.#{method}(#{definition})                      #     _.name(*args, &block)
        end                                               #   end
      end                                                 # end
    EOS
  else
    exception = %(raise DelegationError, "#{self}##{method_prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")

    module_eval(<<-EOS, file, line - 2)
      def #{method_prefix}#{method}(#{definition})                                          # def customer_name(*args, &block)
        _ = #{to}                                                                           #   _ = client
        _.#{method}(#{definition})                                                          #   _.name(*args, &block)
      rescue NoMethodError => e                                                             # rescue NoMethodError => e
        if _.nil? && e.name == :#{method}                                                   #   if _.nil? && e.name == :name
          #{exception}                                                                      #     # add helpful message to the exception
        else                                                                                #   else
          raise                                                                             #     raise
        end                                                                                 #   end
      end                                                                                   # end
    EOS
  end
end

fim

questionAnswers(2)

yourAnswerToTheQuestion