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

Мета-программирование: тело метода вывода как текст

Я динамически определяю метод в модуле, и я хотел бы проверить, что, как только метод привязан к экземпляру класса, тело моего метода является тем, что я ожидаю. Есть ли способ вывода (как текста) тела метода?

Модуль controller_mixins.rb:

module ControllerMixin

  instance_eval "def search_by_vendor (*args) \n" \
    " @#{self.class.name.sub(/Controller/, '').tableize} = #{self.class.name.sub(/Controller/, '')}.find_all_by_vendor_id(params[:vendor_id])  \n"\
    "respond_to do |format| \n" \
    " format.html { render :template=>'/#{self.class.name.sub(/Controller/, '').tableize}/index',  :layout=>'vendor_info'} \n" \
    " format.xml  { render :xml => @#{self.class.name.sub(/Controller/, '').tableize} } \n" \
    "end \n"\
  "end \n"

end

класс смешивается с:

class VendorOrdersController < ApplicationController
  # GET /vendor_orders
  # GET /vendor_orders.xml
  require 'controller_mixins'
  include ControllerMixin
 <rest of class>

Итак, я хотел бы увидеть реализацию mixin при применении к VendorOrdersController вероятно, через script/console для удобства.

UPDATE: Per @~/Я сохранил строку для переменной и puts 'd it. Это сработало отлично. Это выявило ошибку в моем коде (причина, по которой я хотел увидеть код в первую очередь). Код ниже намного лучше и работает как ожидалось.

module ControllerMixin

  def self.included(mod)
     method_body = "def search_by_vendor \n" \
      " @#{mod.name.sub(/Controller/, '').tableize} = #{mod.name.sub(/Controller/, '')}.find_all_by_vendor_id(params[:vendor_id])  \n"\
      "respond_to do |format| \n" \
      " format.html { render :template=>'/#{mod.name.sub(/Controller/, '').tableize}/index',  :layout=>'vendor_info'} \n" \
      " format.xml  { render :xml => @#{mod.name.sub(/Controller/, '').tableize} } \n" \
      "end \n"\
    "end \n" 

    puts method_body
    mod.class_eval(method_body)
  end

end
4b9b3361

Ответ 1

Нет, вы не можете получить исходный код за методом.

Лучшее, что вы можете сделать, это получить объект Method, который представляет метод, используя Object#method. Например:

m = VendorOrdersController.method(:search_by_vendor)

Но вы обнаружите, что там не больше, чем Method#name, Method#arity, Method#source_location и т.д.

В вашем случае, однако, почему бы просто не сохранить строку в переменной, напечатать ее, прежде чем использовать instance_eval?

Независимо, ваш instance_eval будет выполнен в момент объявления модуля. Вероятно, вы захотите обернуть его в обратном вызове included, чтобы он выполнялся в момент включения.

module ControllerMixin
  def self.included(mod)
    mod.instance_eval([...])
  end
end

Ответ 2

Лучший способ гарантировать, что вы получите свой предполагаемый результат в результате... - это написать тест.

Кроме того, я не одобряю использование instance_eval. Если вы ДОЛЖНЫ метапрограммировать, используйте define_method, или вы, вероятно, можете уйти, не выполняя ничего из этого, передав параметр из маршрутов, конечно, это немного больше печатает, но это много метапрограммирование просто нехорошо.

Ответ 3

Не могли бы вы присвоить строку переменной перед запуском instance_eval и вывести ее на консоль?

Поскольку ваша строка определяет весь метод, у вас по существу уже есть исходный код.

Ответ 4

как сказано выше, лучший способ сделать то, что вы пытаетесь сделать, - define_method.

если кто-то ищет "Мета-программирование: тело метода вывода как текст" попробуйте это:

если вы хотите получить код класса или метода, выберите ParseTree и ruby2ruby

ParseTree может захватывать абстрактное синтаксическое дерево класса или метода и генерирует "символические выражения", такие как lisp, он выглядит как списки списков символов и очень интересен.

ruby2ruby берет эти s-exps и превращает их в обычный рубин.

ParseTree в настоящее время не работает в ruby ​​1.9.