Posted by John Wang about 11 hours ago
Some considerations when displaying dates and times on a website include showing delta times, customized timezones and caching. Often it's nice to show a delta time like "10 minutes ago" or "5 days ago" to give readers a frame of reference instead of an absolute date. When the date is far enough in the past and an absolute date becomes desired, customizing the date to the user's timezone is useful. And if your site grows large enough that caching becomes useful, finding a way to display customized deltas and timezone information in a cacheable static page becomes an ideal solution.
JavaScript is an ideal solution for all three issues. With JavaScript you can place an absolute date in the web page and have the JS dynamically update it when the page is loaded. This can be used to calculate delta times and accommodate timezones as well. The result is that the page can embed the same date every time and thus becomes more cache-friendly.
The Typo blog engine (which runs this blog) comes with a useful MIT-licensed JavaScript in it's typo.js script. Just copy three of the JS date/time functions, wrap your dates with spans (using the appropriate class name and absolute date in the span title) and then call show_dates_as_local_time() when your page is finished loading. The two other functions you'll need are get_local_time_for_date(time) and distance_of_time_in_words(minutes). This is what I did for Planet Catalyst's Plagger theme a while back.
Although it's pretty easy to accommodate timezones, the Typo script doesn't do that. I've done this for some projects and might post some code in the future but it's not hard.
Customization and cacheability, two great advantages for using JavaScript to handle dates and times.
Posted by John Wang about 11 hours ago
Some considerations when displaying dates and times on a website include showing delta times, customized timezones and caching. Often it's nice to show a delta time like "10 minutes ago" or "5 days ago" to give readers a frame of reference instead of an absolute date. When the date is far enough in the past and an absolute date becomes desired, customizing the date to the user's timezone is useful. And if your site grows large enough that caching becomes useful, finding a way to display customized deltas and timezone information in a cacheable static page becomes an ideal solution.
JavaScript is an ideal solution for all three issues. With JavaScript you can place an absolute date in the web page and have the JS dynamically update it when the page is loaded. This can be used to calculate delta times and accommodate timezones as well. The result is that the page can embed the same date every time and thus becomes more cache-friendly.
The Typo blog engine comes with a useful MIT-licensed JavaScript in it's typo.js script. Just copy three of the JS date/time functions, wrap your dates with spans (using the appropriate class name and absolute date in the span title) and then call show_dates_as_local_time() when your page is finished loading. The two other functions you'll need are get_local_time_for_date(time) and distance_of_time_in_words(minutes). This is what I did for Planet Catalyst's Plagger theme a while back.
Although it's pretty easy to accommodate timezones, the Typo script doesn't do that. I've done this for some projects and might post some code in the future but it's not very hard.
Customization and cacheability, two great advantages for using JavaScript to handle dates and times.
Posted by Piers Cawley 6 days ago
At RailsConf Europe this year, DHH went in quite strong on the idea that simply using an opensource framework, like, say, Rails didn’t entitle you to much of anything. The precise phrase used was, if memory serves “We don’t owe you shit.”
Which is why I’m finding a certain irony in this, from the official Rails weblog:
Versions [of SQLite] after 3.3.7 incompatibly changed the way in which default values are stored, making it so that current versions of Rails get into quoting issues and problems with columns with NULL defaults . . . hopefully this will be recognized as an unacceptable change for a point release and will be reverted.
There appears to be a fair amount of confusion going on in the responses to Jamis’s bug report on the SQLite trac. It seems a Rails user has already reported the bug in the changed behaviour of 3.3.8, but the fix for it seems to be differently incompatible with Rails’ expectations…
This one could run for a while I think.
Posted by glyph 11 days ago

