Archive for March, 2008

More new nouns

Sunday, March 30th, 2008

This WIRED article about Ray Kurzweil’s visions of the singularity is pretty good. But I learned something that wasn’t about the future: apparently you can now have “a speculation”:

Kurzweil transformed the singularity from an interesting speculation into a social movement.

Fascinating. This can join “a good value” in my list of recent noun-izations in English about which I had no idea until experiencing the Fait Accompli of reading them in mainstream media.

On another note, I upgraded WP to 2.5.0, which broke code highlighting on the front page. Until I get it fixed, if you’re trying to read some code, just click on the post title – it still works on subpages. I normally wouldn’t have upgraded immediately but I wanted password hashing. The rest of it is slightly improved, I suppose, though in a hilarious continuation of WP’s famously lax attitude towards security – it asks for server login credentials to “auto-update” plugins!

You can also see some SVN detritus around the place, I’ll clean it up when I have time.

PostgreSQL macports upgrade destroyed data

Friday, March 28th, 2008

A little reminder for me today of a few things:

1. Don’t install important things like databases via convenience systems like macports
2. If you do, keep very good backups.

Luckily, although I’d neglected 1, I hew very closely to 2 so I didn’t lose anything. But I’m pretty astonished that an uninstall/reinstall of a port destroyed user data – totally unacceptable.

Be careful with what you install via port systems. For convenience, they’re wonderful. But don’t trust them with your data, and BACKUP!

Song of the Week – I.V.

Thursday, March 27th, 2008

New song by X Japan, the best rock band in the history of the world, who are playing live in a reunion concert this weekend. Wish I was going! Why not check out the video as well, which will make you miss Odaiba!

I.V. – X-Japan

Note that the track is ripped from a DVD, no official single is yet available, so segues into another song before cutting off.

UPDATE: I replaced the video file with a better copy I made myself.

Click for lyrics. (more…)

More karaoke bliss

Thursday, March 27th, 2008

If you want to just float away into karaoke bliss, or alternatively if you have a music-loving friend who is looking for an excuse to commit suicide, I recommend downloading one of my latest “masterpieces”.

She’s An Angel, by They Might Be Giants.

I made an exception to my normal “one take only” rule this time, and did another take and mixed it to the other channel. It sounds even worse!!!

UPDATE: If you like that, you’ll LOVE this rendition of Where Your Eyes Don’t Go.

Stay tuned for the hauntingly beautiful opera solo at the end!

100 numbers a month for only $4.95

Wednesday, March 26th, 2008

WOW! I just discovered the best bargain on earth!

This company, Numly, is giving you the chance to receive up to 100 of its special numbers for the tiny price of $4.95 per month! That’s less that 5c per auto-generated number. Plus, you can then display the number on your website!

You can get your numbers by the OSX Dashboard or even a special Firefox plug-in. It’s never been easier – or cheaper – to get numbers. What a deal!

I’m signing up right away.

UPDATE: After careful analysis, I’ve decided to enter competition with Numly. It’s going to be tough, but you, the customer, can only win.

Accordingly, I’m offering not 100, not 200, but 1000 numbers a month for the very same price. In fact, order now, and I’ll throw in another 1000 numbers. Actually, you don’t even need to pay. Just post your email and I’ll fill up your mailbox with billions of fucking numbers.

Long numbers, short numbers, we got all sorts of numbers just waiting to satisfy your every numeric need. Free numbers for all, only on Sho Fukamachi Online!

First thousand FREE numbers after the jump – first come first serve!

(more…)

Ruby script to recursively svn up/git pull your sources directory

Monday, March 24th, 2008

Do you have a directory full of checkouts of other people’s projects? For reference, for contribution work, etc?

Wouldn’t you like to update them all at once?

Here’s a basic ruby script that you should run at the root level of your “sources” directory. It will go through the directory, find all the folders, check if they’re under source control (supports svn, git and bzr) and if so, attempts to update them.

It’s not very nice to look at, but it’s saved me a lot of time, and I can’t find anything similar around so I thought I’d release it here. Please tell me if you have any suggestions or improvements.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#!/usr/bin/env ruby
 
# Basic script to walk through all directories in a folder and perform an "scm up" on them.
# Supports svn, git and bzr
# Copyright (c) 2008 Sho Fukamachi
# Released under MIT License: http://www.opensource.org/licenses/mit-license.php
 
require 'pathname'
 
