lazy-images-rails or: How I Learned to Stop Worrying and just wrote a Rails plugin

I worry

One thing that really bugs me, which apparently seems a ubiquitous trend on the web today, is the ever increasing size and sluggishness of many web pages. Why optimise for speed and effectiveness when you can plaster your users with megabytes of Javascript and a plethora of huge images? Surely we as developers shouldn’t bother ourselves with concerns of such trivial nature. Network technology will save us, right?

No.

You can strap a rocket to a turd, but that doesn’t make it a spaceship.

One part of it is surely monstrous asset packages, but another just as important part is images. Does this look familiar?

KyTy8LV

Succinctly, reddit user ReadyAurora5 puts it beautifully:

The fury this incites in me is unhealthy. I want to find whoever was responsible for this, tie him/her up and give them a touchscreen that says: Should you be let go? YES or NO… with absolute assurance that which ever they click on will be fulfilled. I’m leaving it completely up to them. Of course they’ll click yes, but when they go to, IT FUCKING MOVES TO ‘NO’ HAHAHAHA. NOW DO YOU SEE!? REAL FUCKIN’ FUNNY ISN’T IT!?

Sorry…I lost myself there.

So what to do?

I worry no more

On a personal project I decided to deal with the image part of the problem. The solution I decided to go with, was to simply have inline SVG placeholders, with the exact dimensions of the target image. Inlining the placeholders directly in the markup adds the benefit of instantly taking up the required space on a page instead of having to wait for another round-trip to fetch the image from the server.

I’m aware that the same effect can be achieved by explicitly setting the width and height attributes of the img tag. The downside of that however, is that it will leave a blank space on the page until the image has been loaded. Aside from this, even if you know the desired size of the image, you might want it to scale with the width of it’s parent container. For square images, the ratio is all the same, so you shouldn’t have to specify a height. This also makes the solution easier in this case. Simply insert an element that renders and scales the same way as the image and replace once it’s ready.

I’m almost certainly not the first to do it this way, but I couldn’t immediately find a ready made drop-in solution for Rails.

For another CSS based solution using bottom padding, see Related below.

The plugin

There’s a more thorough explanation in the project readme, but the main idea for the plugin was to have this functionality added, with the least amount of intrusion into the consuming application.

To achieve this, when including the gem, the Rails image_tag helper is aliased so instead of a bare img tag, a wrapped SVG is inserted instead, along with the necessary data for lazy-loading the image in-place.

A smidgen of javascript is then used to trigger the lazy-load on document ready, but basically that’s it.

Give it a look-see and take it for a spin the next time you want to fill your site with a bunch of images.


GitHub

https://github.com/rhardih/lazy-images-rails

RubyGems

https://rubygems.org/gems/lazy_images-rails

Here’s a different solution to the same problem by Corey Martin on aspiringwebdev.com.

Rails 4 how to: User sign up with email confirmation in five minutes, using Devise and Mailcatcher

Sometimes you might find yourself wanting to quickly prototype an application that requires user sign ups. Here’s a quick guide to setting up a new rails application with user signup and email confirmation.

Example project available here: https://github.com/rhardih/rails4-with-user-signup. Each step below will be annotated with a commit linked on Github.

Set up a new rails project

  1. rails new rails4-with-user-signup -d postgresql
  2. cd rails4-with-user-signup
  3. bin/rake db:create
  4. bin/rails s

If you’re on OS X using PostgreSQL, you might see this error intially:

could not connect to server: No such file or directory Is the server running locally and accepting connections on Unix domain socket “/var/pgsql_socket/.s.PGSQL.5432”?

One extra step adjusting the config/database.yml is needed. Just uncomment the host option and you should be good to go. If you go to http://localhost:3000, you should see the familiar “Welcome aboard, You’re riding Ruby on Rails!” message page.

Commits d991a6b8d2f4.