→
Register
Updated with more info about content and times.
Posted by Piers Cawley 11 days ago
Right. I’ve bundled acts_as_resource up and stuck it on the typosphere SVN server. You can grab it from http://svn.typosphere.org/typo/plugins/acts_as_resource if you’re interested.
It’s currently in what I’d call an all convention, no configuration state – if your resources don’t look pretty similar to the kind of things you get from the resource scaffolding, you’ll probably have some pain, but I expect to rectify that with coming releases. One thing I want/need to do for instance is to allow for ‘relative’ ids in your resource url. For instance, if you’re looking at /albums/10/tracks/982, it’s not the most readable of permalinks… next trick is to allow you to have urls like /albums/because-its-there/tracks/1, ie: the first track on the album ‘Because it’s There’. I’m sort of expecting that you’d do that by doing:
class Album
has_many :tracks
acts_as_resource :uri_field => :name_dasherized
end
class Track
belongs_to :album
acts_as_list
acts_as_resource :uri_field => :position, :parent => :album
end
However, my first priority is to add some tests (or, more likely,Rspec specifications) so I’ve got some confidence that I’m not breaking things as I go.
Anyhow, go grab the plugin, have a play, let me know what you think.
Posted by Piers Cawley 11 days ago
I’m very nearly ready to release acts_as_resource, I just have to pull up and tidy code that’s currently in my working directory’s ApplicationController and we’re laughing. However, I thought you’d like to see what my nested controller looks like.
Posted by Piers Cawley 12 days ago
While I was working on the acts_as_resource plugin trying to fix things up so that the resource finding side of things works neatly, I realised that I needed some way to get at the ordered list of parameter keys that were matched by the routing system.
One way to do it would have been to parse the path again, but that smacked a little too much of repetition, after all, the routing system knows this stuff already, but how to get at it?
Posted by Piers Cawley 13 days ago
So, you’ve upgraded to Rails 1.2.1 and you’re working on a tool to maintain a database of all the tunes you have in your various songbooks and (eventually) your record collection. You start with:
$ ./script/generate rspec_resource MusicBook title:string author_id:integer \
abstract:text
$ ./script/generate rspec_resource Tune title:string composer_id:integer \
abc:text book_id:integer
You decide to come back to composers and authors later, so you set up your models1:
MusicBook.has_many :tunes
Tune.belongs_to :music_book
And your routes:
map.resource :music_books do |book|
book.resource :tunes
end
Problems start here
Being a cautious sort, before you start adding behaviour, you fire up a development server and go and check things with the browser. The /music_books/ stuff works fine, but once you start looking at /music_books/1/tunes things start to get weird; all of a sudden your links aren’t making sense.
Posted by glyph 15 days ago
In Part I I showed how easy it is to install memcached and use it for simple queries or manually stored objects.
Today I was experimenting with running against multiple memcached servers. I was preparing myself for a gruelling process of learning the memcache server configuration format, headaches while trying to get the two servers to talk to each other, etc. Then I read this:
...the API hashes your key to a unique server. If a host goes down, the API re-maps that dead host’s requests onto the servers that are available.
So the servers run without any knowledge of each other. All you have to do is start up a few instances of memcached, then tell your Rails app about them. The Ruby API is responsible for finding out about the servers and distributing the keys between them. It even recovers if one dies.
So easy!
Configuration
I tried this out using a laptop and a desktop machine. The only quirk was that I initially had a firewall on my desktop machine so the memcached port wasn’t open.
# In production.rb
require 'cached_model'
memcache_options = {
:c_threshold => 10_000,
:compression => true,
:debug => false,
:namespace => 'rails_production',
:readonly => false,
:urlencode => false
}
CACHE = MemCache.new memcache_options
# These are the IP addresses and ports of the memcached servers
CACHE.servers = ['192.168.0.3:11211', '192.168.0.2:11211']
Sessions
UPDATE: Argh…my current implementation of this isn’t any faster than ActiveRecord sessions. Back to the drawing board…
UPDATE 2: See the updated plugin for a Memcache session class that uses memcache-client instead of ruby-memcache.
Memcached can sometimes be faster than sessions stored in the database (see RailsExpress). The problem is that you lose all your sessions if you restart the memcached servers.
I have heard legends of an epic session library used at the Robot Coop. Sessions are stored primarily in the database but are also kept in memcached for speedy lookup. I found a nice SqlBypass session template in the Rails source and modified it to work similarly (packaged as a plugin). I’ll be benchmarking it later this week, but it works in the situations I’ve tried it in.
./script/plugin install http://topfunky.net/svn/plugins/db_memcache_store/
# In the Initializer section of environment.rb.
# Explicitly load the library since plugins haven't been loaded at this point.
require "#{RAILS_ROOT}/vendor/plugins/db_memcache_store/lib/db_memcache_store"
config.action_controller.session_store = CGI::Session::DBMemcacheStore
Startup a few memcached servers.
# Verbose, Port 11211, 128 MB RAM, IP to listen on
$ memcached -vv -p 11211 -m 128 -l 192.168.0.2
You’ll see sessions being stored and retrieved. Kill one or more servers and it will keep working.
# Memcached server output
<3 server listening
<6 new client connection
<6 get rails_development:session:bd8b2ba714c059002af6a19283072997
>6 sending key rails_development:session:bd8b2ba714c059002af6a19283072997
>6 END
<6 set rails_development:session:bd8b2ba714c059002af6a19283072997 0 0 216
>6 STORED
<6 get rails_development:session:bd8b2ba714c059002af6a19283072997
>6 sending key rails_development:session:bd8b2ba714c059002af6a19283072997
>6 END
TODO
Actually, it’s back to the drawing board for this one…I found other details about the system used at the Robot Coop and other uses of memcached elsewhere.
Posted by Piers Cawley 16 days ago
Your mission, should you choose to accept it, is to explain what the following code does:
class Amb
def initialize
@error = Exception.new("Ran out of possibilities")
@failure_continuation = lambda {|v| @error}
end
def assert(assertion)
if !assertion
self.fail
end
end
def deny(assertion)
assert !assertion
end
def fail
@failure_continuation.call(nil)
end
def maybe
one_of [true, false]
end
def one_of(collection = [])
k_prev = @failure_continuation
callcc do |k_entry|
collection.each do |item|
callcc do |k_next|
@failure_continuation = lambda do |v|
@failure_continuation = k_prev
k_next.call(v)
end
k_entry.call(item)
end
end
result = k_prev.call(nil)
if result == @error
raise @error.message
end
end
end
def all_values(&a_block)
k_prev = @failure_continuation
results = []
callcc do |k_retry|
@failure_continuation = lambda {|v| k_retry.call(false)}
results << a_block.call
k_retry.call(true)
end && fail
@failure_continuation = k_prev
results
end
end
Easy? Now explain how it does it.
Posted by Piers Cawley 21 days ago
I’ve been watching this five things meme coursing round the blogs I read for a while now, and chromatic just tagged me to share five little-known personal facts.
- I like to collect gameroom decorations, like neon signs, antique beer signs, old juke boxes, pool cues, and pool tables, .
- I build cedar wood dog houses.
- Unless you were at that OSCON that time, you probably don’t know that I can make a trampoline out of modelling balloons. If anyone has worked out how to make a buckyball from balloons, I would be grateful for the pointer.
- I’m a compulsive gambler: it’s been years since I had a bet and more years still since I attended a Gamblers’ Anonymous meeting. I basically threw away thousands of dollars and any chance I had of a decent degree. I can’t say I’d recommend it.
- I am a tea snob. Not for me the Tetley’s teabag in a mug being doused with boiling water (but if must be bag tea, then Tetley’s is the one), oh no. In the afternoon it must be a pot made with loose leaf Earl Grey China Moon from Imperial Tea and Coffee of Lincoln in the morning, a pot of Superior English Breakfast from the same place. Tell ‘em I sent you.
Tradition demands that I now tag 5 others to do the same thing, so I call on: Scott Laird, Jesse Vincent, Dave Cross, Andy Lester and Mary Branscombe to spill their beans.
Posted by Piers Cawley 24 days ago
A friend of mine, David Morton, just pointed me at a transcript of a lecture given by one Christopher Small. In it Small nails something I’ve been trying to articulate for ages. I don’t know whether to applaud madly or seethe with silent resentment that someone said it so much better, and in 1995 at that.
On balance, I’m applauding. Read it, it’s worth it for Opera story alone.
Posted by glyph 31 days ago
Capistrano Concepts is the newest PeepCode (released earlier this week). People are already saying that “it put a lot of things into perspective and really helped [me] grasp the concepts. I think I’ve learned Rails faster from your screencasts than all the books I have combined.” (Jon Baer, Developer)
Also available as a free bonus is a 10 minute screencast showing you how to install a full Rails stack on Ubuntu using the deprec gem. It’s a great way to build a staging server or a sandbox for experimentation. Download it at the new site.
The new site also has some new features that people have been asking for:
- The option to sign up and keep a record of the screencasts you purchase from now on
- Subscribe for a discount
- All screencasts are now available in a format compatible with the video iPod
- Now running on Mongrel and speedy new RailsMachine hardware
- Other features coming soon…
Posted by glyph 33 days ago
The Rails Analyzer Tools are a very useful way to keep tabs on the performance of your site. They were written by Eric Hodel and have been open-sourced to the community by the Robot Co-Op.
The problem is that you need to install and run SysLogLogger to make it work. If you have more than one Rails app on a box, or if you don’t have root access to the box, or if you are on a shared host, you are out of luck.
No longer!
Install the Gems on Your Server
gem install production_log_analyzer
gem install rails_analyzer_tools
Install the Hodel3000CompliantLogger in your Rails app
Download the logger replacement and put it in your lib directory. (I’ll make this into a proper plugin soon.)
In the Initializer section of environment.rb, add
require 'hodel_3000_compliant_logger'
config.logger = Hodel3000CompliantLogger.new(config.log_path)
Run your app
For some reason, the standard script/server forces logging to work the old way. If I run mongrel_rails start, it works fine. I’m also running a production app with mongrel and it works as expected.
If you tail -f log/development.log you should see something like this:
Jan 03 10:08:09 topfunky rails[4535]: Rendered shared/_menu (0.14430)
Jan 03 10:08:09 topfunky rails[4535]: Rendered shared/_flashes (0.00882)
Jan 03 10:08:09 topfunky rails[4535]: Completed in 1.70117 (0 reqs/sec) | Rendering: 1.61409 (94%) | DB: 0.02340 (1%) | 200 OK [http://localhost/products/capistrano-concepts]
Analyze
Deploy your app, or just try this out locally from the command-line.
$ pl_analyze log/development.log
Request Times Summary: Count Avg Std Dev Min Max
ALL REQUESTS: 33 0.226 0.304 0.005 1.695
OrdersController#show: 6 0.071 0.112 0.005 0.316
ProductsController#show: 3 0.306 0.061 0.231 0.381
ProductsController#home: 2 0.318 0.068 0.249 0.386
OrdersController#index: 2 0.328 0.206 0.123 0.534
PagesController#show: 2 0.432 0.047 0.385 0.479
ProductsController#index: 1 0.279 0.000 0.279 0.279
Slowest Request Times:
OrdersController#index took 0.534s
PagesController#show took 0.479s
ProductsController#home took 0.386s
PagesController#show took 0.385s
ProductsController#show took 0.381s
# DB times and Render times follow
rails_stat is also nice for seeing a live report of the performance of your app.
$ rails_stat log/development.log
~ 0.9 req/sec, 14.2 queries/sec, 19.9 lines/sec
~ 0.3 req/sec, 14.1 queries/sec, 17.0 lines/sec
Once more, with feeling!
Run the report in a cron task and send the results to yourself via email (see the -e flag to pl_analyze), or automate the reporting with Capistrano.
desc "Analyze Rails Log instantaneously"
task :pl_analyze, :roles => :app do
run "pl_analyze #{shared_path}/log/#{rails_env}.log" do |ch, st, data|
print data
end
end
desc "Run rails_stat"
task :rails_stat, :roles => :app do
stream "rails_stat #{shared_path}/log/#{rails_env}.log"
end
For more details on Capistrano, buy the new PeepCode Capistrano Concepts screencast.
And again, from the top
For extra credit, use my Mint Pepper plugin (download) and my Mint Rails Plugin to put nightly data into the database (also requires the setup of logrotate, to be discussed later).
Note
Remember, this is not about maximum performance. These reports show you the actual performance of your app as it is being browsed, not the theoretical maximum performance.
Shameless Advert
Posted by glyph 49 days ago
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.
Add a Content-Type
In config/environment.rb:
# For the drawing
require "RMagick"
Mime::Type.register "image/png", :png
The Action
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
Reference the dynamic icon
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)
The result

