Мета-программирование: тело метода вывода как текст
Я динамически определяю метод в модуле, и я хотел бы проверить, что, как только метод привязан к экземпляру класса, тело моего метода является тем, что я ожидаю. Есть ли способ вывода (как текста) тела метода?
Модуль 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
Ответы
Ответ 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.