def execute(*cmd)
  begin
    cmd.flatten!
    $stdout.puts "executing: " + cmd.join(' ')
    system(*cmd)
    unless $?.success?
      fail "Command failed: " + cmd.join(' ')
       << 
    else
       << 
    end
    rescue RuntimeError
      puts "!!! command '#{cmd.join(' ')}' failed in #{}, retry manually?"
       << 
  end
end
 
 = ENV['PWD']
 = Dir.entries() # very dumb, basically an ls -la
 
 = []
 = []
 = []
 
def purge_invisible dirlist
  dirlist.delete_if do |entry|
    entry[0] == 46  # 46 is the hex number for fullstop
  end
end
 
def check_directories dirlist
  dirlist.delete_if do |entry|
    !FileTest.directory?(entry)
  end
end
 
purge_invisible 
check_directories 
puts "found directories: " + .join(' ')
 
def update_recursively dirlist
  dirlist.each do |path|
     = path
    Dir.chdir path
    if File::exists?('.svn')
      puts 'svn detected in ' + path
      execute 'svn', 'up'
    elsif File::exists?('.git')
      puts 'git detected in ' + path  
      execute 'git', 'pull'
    elsif File::exists?('.bzr')
      puts 'bazaar dected in ' + path
      execute 'bzr', 'pull'
    else
       << path
    end
    Dir.chdir 
  end
end
 
update_recursively 
puts
puts "updated in: " + .join(' ') if 
puts
puts "failed in: " + .join(' ') if 
puts
puts "unsupported: " + .join(' ') if 
puts
puts 'done!'

I know you might think it’s strange to put a copyright license on such a small script but it’s for your protection, not mine. I’ve seen copyright claims over lesser things. By explicitly releasing software code, no matter how simple, you can rest easy in its use anywhere. I wish everyone included licenses with everything; I’d feel a lot more comfortable using things I find on the net if it’s been explicitly released in this manner.

Simple irb extension script

Wednesday, March 19th, 2008

I tend to create projects as a directory, with a few files in it. I might have a classes.rb file, a Rakefile, utilities.rb, that kind of thing. And chances are, I want to access them all – and any gems they require – in irb.

I often wish I had a simple way to auto-require these gems or files on a per-project level for use in irb. MacOSX 10.5 comes with a very nice .irbrc file in /etc/irbrc, and I didn’t want to get into the game of rewriting it every time I make a new project. If you make your own .irbrc, the default one won’t load – and my editor of choice, TextMate, doesn’t show .files by default in its folder view.

So what I really want is a plain file I can put in the directory with a few requires in it. I’ll have a standard name, say, “irbx” for “irb extension”. If it’s there, anything in it will be loaded – if not, no problem. I want the script that loads it in /etc/irbrc so everything to do with irb config is in one place. And I want it super simple! Just some requires! Anything more complex, and I’ll put it in another file which is then required.

So I added a few lines to /etc/irbrc so it does what I want.

First, make a backup:

$ cp /etc/irbrc /etc/irbrc.backup

Then, add this:

## in /etc/irbrc
# Require local additions
begin
  require 'pathname'
  ext_file = ENV['PWD'] + "/irbx"
  if File::exists?(ext_file)
    eval File::read(ext_file)
    puts "extension file loaded from " + ext_file.to_s
  else
    puts "no extension file at " + ext_file.to_s
  end
end

I put it underneath “Activate auto-completion” but you can put it anywhere. I kept it inside the “unless defined” statement so it can still be overridden by an .irbrc file if chosen.

Now, if you create an irbx file like this:

## in ~/my_project_directory/irbx
require 'sequel'
require 'active_couch'
require 'classes' # local classes.rb file

and then start irb in the same directory, those libraries will be loaded. If it doesn’t exist, irb starts same as always. Excellent!

There are a zillion different ways to do this kind of thing, of course. But for me, the main priority is simplicity. Convention over configuration. If you want more, writing your own .ircbc file will override the default, including this. But for the vast majority of the time, this is exactly what I need.

Sydney Metro announced

Tuesday, March 18th, 2008

Finally, the hopeless, incompetent NSW government realises it has to upgrade Sydney’s train system, and it’s a good start – a $12b metro line from St James (CBD) to Rouse Hill in the Northwest. Over 32km of new tunnel – because they didn’t reserve land years ago, like they should have – and 17 new stations, including one at Epping, which will connect to Chatswood. Major construction to start next year. Metro-style single deck trains, probably driverless – good shit.

Sydney Metro - Proposed Route

(warning – 2.5MB PDF)