Caveat
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).
Resources
Posted by glyph 61 days ago
This is an awesome time to be a Rubyist. Ruby is already a great language, but many brilliant programmers are working on projects that will make it even better.
After hearing a RubyConf presentation from Seattle.rb member Evan Phoenix I had wanted to find a way to help. It’s a project to write a fast, maintainable, Ruby 1.8-compatible interpreter. Last night I learned that Evan is between jobs and won’t start his next job until January. This seemed like a fantastic opportunity to support the Rubinius Project financially and give him a reason to spend the month of December working on it.
PeepCode Screencasts has been more successful than I expected and I want to give back to the community. So I’m putting US$1,000 toward the development of Rubinius in the month of December. Yes, there have been other funding drives recently, but this is a real project with real code that is being actively worked on.
If you want to support the Rubinius Project now or in the future, you can send money via PayPal to evan (at) fallingsnow (dot) net.
Questions
- Why?: The current Ruby interpreter powers many high traffic sites and enterprise projects, but a faster, more maintainable interpreter would be a huge benefit to current and future Ruby projects.
- Why not YARV?: Matz has expressed support for other Ruby interpreters and has said that YARV has a difficult task since the specs of Ruby 1.9/2.0 are not finalized yet (mentioned in this interview). Rubinius is targeting the current Ruby 1.8 series and is using the existing interpreter to bootstrap the project.
- When will it be done?: There is no timeline. It may take 6 months, 6 years, or never. The idea is to put resources behind this project so progress can be made.
Other information about Rubinius
Posted by Piers Cawley 62 days ago
Cast an eye over my new venture. It’s pretty much a place holder site at the time being, but I’m beavering away at the bits that need beavering away at.
Posted by Piers Cawley 62 days ago
My dad drives a vintage Fraser Nash. I say drives, but that’s only half the battle, a large part of his Nash time is spent fettling it. It’s an old car; bits wear out, break or drop off. And because it’s an old car, you can’t just nip round to Halfords and pic up a replacement; nor can you head down to the breaker’s yard and cannibalize something else. So he has a lathe and a milling machine and a bewildering collection of tools. When he needs a part, he will disappear into the machine shop and, after sufficient swearing and/or bleeding, he will emerge with a newly made part. For dad, it’s all part of the fun of running a vintage car. If he weren’t able to do the work, the Nash would have had to remain a pleasant pipedream.
I don’t know my way around a machine shop, except in the vaguest and most theoretical way. The tools I’ve grown up knowing to use are programming languages, editors, fine manuals and the mental tools a grounding in mathematics brings.
So, when I’m putting a new photography business together, and I realise that a couple of the supporting software tools that I had vaguely assumed ‘should exist’ don’t actually exist, I know that it doesn’t matter. I may not know Cocoa programming yet, but I know programming, so I’m confident that, like dad in his machine shop, I’ll be able to knock something up that does the job.
On reflection, I realised that this is probably a good thing. If I can set up and run the business with a combination of off the shelf software, then it’s trivial for potential competitors to reverse engineer the business and do the same (let’s assume here that the business is a success) and I’m left competing on margin in a service industry. No fun at all.
Being able to make my own tools gives me a competitive edge.
Why aren’t there more tool makers?
Posted by glyph 68 days ago

