The upcoming release of Rails 1.2 has some nice features for creating dynamic graphics in your application.
Here we have a simple shopping cart icon (purchased and modified from the Iconfactory).

I want to show the number of items in the cart. I could manually create a series of graphics with each number, but that seems inelegant. Anytime the icon needed tweaking, I would have to regenerate all the icons.
Rails 1.2 has the ability to send different types of content from the same action. Basically, I just want a graphical representation of the shopping cart. I’ll use the Cart#show action to render a graphic if it is accessed with a “png” extension. Otherwise, it will show the shopping cart items, total price, and checkout button in HTML.
In config/environment.rb:
# For the drawing require "RMagick" Mime::Type.register "image/png", :png
Purchase a TTF font and copy it to your Rails app so it can be deployed to the server. I put it in artwork/fonts.
class CartsController < ApplicationController
def show
@order = Order.find(params[:id])
respond_to do |format|
format.html do
# Render the show.rhtml template
end
format.png do
# Show cart icon with number of items in it
icon = Magick::Image.read("#{RAILS_ROOT}/public/images/cart.png").first
drawable = Magick::Draw.new
drawable.pointsize = 18.0
drawable.font = ("#{RAILS_ROOT}/artwork/fonts/VeraMono.ttf")
drawable.fill = 'black'
drawable.gravity = Magick::CenterGravity
# Tweak the font to draw slightly up and left from the center
drawable.annotate(icon, 0, 0, -3, -6, @order.quantity.to_s)
send_data icon.to_blob, :filename => "#{@order.id}.png",
:disposition => 'inline',
:type => "image/png"
end
end
end
Add some logic to your view (or a helper) to draw the icon with the number if the cart has items in it. You can use a regular image tag, but reference the controller instead:
# Generates an image tag to "/carts/1.png" image_tag formatted_cart_path(@order, :png)

If you can, use caching to speed up the delivery of images that have already been rendered. Rails doesn’t automatically apply the correct extension for non-standard content-types CORRECTION: This has been fixed and now works smoothly with rev 5736 (and maybe earlier revisions).
I like this, but Rails caching won’t work with this construct – how does anything know that the number of items in the @order object is important to the cache response?
Luckily, there is a solution!
My action_cache plugin allows you to cache these images, and serve them up correctly, using the custom cache key method to make the order ID, response type, and order quantity part of the fragment key used by the action cache code.
Ben’s plugin site has the lowdown: http://agilewebdevelopment.com/plugins/action_cache
My blog has info too, but is less structured! I usually answer email too.
I’m assuming formatted_cart_path(@order, :png) is auto-generated, is that right?
@Tom: Nice work…I’ll look at your plugin. Still, what’s wrong with explicitly expiring the cache when a new item is added to the cart? That’s how I’ve done most caching in Rails…as a cache/expire pair.
@Daniel: Yes, formatted_cart_path is auto-generated by Rails 1.2 when you do map.resources :carts. See also this cheatsheet.
Wow, this is really cool. I never thought about graphical representations of a resource.
One small change i’d make is to extract all of that image creation code into some kind of factory or an object of its own (a simple facade to the rmagick stuff).
Great tip.
Alex Wayne’s FlexImage plugin works about the same way, abstracting out most of the ImageMagick details.
@Luke: Indeed. I put it inline for the purposes of the tutorial, but often make a plugin or a module in “lib” to do the actual rendering of the graphics.
Side question: What is the benefit of rendering the count of items into the image of the cart? Wouldn’t we almost always be better off leaving the count as text and using, say, CSS to superimpose the text and image? (It seems that by rendering the text into the art we would lose on both accessibility and intermediary-caching grounds.)
If I’m missing something, please let me know.
Cheers. —Tom
@Tom: Using graphics doesn’t mean you have to sacrifice accessibility. You can still do alt text, use CSS hacks to replace the page text with images, etc.
Having the ability to generate dynamic graphics is just another tool. It makes it much easier to use alternate typefaces or do pixel-perfect placement of elements.
CSS can add text to a page, but what about taking it further to include shapes and lines? These make it possible to describe a lot of information in a small amount of space.
With the default Rails action cache, you’d get an image cached per user. With the right configuration, you could make it cache one image per number of items by dropping the params[:id] from the cache key. This is what I was thinking as I read the post.
You would also have to keep the per-user cache entry for the HTML format whilst doing the above for the PNG format.
This is an advanced technique!
@Tom: Good point. I was thinking of using page caching and doing something similar, but it seems to go against the philosophy of REST (i.e. having different keys for the same resource). So action caching and a relevant key might be the better solution.
topfunky, I agree with you i definitely want to try this png creation method out !
Tom Moertel, of course this example might bebetter to do with plain text, but its an example!! imagine the use of this in your own real situations, for example captcha! this article is about learning, not just doing exact as it say. and im very thankful to the author! <3
RE: dropping params[:id]
You could map this as a singleton resource, since, most likely, each user won’t have multiple carts.
Another idea:
Make /carts/1.png return a static file, if it exists. If it doesn’t, create it using the above technique. This way you only create it dynamically once, ever.
Yet another idea:
Make /carts/1.png return a db blob, if it exists. If it doesn’t create it using the above technique and save the image as blob in the db.
:)
Would someone be able to clarify the “Image Overlay with CSS” approach regarding the displaying of dynamic text within an image?
How is this accomplished? Is it based on HTML 4.0 “Button”? Is the idea to slice up the image (say a button) that you want so you can then use DIV’s to have the edges of the cutup image grow/shrink depending on the size of the dynamic text?
Background – I’m keen to include web 2.0 styles buttons I have from photoshop however don’t really want to lose the flexibility of text (e.g. changing the text, multiple languages supports – don’t need to have multiple images)
Regards Greg
@Mark: This technique is exactly how page caching works. However, it won’t work in this case, as the URL does not contain any information about the number to display in the image. The ID in the url is the order ID, and the image should contain the current item count from the order. This can be worked around with expiring the page cache item on order update, but this adds a whole bunch of extra complexity (get page caching working with PNG files, and expire the cached items correctly)
Hiya, seemed a good place to announce Saucy, my new plugin for rails that renders images unobtrusively.
Check it out here
Like to hear any feedback
It seems very complciated to me.
I’ve done this in the past by using a graphic as a background for a table (or other cell) . You just write the number if not zero/nil into the cell.
K.I.S.S.
wow, does this mean that anyone has created a SIIR type plugin for rails?
While the overlay with CSS stuff is an option in many cases, in some cases, you just need this stuff. Case in point – images for pins on a Google Map. I can definitely use this technique to avoid creating tonnes of new, custom, images for use on our Google Maps.