Default Values for Boolean Options in Ruby

Let’s say you read settings from a YAML file and have some sort of settings object. Then you check if certain options are set with custom values or you have to set default/fall-back values on them. If you are dealing with Boolean options you have to be careful … as I had to find out myself.

Initially you would probably do something like the following to set a default value on a Boolean option:

settings[:some_option] ||= true # set default value if nothing set

Do you see the problem? What happens if the option was deliberately set to

false

? You would overwrite it because both cases

nil

(i.e. nothing set) and

false

would evaluate to

false

in the context of the

||=

operator and you would in both cases assign the right hand value (and overriding an explicit user choice in one case) … *ouch*.

So the correct solution is something like the following:

settings[:some_option] = true if settings[:some_option].nil?

Just be careful … 😀

Render Rails assets to string

If you ever needed a way to render a Rails assets to a string, Hongli Lai from Phusion describes how. 🙂

I prepared a Gist wrapping it into a nice helper. 😀

module ApplicationHelper
  # thanks to http://blog.phusion.nl/2011/08/14/rendering-rails-3-1-assets-to-string/
  # you may need to change the owner of the tmp/cache/* directories to the web servers user
  # e.g. for Debian systems: `chown -R www-data:www-data tmp/cache/*`
  def render_asset(asset)
    Conferator::Application.assets.find_asset(asset).body.html_safe
  end
end

 

Sortable Columns Across Tables

If you follow Railscast 228 you get sortable table columns for your model. But what if you don’t want to expose the name of the actual database columns or more interesting if you want to sort across tables? Here is how I do it.

In your controller add order or reorder if you already have an order clause in one of the used scopes (default_scope counts too).

class AttendeesController < ApplicationController
  def index
    @attendances = @conference.attendances.reorder(sort_query).page(params[:page])
  end

  # ...
end

As I’m using this mechanism in different controllers I added the common functionality to the application_controller.rb file.

class ApplicationController < ActionController::Base
  helper_method :navigation_params, :sort_column, :sort_direction

  protected

  # supports only attendances for now
  ALLOWED_SORT_COLUMNS = {
    "fee_payed" => "fee_payed",
    "fee_payed_on" => "fee_payed_on",
    "name" => "users.last_name, users.first_name",
    "payment_confirmation_sent_at" => "payment_confirmation_sent_at",
    "registered_at" => "registered_at",
    "registration_confirmation_sent_at" => "registration_confirmation_sent_at",
    "town" => "users.town"
  }

  # use this in views
  def sort_column
    ALLOWED_SORT_COLUMNS.keys.include?(params[:sort]) ? params[:sort] : "name"
  end

  def sort_direction
    %w[asc desc].include?(params[:direction]) ? params[:direction] : "asc"
  end

  def navigation_params
    { direction: params[:direction], page: params[:page],  sort: params[:sort] }
  end

  def sort_query
    sort_query_column.split(',').map{ |column| column + ' ' + sort_direction }.join(', ')
  end

  # use this in controllers
  def sort_query_column
    ALLOWED_SORT_COLUMNS[sort_column]
  end
end

This will use the ALLOWED_SORT_COLUMNS hash to map between user visible and actual database sort columns. Adding sort_query also allows us to sort by multiple columns at once. navigation_params is a shortcut I use when generating URLs (e.g. in link_to) and I want to preserve pagination, sorting, filters/searches, etc. across pages.

  def link_to_sortable(column, title = nil)
    title ||= column.titleize
    sort_icon = column == sort_column ? content_tag(:i, nil, class: (sort_direction == "asc" ? "icon-chevron-down" : "icon-chevron-up")) : ""
    direction = column == sort_column && sort_direction == "asc" ? "desc" : "asc"
    link_to (title+" "+sort_icon).html_safe, params.merge(sort: column, direction: direction, page: nil)
  end

Note that sort_icon assumes you are using Bootstrap.

Now we can have sortable columns in our views:

<%= link_to_sortable "name" %>
...