How to handle user time zones in Ruby on Rails

Displaying a date time correctly for users around the world is made very easy with Ruby on Rails. The time zone can be set per request and loaded from the logged in user's configuration. All of the default time zones are already built into Ruby on Rails and provides the ability to add custom ones, for custom cities too.

Model

First we need to allow the user to choose their desired timezone, so a migration is required:

class AddTimezoneToUser < ActiveRecord::Migration
  def change
    add_column :users, :timezone, :string
  end
end

Then of course this change needs to be applied to the database.

$ rake db:migrate

Then ensure the User model has timezone under its accessible attributes

class User < ActiveRecord::Base
    attr_accessible :email, :name, :timezone, :created_at, :updated_at
end

View

Next we must provide a field to choose from. In this example I am simply adding a new form field to my users/_form.html.erb partial. Here we use the built in time_zone_select form helper.


Which will automatically give a result similar to the following screenshot. The select menu is prefilled from the Rails ActiveSupport::TimeZone class.

image

Controller

Lastly, an application controller before_filter is required that will set the time zone for each request.

class ApplicationController < ActionController::Base
  protect_from_forgery
  before_filter :set_timezone

  private

  def set_timezone
    tz = current_user ? current_user.timezone : nil
    Time.zone = tz || ActiveSupport::TimeZone["London"]
  end
end

Here current_user points to the logged in user record. This is the default behavior when using the "devise" gem. If the current user does not have a timezone, I choose to fallback to GMT (London) time. This can be changed for whatever default makes sense in your given project.

Append an HTML string to the DOM without a JavaScript Library

In the event where appending new nodes to the DOM via JavaScript is required but there is no access to a library such as jQuery or Prototype.JS, this handy snippet allows an easy librabry-like API with a tiny amount of code:

 function append ( elString, parent ) {
    var div = document.createElement( "div" );
    div.innerHTML = elString;
    document.querySelector( parent || "body" ).appendChild( div.firstChild );
 }

This snippet can be used easily:

// Append a link tag to the head
append( "<link rel='stylesheet' href='css/foo.css' />", "head" );

// Append a parapgraph to the body
append( "<p>My text</p>" );

// Append a new list item
append( "<li>Cheese</li>", "ul.shopping-list" );

Install Node.JS on Debian Wheezy

Official Method

The official Node.JS install guide recommends manually downloading and building the Node.JS package for Debian. This can be done using the following set of commands:

sudo apt-get install python g++ make checkinstall
mkdir ~/node_js_src && cd $_
wget -N http://nodejs.org/dist/node-latest.tar.gz
tar xzvf node-latest.tar.gz && cd node-v*
./configure
checkinstall
sudo dpkg -i node_*

This install method should work fine but will take a long time to complete on the Raspberry Pi's hardware. For this reason I experimented a little and found a quicker install method that doesn't require a manual compile, by using the Ubuntu package.

Ubuntu Method

This install method will be much faster than the official method but is not designed for Debian Wheezy, so use at your own risk! Personally I have not had any trouble with my node install yet but I cannot guarantee this will be the case for anyone else.

sudo apt-get update
sudo apt-get install -y python-software-properties python g++ make
sudo add-apt-repository ppa:chris-lea/node.js

At this point the file with the new repository details must be edited. Open the file: /etc/apt/sources.list.d/chris-lea-node_js-wheezy.list with a text editor like vim and change the word "wheezy" to "lucid". Thanks to Ken Yap for this hint.

Now, go on and finish installing Node.

sudo apt-get update
sudo apt-get install -y nodejs

Install LAMP server on Debian Wheezy from scratch

Debian Wheezy is the recommended UNIX package for a Raspberry Pi. After setting up your device and completing the raspi-config steps, simply run the following commands line-by-line in the bash terminal to create a functioning web server.

sudo apt-get install -y build-essential make
sudo apt-get install -y mysql-server mysql-client
sudo apt-get install -y apache2
sudo apt-get install -y php5 libapache2-mod-php5

The web server root can be found at /var/www/ and will contain a default index.html file. Type the IP address of the Raspberry Pi into a web browser and a page similar to this should be presented.

Apache default index

The root web directory is owned by the Apache user so to make changes within it write and execute rights must be given to the current user. Please note that this may compromise security, so not advised for a publicly accessible server.

sudo chmod a+wx /var/www/

Now files can be created and saved in the www directory. Create a new file called info.php and type the following into it:

 <?php phpinfo(); ?>

