0x06: Body works

One cool Rails feature – however undervalued – are after-filters. You might ask: What is the point of doing things after I am already done doing all things? Well, to prevent micromanagement: Sometimes you just want to do things all the time, or regardless of the specific request: say, embedding some SQL stats on the bottom of all of your pages. This is just one example where you could should look into after_filters first thing in the morning.

When an after_filter is activated the “result” of the action is already build completely and stored in “result.body”. Now all activate after_filters have a chance to take matters into their own hands; they are even free to throw away everything! So this is the time where you are able to modify an action’s result any way you want. There is, however, some rope to hang yourself: beware that

  • not every server response is HTML: RESTful responses are not HTML. AJAX responses might or might not be HTML, depending on the way you implement and use AJAX. Some requests might even send image data generated on-the-fly. Your code must be able to detect those cases – and, when unsure, leave the response alone. ‘request.xhr?’ and ‘response.content_type’ are quite handy to figure out what kind of data you are dealing with.
  • While you can append some HTML data to a (X)HTML document, you shouldn’t do that: the result would not validate (even though browsers are usually displaying such content regardless). Instead you should put your inserts in a place where they are valid: insert HTML code, that should be visible, into the <body /> tag. CSS rules and links belong in the <head /> node, as should <script src=” /> tags.
  • Don’t even think about parsing the HTML response. This would be terribly slow. Instead use string relacements to add your code in the right place. After all, if your application produces otherwise valid (X)HTML you know that your code has a <head> node in the beginning and a body node in the end.
  • To add javascript code to a HTML response you use a <script> tag (obviously). Embedding it into a CDATA section might be wise.
  • Adding HTML nodes to a javascript response? Think twice – what do you want to achieve? To add some nodes to the document that is visible right now you could use a “document.write(‘…’);” sequence.

Enough sweettalk, here is a code example:

class OradController < ApplicationController

  # ...


  # --- check for response types --------------------------------------
  def response_is_html?                                 #:nodoc:
    # TODO: this is certainly not the only HTML Content-Type!? 
    headers["Content-Type"] == nil 
  # --- append some HTML to the response body -------------------------

  def append_html(html)
    return unless response_is_html?
    response.body.gsub! /<\/body>\s*<\/html>\s*$/i, "#{html}</body></html>"

  after_filter :hello_after_filter
  def hello_after_filter
    append_html, "<p>Kilroy was here!</p>"

Bold announcement: next week I will revisit Body Works again with an actually working plugin, fully tested and all.


One response to “0x06: Body works

  1. i’ve been using this for similar things, like embedding auto generated meta data and bread crumbs. for those cases it’s actually better to use content_for(:whatever) do; ‘content’ end in your view and yield(:whatever) in your layout. but when you want to insert data that you only know after the request is done (like runtimes) the after_filter approach is the way to go.

    by the way: “RESTful responses are not HTML” – rest doesn’t have to do anything with html or not. rest has to do only with urls and http methods – it doesn’t care about content.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s