This is so long overdue it’s not funny, but it’s a good start. Now we need only another 5 or so of these lines. With the Greater Sydney Metro Region over 5 million now – almost a quarter of the whole country lives here – the lack of investment in Rail infrastructure has long been the single worst thing about the NSW government – pouring billions into roads but nothing into rail! Don’t get me wrong, I love roads, but for commuters trains are many times more efficient.

I am not exactly a true believer in the current Greenhouse Effect scare campaign but that doesn’t mean I think it’s a good idea to burn away millions of litres of increasingly precious oil, and fill the atmosphere with megatons of CO2, while commuters sit one per car in gridlocked traffic. If fear of Global Warming leads to more investment in public transport, maybe I’ll jump on that bandwagon after all!

.asia, domain squatter’s paradise

Tuesday, March 18th, 2008

The .asia landrush has ended, and a list of domains with more than 1 applicant – which will now go to auction – has been released here. But these results are pre-verification, and there’s a lot of weird entries in the list.

For example, I have a hard time believing that not one but two or more people bid $20 per domain to get:

xym.asia xyn.asia xyo.asia xyp.asia xyq.asia xyr.asia xys.asia xyt.asia xyu.asia xyv.asia xyw.asia xyx.asia xyy.asia xyz.asia xza.asia xzb.asia xzc.asia xzd.asia xze.asia xzf.asia xzg.asia xzh.asia xzi.asia xzj.asia xzk.asia xzl.asia xzm.asia xzn.asia xzo.asia xzp.asia xzq.asia xzr.asia xzs.asia xzt.asia xzu.asia xzv.asia xzw.asia xzx.asia xzy.asia xzz.asia yaa.asia yab.asia yac.asia

At this stage. Why? Is domain squatting such a profitable activity these days that squatters launch advance bids for useless domains, just to get a TLA? And are fighting over them even in the landrush stage?! I doubt it, but I have no idea what’s going on. And how the fuck were there two+ bids for

suffixecomputerworld.asia
americaneaglerjregionaljet.asia
andyzimmerihringen.asia
apischmidt-bretten.asia
api-schmidt-bretten.asia

etc??? What terrible domain names. The list is replete with odd examples like this. One can’t help but wonder if the system isn’t as blind as promised – several entries on the list give me the strong feeling someone had information on bids and is (perhaps automatically) counterbidding speculatively. Or, more than one parties are bidding according to domain squatting “top 50,000 domains” results or something. I fucking hate that kind of shit.

Seems like .asia is an instant domain squatter’s paradise. That’s unfortunate, and will only reduce the value of the TLD in anything other than the initial take short term.

Tibet through Chinese eyes

Monday, March 17th, 2008

With the recent trouble in Tibet, I was reminded of this excellent 1999 article from The Atlantic, which influenced me greatly on the issue, prompted me to research such matters independently rather than relying on oversimplified and biased accounts from activists and/or governments, and provided yet another good lesson on how things are never as simple as they might seem.

On the subject of the youtube ban – with so many people so utterly ignorant of the Tibet situation, yet happy to protest anyway (FREE TIBET!) – who can blame the Chinese government for wanting to restrict media they believe will further inflame misinformed tension? Personally, I believe in near-total free speech – I don’t know anyone more hardcore about it, in fact. But it’s pretty easy to see the other side of the coin, too – I have never met a “FREE TIBET” type who knew even the most basic facts about the situation, but that’s never stopped them.

The debate between an assumption of a rational, informed, curious and self-educating population (who should be granted access to all speech, of all type, always) and an ignorant, uninformed, incurious population who believe whatever crap they hear (for whom a rational case can certainly be made to censor unhelpful or misleading information) is far from black and white. I have my beliefs, axiomatic to my broader political opinion, and I won’t back down from them – but I can easily understand how others reach different conclusions about the informational trustworthiness of the public.

Finder hiding app version numbers

Saturday, March 15th, 2008

Learned something about Finder hiding application version numbers that I didn’t know. I’d copied a new version of OmniGraffle into /Applications, expecting it to overwrite the previous – when it didn’t, I was curious.

Two Omnigraffles?

The truth, revealed

Now, that is interesting. It’s cutting off the last number – is this something configured by the developer? I know the displayed name is localisable – have they “localised” it to remove the version number?

So, who’s gonna lend me $60m

Friday, March 14th, 2008

The Gulfstream G650 has been announced.

Check out their fast-loading flash presentation or the still-incomplete wikipedia page – almost 13,000km range. Sweet.

So, who’s gonna be the lucky donor? First come first serve!