Sydney, Australia is a great place. I taught a basic and an advanced Rails workshop there and only got back to Seattle last week. It was a little rough leaving 25°C and coming back to 25°F, but I’ll survive!
The Ruby community there is awesome. Myles Byrne gave a talk on a presenter app he wrote in Camping. A few months ago I had an idea for an app that could be used to present and could also be viewed locally by people listening. This makes that possible and could even make an interactive presentation possible where one of the slides is just a frame to Try Ruby or a screen of code that people could copy to their own machine.
I also offered an idea for a Ruby/CSS plugin in the vein of Builder and Markaby (originally developed by Scott Barron). Keith Rowell immediately took the idea on and is working on a plugin (the name may change).

PeepCode 4: Test-First Development
The Ruby community is awesome and has fully embraced my idea for a screencast series. I’m glad to be able to spend time on it.
This week, it’s Test-First Development. Already, people have said
I really love the Test First method. I’m doing it now and it is MUCH better. I feel far more confident about my code than I did before.
Thank for you for the peepcode video – it made my day to see a new post on the peepcode blog in my RSS reader, especially on test-first development.
I was also experimenting yesterday with running Debian on Parallels, which allows me to try out some EXTREME!! Capistrano programming without fear of turning my VPS into a brick. I’ve already got a series of tasks that helps me do a full build of Apache2.2 and remotely control apt-get from Capistrano. Look for a gem and screencast in a few weeks…
Posted by Piers Cawley 72 days ago
I’ve been eyeing the rspec and rspec on rails packages and thinking I should give them a go.
To my eye at least, something like:
context 'Given a published article' do
fixtures :contents
setup { @article = contents(:published_article) }
specify 'changing content invalidates the cache' do
@article.body = 'new body'
@article.invalidates_cache?.should_be true
end
end
context 'Given an unpublished article' do
fixtures :contents
setup { @article = contents(:unpublished_article) }
specify 'changing content keeps the cache' do
@article.body = 'new body'
@article.invalidates_cache?.should_be false
end
end
reads far more fluently than the equivalent Test::Unit based tests:
class CacheSupportTest < Test::Unit::TestCase
fixtures :contents
def test_changing_published_article_invalidates_the_cache
art = contents(:published_article)
art.body = 'new body'
assert art.invalidates_cache?
end
def test_changing_unpublished_article_keeps_the_cache
art = contents(:unpublished_article)
art.body = 'new body'
assert ! art.invalidates_cache?
end
end
So, I installed everything and started to work on a new class in Typo using rspec. Rather annoyingly, this seemed to break the current test suite, so instead of working on my new model class, I set to porting the existing suite.
And, on about my third test suite, I found what I think is a bug in the suite. I’m not sure it’s a bug, because, the way the test is written (by me, I admit it), masks the intent quite dramatically. I’m also finding that the freedom to name specifications and contexts in English rather than method_names_that_go_on_for_ever is forcing me to come up with much more useful descriptions of what I’m testing. I find myself working on making the spec runner output read reasonably well as English, and doing that casts light on what is and isn’t being tested.
I’ve known for a while that Typo’s test suite is, um, spotty, but the porting process is really helping me get familiar with what’s being tested. I’m half tempted to start adding extra specs as I go, and if I could work out how to keep the existing tests working while I did it, I would, but my priority for now is to get to the point where I can check the specs and be confident that the new specs are no worse than the old tests.
Because I’m much more confident that I know what the specs are doing, I’m also confident that it won’t be hard to revisit them to help specify typo’s behaviour better. I’ll just have to give myself the discipline of beginning each coding session with half an hour of fleshing out the specifications before I get back to adding behaviour.