Rails I18n translate with empty string – bug?

Normally, it's pretty easy in Rails to take advantage of the simple i18n tools provided.  You just write I18n.t("some_key") and it'll translate that into whichever language.

Today, however, I discovered what seems like a problem.  If you do I18n.t(""), it returns a hash of all possible translations!  And if you give it a nil, it returns an error because that's not a key in the DB.

The first behavior doesn't sound correct at all, and the second seems dubious as well.  I'm thinking about fixing it like so:
 

  def translate(key, options = {})
    return nil if key.nil?
    return "" if key == ""
    locale = options.delete(:locale) || I18n.locale
    backend.translate(locale, key, options)
  rescue I18n::ArgumentError => e
    raise e if options[:raise]
    send(@@exception_handler, e, locale, key, options)
  end

Is there any reason not to do this?

Ruby Shootout

Antonio Cangiano has an interesting set of Ruby benchmarks here:

http://antoniocangiano.com/2008/12/09/the-great-ruby-shootout-december-2008/

The two results that were interesting to me were that JRuby does so well, and that REE (“Ruby Enterprise Edition”) was a significant improvement over “plain old Ruby” too. I knew that the JRuby guys have done a lot of work, and done a nice job with their implementation, but didn’t realize just how good it was. REE is a clever idea, but not that much code, and aimed at memory usage, not CPU, so it’s interesting to see how much faster it ends up according to these tests.

A few things that would be interesting to see:

  • Raw startup time. Yeah, that probably penalizes JRuby, but it is still a scripting language, and using it like one isn’t possible if it takes forever to start up. How much slower is it?

  • A more detailed look at REE’s times. How is it that it’s that much faster because of some memory tweaks?

In any case, it’s a nice bit of work by Antonio – grazie!

Update – old, slow davidw missed the fact that there was a startup time benchmark included. Oops.

Ruby YAML Bug & Fix

Besides the nasty Ubuntu bug, which I was unable to do anything meaningful with, yesterday I also found a small bug in Ruby’s YAML package:

irb(main):004:0> YAML::load('1900-01-01T00:00:00+00:00')
ArgumentError: time out of range

