Choosing Which Version of a Gem to Run

Paul Sturgess posted a nice little tip on how to How to use Capistrano 1.x to deploy Ruby on Rails apps when you have Cap 2.x. Basically, the following syntax will allow you to choose which version of a gem to run:

cap _1.4.1_ deploy

Or say you want to generate a 1.2.3 rails app:

rails _1.2.3_ my_old_app

Nice one Paul!

jQuery: Embedding Behavior

The fellows over at Err have finally seen the light of jQuery. My first thought is welcome to the party, we've been wondering when you'd get here. From my first days with rails, I've been using jQuery over Prototype. Why? Well, a lot of reasons, but the biggest one is because my rails mentor is Yehuda Katz, of Visual jQuery and jQuery in Action. From the first time we met, Yehuda echoed to me exactly what he says in the Err post comment thread.

jQuery is a perfect companion to Rails; most people who do any kind of non-trivial JS end up writing custom stuff anyway, and the built-in Rails helpers end up being more trouble than they’re worth.

Needless to say, I agree. In honor of the Chris and PJ jumping to the jQuery camp, I thought I'd answer a thoughtful question in the comments from partage. I know this is a Ruby blog, but hey, jQuery also saved my life.

I’ve also enjoyed mixing jQuery with rails; however, I wonder what techniques people are using to keep the javascript unobtrusive in div replacement scenarios?

To my way of thinking, it sometimes seems natural to package a chunk of markup with the behaviors it needs to operate properly. It’s particularly true when the markup operates as self contained component which may be used several places (for instance, via both partial includes and xhr div replacement).

In those situations, I prefer to call the behavioral javascript at the bottom of the markup rather than trying to ensure I add the behaviors externally (and repeatedly) on all page loads, xhr success handlers, etc. However, I usually suffer a few pangs of guilt with respect to ‘unobtrusive’ javascript.

If I understand correctly, the problem is related to the jQuery methodology of binding events on document ready.

Basic Event Binding

The key to remaning unobtrusive with jQuery is to:

  1. Select a piece of DOM, i.e $("a.update")
  2. Bind something to it, i.e $("a.update").click(function(){
    $(this).parent()
    .load('/posts/'+this.href.split("/").slice(-1)[0].match(/^\d+/))})