Photoshop Disasters

Friday, March 14th, 2008

I’ve been enjoying this blog.

Laika’s ThingFish hits 0.2.0

Thursday, March 13th, 2008

Laika’s interesting REST network filestore, ThingFish, has just reached milestone 0.2.0. The project can be thought of as a ruby-language, more flexible and lightweight implementation of LiveJournal’s MogileFS, and is actually by the same developers.

To get it running, you’ll need to do the following:

## install dependencies
$ sudo port install ImageMagick 
$ sudo gem install mp3info exifr mongrel pluginfactory uuidtools rdf-redland rmagick sequel_core sequel_model
$ wget http://prdownloads.sourceforge.net/clxmlserial/clxmlserial.1.0.pre4.zip
$ unzip clxmlserial.1.0.pre4.zip
$ cd unzip clxmlserial
$ sudo ruby install.rb
$ cd ..
## download and run thingfish
$ svn co svn://opensource.laika.com/thingfish/releases/0.2.0 thingfish
$ cd thingfish
$ chmod +x start
$ ./start

Thingfish should now be available at http://0.0.0.0:3474/.

UPDATE: To actually be able to upload a file, you’ll need to manually create the directory /tmp/thingfish:

mkdir /tmp/thingfish

UPDATE 2: Not easily usable yet. After a two-hour adventure to try and build the gem, no dice – this ticket pretty much sums it up. Sorry if I’ve spoilt the “fun” for you! UPDATE: might have a fix for this, see comments.

I’m still excited about this project, and they don’t owe me a thing, but unsatisfiable dependencies to some untagged trunk version of some guy’s library in his personal svn is not exactly good practise. I’m looking forward to getting started with a howto article or two – but if I can’t even install it as a gem with reasonable efficacy that’s not going to happen. Here’s hoping an 0.2.1 release follows soon fixing some of these showstopper issues.

UPDATE 3: Many of these issues have been fixed in trunk (thanks devs!), stay tuned for an initial howto.

Issues remain in running from trunk, at least in OSX 10.5.2. It’s currently impossible, apparently, to install the redland bindings necessary to properly load the rdf-redland gem used by ThingFish using macports. I’m unwilling to start installing stuff from source when I have a perfectly good ports system, so I’ve emailed the maintainer asking for an update and will keep on top of this.

That’s a lot of data

Thursday, March 13th, 2008

Apple announced that the recently announced iPhone Software Developement Kit (SDK) has been downloaded over 100,000 times within the first four days of its launch.

That iPhone SDK is 2.1GB. 100,000 * 2.1GB is 210TB in 96 hours, over 600 megabytes a second sustained.

That’s a lot of data!

Horrible WP export data format

Wednesday, March 12th, 2008

So, I’m writing blog software, and one of the obvious things I want to do is import from this blog. As a first step towards that, I export the entire contects using WP’s export tool (after purging more than 17,000 comment spams since I last manually purged) and this is what it looks like (previous post to this):

>
>Interesting words in your OSX Dictionary>
>https://fukamachi.org/wp/2008/03/11/fake-words-in-your-osx-dictionary/>
>Tue, 11 Mar 2008 03:03:46 +0000>
>Sho>
        >>
        >>
        >>
         domain="tag">>
         domain="tag">>
 
 isPermaLink="false">https://fukamachi.org/wp/2008/03/11/fake-words-in-your-osx-dictionary/>
>>
>>
>713>
>2008-03-11 12:03:46>
>2008-03-11 03:03:46>
>open>
>open>
>fake-words-in-your-osx-dictionary>
>publish>
>0>
>0>
>post>
        >

Jesus, that is *horrible*. Firstly, if the post_type is defined only towards the end, what’s with the post_id, post_date, post_name etc? It’s a post – of post_type post! Secondly, where’s the “updated at” field? What’s the “dc:” namespace for the creator tag only? What’s with having an “isPermalink” switch in the guid tag? The permalink is in the link tag, I presume. Why does it need to be content:encoded when obviously the content is CDATA – implying that WP somehow supports XML parsing inside some contents!? Why is pubDate camelCase while everything else is underline_style? Man, I hate camelCase. Etc etc. What a mess.

I know what you’re thinking: that’s just RSS format! Sure it’s ugly, it’s RSS! Well, no. The RSS is similar but different for this post – I examined the feed for that, too. Note that the description is empty, it isn’t in the RSS. So they’re using a modified RSS format to store internal data. If they’re not going to store description, but just generate it on the fly – why export empty description tags?!

