Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Meta-programming: output method body as text

I'm dynamically defining a method in a module, and I'd like to check that once the method is bound to a class instance that the body of the method is what I'm expecting. Is there a way to output (as text) of the body of a method?

Module 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 being mixed with:

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

So I'd like to see the implementation of the mixin when applied to VendorOrdersController probably via script/console for convenience.

UPDATE: Per @~/ I saved the string to a variable and puts'd it. That worked perfectly. Which brought to light an error in my code (the reason I wanted to see the code in the first place). Code below is much better, and works as expected.

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
like image 495
SooDesuNe Avatar asked Jul 10 '10 19:07

SooDesuNe


3 Answers

No, you cannot get the source code behind a method.

The best you can do is get the Method object that represents the method using Object#method. For example:

m = VendorOrdersController.method(:search_by_vendor)

But you'll find that there's not much more than a Method#name, Method#arity, Method#source_location, etc.

In your case however, why not simply store the string in a variable, print it, before using instance_eval?

Regardless, your instance_eval will be executed at the moment of module declaration. You probably want to wrap it in an included callback to have it executed at the moment of inclusion.

module ControllerMixin
  def self.included(mod)
    mod.instance_eval([...])
  end
end
like image 174
Stéphan Kochen Avatar answered Oct 15 '22 16:10

Stéphan Kochen


The best way to ensure you get your intended output as a result ... is to write a test.

Also - i don't approve of using instance_eval like that. If you MUST metaprogram like that, use define_method, or you could probably get away without doing any of that by passing a parameter from the routes, sure, it's a little more typing, but that much metaprogramming is just icky.

like image 44
Omar Qureshi Avatar answered Oct 15 '22 16:10

Omar Qureshi


Couldn't you assign the string to a variable before instance_eval runs and output it to the console?

As your string defines the whole method you essentially already have the source code.

like image 35
Joc Avatar answered Oct 15 '22 16:10

Joc