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.

Small weekend project – gemoji-chrome

I use GitHub and Flowdock everyday and one thing they both support is these little emoji short-codes like :+1: and :smile :.

Usually I go to emoji-cheat-sheet.com/ and lookup new codes but, then I decided It might be more fun to write a quick access popup extension for Chrome. Et voila, turns out it was much easier than I thought.

Install the plugin from the Chrome Web Store or check it out, with a small demo, on GitHub.

Latest tinkerings – simply-json

Intro

Working with JSON data it is sometimes necessary to visualise it in a human readable way. Since we care about the number of bytes we send to the browser, JSON is usually stripped of any kind of unnecessary whitespace.

Good for size, bad for readability.

Doing a quick search reveals that there are already plenty of options for online formatters. Here’s just a few examples:

For their intended purpose, they all work – Trying them out though, I started to think of how it could be done in a simpler way and that it might be a fun little project, to try out on my own.

Application

For the lack of a better name, as the post title suggests, I decided to call it simply-json, feel free to suggest a more fitting name.

I wanted a minimal feature set, mostly what all the other formatters also provide:

  • Input a URL which points to some JSON data. Have it fetched and then formatted.
  • Input raw JSON and have it formatted.

I also included some design criteria, to formalise my idea of what “simpler” is:

  • No page noise. Content that isn’t relevant to solving the task at hand, should be kept to an absolute minimum.
  • No buttons. Why should I have to click a button, when the browser is perfectly capable of detecting when I’ve input some text in a textfield?

Supplemental features:

  • Highlighting of matching brackets. A feature I like, that some editors have – When moving the cursor over a bracket, the matching opening or closing bracket is indicated.
  • Collapsible regions. Clicking a bracket should collapse the content. Also an editor feature.
  • Loading indication when fetching data via URL. (http://www.ajaxload.info/)
  • Unobtrusive error indication. Borders around text boxes goes red on error.

Considerations

Due the to the same origin policy enforced by most browsers, it’s not immediately possible to request JSON from a different domain than the current one. To do it, some form of proxy is needed.

An easy, fire-and-forget solution would be to use Yahoo! Query Language. The problem with YQL though, is that it transforms the JSON into XML and, if requested as JSON, transforms it back again to JSON. This transformation is lossy, which means e.g. numbers are sent back as strings. {"number":42} => {"number":"42"}. According to the docs:

To prevent this “lossy” transformation, you append the query string parameter jsonCompat=new to the YQL Web Service URL that you are using.

At the time of writing, testing this does in fact reveal, that the loss in number transformation is fixed. What hasn’t been fixed yet though is null values, which is returned as "null" strings instead.

So much for YQL.

Custom proxy

Keeping things in the spirit of simplicity, a service that can proxy a GET request, shouldn’t be more than a few lines of code.

To that end, I chose to use Sinatra, which I’ve had good experiences with in the past. It really is an awesome lightweight web framework. Using Sinatra, this is all it takes:

require 'rubygems'
require 'sinatra'
require 'net/https'
 
get '/' do
  uri = URI(URI.encode(params[:uri]))
  https_session = Net::HTTP.new(uri.host, uri.port)
  https_session.use_ssl = true if uri.port == 443
  https_session.start
  response = https_session.get(uri.path + "?" + uri.query)
  response.body
end

N.B. Updated Feb 10, 2012. New version includes uri query part.

I specifically chose 'net/https' so https sources were also supported, it only adds one extra line and can handle plain http just as well.

The proxy should be very straightforward, assuming the endpoint is at thelabs/simply-json/proxy, making a request to "~thelabs/simply-json/proxy?uri=http://google.com" should be the equivalent of a request directly to "http://google.com".

The immediate problem though, is that the current web server, Apache, is already running on port 80 and the same origin policy even blocks requests to different ports on the same domain.

Fortunately, this can be solved with some config magic server-side.

Apache

Firing up the Sinatra service on localhost:6789, we need Apache to redirect traffic to the /proxy path, back to the service instead of trying to serve content from that directory. This is what is needed in the config, in my case in the virtual host for éncoder.dk:

<Proxy *>
Order deny,allow
Allow from all
</Proxy>
 
ProxyPass /thelabs/simply-json/proxy http://localhost:6789
ProxyPassReverse /thelabs/simply-json/proxy http://localhost:6789

In case Apache hasn’t loaded mod_proxy, enabling it can be done with this command:

$ a2enmod proxy proxy_http

That’s all

Working demo can be found here:

http://éncoder.dk/thelabs/simply-json/

Go check it out.