The standard Ruby reflection does not provide for getting method parameter names (at least afaik), and the only other solution that I found is using parsetree, which is “more clean” then this method, but requires another gem…

So here it comes Monkey-Style:

def methods_with_parameters(klass, path_to_file)
  methods = klass.instance_methods(false).sort
  lines = File.read(path_to_file)

  #find params for methods
  methods.map do |method|
    lines.detect{|l|l=~/def #{method}(.*)$/}
    parameters = $1.tr('()','').split(',').map{|p| p.gsub(/=.*/, '')}.reject(&:blank?).map(&:strip)
    [method, parameters]
  end
end
  • Developers see what they are dooing (just hit F5)
  • Livesaver for HTML Emails
  • Non-developers can check alignment/spelling/… sooo easy
  • Generated mails can be shared through url so no need for forwarding/screenshots

View
Just make a form that submits:

  • the method you want to call
  • list of arguments to use
  <%form_tag({:action=>'make_mail'}, :method=>:get) do%>
    <%=select_tag :method_name, options_for_select(UserMailer.public_instance_methods(false).sort)%>
    Models: Product.find(12322) / String: "abc" / Numbers: 123 / Array: [1,2,3]
    <table>
      <%5.times do |i|%>
        <tr>
          <td><%=i%></td>
          <td><%=text_field_tag "args[]", '', :style=>'width:300px'%></td>
        </tr>
      <%end%>
    </table>
    <%=submit_tag 'Preview'%>
    <%=submit_tag 'Send'%>
  <%end%>
<%end%>

Controller
Evals the ‘args’ and send or previews the mail.

def make_mail
  args = params[:args].map{|arg| eval arg}.compact

  if params[:commit]=='Preview' #preview button
    @text = UserMailer.send("create_#{params[:method_name]}", *args).body
    if @text =~ /<body>/
      render :text=>@text
    else
      render :text=>"<pre>#{@text}</pre>"
    end
  else #Send button
    UserMailer.send("deliver_#{params[:method_name]}", *args)
    flash[:notice] = 'Mail sent!'
    redirect_to params.merge(:action=>:index)
  end
end

Happy mail preview too you :D

Static methods are the syntactic vinegar of Ruby, they need some extra Syntax to declare and are cumbersome to call. Yet they are the best one can do to get reusable and testable components. Magic to the rescue!

class SomeKindOfClass
  def normal
    self.class.static_normal
  end

  def self.static_normal
  end
end

#self.class.static_normal is not very nice...
#but we could do klass.static_method or static.static_method with:
class Object
  def klass
    self.class
  end
end

#Or go extreme and allow class methods to be called like instance methods
#SomeKindOfClass.new.static_normal
class Object
  def method_missing name, *args
    return self.class.send(name, *args) if self.class.respond_to? name
    super
  end
end

Need to know which method called you ? Its as simple as:
caller = CallChain.caller_method

Alternatively it could be added to Object, but that would be dirty ;)

class CallChain
  def self.caller_method(depth=1)
    parse_caller(caller(depth+1).first).last
  end

  private

  #Stolen from ActionMailer, where this was used but was not made reusable
  def self.parse_caller(at)
    if /^(.+?):(\d+)(?::in `(.*)')?/ =~ at
      file   = Regexp.last_match[1]
      line   = Regexp.last_match[2].to_i
      method = Regexp.last_match[3]
      [file, line, method]
    end
  end
end

Here I use it to render an .xml.builder. For Rails <= 2.1.1 the trick is to stub the controller with something with a .content_type, thereafter it is no longer necessary.

render_xml_builder('lib/data_feed.xml.builder', :products=>products)

def self.render_xml_builder(file, locals={})
  builder = ActionView::Base.new
  builder.instance_variable_set '@controller', OpenStruct.new(:content_type=>'un/important') #only needed for Rails <= 2.1.1
  builder.render( :inline => File.read(file), :type => :rxml, :locals => locals )
end

Happy rendering!