The problem is that the YAML implementation that Ruby is using, called “syck” interprets that kind of date as something that it should make a Time for:

        else if ( strcmp( type_id, "timestamp#iso8601" ) == 0 )
        {
            obj = rb_syck_mktime( n->data.str->ptr, n->data.str->len );

So I did a little bit of hacking on the C code, and made it create a DateTime object instead. My fix works, although I’m not sure it’s the best possible way of tackling the problem. The patch is attached to the bug report here:

http://redmine.ruby-lang.org/issues/show/752

This matters because my Rails text fixtures had some dates in them that were quite old, and this was causing problems.

Cache it all

I recently redid my personal web site, at welton.it. Wanting to be quick about it, and make the look and feel a bit more uniform than it has been in the past, I hacked together some pages in Rails. Despite this being sort of a “killing a fly with a bazooka” situation, I’ve been doing lots with Rails, so it was quick to use. Here’s the thing, though: Rails is definitely overkill, as the site is basically static. I don’t need to calculate anything or fetch stuff from a database – I just wanted a reasonably good template system, and I am quite comfortable with Rails these days.

But the idea of leaving Rails running for a static site was of course no good: I basically need to cache the entire thing, so that Rails is simply not involved. How to do this as quick as possible (in between diaper changing and other baby duties!) ? Ideally, it would be possible to introspect Rails in order to know exactly which pages are present, then cache those, and avoid Rails on the server entirely (just generate them locally and put them in subversion), but that proved to be fairly hacky, so I settled for this code, which simply caches all pages which comes across, when caches_pages is placed in application.rb:

class CacheFileName
  include ActionController::Caching::Pages
  include ActionController::Caching::Pages::ClassMethods

  def cachedname(path)
    page_cache_file(path)
  end
end

def caches_pages()
  return unless perform_caching
  after_filter do |c|
    res = c.cache_page
    cfn = CacheFileName.new
    cf = Cachedfile.new :filename => cfn.cachedname(c.request.path)
    cf.save!
  end
end

It simply caches everything. To be able to easily clear out the cache if there are any changes to the site, we record the changes in the Cachedfile model, which is defined like this:

create_table "cachedfiles", :force => true do |t|
  t.string   "filename"
  t.datetime "created_at"
  t.datetime "updated_at"
end

with this model:

class Cachedfile < ActiveRecord::Base

  def Cachedfile.clean_cache
    Cachedfile.find(:all).each do |cf|
      begin
        fn = ActionController::Base.page_cache_directory + cf.filename
        File.delete fn
      rescue => e
        logger.error "Error deleting #{fn}: #{e.inspect}"
      ensure
        cf.destroy
      end
    end
  end

end

which has a class method to go through and clean out all the cached files. I call it manually from ./script/clean_cache:

#!/usr/bin/env ruby

ENV['RAILS_ENV'] = ARGV.first || ENV['RAILS_ENV'] || 'development'

require File.dirname(__FILE__) + '/../config/boot'
require "#{RAILS_ROOT}/config/environment"
require 'console_app'

Cachedfile.clean_cache

It’s not a beautiful system, but it gets the job done.

Rails and Hype

Yoav Shapira wonders about the “rails backlash”:
http://yoavs.blogspot.com/2008/02/railsshudder.html

I’m a big fan of Rails, and of course I realize that it has some flaws and is certainly not the optimal choice for all situations, but I don’t buy into the bashing.

Sure, Rails was hyped, but there is no way that it could have possible reached the level of popularity it has on hype alone. We’re not talking about multi-million dollar marketing budgets commanded by the likes of Sun or Microsoft, but about a video and some nice dressings by DHH and 37signals. A small group like that just doesn’t have the resources to push something like Rails as far as it’s gone without some substance to back it up.

Look at all the imitation. People realize that the basic idea is good, and even though the basic idea is nothing phenomenal, it hadn’t really been done before. What is this idea? Take a bunch of components, and good (if not always the absolute best) practices, and integrate them nicely. Database, templating, testing, a clean application structure, ajax/javascript integration, with some code generation, all done with a language that’s flexible enough to do all of the above pretty well. All those things existed, but Rails did a great job of tying them all together. And that’s been imitated far and wide. That seems to indicate that, even though the imitators may change and improve bits and pieces, they’re quite smitten with the idea itself. So much that they’ve dedicated a lot of time and effort to reproduce it. That’s quite a tribute.

Look at the adoption. Moving to a new language/platform is a big step, not one to be taken lightly, and for people with lots of existing code in some other language, perhaps simply not possible. Hype will take things only so far, but Rails has been taken up by a large number of people who certainly didn’t do so because it was the safe, easy path to take. Perhaps some of those people have found that Rails really wasn’t for them, but that’s pretty normal. No one said it was perfect. However, when you look at what it takes to make people jump ship from what they know, and are comfortable with, you realize that it really must have been that much better to make people go out on a limb.

Furthermore, it’s easy to point out the failings and drawbacks of something after the fact, but much more difficult to do creative, innovative work of your own. Compounding that is the fact that criticizing others’ work is often an easy and effective way of looking smart; there’s an interesting study referenced here http://bobsutton.typepad.com/my_weblog/2006/08/brilliant_but_c.html that talks about this effect.

It’s something I’ve come to dislike over the years. Our field is so new, so dynamic, and so immature, that it’s usually pretty easy to take a look at something and say that it “sucks”. It is much harder to write something of your own and put it out there, knowing full well that lots of people who have never done much of anything will jump on it and bemoan its “copious failings”. That’s not to say that it’s impolite to criticize, but – do it constructively please. Imagine that you were there in the same room, talking to the person who wrote the code in question. You would still say you don’t like it, but you’d probably be much more polite and helpful in your commentary. I hope so, at least.

One reason to think Rails is “all that”

The economics of programming languages point to Rails being significantly better than what went before it.

I got to thinking about this when reading a comment on a site I like to read, which said:

Rails in itself is, to me, not that impressive. It does a lot of things right, but it does probably just as many wrong. Not the least of which is scaling.

It seems that these sorts of “after the fact” “I know better” comments are a dime a dozen in the world of programming discussions. It’s easy to come along after something’s been built and puff yourself up by pointing to defects in existing systems and show that, therefore, by comparison, you’re a clever fellow.

That’s not my point, though – what I wish to explain is that yes, Rails really was that much better than what was around before it came onto the scene:

“Switching costs” between languages are high. Less so for really sharp programmers, but for the masses that use one or two languages, learning a new language, tools, deployment, etc… is a big step to take, with potentially high risks. Even most A-list programmers I know use a few languages at a time – it’s simply easier if you’re not tripping over your own feet by switching to a different system every day. “Flow” is easier to attain when you’re ensconced in the thinking of one language. For companies, this effect is magnified, and switching to something new is not done lightly.

Since companies are beginning to explore Rails, successfully, I might add, you have to conclude that the big step into the unknown was worth it for some reason. Especially considering that a number of other languages rushed to copy various nice aspects of Rails, lessening the need for users of those systems to consider taking the leap.

Of course, that’s not to say it’s a perfect system, without reproach, or has no negative aspects, but in the spirit of honesty, and credit where credit is due, Rails really did move things a step forward, and the willingness of people to incur high switching costs to obtain its benefits is strong evidence of that.

Chartr – Plotr generation for Rails

I’ve been working on a project that uses graphics created with Plotr, and I wanted to automate that from Rails, so I created Chartr. It’s not very complete, but works well enough to create stuff like this:


<script src="http://chartr.rubyforge.org/excanvas.js" type="text/javascript"></script>
<script src="http://chartr.rubyforge.org/plotr_uncompressed.js" type="text/javascript"></script>
  <canvas id="results_chart" height="300" width="500"></canvas>
</div>

var dataset = {“Number/Decade”: [[0, 1000], [1, 5000], [2, 20000], [3, 22000]]};
var options = {colorScheme: “#22aa22”, legend: {position: {}}, axis: {x: {ticks: [{label: “1960”, v: 0}, {label: “1970”, v: 1}, {label: “1980”, v: 2}, {label: “1990”, v: 3}]}}, padding: {left: 100}, background: {}, stroke: {}, legend: {position: {left: ‘120px’}}};
var discvar = new Plotr.BarChart(“results_chart”, options);
discvar.addDataset(dataset);
discvar.render();


Help is more than welcome! I think the API is still a bit clumsy, more chart types need adding, and there are other things that could probably be improved, but it does work.