Creating HTML in jquery is a tedious and, quite frankly, boring task. Coming from a Rails web development background, I always wanted a way to create HTML like Rails does, with a content_tag method helper that can take attributes as has and be passed function blocks to quickly generate HTML within ruby. This is where the inpsiration for EasyHTMLTags comes from.

Let’s just dive in.

This plugin isnt anything complicated, just useful, so let’s see how it works.

1. Download the plugin and install into the appropriate directory.

2. Link to the plugin from your HTML

  
<script src="/path/to/javascripts/jquery.easy_html.js" type="text/javascript" charset="utf-8"></script>


3. Start creating tags

 
$.tag("li")
# =>  "<li></li>"

$.tag("li", "some content")
# =>  "<li>some content</li>"

$.tag("li", "some content", { class: "my_class", id: "special_id"})
# =>  "<li class="my_class" id="special_id">some content</li>"

$.tag("li", "some content", { class: "my_class", id: "special_id"}, function() { return $.tag("span", "some extra stuff")})
# =>  "<li class="my_class" id="special_id">some content<span>some extra stuff</span></li>"

That’s it. Hope you find it useful.

Check out the docs for more information.

—jake


A Simple Pattern for Ruby's inject method

Posted by admin 24/01/2010 at 14h05

Ruby’s inject is an often misunderstood method, but you would be surprised at how much it can help you clean up your code. I see many developers skipping over this method in favor of the non-injectified way, mainly because they dont get how to use inject properly. So we’ll take a look at some common examples and then you will be able to see the pattern that allows you to take advantage of inject. Let’s take a look at inject’s signature:

enum.inject(initial) {| memo, obj | block } => obj

This method is part of the Enumerable module, so you can use this method on many different types, including Arrays, Hash, Strings and many others. Now, let’s take a look at each part of the signature:

  • initial - this is the initial value that you want to set for the memo
  • memo - this is the accumulator object (the sum variable from your standard array summing function)
  • obj - this is the individual obj from the enumerablized container.
  • block - you should know what this is :D

Alright, now that we know what each piece is, let’s look at some examples and the refactor them with inject.

Example 1

This will be a simple summing example:

sum = 0
(1..30).each do |num|
  sum = sum + num
end

Now let’s refactor this with inject:

(1..30).inject(0) do |sum, num|
  sum + num
end

Here, we take the initial value of sum (0) and pass it as the argument to inject, this is the initializer. We then take the variable name sum and use it as the initial argument to the block, this is the memo parameter. num then becomes the enumerated object and we continue as we did in our original each method, but instead of assigning the value of the aggregation to the aggregator, we can just aggregate and ruby will take the value of the last line of the block and assign it to the memo.

Example 2

Let’s take a look at a common Rails example that I see all the time in helpers:

ret = ""
@products.each do |prod|
  ret << content_tag(:li, prod.title)
end
content_tag(:ul, ret)

Now we refactor with inject:

lis = @products.inject("") do |ret, prod|
  ret + content_tag(:li, prod.title)
end
content_tag(:ul, lis)

This time we initialize inject with “” and we are aggregating li tags for a nice HTML list. There is a more Ruby way to refactor this which would look like:

content_tag(:ul, nil) do 
  @products.inject("") do |ret, prod|
    ret + content_tag(:li, prod.title)
  end
end

Now, dont think that you can only use one-liners for your inject methods. They are Ruby blocks, and given all the freedoms that all blocks are given. We could do something like this if necessary:

content_tag(:ul, nil) do 
  @products.inject("") do |ret, prod|
    if prod.active? && prod.is_special?
      ret + content_tag(:li, prod.special_title)
    elsif prod.active?
      ret + content_tag(:li, prod.title)
    else
      ret + content_tag(:li, prod.inactive_title)
    end
  end
end

One other thing to note, inject’s memo object isn’t just for aggregating, it can be used as a simple container for checks:

success = @products.inject(true) do |ret, prod|
  ret && prod.active?
end

The above is a way to write the Enumerable#all? method. If you replace && with || you get the any? method.

That’s it for this tip, stay tuned for other refactoring tips with Ruby.


Deep in Rails: ActionMailer#deliver Part VI

Posted by admin 20/12/2009 at 09h41

So now we know how Rails sends emails. What can we do with that knowledge?

All kinds of stuff.

One of my clients needed the ability to send messages through 3 different SMTP servers. They wanted it to be easy to use for their developers and allow them to change out SMTP servers for each mailer method. Now that we know how AM does itâ??s thing, we can use that knowledge to create a quick patch and solve the above problem.

Weâll create a new file in out Rails app inside lib/ and call it action_mailer_hacks.rb. Inside of the file add the following (dont worry, weâll go over each line)

module ActionMailerHacks

    module InstanceMethods
        def deliver_with_switchable_smtp!(mail = @mail)
            unless logger.nil?
                logger.info  "Switching SMTP server to: #{new_smtp.inspect}"
            end
            ActionMailer::Base.smtp_settings = new_smtp unless new_smtp.nil?
            deliver_without_switchable_smtp!(mail = @mail)
        end

    end

    def self.included(receiver)
        receiver.send :include, InstanceMethods
        receiver.class_eval do
            adv_attr_accessor :new_smtp
            alias_method_chain :deliver!, :switchable_smtp
        end
    end
end


ActionMailer::Base.send :include, ActionMailerHacks

First, in module InstanceMethods, we create a new deliver method that will switch the SMTP server from AM. We do some quick logging and then set AM::Base.smtp_settings to the new smtp server. After we do that, we call deliver_without_switchable_smtp which gets created when we do our alias_method_chaining later down the page.

When this module is included we include the InstanceMethods module into the receiver and then run class_eval so that we can create a new adv_attr_accessor called new_smtp and then call alias_method_chain to create our without method. After that we include this whole module into AM::Base and weâre done with the hack. Add require "action_mailer_hacks.rb" to your environment.rb file and weâre ready for the mailer.

Now we move on to MessageMailer and actually use the hack.

class MessageMailer < ActionMailer::Base
  def contact_form(msg_from, message)
    subject    'Im contacting you'
    recipients "someemail@somedomain.com"
        new_smtp   :address => 'mail.someplace.com', :port => 25, :domain => 'someplace.com', :authentication => :login, :user_name => 'xxxxdddd', :password => 'secret'
    from       msg_from
    sent_on    Time.now
    body       :message => message
  end
end

All we do is call new_smtp with our new server address and then this method gets delivered through that SMTP server. Thatâ??s it. Simple and clean, exactly how I like it.

Well, that was fun. Next time, weâll look through something a little shorter, maybe migrations. Until then, have fun and keep codinâ


Deep in Rails: ActionMailer#deliver Part V

Posted by admin 14/12/2009 at 12h54

So now we have our mail object. Let’s send it.

For the sending of the message, we need to go back to the beginning, method_missing:

(line 62 action_mailer/base.rb)
def method_missing(method_symbol, *parameters) #:nodoc:
  if match = matches_dynamic_method?(method_symbol)

    case match[1]
      when 'create'  then new(match[2], *parameters).mail
      when 'deliver' then new(match[2], *parameters).deliver!
      when 'new'     then nil
      else super
    end
  else
    super
  end
end

You remember method_missing from a long time ago. We just finished the first part of the line:

when 'deliver' then new(match[2], *parameters).deliver!

We created the new AM object and now we call deliver! on it. That’s our next stop:

(line 520 action_mailer/base.rb)
def deliver!(mail = @mail)
  raise "no mail object available for delivery!" unless mail
  unless logger.nil?
    logger.info  "Sent mail to #{Array(recipients).join(', ')}"
    logger.debug "\n#{mail.encoded}"
  end

  begin
    __send__("perform_delivery_#{delivery_method}", mail) if perform_deliveries
  rescue Exception => e  # Net::SMTP errors or sendmail pipe errors
    raise e if raise_delivery_errors
  end

  return mail
end

The beginning of this method does some checking and logging. The main part of this method is:

__send__("perform_delivery_#{delivery_method}", mail) if perform_deliveries

We are again using __send__ to bypass any possible overridden method and calling perform_delivery_#{delivery_method} with the mail object. delivery_method is a configuration attribute on the AM class along with perform_deliveries. AM defaults to “smtp” for delivery_method and “true” for perform_deliveries so the method we end up calling in our example is perform_delivery_smtp. Let’s see that code:

(line 680 action_mailer/base.rb)
def perform_delivery_smtp(mail)
  destinations = mail.destinations
  mail.ready_to_send
  sender = (mail['return-path'] && mail['return-path'].spec) || mail.from

  smtp = Net::SMTP.new(smtp_settings[:address], smtp_settings[:port])
  smtp.enable_starttls_auto if smtp_settings[:enable_starttls_auto] && smtp.respond_to?(:enable_starttls_auto)
  smtp.start(smtp_settings[:domain], smtp_settings[:user_name], smtp_settings[:password],
             smtp_settings[:authentication]) do |smtp|
    smtp.sendmail(mail.encoded, sender, destinations)
  end
end

This is the finale. The climax. The whole reason we even engage in ActionMailer, to actualy send an email. Here we go.

The first line pulls the destinations (recipients) from the mail object and stores them in a variable. Next, we call ready_to_send on the mail object which is also the TMail object (remember that we assigned the final TMail object to the @mail attribute on our AM object). This method is defined in TMail::Net:

(line 62 action_mailer/tmail-1.2.3/tmail-1.2.3/net.rb)
def ready_to_send
  delete_no_send_fields
  add_message_id
  add_date
end

NOSEND_FIELDS = %w(
  received
  bcc
)

def delete_no_send_fields
  NOSEND_FIELDS.each do |nm|
    delete nm
  end
  delete_if {|n,v| v.empty? }
end

def add_message_id( fqdn = nil )
  self.message_id = ::TMail::new_message_id(fqdn)
end

def add_date
  self.date = Time.now
end

Now, if you scan through that code you may notice that we are calling delete and delete_if like we were in a standard Enumerable object, but we are not. TMail also defines those methods:

def delete( key )
  @header.delete key.downcase
end

def delete_if
  @header.delete_if do |key,val|
    if Array === val
      val.delete_if {|v| yield key, v }
      val.empty?
    else
      yield key, val
    end
  end
end

What we are doing with all this is stripping and setting some defaults. delete_no_send_fields get’s rid of the “received” and “bcc” keys from our header and we are getting rid of any headers that have empty values. After that we set a nice new hex type id for this email with TMail.new_message_id which is defined in TMail::Utils which gives us a string with a hex code attached to your machine’s hostname. The last line adds the date to the mail message and that’s it.

The next line sets the sender attribute.

sender = (mail['return-path'] && mail['return-path'].spec) || mail.from

Now we get into the actual sending of the email. We aren’t going to go into it because it is part of Ruby’s core lib, but we will quickly skim over it to see what’s going on:

smtp = Net::SMTP.new(smtp_settings[:address], smtp_settings[:port])
smtp.enable_starttls_auto if smtp_settings[:enable_starttls_auto] && smtp.respond_to?(:enable_starttls_auto)
smtp.start(smtp_settings[:domain], smtp_settings[:user_name], smtp_settings[:password],
           smtp_settings[:authentication]) do |smtp|
  smtp.sendmail(mail.encoded, sender, destinations)
end

First, we create the new SMTP object and then we enable StartTLS if necessary (this allows you to send email through SMTP hosts that require SSL connections). Next we login to the SMTP server (with the start command) and call sendmail inside the block to do the actual delivery.

We’re done. That’s how an email gets sent in Rails. Next week we will create a quick monkey patch for ActionMailer based on the information that we learned from the trek.


Deep in Rails: ActionMailer#deliver Part IV

Posted by admin 06/12/2009 at 09h42

So we just opened up CompiledTemplates and defined (or compiled) our unique method for this particular template. Now we continue with render:

# (line 193 action_pack/action_view/renderable.rb)
def render(view, local_assigns = {})
  compile(local_assigns)

  view.with_template self do
    view.send(:_evaluate_assigns_and_ivars)
    view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type)

    view.send(method_name(local_assigns), local_assigns) do |*names|
      ivar = :@_proc_for_layout
      if !view.instance_variable_defined?(:"@content_for_#{names.first}") && view.instance_variable_defined?(ivar) && (proc = view.instance_variable_get(ivar))
        view.capture(*names, &proc)
      elsif view.instance_variable_defined?(ivar = :"@content_for_#{names.first || :layout}")
        view.instance_variable_get(ivar)
      end
    end
  end
end

and our params:

view => #"Send this message"}, 
          @helpers=#, 
          @controller=#"Send this message"}, 
              @mailer_name="message_mailer", 
              @recipients="someemail@somedomain.com", 
              @parts=[], 
              @template=#, 
              @charset="utf-8">, 
          @_current_render=nil, 
          @assigns_added=nil, 
          @_first_render=nil, 
          @view_paths=["/example_app/app/views"]>
local_assigns => {}

The first thing we do after we compile our unique method is call with_template on view which is a convenient method that allows us to temporarily swap out the current template for the view, set it to another one, do some processing and then return the original template back to the view. Letâ??s see with_template:

# (line 298 action_pack/action_view/base.rb)
def with_template(current_template)
  last_template, self.template = template, current_template
  yield
ensure
  self.template = last_template
end

Quite simple indeed. Now letâ??s see exactly what we are doing to the template. The first thing we do is send the method _evaluate_assigns_and_ivars to the view. This is private method in ActionView::Base which creates instance variables for each key in the `assigns@ attribute hash. This hash is filled with ourbodycall in the mailer model you create. Here isevaluateassignsandivars`:

# (line 307 action_pack/action_view/base.rb)
def _evaluate_assigns_and_ivars #:nodoc:
 unless @assigns_added
   @assigns.each { |key, value| instance_variable_set("@#{key}", value) }
   _copy_ivars_from_controller
   @assigns_added = true
 end
end

After the instance variables are set, we call _copy_ivars_from_controller which does exactly that, copies the instance variables from this viewâ??s controller to this view. This is how instance variables that you create in normal controller methods are accessed in the view. Letâ??s look at that definition:

# (line 315 action_pack/action_view/base.rb)
def _copy_ivars_from_controller #:nodoc:
  if @controller
    variables = @controller.instance_variable_names
    variables -= @controller.protected_instance_variables if @controller.respond_to?(:protected_instance_variables)
    variables.each { |name| instance_variable_set(name, @controller.instance_variable_get(name)) }
  end
end

This method finds all proper instance variables in the attached controller and sets the same instance variables in the current view object. Once we have copied the instance variables we move to the next line in render:

view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type)

This is another private method in ActionView::Base and itâ??s job is quite simple: set the content_type for the response attribute if this viewâ??s controller has one. Letâ??s look at the code:

# (line 323 action_pack/action_view/base.rb)
def _set_controller_content_type(content_type) #:nodoc:
  if controller.respond_to?(:response)
    controller.response.content_type ||= content_type
  end
end

In our case, the controller is the MessageMailer object and it doesnt respond to response so we dont set a content_type. Now we come back to render:

# (line 193 action_pack/action_view/renderable.rb)
def render(view, local_assigns = {})
  ...
  view.with_template self do
    ...
    view.send(method_name(local_assigns), local_assigns) do |*names|
      ivar = :@_proc_for_layout
      if !view.instance_variable_defined?(:"@content_for_#{names.first}") && view.instance_variable_defined?(ivar) && (proc = view.instance_variable_get(ivar))
        view.capture(*names, &proc)
      elsif view.instance_variable_defined?(ivar = :"@content_for_#{names.first || :layout}")
        view.instance_variable_get(ivar)
      end
    end
  end
end

The next thing we are going to do is call our dynamically generated (and now compiled) view method for this template. We call it with a block that is used to capture content pieces that this view may use (think content_for). AM does not use any of these so we end up skipping over this whole section and just executing our template method. In a future series on ActionController, we will dive more into what the block does and how Rails uses itâ??s information, but for now we will move on.

So what exactly did we do when we called that dynamically generated method? Letâ??s find out. First, we will look at our method again:

def _run_erb_app47views47message_mailer47contact_form46erb(local_assigns)
        old_output_buffer = output_buffer;;@output_buffer = '';  __in_erb_template=true ; @output_buffer.concat "Hi,\n"
@output_buffer.concat "\n"
@output_buffer.concat "I am contacting you with this message:\n"
@output_buffer.concat "\n"
@output_buffer.concat(( @message ).to_s); @output_buffer.concat "\n"
@output_buffer.concat "\n"
@output_buffer.concat "\n"
@output_buffer.concat "\n"@output_buffer.concat "Thanks\n"
@output_buffer.concat "\n"
@output_buffer.concat "Jake"; @output_buffer
ensure
  self.output_buffer = old_output_buffer
end

Remember that output_buffer is an attribute inside of ActionView::Base and that it holds the current output for the view that is being processed. Our dynamic method first stores the current output_buffer and then resets it so that it can insert the newly parsed message. When we execute our method, we concat our entire view (with any ruby processing) into the output_buffer. So after we execute _run_erb_app47views47message_mailer47contact_form46erb our output_buffer buffer looks like:

@output_buffer => "Hi,\n\nI am contacting you with this message:\n\nSend this message\n\n\n\nThanks\n\nJake"

which is the final email body that will be sent.

Alright, now we have rendered our message and we hit the bottom of this method chain. We now pop all the way back up create!. I know, I know. It was so long ago that you dont remember. Here is a refresher:

(line 458 action_mailer/base.rb)
def create!(method_name, *parameters) #:nodoc:
  unless String === @body
    ...
    @body = render_message(@template, @body) if template_exists

    if !@parts.empty? && String === @body
      @parts.unshift Part.new(:charset => charset, :body => @body)
      @body = nil
    end
  end

  @mime_version ||= "1.0" if !@parts.empty?

  @mail = create_mail
end

We just got through with render_message and now we have our @body attribute filled with our message. The next line checks to see if we have parts and if we have a body thatâ??s a String. If we do, we create a new Part with the current @body and add it to all of our parts. After that, we get rid of @body. We do this so that create_mail doesnt try and rerender the body again if we have other parts. Since we dont, we skip over this and go straight to:

@mail = create_mail

Now weâ??re gettinâ?? somewhere. This starts the actual email processing part of AM. Letâ??s see create_mail:

(line 638 action_mailer/base.rb)
def create_mail
  m = TMail::Mail.new

  m.subject,     = quote_any_if_necessary(charset, subject)
  m.to, m.from   = quote_any_address_if_necessary(charset, recipients, from)
  m.bcc          = quote_address_if_necessary(bcc, charset) unless bcc.nil?
  m.cc           = quote_address_if_necessary(cc, charset) unless cc.nil?
  m.reply_to     = quote_address_if_necessary(reply_to, charset) unless reply_to.nil?
  m.mime_version = mime_version unless mime_version.nil?
  m.date         = sent_on.to_time rescue sent_on if sent_on

  headers.each { |k, v| m[k] = v }

  real_content_type, ctype_attrs = parse_content_type

  if @parts.empty?
    m.set_content_type(real_content_type, nil, ctype_attrs)
    m.body = normalize_new_lines(body)
  else
    if String === body
      part = TMail::Mail.new
      part.body = normalize_new_lines(body)
      part.set_content_type(real_content_type, nil, ctype_attrs)
      part.set_content_disposition "inline"
      m.parts << part
    end

    @parts.each do |p|
      part = (TMail::Mail === p ? p : p.to_mail(self))
      m.parts << part
    end

    if real_content_type =~ /multipart/
      ctype_attrs.delete "charset"
      m.set_content_type(real_content_type, nil, ctype_attrs)
    end
  end

  @mail = m
end

The first thing we do is initialize a new TMail::Mail instance and then we go on to assign several attributes for it.

m.subject,     = quote_any_if_necessary(charset, subject)
m.to, m.from   = quote_any_address_if_necessary(charset, recipients, from)
m.bcc          = quote_address_if_necessary(bcc, charset) unless bcc.nil?
m.cc           = quote_address_if_necessary(cc, charset) unless cc.nil?
m.reply_to     = quote_address_if_necessary(reply_to, charset) unless reply_to.nil?
m.mime_version = mime_version unless mime_version.nil?
m.date         = sent_on.to_time rescue sent_on if sent_on

The quote_any_if_necessary, quote_any_address_if_necessary and quote_address_if_necessary are helper methods included from the module ActionMailer::Quoting. Letâ??s take a gander at those methods:

(line 5 action_mailer/quoting.rb)

def quoted_printable(text, charset)
  text = text.gsub( /[^a-z ]/i ) { quoted_printable_encode($&) }.gsub( / /, "_" )
  "=?#{charset}?Q?#{text}?="
end

def quoted_printable_encode(character)
  result = ""
  character.each_byte { |b| result << "=%02X" % b }
  result
end

if !defined?(CHARS_NEEDING_QUOTING)
  CHARS_NEEDING_QUOTING = /[\000-\011\013\014\016-\037\177-\377]/
end

def quote_if_necessary(text, charset)
  text = text.dup.force_encoding(Encoding::ASCII_8BIT) if text.respond_to?(:force_encoding)

  (text =~ CHARS_NEEDING_QUOTING) ?
    quoted_printable(text, charset) :
    text
end

def quote_any_if_necessary(charset, *args)
  args.map { |v| quote_if_necessary(v, charset) }
end