Add Devise for user sign up and authentication

  1. Follow the Getting Started section of the Devise README. Commits be1c60be34ad3625cd.
  2. Then follow the the Devise wiki page for adding :confirmable to Users. Commits eedaab862a3e2733d84ee44b.
  3. Additonally, to make sure you can actually send email in development mode, add the following options to config/environments/development.rb:
    config.action_mailer.default_url_options = { host: ‘localhost’, port: 3000 }
    config.action_mailer.delivery_method = :smtp
    config.action_mailer.smtp_settings = {:address => “localhost”, :port => 1025} 

    Commit: 35e177.

Setup and run Mailcatcher to capture Devise sign up emails

  1. Install: gem install mailcatcher
  2. Run: mailcatcher

Test run

Open another tab at http://localhost:1080, where you can see the mailcatcher interface with an empty mail queue.

Screen Shot 2014-06-03 at 7.57.56 PM

Now go to http://localhost:3000/users/sign_up and create a new user.

Check again in the mailcatcher interface. You should now see an email with the subject “Confirmation instructions”.

Screen Shot 2014-06-03 at 8.06.06 PM

Congratulations! You’ve just set up a a new rails application with user sign up, authentication and email confirmation.

Page specific Javascript in Rails 3

Premise

One of the neat features from Rails 3.1 and up is the asset pipeline:

The asset pipeline provides a framework to concatenate and minify or compress JavaScript and CSS assets. It also adds the ability to write these assets in other languages such as CoffeeScript, Sass and ERB.

This means that in production, you will have one big Javascript file and also one big CSS file. This reduces the number of request the browser has to make and generally loads the page faster.

In the case of Javascript concatenation however, it does bring about a problem. Executing code when the DOM has loaded is commonplace in most web applications today, but if everything is included in one big file, and more importantly the same file, for all actions on all controllers, how do you run code that is specific to just a single view?

Solution(s)

Obviously there is more than one way of solving this problem, and rather unlike Rails, there doesn’t seem to be any “best practice” dictated. The closest I found is this excerpt from section 2 of the Rails Guide about the Asset Pipeline:

You should put any JavaScript or CSS unique to a controller inside their respective asset files, as these files can then be loaded just for these controllers with lines such as <%= javascript_include_tag params[:controller] %> or <%= stylesheet_link_tag params[:controller] %>.

And it isn’t even followed by an example, which seems more of an indication, that this isn’t something you should do at all.

Let’s start by this example nonetheless.

Per controller inclusion

By default Rails has only one top level Javascript manifest file, namely app/assets/javascripts/application.js which has the following content:

// This is a manifest file that'll be compiled into including all the files listed below.
// Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
// be included in the compiled file accessible from http://example.com/assets/application.js
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// the compiled file.
//
//= require jquery
//= require jquery_ujs
//= require_tree .

And this is included in the default layout with:

<%= javascript_include_tag "application" %>

N.B. When testing production on localhost, with e.g. rails s -e production, rails by default wont serve static assets, which application.js becomes after pre-compilation, so to avoid any problems when locally testing production, the following setting needs to be changed from false to true in config/environments/production.rb:

# Disable Rails's static asset server (Apache or nginx will already do this)
config.serve_static_assets = true

Now let’s say we have a controller, let’s call it ApplesController, and its corresponding Coffescript file, apples.js.coffee. We might try to include it as per the Rails Guide suggestion like so:

<%= javascript_include_tag params[:controller] %>

And this will work just fine in development mode, but in production produce the following error:

ActionView::Template::Error (apples.js isn't precompiled):

To remedy this, we need to do a couple of things. First off we should remove the require_tree . part from application.js, so we don’t wind up including the same script twice. Just removing the equal sign is enough:

//  require_tree .

To avoid a name clash rename apples.js.coffee to something else, e.g. apples.controller.js.coffee. Then create a new manifest file named apples.js, which includes your coffeescript file:

//= require apples.controller

Lastly, the default configuration of Rails only includes and pre-compiles application.js, so we need to tell the pre-compiler to now also include apples.js. This is also in config/environments/production.rb. Uncomment the following setting, and change search.js to apples.js:

# Precompile additional assets (application.js, application.css, and all non-JS/CSS are already added)
config.assets.precompile += %w( apples.js )

Note that this is a match, so it could also be something like '*.js' in case you have more manifests, which would be the case for per controller inclusion.

Views

The same concept as above could be extended to target individual actions/views of each controller, by having the actions be part of the manifest name. Individual javascript files could then be included like so:

<%= javascript_include_tag "#{params[:controller]}.#{params[:action}" %>

This makes an assumption that all actions on all controllers have a dedicated Javascript file. An assumption which most likely won’t be true in most cases. Another option could be an conditional include like so:

<%= yield :action_specific_js if content_for?(:action_specific_js %>

And then move the include tag to the specific views that need it.

Testing for existence of a page element or class

The DOM loaded event handler could look something like this:

jQuery ->
  if $('#some_element').length > 0
    // Do some stuff here

This could also be a class on body eg.:

jQuery ->
  if $('body.controller_name_action_name').length > 0
    // Do some stuff here

And then then the erb would be like this:

<!DOCTYPE html>
<html>
<head>
  <title>AppName</title>
  <%= stylesheet_link_tag    "application" %>
  <%= javascript_include_tag "application" %>
  <%= csrf_meta_tags %>
</head>
<body class="<%= "#{params[:controller]}_#{params[:action]}" %>">
 
<%= yield %>
 
</body>
</html>

Function encapsulation and on-page triggering

Instead of registering the handlers for DOM loaded, wrap the necessary code in a function that can be called later and then trigger that function directly in the respective view.

There is one thing we need to consider though. All Coffeescript sources for each controller get wrapped in it’s own closed scope, i.e this Coffeescript in apples.js.coffee:

apples_index = ->
  console.log("Hello! Yes, this is Apples.")

Becomes:

((
  function(){
    var a;
    a=function(){
        return console.log("Hello! Yes, this is Apples.")
    }
  }
)).call(this);

So in order for us to have a globally callable function, we must first expose it somehow. We can do this by attaching the function to the window object. Changing the above code like so:

window.exports ||= {}
window.exports.apples_index = ->
  console.log("Hello! Yes, this is Apples.")

If we insert this line in application.html.erb layout just before the closing body tag:

<!DOCTYPE html>                                                                           
<html>                                                                                    
<head>                                                                                    
  <title>AppName</title>                                                                   
  <%= stylesheet_link_tag    "application" %>                                             
  <%= javascript_include_tag "application" %>                                             
  <%= csrf_meta_tags %>                                                                   
</head>                                                                                   
<body>                                                                                    
 
<%= yield %>                                                                              
 
<%= yield :action_specific_js if content_for?(:action_specific_js) %>                     
</body>                                                                                   
</html>

We can now call the exposed function directly from our view like so:

<% content_for :action_specific_js do %>
<script type="text/javascript" language="javascript">
  $(function() { window.exports.apples_index(); });
</script>
<% end %>

Wrap up

Neither of these three examples is a “one-fit-all” solution I would say. Dividing up the Javascript source will start to make sense as soon as the Javascript codebase grows past a certain size. It might be interesting to test out, just how big that size is on a certain bandwidth, but I think that’s out of the scope for this post.

Given the fact that there isn’t really a defined best practice yet, perhaps the ruby community will come up with something better than the examples I presented here. In my opinion I think this is definitely something that could be better thought out.

Ruby on Rails: Sphinx, thinking-sphinx and PostgreSQL on Mac OS X

Premise

On a project of mine, I needed a full text search feature and after a bit of digging, decided to go with Sphinx. It seems like a very proven search engine and with Rails, it’s easy to use through the thinking-sphinx plugin. Normally I just go with the standard SQLite database, if the application doesn’t require a high powered database backend. Unfortunately Sphinx does not yet work with SQLite and as far as I know, needs to run against either MySQL or PostgreSQL. The choice of either MySQL or PostgreSQL is a bit religious I feel. They are both battle hardened DBMS’s and I won’t make compelling argument towards either one. This time however, PostgreSQL is the favored candidate.

Requirements

Before starting, make sure you can compile custom software on your system. For this it is assumed you have the following installed:

PostgreSQL

First things first, we need to install our database server and enable access from rails. PostgreSQL is readily available through MacPorts, so open up a terminal and enter the following command:

sudo port install postgresql84 postgresql84-server

When that is done we need to add the bin folder of the PostgreSQL installation to our PATH:

nano ~/.bash_profile

And then, depending on where your MacPorts installation puts your ports, mine is under /opt/local, add the following line to the file:

export PATH=/opt/local/lib/postgresql84/bin:$PATH

Now we can start up the server using the following command:

sudo launchctl load -w /Library/LaunchDaemons/org.macports.postgresql84-server.plist

Or the shortcut version:

sudo port load postgresql84-server

This goes to the background and makes sure the server is started up again after reboot. Next we want to create a default database and make the server listen for connections:

sudo mkdir -p /opt/local/var/db/postgresql84/defaultdb
sudo chown -R postgres:postgres /opt/local/var/db/postgresql84
sudo su postgres
initdb -D /opt/local/var/db/postgresql84/defaultdb
pg_ctl -D /opt/local/var/db/postgresql84/defaultdb -l ~/postgresql.log start

The logfile postgresql.log is placed in /opt/local/var/db/postgresql84/.

Although I’m a fan of using the command line, I recommend using a tool such as pgAdmin for everyday administration tasks, such as adding new users etc.

Per default, you can login with postgres as user, with a blank password. This is the default superuser account so I suggest changing it sooner rather than later.

Sphinx

Now Sphinx is available through MacPorts as well, even with a postgresql84 variant. I’ve tried installing this version, but couldn’t seem to get through without error. Somehow it still maintained some library dependencies for mysql5 and thus wouldn’t compile. So, instead we opt for a manual installation. First we need to install a couple of dependencies for Sphinx. Download these two archives and extract their content:

You should now have two folders named expat-2.0.1 and libiconv-1.13. From a terminal, navigate to each folder and type the following commands:

./configure --prefix=/usr/local
sudo make && sudo make install

Now it’s time to install Sphinx. Download it and extract as before:

Navigate to the sphinx-0.9.9 folder and enter:

export LDFLAGS="-L/usr/lib"
./configure --prefix=/usr/local --with-pgsql --without-mysql
sudo make && sudo make install

And that is it for Sphinx.

thinking-sphinx

Assuming you already have a Rails project, install the thinking-sphinx plugin like so:

script/plugin install git://github.com/freelancing-god/thinking-sphinx.git

This will add a bunch of new features and a couple of rake tasks. Now, let’s say we have a model Employee, with first names and last names and we would like to be able to search employees by either one. In our model, we can define what we want to have indexed, using the define_index method:

class Employee < ActiveRecord::Base
 
    define_index do
        indexes first_name
        indexes last_name
        set_property :enable_star => true
        set_property :min_infix_len => 4
    end
 
end

The set_property calls, are to enable wildcard searching with asterisks, *, and to index substrings. Eg. If we have an employee named “McLovin”, then we would get matches on “McLo”, “cLov”, “Lovi” and so forth.

Before we can utilize these indices, we need to tell Sphinx to do an index run. In your projects directory, issue these two rake tasks:

rake thinking_sphinx:index
rake thinking_sphinx:start

The Sphinx server should now be running in the backround and in your controller, you can now search for Employee names like so:

class SomeController < ApplicationController
 
    def index
        @employee = Employee.search params[:first_name]
    end
 
end

For more detailed information, see the documentation for Sphinx and thinking-sphinx:

References