Мета-программирование: тело метода вывода в виде текста - PullRequest
12 голосов
/ 10 июля 2010

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

Модуль 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 для удобства.

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

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

Ответы [ 4 ]

5 голосов
/ 11 июля 2010

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

Лучшее, что вы можете сделать, - это получить объект 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 голосов
/ 11 июля 2010

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

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

2 голосов
/ 11 июля 2010

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

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

0 голосов
/ 23 сентября 2010

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

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

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

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

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

будьте осторожны, ParseTree в настоящее время не работает в ruby ​​1.9.

...