def quote_address_if_necessary(address, charset)
  if Array === address
    address.map { |a| quote_address_if_necessary(a, charset) }
  elsif address =~ /^(\S.*)\s+(<.*>)$/
    address = $2
    phrase = quote_if_necessary($1.gsub(/^['"](.*)['"]$/, '\1'), charset)
    "\"#{phrase}\" #{address}"
  else
    address
  end
end

def quote_any_address_if_necessary(charset, *args)
  args.map { |v| quote_address_if_necessary(v, charset) }
end

Im not going to bore you with the inane details, needless to say, these methods take illegal characters and quote them if necessary. Once these are quoted and sanitized, they are inserted into the Mail object. After we initialize those attributes, we set the header attributes for the Mail object with the following line:

headers.each { |k, v| m[k] = v }

real_content_type, ctype_attrs = parse_content_type

We cycle through all the headers of our mailer object and stick them into the TMail::Mail object. Next we call parse_content_type which is a private method that we get from ActionMailer::PartContainer:

(line 43 action_mailer/part_container.rb)
def parse_content_type(defaults=nil)
  if content_type.blank? 
    return defaults ? 
      [ defaults.content_type, { 'charset' => defaults.charset } ] : 
      [ nil, {} ] 
  end 
  ctype, *attrs = content_type.split(/;\s*/)
  attrs = attrs.inject({}) { |h,s| k,v = s.split(/=/, 2); h[k] = v; h }
  [ctype, {"charset" => charset || defaults && defaults.charset}.merge(attrs)]
end

All we are doing here is parsing the contenttype of the mailer (in our case â??text/plainâ?) and returning an array with the contenttype and a hash. Letâ??s see what our example ends up returning:

parse_content_type => ["text/plain", {"charset"=>"utf-8"}]
real_content_type => "text/plain"
ctype_attrs => {"charset"=>"utf-8"}

Now we move on to:

if @parts.empty?
  m.set_content_type(real_content_type, nil, ctype_attrs)
  m.body = normalize_new_lines(body)
else
  ...
end

In our case, @parts is empty so we set the content type of our TMail object with the results from our parsing. After that, we set the body of the TMail object to the current AM objectâ??s body, but we first do some sanitation duties with normalize_new_lines which is a method in ActionMailer::Utils. In fact, it is the only method in that module:

(action_mailer/utils.rb)
module ActionMailer
  module Utils #:nodoc:
    def normalize_new_lines(text)
      text.to_s.gsub(/\r\n?/, "\n")
    end
  end
end

What we are doing here is cleansing Microsoft evil from the message. Windows likes to use â??\r\nâ? for line breaks in text, the rest of computing thinks itâ??s enough to just use â??\nâ?. I agree with the rest of computing. Anyway, this method changes all â??\r\nâ??â??s to â??\nâ??â??s so that our email message parses correctly on all email servers.

After we normalize, we are done with setting up the object and the final line of create_mail is:

@mail = m

Now we set the AM attribute @mail to the newly instantiated TMail object and weâ??re doneâ?¦ with the creation of the email message. Now we have to actually send it. Weâ??ll get to that next. See ya then.


Deep in Rails: ActionMailer#deliver Part III

Posted by admin 29/11/2009 at 10h47

When we last left off we were just about to jump into Renderable#render. Thatâ??s where we are going next:

# (line 193 action_pack/action_view/renderable.rb)
def render(view, local_assigns = {})
  compile(local_assigns)

  view.with_template self do
    view.send(:_evaluate_assigns_and_ivars)
    view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type)

    view.send(method_name(local_assigns), local_assigns) do |*names|
      ivar = :@_proc_for_layout
      if !view.instance_variable_defined?(:"@content_for_#{names.first}") && view.instance_variable_defined?(ivar) && (proc = view.instance_variable_get(ivar))
        view.capture(*names, &proc)
      elsif view.instance_variable_defined?(ivar = :"@content_for_#{names.first || :layout}")
        view.instance_variable_get(ivar)
      end
    end
  end
end

Alright, this is where we get real deep into Rails. This particular method does the vast majority of the rendering work in Rails. Letâ??s see whatâ??s going on:

compile(local_assigns)

This is the first line and it sets up a series of methods that we are going to run through. compile is a private method in Renderable:

# (line 193 action_pack/action_view/renderable.rb)
def compile(local_assigns)
  render_symbol = method_name(local_assigns)

  if !Base::CompiledTemplates.method_defined?(render_symbol) || recompile?
    compile!(render_symbol, local_assigns)
  end
end

which starts out creating a method symbol for this renderable object by calling another private method aptly named method_name. This is the definition for method_name:

# (line 45 action_pack/action_view/renderable.rb)
def method_name(local_assigns)
  if local_assigns && local_assigns.any?
    method_name = method_name_without_locals.dup
    method_name << "_locals_#{local_assigns.keys.map { |k| k.to_s }.sort.join('_')}"
  else
    method_name = method_name_without_locals
  end
  method_name.to_sym
end

Here are the params for our example:

local_assigns => {}

Since our local_assigns is empty, we end up executing the else part of the conditional which sets method_name to the result method_name_without_locals which is yet another private method in Renderable. Here it is:

# (line 22 action_pack/action_view/renderable.rb)
def method_name_without_locals
  ['_run', extension, method_segment].compact.join('_')
end

This method gives us unique method name to use for the definition of the compiled rendered template. You may have seen something like the following in your Rails development logs:

Account Columns (251.9ms)   SHOW FIELDS FROM `accounts`
AccountSettings Columns (4.2ms)   SHOW FIELDS FROM `account_settings`
Account Load (0.5ms)   SELECT * FROM `accounts` WHERE (`accounts`.`id` = 47715) 
  app/controllers/application.rb:144:in `current_user'
  app/views/layouts/bare.rhtml:21:in `_run_rhtml_app47views47layouts47bare46rhtml'
  app/controllers/administration_controller.rb:10:in `index'
  vendor/plugins/mongrel_proctitle/lib/mongrel_proctitle.rb:114:in `process_client'
  vendor/plugins/mongrel_proctitle/lib/mongrel_proctitle.rb:33:in `request'
  vendor/plugins/mongrel_proctitle/lib/mongrel_proctitle.rb:112:in `process_client'
  vendor/plugins/mongrel_proctitle/lib/mongrel_proctitle.rb:102:in `run'

You see that line:

app/views/layouts/bare.rhtml:21:in `_run_rhtml_app47views47layouts47bare46rhtml'

the _run_rhtml_app47views47layouts47bare46rhtml is actually the name of the method that is given to your template. This is done because Rails executes your template, just like any piece of Ruby code. To do that, it needs a method name, and that is what we are doing in the method_name_without_locals method, creating a unique method name for this template we are about to run. method_segment is a method that is included in from ActionView::Template and extension is an attribute in ActionView::Template. Here is method_segment and extension:

# (line 22 action_pack/action_view/template.rb)
def method_segment
  relative_path.to_s.gsub(/([^a-zA-Z0-9_])/) { $1.ord }
end

extension => "erb"

which calls another method in Template called relative_path displayed here:

# (line 172 action_pack/action_view/template.rb)
def relative_path
  path = File.expand_path(filename)
  path.sub!(/^#{Regexp.escape(File.expand_path(RAILS_ROOT))}\//, '') if defined?(RAILS_ROOT)
  path
end

First, we get the full pathname to the current template and then we run sub on the path to remove anything that may look like the full expanded path of RAILS_ROOT if RAILS_ROOT is defined. The templateâ??s filename is stored in the attribute filename. Here are the params:

path => app/views/message_mailer/contact_form.erb
filename => /example_app/app/views/message_mailer/contact_form.erb

As you can see, the main purpose for this method is to give us the relative path to the mailer. Now that we got our path, letâ??s see what method_segment does to it:

relative_path.to_s.gsub(/([^a-zA-Z0-9_])/) { $1.ord }

We call gsub on the path, using the less familiar block usage. This particular usage passes each matched string to the block for processing. In this case, we want to match any character that isnt alphanumeric or an â??_â?. We take each of these matches (which are the individual characters that matched our expression) and call ord on them which returns their ASCII number. So in our case, method_segment would end up returning app47views47message_mailer47contact_form46erb.

Good, now we have our method_segment and we know what our extension is, so letâ??s see what we get from method_name_without_locals for our example:

_run_erb_app47views47message_mailer47contact_form46erb

Whew, now weâ??re back to Renderable#method_name with our method name and we return back from that to compile. Letâ??s refresh our memories real quick:

# (line 193 action_pack/action_view/renderable.rb)
def compile(local_assigns)
  render_symbol = method_name(local_assigns)

  if !Base::CompiledTemplates.method_defined?(render_symbol) || recompile?
    compile!(render_symbol, local_assigns)
  end
end

and we get back for render_symbol:

:_run_erb_app47views47message_mailer47contact_form46erb

The next section is where the super-crazy-secret-sauce magic happens, but first we have to do some checks. First, we check to see if our render_symbol is defined as a method in the module ActionView::Base::CompiledTemplates. Letâ??s take a gander at CompiledTemplates:

# (line 193 action_pack/action_view/renderable.rb)
module CompiledTemplates #:nodoc:
  # holds compiled template code
end

What theâ?¦. Where is the code?

Well, remember that we just created a new method name for our template. Why did we do that? Because Rails needs to execute the template as with everything else. So we have to define the method somehow so that it can be executed, right? Exactly. That is what CompiledTemplates is for. Itâ??s the placeholder module where we will keep these method definitions. Basically, itâ??s a nice container to hold these dynamically created methods so they arent spread out all over ObjectSpace. Plus, it makes clearing the methods cache pretty simple, just undef every method in CompiledTemplates. So first, we check to see if there is a method defined for this particular template. Then we check the private method recompile?, which is so simple, that I think it may be a placeholder for something in the future. Here is recompile?:

# (line 92 action_pack/action_view/renderable.rb)
def recompile?
  false
end

So basically, we are just checking the first part of our condition for the method definition. In our case, we dont have a method defined as such and we go on to compile!, which is a private method in Renderable and looks like this:

# (line 66 action_pack/action_view/renderable.rb)
def compile!(render_symbol, local_assigns)
  locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join

  source = <<-end_src
    def #{render_symbol}(local_assigns)
      old_output_buffer = output_buffer;#{locals_code};#{compiled_source}
    ensure
      self.output_buffer = old_output_buffer
    end
  end_src

  begin
    ActionView::Base::CompiledTemplates.module_eval(source, filename, 0)
  rescue Errno::ENOENT => e
    raise e # Missing template file, re-raise for Base to rescue
  rescue Exception => e # errors from template code
    if logger = defined?(ActionController) && Base.logger
      logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"
      logger.debug "Function body: #{source}"
      logger.debug "Backtrace: #{e.backtrace.join("\n")}"
    end

    raise ActionView::TemplateError.new(self, {}, e)
  end
end

Here, we start by creating the string that will assign the local variables to their proper values and storing it in the variable locals_code. Then we move on to creating the source code that we want dynamically created and inserted into CompiledTemplates. This code uses the method compiled_source which uses another method named handler. handler is a wrapper method for handler_class_for_extension which is a class method belonging to Template. This method is defined in TemplateHandlers and extended by Template. All of these methods are illustrated below:

# (line 66 action_pack/action_view/renderable.rb)
def handler
  Template.handler_class_for_extension(extension)
end

def compiled_source
  handler.call(self)
end

# (action_pack/action_view/template_handlers_.rb)
module ActionView #:nodoc:
  module TemplateHandlers #:nodoc:
    ...
    def self.extended(base)
      base.register_default_template_handler :erb, TemplateHandlers::ERB
      base.register_template_handler :rjs, TemplateHandlers::RJS
      base.register_template_handler :builder, TemplateHandlers::Builder

      base.register_template_handler :rhtml, TemplateHandlers::ERB
      base.register_template_handler :rxml, TemplateHandlers::Builder
    end
    ...
    def registered_template_handler(extension)
      extension && ``template_handlers[extension.to_sym]
    end
    ...
    def handler_class_for_extension(extension)
      registered_template_handler(extension) || ``default_template_handlers
    end
  end
end

When TemplateHandlers is extended it registers several extension handlers. You are probably familiar with all of them, so we wont go into what each extension is for. handler_class_for_extension returns the result of registered_template_handler or @default_template_handlers. registered_template_handler returns the proper handler (TemplateHandlers::ERB, TemplateHandlers::RJS or TemplateHandlers::MyCustomTemplateHandler) from the class variable @template_handlers which holds the defaults plus any newly registered templates.

So we get the proper handler back (in our case ActionView::TemplateHandlers::ERB) and we execute the call method with our ReloadableTemplate as the parameter. call is a class method in the module TemplateHandlers::Compilable and included into ERB. It looks like this:

# (line 4 action_pack/action_view/template_handlers_.rb)
module Compilable
  def self.included(base)
    base.extend(ClassMethods)
  end

  module ClassMethods
    def call(template)
      new.compile(template)
    end
  end

  def compile(template)
    raise "Need to implement #{self.class.name}#compile(template)"
  end
end

When we include this module we extend the base class with the module ClassMethods which contains the call method. This method creates a new instance of the base class and calls the compile instance method with the template. The compile in this module raises an error to let you know that it is an abstract method and needs to be defined by the base class, in our case ERB. Letâ??s see what ERB looks like:

# (line 4 action_pack/action_view/template_handlers/erb.rb)
class ERB < TemplateHandler
  include Compilable

  cattr_accessor :erb_trim_mode
  self.erb_trim_mode = '-'
  ...
  def compile(template)
    src = ::ERB.new("<% __in_erb_template=true %>#{template.source}", nil, erb_trim_mode, '@output_buffer').src
    RUBY_VERSION >= '1.9' ? src.sub(/\A#coding:.*\n/, '') : src
  end
end

compile is pretty simple. We create a new instance of ERB parser and pass it the template source and the variable that we want to build the output on (@output_buffer) and returning the src attribute which holds the ruby code generated by the ERB parser. @output_buffer is the name of an attribute inside of ActionView::Base that is used as the output buffer (of course) which will get flushed when we do the final rendering (towards the end of the deliver method). In the case of an AM view, the rendering is pretty simple, but when we get to full controller views with partials we need a place to concat all the different views together which is what we use @output_buffer for. The next line checks for Ruby 1.9 and does some cleaning of the result before returning it. Here is what src outputs for our example:

src => "@output_buffer = '';  __in_erb_template=true ; @output_buffer.concat \"Hi,\\n\"\n@output_buffer.concat \"\\n\"\n@output_buffer.concat \"I am contacting you with this message:\\n\"\n@output_buffer.concat \"\\n\"\n@output_buffer.concat(( `message ).to_s); @output_buffer.concat \"\\n\"\n@output_buffer.concat \"\\n\"\n@output_buffer.concat \"\\n\"\n@output_buffer.concat \"\\n\"\n@output_buffer.concat \"Thanks\\n\"\n@output_buffer.concat \"\\n\"\n@output_buffer.concat \"Jake\"; @output_buffer"

This is one long string that represents the compiled version of the ruby code for our mailer view. Now that we have the compiled_source, letâ??s go back compile!:

# (line 66 action_pack/action_view/renderable.rb)
def compile!(render_symbol, local_assigns)
  locals_code = local_assigns.keys.map { |key| "#{key} = local_assigns[:#{key}];" }.join

  source = <<-end_src
    def #{render_symbol}(local_assigns)
      old_output_buffer = output_buffer;#{locals_code};#{compiled_source}
    ensure
      self.output_buffer = old_output_buffer
    end
  end_src
  ...
end

So now we know what all the pieces mean, letâ??s see what our example becomes:

def _run_erb_app47views47message_mailer47contact_form46erb(local_assigns)
  old_output_buffer = output_buffer;;@output_buffer = '';  __in_erb_template=true ; @output_buffer.concat "Hi,\n"
  @output_buffer.concat "\n"
  @output_buffer.concat "I am contacting you with this message:\n"
  @output_buffer.concat "\n"
  @output_buffer.concat(( `message ).to_s); @output_buffer.concat "\n"
  @output_buffer.concat "\n"
  @output_buffer.concat "\n"
  @output_buffer.concat "\n"@output_buffer.concat "Thanks\n"
  @output_buffer.concat "\n"
  @output_buffer.concat "Jake"; @output_buffer
ensure
  self.output_buffer = old_output_buffer
end

Alright, now we got the code we want evalâ??ed, letâ??s keep going through compile!

# (line 66 action_pack/action_view/renderable.rb)
def compile!(render_symbol, local_assigns)
  ...
  begin
    ActionView::Base::CompiledTemplates.module_eval(source, filename, 0)
  rescue Errno::ENOENT => e
    raise e # Missing template file, re-raise for Base to rescue
  rescue Exception => e # errors from template code
    if logger = defined?(ActionController) && Base.logger
      logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}"
      logger.debug "Function body: #{source}"
      logger.debug "Backtrace: #{e.backtrace.join("\n")}"
    end

    raise ActionView::TemplateError.new(self, {}, e)
  end
end

Now we open up CompiledTemplates and perform a module_eval with the source we just made and the filename of this template (/example_app/app/views/message_mailer/contact_form.erb) and 0. The filename and the number 0 are used in error reporting during module_eval. Letâ??s say something went wrong with the module_eval, in our case, the error would look something like this:

/example_app/app/views/message_mailer/contact_form.erb:0:in `module_eval': the real problem goes here

Thatâ??s it for this week. Next week we continue with our dynamically compiled method and see what Rails does with it.


Deep in Rails: ActionMailer#deliver Part II

Posted by admin 22/11/2009 at 10h34

When we left off last, we were just about to get into rendering the message. Letâ??s recap that code:

(line 553 action_mailer/base.rb)
def render_message(method_name, body)
  if method_name.respond_to?(:content_type)
    @current_template_content_type = method_name.content_type
  end
  render :file => method_name, :body => body
ensure
  @current_template_content_type = nil
end

When we call render_message in create!, we call it with @template and @body, letâ??s see what those get set to inside of render_message for our example:

@template   = method_name     => "contact_form"
@body       = body            => {:message=>"Send this message"}

At the beginning of render_message @body is set to the hash of instance variables that we set in our contact_form method in our mailer. Once we are finished processing, that hash will be gone and @body will be replaced with the full rendered message.

Next, we see that we want to set the @current_template_content_type instance variable to the proper content type. This is mainly used for multipart so that if render is passed one of those fancy ActionView::ReloadableTemplate objects it renders it with the proper parser. Since we did not specify a content_type the method_name â??contact_formâ? is passed and we get to skip over that part and go straight to the meat:

render :file => method_name, :body => body

This is not the same render from ActionController or ActionView, but it does use the AV template render methods. Letâ??s take a look at the definition:

(line 562 action_mailer/base.rb)
def render(opts)
  body = opts.delete(:body)
  if opts[:file] && (opts[:file] !~ /\// && !opts[:file].respond_to?(:render))
    opts[:file] = "#{mailer_name}/#{opts[:file]}"
  end
  
  begin
    old_template, @template = @template, initialize_template_class(body)
    layout = respond_to?(:pick_layout, true) ? pick_layout(opts) : false
    @template.render(opts.merge(:layout => layout))
  ensure
    @template = old_template
  end
end

and the params with our example:

opts => {
  :file => "contact_form",
  :body => {:message=>"Send this message"}
}

The first thing we do is set the variable body to opts[:body] and delete it from the options hash (this is all done in one stroke by the call delete which returns the value that was deleted). Next, we test to see if opts[:file] does not match â/â? and does not respond to render then we set opts[:file] to the same thing but prepended with the mailer_name, in our case âmessage_mailerâ?.

Now we are getting to the real meat. First, we need to set some quick variables before we render. Here are the 2 lines we will be examining next:

old_template, @template = @template, initialize_template_class(body)
layout = respond_to?(:pick_layout, true) ? pick_layout(opts) : false

In the first line we are swapping out the current @template variable with the result of initialize_template_class(body) and storing the old value in old_template. Here is the definition of initialize_template_class:

(line 603 action_mailer/base.rb)
def initialize_template_class(assigns)
  template = ActionView::Base.new(self.class.view_paths, assigns, self)
  template.template_format = default_template_format
  template
end

and assigns gets set to:

body => {:message=>"Send this message"}

The first thing we see is that we create a var named template that is a new instantiation of ActionView::Base with the view_paths, assigns, and the model (MessageMailer in our example) passed to it. Letâ??s dig in and cross over to ActionView::Base and look at initialize:

(line 221 action_pack/action_view/base.rb)
def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil)#:nodoc:
  @assigns = assigns_for_first_render
  @assigns_added = nil
  @controller = controller
  @helpers = ProxyModule.new(self)
  self.view_paths = view_paths
  @_first_render = nil
  @_current_render = nil
end

and the params with our example:

view_paths               => ["/example_app/app/views"]
assigns_for_first_render => {:message=>"Send this message"}
controller               => #<MessageMailer:0x34a579c 
                @action_name="contact_form", 
                @subject="Im contacting you", 
                @content_type="text/plain", 
                @implicit_parts_order=["text/html", "text/enriched", "text/plain"], 
                @from="jake@somewhere.com", 
                @sent_on=Sun Nov 08 11:34:17 -0600 2009, 
                @headers={}, 
                @default_template_name="contact_form", 
                @mime_version="1.0", 
                @body={:message=>"Send this message"}, 
                @mailer_name="message_mailer", 
                @recipients="someemail@somedomain.com", 
                @parts=[], 
                @template="contact_form", 
                @charset="utf-8">

This allows us to wrap the mailers view around the ActionView templating system and reuse the rendering module from ActionView in AM. This also stores our body variables for use inside of our view. Now that we have a template, we need to initialize the template_format with our mailerâ??s default template format.

template.template_format = default_template_format

Letâ??s open up the definition for default_template_format

(line 577 action_mailer/base.rb)
def default_template_format
  if @current_template_content_type
    Mime::Type.lookup(@current_template_content_type).to_sym
  else
    :html
  end
end

This method checks the @current_template_content_type ivar and returns the proper mime-type. This is used as a switch by the renderer. If you dont explicitly set the content type AM defaults to HTML and your message is rendered in HTML mode. Since we didnt define a content_type in our example @current_template_content_type is nil and :html will be returned for the default_template_format.

After that we return the created template instance and that gets us through the initialize_template_class method.

So now that we have set the @template ivar in:

old_template, @template = @template, initialize_template_class(body)
layout = respond_to?(:pick_layout, true) ? pick_layout(opts) : false

We move down to setting the layout for the mailer. In Rails you have the ability to use layouts just like in regular views. In our case, if we wanted to use a layout, we would have to add a file named message_mailer.html.erb in app/views/layouts. The other option is specifying a layout name in our mailer model (just like we would in a controller). So we could define MessageMailer as:

class MessageMailer < ActionMailer::Base
  layout "my_email_layout"
  def contact_form(msg_from, message)
    ...
  end
end

In that case, we would need a file named my_email_layout.html.erb in our layouts directory. Since we include ActionController::Layout in AM::Base our model will respond to pick_layout. This is the method that will determine the layout needed for the renderer. Letâ??s dive into pick_layout, which is defined in ActionController::Layout

# (line 229 action_pack/action_controller/layout.rb)
def pick_layout(options)
  if options.has_key?(:layout)
    case layout = options.delete(:layout)
    when FalseClass
      nil
    when NilClass, TrueClass
      active_layout if action_has_layout? && candidate_for_layout?(:template => default_template_name)
    else
      active_layout(layout, :html_fallback => true)
    end
  else
    active_layout if action_has_layout? && candidate_for_layout?(options)
  end
end

and the params for our example:

options => {:file=>"message_mailer/contact_form"}

The first part of pick_layout checks for the key :layout in options and since we didnt define it in our example we will skip over it and straight to the else part of the conditional:

active_layout if action_has_layout? && candidate_for_layout?(options)

Here, we call the method active_layout if action_has_layout? returns true and candidate_for_layout?(options) returns true. action_has_layout? is a simple method to check if this action or mailer method has a layout and is basically making sure that your :except and :only actions render properly. Letâ??s take a look at action_has_layout? with our example

# (line 244 action_pack/action_controller/layout.rb)
def action_has_layout?
  if conditions = self.class.layout_conditions
    case
      when only = conditions[:only]
        only.include?(action_name)
      when except = conditions[:except]
        !except.include?(action_name)
      else
        true
    end
  else
    true
  end
end

and conditions would be:

conditions => nil

Since we are not setting a layout for our message the method self.class.layout_conditions returns nil. layout_conditions is a class method on AM that reads the attribute accessor hash layout_conditions. These conditions are written when you make the call:

layout "my_template" # or layout "my_template", :except => "some_message"

This writes all your conditions to a attribute hash for easy reading and subclassing. Since our example returns nil for layout_conditions we end up at the outer else statement and return true out of this method and on to candidate_for_layout?(options). This is a base method is written in layout.rb and overridden in AM::base.rb:

# (line 585 action_mailer/base.rb)
def candidate_for_layout?(options)
  !self.view_paths.find_template(default_template_name, default_template_format).exempt_from_layout?
rescue ActionView::MissingTemplate
  return true
end

and our paramaters and extra variables/methods :

options => {:file=>"message_mailer/contact_form"}
default_template_name => "contact_form"
default_template_format => :html

In this method, we are checking to see if there is a template with the above params. Since we dont have any layouts Rails throws an ActionView::MissingTemplate exception which we catch and just return true from it. This sets up the final part of pick_layout which is active_layout. Here is the definition for that:

# (line 201 action_pack/action_controller/layout.rb)
def active_layout(passed_layout = nil, options = {})
  layout = passed_layout || default_layout
  return layout if layout.respond_to?(:render)
  
  active_layout = case layout
    when Symbol then __send__(layout)
    when Proc   then layout.call(self)
    else layout
  end
  
  find_layout(active_layout, default_template_format, options[:html_fallback]) if active_layout
end

In our case, we dont pass anything to active_layout so the first line will get set to default_layout, which is a private method that looks like:

# (line 220 action_pack/action_controller/layout.rb)
def default_layout #:nodoc:
  layout = self.class.read_inheritable_attribute(:layout)
  return layout unless self.class.read_inheritable_attribute(:auto_layout)
  find_layout(layout, default_template_format)
rescue ActionView::MissingTemplate
  nil
end

Since we didnt set a layout, the first line will return nil and the second line will return that value because auto_layout will also be nil. When we get back to active_layout, the variable layout is set to nil. The variable active_layout gets set to nil as well since we are layout-less. Now we get to find_layout which doesnt get called in our example because active_layout is nil. Thatâs it, we are now ready to do the main rendering. Letâs look at the code again:

(line 562 action_mailer/base.rb)
def render(opts)
  ...
  begin
    old_template, @template = @template, initialize_template_class(body)
    layout = respond_to?(:pick_layout, true) ? pick_layout(opts) : false
    @template.render(opts.merge(:layout => layout))
  ensure
    @template = old_template
  end
end

and the params and current variables with our example:

opts => {
  :file => "contact_form",
  :body => {:message=>"Send this message"}
}
old_template  => "contact_form"
@template => #<ActionView::Base:0x3496c10 
                @template_format=:html, 
                @assigns={:message=>"Send this message"}, 
                @helpers=#<ActionView::Base::ProxyModule:0x3496b84>, 
                @controller=#<MessageMailer:0x34a5814 
                @action_name="contact_form", 
                @subject="Im contacting you", 
                @content_type="text/plain", 
                @implicit_parts_order=["text/html", "text/enriched", "text/plain"], 
                @from="jake@somewhere.com", 
                @sent_on=Sun Nov 08 18:15:42 -0600 2009, 
                @headers={}, 
                @default_template_name="contact_form", 
                @mime_version="1.0", 
                @body={:message=>"Send this message"}, 
                @mailer_name="message_mailer", 
                @recipients="someemail@somedomain.com", 
                @parts=[], 
                @template=#<ActionView::Base:0x3496c10 ...>, 
                @charset="utf-8">, 
                @_current_render=nil, 
                @assigns_added=nil, 
                @_first_render=nil,
                @view_paths=["/example_app/app/views"]>

So now we see that layout ends up being nil in our example which we then pass on to @template.render along with our original options. Now weâ??ll jump into render:

# (line 201 action_view/base.rb)
def render(options = {}, local_assigns = {}, &block) #:nodoc:
  local_assigns ||= {}
  
  case options
  when Hash
    options = options.reverse_merge(:locals => {})
    if options[:layout]
      _render_with_layout(options, local_assigns, &block)
    elsif options[:file]
      template = self.view_paths.find_template(options[:file], template_format)
      template.render_template(self, options[:locals])
    elsif options[:partial]
      render_partial(options)
    elsif options[:inline]
      InlineTemplate.new(options[:inline], options[:type]).render(self, options[:locals])
    elsif options[:text]
      options[:text]
    end
  when :update
    update_page(&block)
  else
    render_partial(:partial => options, :locals => local_assigns)
  end
end

and our passed in params:

options => {:file=>"message_mailer/contact_form", :layout=>nil}
local_assigns => {}

Since options is a Hash, we jump right in and first merge in :locals => {} with options. options[:layout] returns nil so we skip that statement and go into the options[:file] conditional

template = self.view_paths.find_template(options[:file], template_format)
template.render_template(self, options[:locals])

Here, we search out the template from the AM models view_paths with our file and the temlpate_format, which in our case is :html. view_paths is an instance method in ActionView::Base that accesses the attribute @view_paths which holds an instance of @ ActionView::PathSet@. This is a helper class that wraps an array with some pretty handy methods, one of which is find_template. This helper class stores each of the view_paths as a ActionView::ReloadableTemplate::ReloadablePath object. This allows us to do other cooler things which we go over later. In our case, view_paths returns:

self.view_paths => ["/example_app/app/views"]

Letâ??s dive into find_template:

# (line 201 action_pack/action_view/paths.rb)
def find_template(original_template_path, format = nil, html_fallback = true)
  return original_template_path if original_template_path.respond_to?(:render)
  template_path = original_template_path.sub(/^\//, '')
  
  each do |load_path|
    if format && (template = load_path["#{template_path}.#{I18n.locale}.#{format}"])
      return template
    elsif format && (template = load_path["#{template_path}.#{format}"])
      return template
    elsif template = load_path["#{template_path}.#{I18n.locale}"]
      return template
    elsif template = load_path[template_path]
      return template
    # Try to find html version if the format is javascript
    elsif format == :js && html_fallback && template = load_path["#{template_path}.#{I18n.locale}.html"]
      return template
    elsif format == :js && html_fallback && template = load_path["#{template_path}.html"]
      return template
    end
  end
  
  return Template.new(original_template_path, original_template_path =~ /\A\// ? "" : ".") if File.file?(original_template_path)
  
  raise MissingTemplate.new(self, original_template_path, format)
end

and our params:

original_template_path => "message_mailer/contact_form"
format => :html

The first line just returns back if we have a full fledged ActionView model that responds to render. Since we dont, we keep going and see that the next line makes sure that our original_template_path isnt an absolute path by removing the initial â??/â? if it is there and storing the final result in template_path. Next, we iterate over ourselves and do some checking (remember, PathSet is a subclass of Array, so we can call each and iterate over the contents internally). In our example, we end up executing the line:

elsif template = load_path[template_path]
  return template

This is part of the fanciness of ActionView::ReloadableTemplate::ReloadablePath. One of the methods implemented is [] which works like a hash and pulls out the proper ReloadableTemplate from the paths array, in our case itâ??s

template => #<ActionView::ReloadableTemplate:0x34a2ad8 
              @base_path="message_mailer", 
              @template_path="message_mailer/contact_form.erb", 
              @_memoized_path=["message_mailer/contact_form.erb"], 
              @name="contact_form", 
              @previously_last_modified=Sat Nov 07 10:32:50 -0600 2009, 
              @_memoized_method_segment=["app47views47message_mailer47contact_form46erb"], 
              @format=nil, 
              @filename="/example_app/app/views/message_mailer/contact_form.erb", 
              @_memoized_relative_path=["app/views/message_mailer/contact_form.erb"], 
              @extension="erb", 
              @locale=nil, 
              @_memoized_path_without_extension=["message_mailer/contact_form"], 
              @_memoized_method_name_without_locals=["_run_erb_app47views47message_mailer47contact_form46erb"], 
              @load_path="/example_app/app/views">
              
So we found the proper view and we are now done with @find_template@. Back to @render@:
# (line 201 action_pack/action_view/base.rb)
def render(options = {}, local_assigns = {}, &block) #:nodoc:
  local_assigns ||= {}
  
  case options
  when Hash
    ...
    elsif options[:file]
      template = self.view_paths.find_template(options[:file], template_format)
      template.render_template(self, options[:locals])
    elsif options[:partial]
      ...
    end
  when :update
    ...
  end
end

The next line renders the template with any locals (in our case there are none). Before we dive into render_template âs definition, letâs take a look at the view we created for our example message:


(message_mailer/contact_form.erb)

Hi,

I am contacting you with this message:

<%= @message %>

Thanks

Jake

A ridiculously simple mailer view for our equally ridiculously simple mailer message. Back to render_template

# (line 193 action_pack/action_view/template.rb)
def render_template(view, local_assigns = {})
  render(view, local_assigns)
rescue Exception => e
  raise e unless filename
  if TemplateError === e
    e.sub_template_of(self)
    raise e
  else
    raise TemplateError.new(self, view.assigns, e)
  end
end

and our current params:

view  => #<ActionView::Base:0x3496c10 
              @template_format=:html, 
              @assigns={:message=>"Send this message"}, 
              @helpers=#<ActionView::Base::ProxyModule:0x3496b84>, 
              @controller=#<MessageMailer:0x34a5814 
              @action_name="contact_form", 
              @subject="Im contacting you", 
              @content_type="text/plain", 
              @implicit_parts_order=["text/html", "text/enriched", "text/plain"], 
              @from="jake@somewhere.com", 
              @sent_on=Mon Nov 09 20:09:58 -0600 2009, 
              @headers={}, 
              @default_template_name="contact_form", 
              @mime_version="1.0", 
              @body={:message=>"Send this message"}, 
              @mailer_name="message_mailer", 
              @recipients="someemail@somedomain.com", 
              @parts=[], 
              @template=#<ActionView::Base:0x3496c10 ...>, 
              @charset="utf-8">, 
              @_current_render=nil, 
              @assigns_added=nil, 
              @_first_render=nil, 
              @view_paths=["/example_app/app/views"]>
local_assigns => {}

As we can see, render_template is a relatively simple method that delegates most of itâs work to Renderable#render. Thatâs where we are going next:

# (line 193 action_pack/action_view/renderable.rb)
def render(view, local_assigns = {})
  compile(local_assigns)
  
  view.with_template self do
    view.send(:_evaluate_assigns_and_ivars)
    view.send(:_set_controller_content_type, mime_type) if respond_to?(:mime_type)
    
    view.send(method_name(local_assigns), local_assigns) do |*names|
      ivar = :@_proc_for_layout
      if !view.instance_variable_defined?(:"@content_for_#{names.first}") && view.instance_variable_defined?(ivar) && (proc = view.instance_variable_get(ivar))
        view.capture(*names, &proc)
      elsif view.instance_variable_defined?(ivar = :"@content_for_#{names.first || :layout}")
        view.instance_variable_get(ivar)
      end
    end
  end
end

Whew, thatâs it for this week. Next week, we get super deep into rendering and see how Rails performs all of itâs rendering duties.


Deep in Rails: ActionMailer#deliver Part I

Posted by admin 15/11/2009 at 14h59

Welcome to the first in a series of articles that will delve deep into several commonly used methods of Rails. The first method we will look at is Actionmailer#deliver. Now, I am going to assume that you know what AM is and how to use. You’ve generated mailer models before and set up views and called these methods via something like: MyMailer.deliver_some_message(insert, your, params, here). What we are going to to look at is what happens when you make the deliver call.

We will be skipping what happens around rails (the ruby core goings-ons) and all the method stacking that goes on in ruby and focus on the rails part in this series. So let’s get started. (We will be using Rails 2.3.2 as the guide for the code)

What’s happening now?

When deliver_some_message is executed, the first method that is fired is AR’s class method method_missing. Here’s the method that gets executed:

(line 391 action_mailer/base.rb)
class << self
  ...
  def method_missing(method_symbol, *parameters) #:nodoc:
    if match = matches_dynamic_method?(method_symbol)
      case match[1]
        when 'create'  then new(match[2], *parameters).mail
        when 'deliver' then new(match[2], *parameters).deliver!
        when 'new'     then nil
      else super
      end
    else
      super
    end
  end
  ...
end

So when you call deliver_some_message(params), ruby passes all that info (method name included) to the method_missing method. I have created a simple Mailer for this series to help us see what’s going on throughout this entire chain of methods and to see each of the params for these methods.

Here is the quick mailer I created:

class MessageMailer < ActionMailer::Base
  def contact_form(msg_from, message)
    subject    'Im contacting you'
    recipients "someemail@somedomain.com"
    from       msg_from
    sent_on    Time.now
    body       :message => message
  end
end

So when I call

MessageMailer.deliver_contact_form("jake@somewhere.com", "Send this message")

the params end up being distributed like so:

method_symbol => :deliver_contact_form
parameters => ["jake@somewhere.com", "Send this message"]

Now that we know what’s being passed, let’s see what happens to it. The first line of the method is

if match = matches_dynamic_method?(method_symbol)

matches_dynamic_method? is a private method in AM and is defined as:

(line 441 action_mailer/base.rb)
private
  def matches_dynamic_method?(method_name) #:nodoc:
    method_name = method_name.to_s
    /^(create|deliver)_([_a-z]\w*)/.match(method_name) || /^(new)$/.match(method_name)
  end

This is pretty simple, we are doing a scan across the method name to match it against create_ or deliver_ and then capturing the rest of the method name or we are looking strictly for new at the beginning of our method name and with nothing else attached. The reason this is done is to disable AM’s initialize capabilities for outside referencing and only offer create and deliver. On line 254 in AR base.rb we see that we have privatize the method new with:

private_class_method :new #:nodoc:

So if someone tries to be clever and call MessageMailer.new, it will hit the method_missing (since the base new method has now been made private) and end up returning nil.

The match method returns a Match object that contains the captures. With our example the match variable would look like:

match.captures => ["deliver", "contact_form"]
match[1] => deliver
match[2] => contact_form

So now we got a match and we can continue thru the case statement. Since match[1] in our example returns deliver, we execute the line

when 'deliver' then new(match[2], *parameters).deliver!

Here, we call the private new method to initialize a new AR instance and we pass it the second part of our capture and all the params that came to method_missing originally. After initializing, the method deliver! is then called (which we will talk about after we talk about what happens during initialization).

Let’s go ahead and see what new looks like (or more precisely, initialize, since you dont create a new method, you create the initialize method which new calls). AR’s initialize method looks like this:

(line 452 action_mailer/base.rb)
def initialize(method_name=nil, *parameters) #:nodoc:
  create!(method_name, *parameters) if method_name
end

This initialize is quite simple, it calls create! if method_name is present. In our case it will be since we passed “contact_form” to it. So let’s go to create! which is also right below it in the source:

   
(line 458 action_mailer/base.rb)
def create!(method_name, *parameters) #:nodoc:
  initialize_defaults(method_name)
  __send__(method_name, *parameters)

  unless String === @body
    if @parts.empty?
      Dir.glob("#{template_path}/#{@template}.*").each do |path|
        template = template_root["#{mailer_name}/#{File.basename(path)}"]
        
        next unless template && template.multipart?
        
        @parts << Part.new(
          :content_type => template.content_type,
          :disposition => "inline",
          :charset => charset,
          :body => render_message(template, @body)
        )
      end
      unless @parts.empty?
        @content_type = "multipart/alternative" if @content_type !~ /^multipart/
        @parts = sort_parts(@parts, @implicit_parts_order)
      end
    end
    
    template_exists = @parts.empty?
    template_exists ||= template_root["#{mailer_name}/#{@template}"]
    @body = render_message(@template, @body) if template_exists
    
    if !@parts.empty? && String === @body
      @parts.unshift Part.new(:charset => charset, :body => @body)
      @body = nil
    end
  end
  
  @mime_version ||= "1.0" if !@parts.empty?
  
  @mail = create_mail
end

Whoa! Don’t worry, it’s not that bad. Let’s take it line by line. Note: I have removed the comments from the above snippet because they wouldnt format in textile properly.

First, let’s figure out what we have in the method:

method_name => "contact_form"
parameters => ["jake@somewhere.com", "Send this message"]

Cool, now we know what we have going in, so let’s step through it. First we have: initialize_defaults(method_name) which is a private method in AM

which looks like:

(line 537 action_mailer/base.rb)
def initialize_defaults(method_name)
  @charset ||= @@default_charset.dup
  @content_type ||= @@default_content_type.dup
  @implicit_parts_order ||= @@default_implicit_parts_order.dup
  @template ||= method_name
  @default_template_name = @action_name = @template
  @mailer_name ||= self.class.name.underscore
  @parts ||= []
  @headers ||= {}
  @body ||= {}
  @mime_version = @@default_mime_version.dup if @@default_mime_version
end

This method initializes several of the attribute accessors, or in this case advanced attribute accessors, with defaults and some class attribute accessors. The advanced attribute accessors are just like regular attribute accessors except they add one extra ability which you have seen several times in your use of ActionMailer models. Take a look at our simple mailer again:

class MessageMailer < ActionMailer::Base
  def contact_form(msg_from, message)
    subject    'Im contacting you'
    ...
  end
end

Do you see it? Notice, how we can make the call:

subject    'Im contacting you'

This is not a normal attribute accessor call. Normally, you can only call

obj.some_attribute

or

obj.some_attribute =  "some stuff"

The advanced attribute accessor gives you a third option of setting the attribute via a regular method call like:

obj.some_attribute("some stuff")

or, more elegantly:

obj.some_attribute "some stuff"

instead of the regular ol’

obj.some_attribute = "some stuff"

Let’s see how AM accomplishes this. Take a look at action_mailer/adv_attr_accessor.rb:

  
module ActionMailer
  module AdvAttrAccessor #:nodoc:
    def self.included(base)
      base.extend(ClassMethods)
    end

    module ClassMethods #:nodoc:
      def adv_attr_accessor(*names)
        names.each do |name|
          ivar = "@#{name}"

          define_method("#{name}=") do |value|
            instance_variable_set(ivar, value)
          end

          define_method(name) do |*parameters|
            raise ArgumentError, "expected 0 or 1 parameters" unless parameters.length <= 1
            if parameters.empty?
              if instance_variable_names.include?(ivar)
                instance_variable_get(ivar)
              end
            else
              instance_variable_set(ivar, parameters.first)
            end
          end
        end
      end
    end
  end
end

The main part of all of that is the adv_attr_accessor(*names) definition. What Rails is doing here is going over each name (or attribute) you want to create and then doing the following:

  • Dynamically creating an instance variable name.
  • Dynamically creating a method= method (or setter method) for the attribute.
  • Dynamically creating a method method (or getter method) for the attribute, but (and this is where the magic is) adding the ability to have 0 or 1 parameters (instead of the normal 0 parameters that Ruby normally provides). If the parameters to the method is 0 then it just gets the instance variable (attribute) as normal, if it is 1, then it sets the instance variable to the parameter (like the normal method= method does).

That’s how we get the sugar coated ability to write obj.some_attribute "some stuff" for our AM methods.

Ok, now back to initialize_defaults. So we see the attributes getting initialized, let’s see how our example would initialize:

method_name           => contact_form
charset               => "utf-8"
content_type          => "text/plain"
implicit_parts_order  => ["text/html", "text/enriched", "text/plain"]
template.inspect      => "contact_form"
default_template_name => "contact_form"
action_name           => contact_form
mailer_name           => "message_mailer"
parts                 => []
headers               => {}
body                  => {}
mime_version          => "1.0"

Cool, now that we got that all initialized, let’s keep going. Back to create!:

(line 458 action_mailer/base.rb)
def create!(method_name, *parameters) #:nodoc:
  initialize_defaults(method_name)
  __send__(method_name, *parameters)
  ...
end

The next line calls __send__ with the method_name and parameters that we passed in originally. The reason __send__ is called is just in case you override the normal send method. This is a nice lesson for you newcomers to Rails. DONT OVERRIDE CORE RUBY METHODS! Sorry for the yelling.

But, since we cant predict everything, more specifically, we cant predict what new rails developers will do with AM, we use the __send__ method the make sure we dynamically call method_name with the parameters and not an overridden send method in your customized AM class.

In our example, we have the following arguments to send:

method_name => "contact_form"
parameters => ["jake@somewhere.com", "Send this message"]

So Ruby sends the message “contact_form” to our instantiated AM model and since we defined contact_form in our mailer model Ruby will execute this method now. Let’s look at our mailer again:

class MessageMailer < ActionMailer::Base
  def contact_form(msg_from, message)
    subject    'Im contacting you'
    recipients "someemail@somedomain.com"
    from       msg_from
    sent_on    Time.now
    body       :message => message
  end
end

Now that we have seen how the adv_attr_accessors work we can see that all that our contact_form method (or any AM deliver method for that matter) is doing is setting all the proper attributes. Your view is just an HTML or plain text version of your email with some fancy ERB parsing to help make it dynamic.

Let’s keep going. We have just set several attributes using the dynamic __send__ method to call our contact_form method in our AM model. Now we are back to the create! method again:

   
(line 458 action_mailer/base.rb)
def create!(method_name, *parameters) #:nodoc:
  initialize_defaults(method_name)
  __send__(method_name, *parameters)

  unless String === @body
    if @parts.empty?
      Dir.glob("#{template_path}/#{@template}.*").each do |path|
        template = template_root["#{mailer_name}/#{File.basename(path)}"]
        
        next unless template && template.multipart?
        
        @parts << Part.new(
          :content_type => template.content_type,
          :disposition => "inline",
          :charset => charset,
          :body => render_message(template, @body)
        )
      end
      unless @parts.empty?
        @content_type = "multipart/alternative" if @content_type !~ /^multipart/
        @parts = sort_parts(@parts, @implicit_parts_order)
      end
    end
    ...
  end
  ...
end

The next thing we do is check to see whether our body attribute is a String. If not, we are going to do some processing. Let’s take a look and see what attributes we have set with our example:

@parts           => nil
@template        => "contact_form"

Now we know that (in our example) parts is empty and we are going to enter into that conditional statement. Now we are going to find all template views that match our template “contact_form”. template_root is a class method on AM that returns the view_path root (usually apps/views/). This method is copied from ActionView::Base and the root is set there.

Next, we will set the variable template to be the value of template_root when the key is the full path (with the mailer_name, in this case “message_mailer”) to the erb or rhtml file. What we get back, is an instance of ActionView::ReloadableTemplate wrapped around our view file. Let’s see that with our example:

template  => 
  #<ActionView::ReloadableTemplate:0x34a4a68 
    @base_path="message_mailer", 
    @template_path="message_mailer/contact_form.erb", 
    @_memoized_path=["message_mailer/contact_form.erb"], 
    @name="contact_form", 
    @filename="/example_app/app/views/message_mailer/contact_form.erb", 
    ...
    @load_path="/example_app/app/views">

Next, we see that we skip over any further processing unless we have a multipart message or have multiple views for our mailer method (thereby making it a multipart message). That takes us out of the loop (for our example) and down to

unless @parts.empty?
  @content_type = "multipart/alternative" if @content_type !~ /^multipart/
  @parts = sort_parts(@parts, @implicit_parts_order)
end

In our example, @parts is empty so we are pretty much done with this area. Let’s take a look and see what happens next:


def create!(method_name, *parameters) #:nodoc: ... unless String === @body if @parts.empty? ... end template_exists = @parts.empty? template_exists ||= template_root["#{mailer_name}/#{@template}"] @body = render_message(@template, @body) if template_exists ... end end

Basically, what we are doing here is making sure we have either an implied template or dynamically generated template. This is mainly for multipart messages. In our example, since there are still no parts, template_exist gets set to true. Since it is already true, the next line gets skipped and we go directly to rendering the message into our protected attribute @body. Let’s see what render_message looks like:

(line 553 action_mailer/base.rb)
def render_message(method_name, body)
  if method_name.respond_to?(:content_type)
    @current_template_content_type = method_name.content_type
  end
  render :file => method_name, :body => body
ensure
  @current_template_content_type = nil
end

When we call render_message in create!, we call it with @template and @body, let’s see what those get set to inside of render_message for our example:

@template   = method_name     => "contact_form"
@body       = body            => {:message=>"Send this message"}

At the beginning of render_message @body is set to the hash of instance variables that we set in our contact_form method in our mailer. Once we are finished processing, that hash will be gone and @body will be replaced with the full rendered message.

That’s it for now. Stay tuned for next week for Part II where we will pick back up and continue our trek through ActionMailer#deliver and get real deep into rendering.


Super InPlace Controls has gone through some upgrades.

We’ve Moved

First and foremost, we have moved the code from google to github.

To install run


script/plugin install git://github.com/flvorful/super_inplace_controls.git

Rails 2.3 Support

Tested with Rails 2.3 and 2.1

Better Jquery Support

We have updated the code to work better with jQuery. Now, in_place_date_selector, uses the jQuery datepicker method if available. JRails is required for jquery support.

Better Validation

Validations were a problem with SIPC, but no longer. Now, if you dont have an error div, SIPC will skip over the rendering of the error box and just add the class “fieldWithError” to the proper input tag.

Overall cleanup

Cleaned up the code in general and made it less brittle.

Check out the demos and the docs at our Open Source Site and stay tuned for some new plugins we’ve been working on.

—jake


Facebook Connect and Your Business

Posted by admin 18/01/2009 at 12h11

If you are about to socialize your app and you’re wondering which API to use and which network to connect, you’re not alone. Facebook’s API alone has grown into a behemoth and now MySpace is on the verge of making their API public. Throw Twitter’s API, Ning and any one of 20 plus other sites and the combinations make any developer want to curl up into a fetal position and start weeping. What to do??

Well, start slim. When OnlyMusicVideos.tv approached us to help them connect their site into Facebook, the only option was a full on application that appeared on the Facebook Canvas. Now there are much easier and cleaner options. Introducing Facebook Connect, a much cleaner way to integrate the power of Facebook without the user ever leaving your site.

What Can It Do?

Several things. The most important of which, is allowing your app to authenticate a user via their Facebook credentials. This allows users the ability to log into your application via Facebook and pull their information from their FB profile into your application (according to FB rules, obviously). So what exactly does this mean? Simple. Every Facebook user now has an account with your app. If a user can login to FB, then they can login to your application and take advantage of your features. Plus, you can now update the user’s profiles (via news feeds, stories, status updates, etc) with information from your application/business.

How Do You Do It?

There are several ways. The simplest is through FB’s own Javascript Library. You can read through the full documentation and tutorials at the Facebook Connect Developer Wiki, but the basic instructions are:

  1. Add some JS and a single HTML file (xd_connect.html) to your website. This JS will connect to FB and allow you to parse out FB’s custom HTML tags (called FBML) and pull down and session information that is pertinent to the current user (like whether they are logged in or not)
  2. If they are logged in redirect them to the normal logged-in version of your site and add a new Account/User to your app with the Facebook Id that is passed to you. This will allow you to connect a FB user into your system. You may need to alter your DB slightly to hold the facebook id. Then your done.
  3. If they are not logged in you should display the FB login button that will trigger a popup and allows the user to login through FB. Once they login, FB will redirect them back to your site will updated session details. At this point you can just follow through step 2.

That’s it, your connected to the web 2.0 world.

Now What?

Well, that’s up to you. Just because you connected to a social network doesnt mean all of the Internet are going to come running to your site. It does make it much easier, but you still need to get out there and promote. Utilize the services that you are connected to. Create groups and invite all your employees and fans of your products to help generate traffic and awareness. Even your offline marketing needs to show that you are connected through the particular networks that you are integrated with. Send an email blast to your current customers that they can now connect with their FB profile (you might want to make an easy way for them to connect their current account with their FB account). The power of a social network isnt that there are a lot of people on there, it’s the speed at which information can zip through the network and generate instantaneous traffic and feedback.