But how and when does this code actually run and get applied to the dom? Well, when the document is ready, as so:

 
$(function() {
  $("a.update").click(function(){
    $(this).parent().load('/posts/'+this.href.split("/").slice(-1)[0].match(/^\d+/));
  });
};
 

Simple, when the document is ready, $(function() {, select all div's with reload, and on click (an event binding), run a function that will load html from an ajax request into the dom that called, the function, represented by $(this). From what URL? From /posts/numbers-in-the-id-of-this-div. What's that mean? Well, I like to do this:

 
for p in @posts
  "
<div id=\"post-#{p.id}\" class=\"reload\">#{p.body}</div>
 
"
end
 

So when you click on that div, it will call /posts/id, which fits in nicely with rails, via ajax. It will take the result of that request, an replace the html of that div with it. Awesome. But if you click on that div again, nothing happens. That's because the dom has been replaced and the onclick event that was bound on document ready, no longer exists. Something must leave instructions for jQuery on what to do next, something must repass behavior instructions.

Talking Back to jQuery

In the example above, reloading the div's dom is a trivial feat - as in, you'd really want to use what I'm about to describe for a more complicated situation - for example, if you load a lightbox that has a form in it and which should be submitted via ajax and if validation fails in rails, should display errors and allow for resubmission until success, when it should display a confirmation, close the lightbox, and update some dom. However, I'm going to continue with the reload example because it's just easier to use, even if it's overkill.

Callbacks and Simple Rebinds

The first way to talk back to jQuery, to have the dom reinstruct the script with post-processing, is to use the many opportunities for callbacks and to simply use logic to rebind things. Now this really only works on the simpler examples, but it's the first option.

 
$.fn.reload_post = function(){
  $(this).click(function(){
    $(this).load('/posts/'+$(this).attr("id").match(/^\d+/));
    $(this).reload_post();
  });
}:
 

The problem with this method is that it's limited in complexity and basically requires a custom function for each implementation - as in that might work for reload_post, but what about reload_comment? But still, that's one options for binding behavior.

The Tempting but Bad Meta Data Idea

There is a great, well great in theory, poor in practice, plugin for jQuery called MetaData. What it basically allows you to do is embed meta data into DOM. At first, this is all I used for binding complex behavior to elements. So I'd have a lot of this in my code:

 
<a href="/products/share/1" class="save simple_post {success: NewSavedProduct, dataType: 'json', error: ErrorSimplePost}">
<a href="/products/share/2" class="share simple_post {success: ShareProduct, dataType: 'text', error: ErrorSimplePost}">
 

At firs,t I thought this was great. I could reuse the same JS function (simple_post - which just creates a POST request from the url of a link), but make it do different things by embedding the options I wanted into the element itself. After about two weeks, I hated it. In the end, it still wasn't flexible enough for me to give complicated instructions, I wasn't reusing that much code as ShareProduct and NewProduct were really custom functions, and in the end, I had totally compromised the semantic nature of my dom. So I nixed that and needed my own solution. Basically what I wanted were the metadata type options, just out of my dom, so I could reuse functions, load elements with tons of behavior information, but have no trace of that in my source.

An External Meta File

 
// Button Meta Data
var simple_post = {
	save_post: {
		{success: NewSavedProduct, dataType: 'json', error: ErrorSimplePost}
	},
	share_post: {
		{success: ShareProduct, dataType: 'text', error: ErrorSimplePost}
	}
};
 

The simple_post function can now use the data stored in this JSON object to instruct itself on what to do based on the link clicked. I've been using this method a great deal for the submit buttons on Designer Pages. There is one function that is bound to all the buttons and that calls a similar type of JSON object to find the instructions for the particular button clicked. This way when I want to add a new behavior for a button, I can just go into that meta.js file and add a new definition. It also integrates nicely with rails helpers. I think there is a lot of potential in this method so I'm going to save the gritty details for another post.

I hope this is a good food-for-thought piece on ways to embed behaviors for your elements in jQuery. In the next week I'll be introducing my first rails plugin is_popular, so subscribe.

Taking Merb to Production

Taking Merb to Production

A good up and running with Merb tutorial.

has_many :through: Finding unassociated objects

has_many :through: Finding unassociated objects

Awesomeness.

Tip: Return True for Model Hooks

Everyone loves before_create and before_save. Recently it came in very handy for logging hits to objects. If the user is logged in, I want to store the user's full name and the user's type, otherwise, keep those null. So I added this to my PostHit model:

 
def before_create
  if self.user
    self.user_name = self.user.full_name
    self.user_type = self.user.class.name
  end
end
 

I fired up my browser to find that the records weren't being created. I was calling them in the Post model via:

 
def log_hit(user = nil)
  self.hits << PostHit.create(:user => user)
end
 

Everything looked right, so what was wrong?

Before Hits Need to Return True

In my before_create hook, if user is nil, then the method returns nil, which is false, which prevents the instance from being created. Thus, return true.

 
def before_create
  if self.user
    self.user_name = self.user.full_name
    self.user_type = self.user.class.name
  end
  return true
end
 

Viola!

render_either - the beginning

There is this helpful method I've been evolving in my apps for a bit, it's called render_either and it goes a little something like this: (in application.rb)

 
  def render_either(opts = {})
    # For MIME:Type Recognition
    # respond_to do |f|
    #   f.js {@wants = :js}
    #   f.xml {@wants = :xml}
    #   f.html {@wants = :html}
    # end
    # Then use @wants
    if request.xhr?
      if opts[:ajax]
        if opts[:ajax][:json]
          render :json => opts[:ajax][:json].to_json
        else
          render opts[:ajax]
        end
      end
    elsif params[:bookmarklet]
      if opts[:bookmarklet]
        if opts[:bookmarklet][:redirect_to]
          redirect_to opts[:bookmarklet][:redirect_to]
        else
          render opts[:bookmarklet]
        end
      end
    else
      if opts[:default]
        if opts[:default][:redirect]
          redirect_to opts[:default][:redirect]
        else
          render opts[:default]
        end
      end
    end
    return false
  end
 

This method is very much incomplete - I mean it works - but I want to take it further (and make it prettier and more condensed, but for now, sadly, that's actually more readable to me while I improve it). So why did I write it, what does it do, and why should you care? Let's say you want to be able to create a Post from a vanilla form, from a lightbox (or an ajax form), and from a bookmarklet. Obviously, you want this all occurring in a PostsController within the new and create methods. Here this is without render_either

 
PostsController < ApplicationController
 
  # For a vanilla form new.rhtml should be rendered with layout application (default)
  # For a bookmarklet, render new.rhtml but with layout bookmarklet
  # For a lightbox, send back new.rhtml without a layout (so that it back be loaded into a lightbox easily)
  def new
    @post ||= Post.new
    if request.xhr?
      render :layout => false
      return false # To prevent DoubleRenderError
    elsif params[:bookmarklet] #indicating that this is being loaded in a bookmarklet
      render :layout => "bookmarklet"
      return false
    end
  end
 
  # For a vanilla form, if post is valid, redirect_to post_path(@post),
  # else, render :action => "new"
  # For a lightbox, if post is valid, render :partial => "post",
  # else, render :action => "new" without layout
  # For a bookmarklet, if post is valid, redirect_to close_bookmarklet_path,
  # else, render :action => "new", :layout => "bookmarklet"
  def create
    @post = Post.new(params[:post])
    if @post.save
      if request.xhr?
        render :partial => "post"
        return false
      elsif params[:bookmarklet]
        redirect_to close_bookmarklet_path
        return false
      else
        redirect_to post_path
        return false
      end
    else
      if request.xhr?
        render :action => "new", :layout => false
        return false
      elsif params[:bookmarklet]
        render :action => "new", :layout => "bookmarklet"
        return false
      else
        render :action => "new"
        return false
      end
    end
  end
 
end
 

Ow, that's painful, right? And that doesn't even deal with some of the added complexities in a real application (returning json, returning xml for api calls, other branches and conditions). The render_either method.

 
PostsController < ApplicationController
 
  # For a vanilla form new.rhtml should be rendered with layout application (default)
  # For a bookmarklet, render new.rhtml but with layout bookmarklet
  # For a lightbox, send back new.rhtml without a layout (so that it back be loaded into a lightbox easily)
  def new
    @post ||= Post.new
    render_either :ajax => {:layout => false},
                  :bookmarklet => {:layout => "bookmarklet"},
  end
 
  # For a vanilla form, if post is valid, redirect_to post_path(@post),
  # else, render :action => "new"
  # For a lightbox, if post is valid, render :partial => "post",
  # else, render :action => "new" without layout
  # For a bookmarklet, if post is valid, redirect_to close_bookmarklet_path,
  # else, render :action => "new", :layout => "bookmarklet"
  def create
    @post = Post.new(params[:post])
    if @post.save
      render_either :ajax => {:partial => "post"},
                    :bookmarklet => {:redirect_to => close_bookmarklet_path},
                    :default => {:redirect_to => post_path(@post)}
    else
      render_either :ajax => {:action => "new", :layout => false},
                    :bookmarklet => {:action => "new", :layout => "bookmarklet"}
                    :default => {:action => "new"}
    end
  end
end
 

That's the point of render_either, in a nutshell, to abstract out display logic from a controller. This is one of the most basic arguments for render_either, and the reason I created it. I wanted to DRY the logic and remove less if / else branches in a controller action. It then began growing into a stronger package that allows me to do some awesome things with xml and json that I will touch on in later posts. Let's just say that set_flash loves render_either. Any questions?

flash[:notice] to the extreme

I previously discussed the difference between flash.now[:notice] and flash[:notice]. I ended with a summarizing rule that says when you end an action with render, you generally want to use flash.now[:notice], and when you end an action with redirect, you want to use flash[:notice]. While simple, I hate remembering that rule, and what really complicates things is request.xhr?

 
# Toward the end of an action in a controller
if request.xhr?
  flash.now[:notice] = "Action Completed"
else
  flash[:notice] = "Action completed."
end
 

An Ajax Request (XHR) always should use flash.now. Why? Because if it's an ajax request, then the page was not reloaded, which means if you set flash[:notice], whatever request comes next from the user, will load with the flash[:notice] of the last request, which in this case, is an XHR request - in short, they will see a persistent, non-relevant, flash[:notice] from the ajax call. So the rule for choosing whether to use flash.now or flash becomes: If an action is a response to an ajax request, or if it ends with a render, use flash.now, otherwise use flash.

But That's Complicated!

True, it is complicated and requires a lot of conditional logic in your controllers. The truth is, the choice between flash and flash.now should be abstracted out. A controller should not be responsible for choosing whether to flash or flash.now.

set_flash

In application.rb define the following function.

 
def set_flash(options={})
  now = options[:now] || (request.xhr? ? true : false)
  if now
    flash.now[:notice] = options
  else
    flash[:notice] = options
  end
end
 

A simple, but powerful and helpful function. Instead of having to call flash.now or flash, you can just do this in your controllers (as opposed to the example above).

 
# Towards the end of an action in a controller
set_flash :text => "Action Completed"
 

See, no more checks for request.xhr? because set_flash has that check. Of course, you can easily override that if you wanted an xhr request to flash and not flash.now.

 
set_flash :text => "Action Completed", :now => true
 

What I really love about the set_flash method is the ability to nicely constructed flash's with a lot of meta data:

 
set_flash :klass => "error",
             :text => "Registration failed",
             :alt => "Did you fill out the form correctly?"
 

I can then build a helper method in application_helper.rb to print out a formatted flash

 
def formatted_flash
 "
<h2 class=\"#{flash[:notice][:klass] || "success"}\">#{flash[:notice][:text]}<span>#{flash[:notice][:alt]}</span></h2>
 
"
end
 

Obviously, you can customize this method to handle a lot of how to format the flash message logic and you can add your keys to your flash[:notice], like type (a flash isn't always a notice, maybe it's a warning). Don't settle for an unintelligent, string-based flash notice.

Excuse the shoddy formatting above, I'm working on it ;-)

Flash[:notice] vs Flash.now[:notice]

You're just starting on rails and you're very excited about the ease of flash[:notice]. But every now and then, you set a flash notice and it seems to follow your clicks to the next action when it shouldn't. Why? I'll tell you.

Intro to flash[:notice]

flash[:notice] belongs to FlashHash.The basic use of the flash hash is flash[:notice], a place to put a message that describes the result of an action in a rails app. Let's look at an example:

 
class WeblogController < ActionController::Base
  def create
    @post = Post.new(params[:post])
    if @post.save
      flash[:notice] = "#{@post.title} has been created."
    else
      flash[:notice]= "#{@post.title} could not be created."
    end
  end
end
 

Pretty self explanatory. However, things get a bit complicated when you think about what that method should be doing. If the post is successfully created, the method should redirect_to post_path(@post), else, render :action => "new". The new action will fill the form in using the already initialized @post object (and it's errors) and the flash message will let them know that there was a problem. So let's add that in real quick:

 
class WeblogController < ActionController::Base
  def create
    @post = Post.new(params[:post])
    if @post.save
      flash[:notice] = "#{@post.title} has been created."
      redirect_to post_path(@post)
    else
      flash[:notice]= "#{@post.title} could not be created."
      render :action => "new"
    end
  end
end
 

Now flash[:notice] becomes a problem. When the user sees there was an error creating the post, he decided to abandon the process and move onto a different page in your application. Well, what's going to happen is that on the next page, he is going to see the same flash[:notice] he just saw notifying him of the error.

Flash Right Now

The Flash hash by default persists to the next action. That's what it's suppose to do - it's a way to send a message to the next action. However, in our create post example, if the post fails creation, there is no next action, rather, the create action simply renders the new action, a very common pattern. That's why when the user moves to another page, the flash[:notice] persists, because that other page is the next action. Obviously, you want to avoid that, and that's exactly what flash.now is for:

 
class WeblogController < ActionController::Base
  def create
    @post = Post.new(params[:post])
    if @post.save
      flash[:notice] = "#{@post.title} has been created."
      redirect_to post_path(@post)
    else
      flash.now[:notice]= "#{@post.title} could not be created."
      render :action => "new"
    end
  end
end
 

A basic rule to know when to use flash[:notice] and when to use flash.now[:notice] is if the action is redirecting - redirect_to - use flash[:notice], if it's rendering - render :action - use flash.now[:notice]. This is really just the beginning of the power of the flash object. Stay tuned for some more posts on how to really make use of this great feature in rails.

Chaining Create Methods to an Object Instance

In my Rails controllers, I have a lot of this:

 
@project = Project.find(params[:project])
if @project
  @project_product = @project.project_products.create(params[:project_product])
  if @project_product.valid?
   # Do something with success
  else
   # Do something with error
  end
else
  # Do something with error
end
 

The thing that I don't like about that style is that the controller is validating the creation of a ProjectProduct. There is the conditional " if @project " branch that basically says that the ProjectProduct requires a valid Project. I feel like the controller shouldn't be the one to determine that - this isn't a case of validating params (like if you preventing url hacking). Rails sort of even lets you know that you shouldn't be using that syntax because attaching the error to @project_product (which you would be sending back to the form as an error message), is impossible as @project_product hasn't even been initiated yet.

For these reasons I have no opted to do something like this:

(assuming ProjectProduct.rb validates the presence of :project, which it should - that's the entire point, that the model in this case should be requiring the existence of a project for ProjectProduct, not the controller)

 
@project = Project.find(params[:project])
@project_product = ProjectProduct.create(params[:project_product].merge(:project => @project))
if @project_product.valid?
  # Do something with success
else
  # Do something with error
end
 

Benefits to this:

  1. Shorter Controller Method
  2. One less conditional branch
  3. Validation by Model and not Controller
  4. No hacks to get Rails to generate error message for you.

A new best practice for myself.