Just for comparison, here’s the much nicer atom feed. Obviously doesn’t have all the wp: internal data, but I much prefer the design:

>
        >
                >Sho>
                >https://fukamachi.org/>
        >
         <span style="color: #000066;">type</span>=<span style="color: #ff0000;">"html"</span><span style="color: #000000; font-weight: bold;">></span>
>
         rel="alternate" type="text/html" href="https://fukamachi.org/wp/2008/03/11/fake-words-in-your-osx-dictionary/" />
        >https://fukamachi.org/wp/2008/03/11/fake-words-in-your-osx-dictionary/>
        >2008-03-11T03:04:31Z>
        >2008-03-11T03:03:46Z>
         scheme="https://fukamachi.org/wp" term="Language" />
         scheme="https://fukamachi.org/wp" term="leopard" />
         scheme="https://fukamachi.org/wp" term="mac" />
         scheme="https://fukamachi.org/wp" term="dictionary" />
         scheme="https://fukamachi.org/wp" term="esquivalience" />
         type="html">>
         type="html" xml:base="https://fukamachi.org/wp/2008/03/11/fake-words-in-your-osx-dictionary/">
                Using Leopard? Try this. Look up the word esquivalience by selecting it and choosing dictionary from the contextual menu. Read the dictionary definition, then the wikipedia one underneath : )

]]>
>
>

Note logical, consistent design, self-closing tags, and other innovations.

UPDATE: Check out the comment format:

>
>3>
>>
>>
>http://nigger.org/>
>127.0.0.1>
>2005-07-16 10:23:48>
>2005-07-16 14:23:48>
>Hey, is this that new gay nigger cock website I've been hearing about?>
>1>
>>
>0>
>

The comment author is CDATA, but the content isn’t? WTF?

Interesting words in your OSX Dictionary

Tuesday, March 11th, 2008

Using Leopard? Try this. Look up the word esquivalience by selecting it and choosing dictionary from the contextual menu. Read the dictionary definition, then the wikipedia one underneath : )

Reducing LOC with ActiveRecord

Sunday, March 9th, 2008

Heh, as you can probably tell I’m going through old code with a chainsaw right now.

If there’s one thing I’ve learnt about maintainability, it’s that lines of code are the enemy. The longer a function, the less you can see on screen at once, the harder it is to understand. So with that in mind, let’s go over a few tricks you can use to shorten your code – this time in Controllers.

Have you ever done anything like this?

def create_message
  message = Message.new(params[:message])
  message.sender_id = .id # we can't trust form input
  message.language_id = .language_id
  message.something_else = Something.else
  if message_save
    redirect_to some_url
  else
   error!
  end
end

OK, this is a pretty bad example from a design perspective. Since the current user should be a model, and whose details are thus available in the Message model, it would probably be a better idea to set things like that inside the model. But let’s say we have to do it from the controller for some reason.

One limiting property of ActiveRecord is that you can’t send more than one attributes hash when you create a new object, so newbies like me when I wrote similar code to that above would create the new object and then manually append endless other properties to it before committing. And we want to keep the params hash for convenience – that’s why we’re using Rails after all!

But that params hash is just a hash, and that means we can merge with other hashes, and since we’ve got it all in one place let’s drop the .new and go straight for .create:

def create_message
  props = {:sender_id => .id, :language_id => .language_id, :something_else => Something.else}
  params[:message].merge!(props) 
  if Message.create(props)
    redirect_to some_url
  else
   error!
  end
end

Yeah, this is pretty basic stuff, not the 1337 tip you might have been expecting. But it took me a long time to get over my fear of messing with the black box of the params hash, and looking back at my old code made me remember. Anyway we dropped the LOC to get this record into the database from 5 to 3 – and it would stay at 3 no matter how much crap you added to the setup hash.

One tip won’t save you that much space, but every bit helps!

UPDATE: I had it back to front. Forgot hash merge order. >_<

Reducing code duplication with Rails ActionMailer

Sunday, March 9th, 2008

ActionMailer is a weird, fussy black box whose intricacies produce a lot of code duplication, especially for dealing with multi-language email, where you need to use a different .erb file for each language. Here’s an example of how you can reduce some of that duplication.

Code speaks louder than words so I’ll just cut and paste – you can see pretty easily what I’ve done. Works, although it’s completely undocumented and is probably not very good practise.

Previous, highly redundant code. I’ve snipped this a lot – you don’t have to use much imagination to see why I don’t like this:

def tokenmail_eng(user, subject)
  subject       subject
  body          :token => user.password_hash,
                :password => user.password,
                :user_id => user.id,
                :name => user.nickname
  recipients    user.email_address_with_name
  from          ''
  sent_on       Time.now
  headers       "Reply-to" => ""
end
 
def tokenmail_jpn(user, subject)
  subject       subject
  body          :token => user.password_hash,
                :password => user.password,
                :user_id => user.id,
                :name => user.nickname
  recipients    user.email_address_with_name
  from          ''
  sent_on       Time.now
  headers       "Reply-to" => ""
end
 
def tokenmail_zhs(user, subject)
  subject       subject
  body          :token => user.password_hash,
                :password => user.password,
                :user_id => user.id,
                :name => user.nickname
  recipients    user.email_address_with_name
  from          ''
  sent_on       Time.now
  headers       "Reply-to" => ""
end

Woah. That is *awful*. When I finally decided to clean up my mail code I found several problems even in my cut and pasted code – when you do that a few times, and then go in to make a change, you are almost guaranteed to leave something out. That is precisely why cut and pasting code is something you should never, ever do.

So how do we clean this up and merge into a “generic” mail, while fooling ActionMailer into using the correct file?

Turns out we can do this:

def tokenmail_gen(user, subject)
  subject       subject
  body          :token => user.password_hash,
                :password => user.password,
                :user_id => user.id,
                :name => user.nickname
  recipients    user.email_address_with_name
  from          ''
  sent_on       Time.now
  headers       "Reply-to" => ""
end
 
def tokenmail_eng(user, subject)
 tokenmail_gen(user, subject)
end
 
def tokenmail_jpn(user, subject)
 tokenmail_gen(user, subject)
end
 
def tokenmail_zhs(user, subject)
 tokenmail_gen(user, subject)
end
 
def tokenmail_zht(user, subject)
 tokenmail_gen(user, subject)
end

That’s so much better it brings a tear to my eye. Needless to say you will need to handle translation of the subjectline in your Controller.

SERIOUS NOTE: This is undocumented and while it seems to work, I haven’t heard of anyone else doing it and it might not work in future. Use at your own risk and make sure your tests cover it!

Eval, friend and enemy

Sunday, March 9th, 2008

The prevailing wisdom in the Ruby world is that using eval is bad. It’s brittle, hackish and inflexible – if you find yourself using it, it’s usually a sign you’re doing something wrong.

Well, I agree, but the keyword is “usually”. Sometimes using eval is a lot better than any alternatives, and it can help work around problems in, say, other people’s code, where the design decisions they made stop you doing what you want. I avoided using it for a long time, maybe based on my acceptance of the consensus opinion – but recently I’ve had cases where it’s the “lesser of two evils”, and I wanted to share one with you.

The example is Rails’ horrible mailing system. It’s one of the worst parts of Rails – but let me temper that by saying it’s a hard problem and difficult to see how else they could have done it. A Mailer in Rails is kind of a pseudo-model black box that waits to be sent a list of parameters, then inserts some of them into a specific text file, then sends that text using the remainder of the parameters.

All very well and good so far. But the problem with this is when you want to have mail in multiple languages. So you don’t just have

Notifier.deliver_welcome_mail(params)

you have

Notfier.deliver_welcome_mail_eng(params)
Notfier.deliver_welcome_mail_jpn(params)
Notfier.deliver_welcome_mail_spa(params)

etc. All with corresponding entries in the model, and matching text files.

Now, I don’t know any way to get around the need for those text files and entries in the model that doesn’t involve writing some huge text generator (bad) or hacking Rails itself (even worse). But we can at least improve on the statements used to call these deliveries.

Here’s an example of what I used to have:

if .language_iso == 'zht'
  Notifier.deliver_invite_zht(, , subject)
elsif .language_iso == 'zhs'
  Notifier.deliver_invite_zhs(, , subject)
elsif .language_iso == 'jpn'
  Notifier.deliver_invite_jpn(, , subject)
else
  Notifier.deliver_invite_eng(, , subject)
end

That’s a shortened version of what can be much longer if or case statements (this is pretty old code..). But you can see this gets very nasty, very quickly.

Using eval we can change that to:

eval("Notifier.deliver_invite_#{.language_iso}(, , subject)")

Yes, it’s still nasty. But it’s one line of nastiness as opposed to 10 (or many more). I think this is one example of a case where using eval is definitely better than the alternative.

Next, I’m experimenting with trying to reduce the horrendous duplication in the Mailer pseudomodel itself.