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

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

Модуль 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>

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

ОБНОВЛЕНИЕ: На ~ / я сохранил строку к переменной и puts'd это. Это работало отлично. Который обнаружил ошибку в моем коде (причина, я хотел видеть код во-первых). Код ниже намного лучше, и работает как ожидалось.

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
12
задан kiamlaluno 10 July 2010 в 22:51
поделиться

3 ответа

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

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

m = VendorOrdersController.method(:search_by_vendor)

Но вы обнаружите, что это не что иное, как Method # name , Method # arity , Method # source_location и т. Д.

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

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

module ControllerMixin
  def self.included(mod)
    mod.instance_eval([...])
  end
end
5
ответ дан 2 December 2019 в 22:37
поделиться

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

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

2
ответ дан 2 December 2019 в 22:37
поделиться

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

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

2
ответ дан 2 December 2019 в 22:37
поделиться