Again, open the Raspberry Pi IP address in a web browser but this time add /info.php to the URL. If everything has been installed correctly an information screen of the PHP environment will be displayed.

PHP Info Screen

To install Node.JS on Debian Wheezy, please see this article: Install Node.JS on Debian Wheezy

A JavaScript Google Analytics event tracking plugin

In todays online world it is common for web developers to improve their websites by enabling a large amount of interactive functionality. Often this takes the form of JavaScript applications that no longer adhere to the simple page-by-page model but instead act more like a desktop program (Gmail, YouTube and Twitter for example).

If many interactions are happening in a single page without the URL changing, this experience is great for the user but can prove problematic when tracking for analytics. Traditionally analytics software such as Google Analytics is included in every web page of a site and each time a page is downloaded it is tracked back to Google. Obviously if the visitor never leaves that URL, this causes the aforementioned issue.

Google have a solution called "Events" that allow a developer to programmatically track an interaction via JavaScript. Events breakdown into the following components:

  • Category
  • Action
  • Label (optional)

For example, to track a customer checking out of an online store through a JavaScript form interface the following events may be used:

Format: Category / Action / Label

  1. Checkout / ViewCart
  2. Checkout / Country Selected / USA
  3. Checkout / Details Provided
  4. Checkout / Order Confirmed

From these tracked events, reports can be generated or funnels (called "Goals" by Google) setup to track conversion rates.

The method that Google provides to programmatically track an event looks as follows:

_gaq.push(['_trackEvent', 'Checkout', 'Country Selected', 'USA']);

This is simple enough, however, it is not wise to litter application code with calls to _gaq. If the Analytics script does not load due to a network issue this variable will be undefined and cause a potentially serious error. Also, if you someday remove Google Analytics it may be problematic to remove all occurrences of this throughout your code base.

For this reason I have created the following wrapper around the tracking code with a simple API and multiple advantages that can be plugged into any JavaScript application.

An effective LESS.js file include pattern

In the previous article I gave an introduction to CSS preprocessors and explained the potential advantages of using this technology. One of the mentioned examples was the ability to break down your stylesheet into multiple files and join them together to create a single final CSS file. This method provides a clean hierarchy for managing stylesheets.

My preferred pattern for the architecture of a preprocessed stylesheet is as follows:

  • Settings
  • Libraries
  • Modules
  • Views

First include the settings for my project (colours, fonts etc. in variables).

Secondly library code you have, for example, reset.css should be included.

Next I include my global styles and separate them into manageable modules. e.g. Links, Forms, Tables.

Finally I include my view related code for specific web pages and any overrides that are required. For example, a "view" could be the website home page as it usually differs from the rest of the website.

Example LESS.js Stylesheet

# screen.less

// Import settings
@include "settings/global.less";

// Import libraries
@include "libs/reset.css"; // Notice the .CSS extension

// Import global modules
@include "modules/core.less";
@include "modules/links.less";
@include "modules/forms.less";
@include "modules/site_header.less";
@include "modules/site_footer.less";

// Import views
@include "views/home.less";
@include "views/contact.less";
@include "views/admin.less";

Although the syntax is in LESS, this will also work with SASS using the import command

LESS/SASS: The Advantages of CSS Preprocessing Explained

CSS has a very powerful syntax but can easily become very verbose when working on non-trivial projects. In recent years the need to solve this problem has brought to life the concept of the CSS preprocessors such as SASS/SCSS and LESS.js. The former is written for the Ruby programming language whilst LESS is compiled using JavaScript.

A preprocessor allows additional leverage over CSS by providing additional syntax that delivers the following advantages:

  • Nested syntax

  • Ability to define variables

  • Ability to define mixins

  • Mathematical functions

  • Operational functions (such as “lighten” and “darken”)

  • Joining of multiple files

SCSS and LESS have very similar syntaxes, with SASS being a slight variant on SCSS. The similarities between them allows a developer to switch seamlessly if need be. In the following demonstration I will focus on the LESS syntax but all of the concepts can be transferred to the Ruby world.

Find and Replace across project with VIM

Many IDE's provide a function to find and replace across files in your project, of course VIM can do this too by using the "arg" commands, without switching to the terminal!

There are 2 steps to the task, first we provide a set of files for the replacement to happen on. The second part is telling VIM what to replace. Take a look at this example:

:args javascript/*
:argdo %s/foo/bar/g | update

Running these 2 commands in VIM will loop through all files in the "javascript" folder and replace all instances of "foo" with "bar".

© Blake Simpson, 2012 – 2017