Posts

  • Installing Powerline on OS X + homebrew

    I've always wanted to get that fancy Powerline status bar and prompt. Chevrons and git branch icons in the terminal just sounds so... defying:

    Powerline screenshot

    I'd failed to get it working properly before because of a combination of outdated Powerline docs and confusion between OS X's system python/vim and homebrew's python/vim. Some of my former colleagues detailed the pains and hours lost getting Powerline setup, so I gave up.

    Anyway, I got a new Mac for work recently and decided to try again, and it was easier than I thought. You (including you, future Chu Yeow) can use this guide if you're using homebrew to install vim and python (and everything else like zsh and tmux).

    Installing Powerline on OS X

    1. Install python with homebrew: brew install python
    2. Install vim with homebrew: brew install vim --env-std --override-system-vim. You must install vim after python so that it'll compile with homebrew's python.
    3. Install powerline with pip: pip install https://github.com/Lokaltog/powerline/tarball/develop. Powerline should get installed to /usr/local/lib/python2.7/site-packages/powerline.
    4. open /usr/local/lib/python2.7/site-packages/powerline - you should see the files required for integration with zsh, vim, tmux, etc. in the bindings directory. All you need to do now is to include these bindings in your zsh, vim and tmux config files - it's that simple!
    5. Add Powerline to vim by adding these lines to ~/.vimrc:
      source /usr/local/lib/python2.7/site-packages/powerline/bindings/vim/plugin/powerline.vim
      set laststatus=2
    6. Add Powerline to tmux by adding this to ~/.tmux.conf:
      source /usr/local/lib/python2.7/site-packages/powerline/bindings/tmux/powerline.conf
    7. Add Powerline to zsh by adding this to ~/.zshrc:
      source /usr/local/lib/python2.7/site-packages/powerline/bindings/zsh/powerline.zsh

      If you use oh-my-zsh, be sure to source the Powerline bindings after oh-my-zsh's.

    8. Grab a patched font from https://github.com/Lokaltog/powerline-fonts (I use Inconsolata), and install it (just download, double-click the font file, and Install). If you don't use any of these fonts, you'll have to patch your favorite font yourself (have fun with that!)

    That's all you need! Go crazy and start tweaking your Powerline colorscheme.

  • Summer 2011 anime (and some from Spring 2011)

    Some of you might know that I'm a rabid anime fan. I don't talk much about it on this blog (actually, I don't talk much on this blog anymore, seeing as I haven't blogged this year). Anyway, yeah, one of my biggest passions and failings is my love for anime and I can't stop myself from collecting those cute Nendoroids too.

    This summer 2011 season, I'm watching 4 new series and 2 older series that started in spring 2011, Hanasaku Iroha and Nichijou:

    Currently watching anime, Summer 2011

    Top row: Usagi Drop, Mawaru Penguindrum, Ikoku Meiro no Croisée.
    Bottom row: No.6, Hanasaku Iroha, Nichijou.

    I'm a sucker for the slice of life genre (5 out of 6 series above are "slice of life"). Usagi Drop in particular is very promising - looking forward to how Daikichi and Rin's story unfolds. Mawaru Penguindrum is probably the winner in terms of "interestingness" though - after all, it has penguins. After the brilliant Ano Hana from spring 2011, Usagi Drop is looking to be my mainstay for this season.

    If you're an anime fan, please leave a comment on what you're watching or ping me on twitter (I'm @chuyeow) - I'd love to know what you guys like to watch when you're not coding.

  • Firefox nightly builds add a combined Stop/Go/Refresh button to the Location Bar

    In an effort to streamline Firefox's UI, the latest Firefox nightly builds have moved the Stop/Refresh button into the right corner of the Location Bar. I like this move even if the Firefox developers are simply copying design ideas from other browsers (in this case, I believe Safari was first).

    When you're typing in the Location Bar, a green Go button is shown:

    That switches to a red Stop button when the page is loading:

    The thin line you see in the location bar is a progress indicator that indicates how much of the current web page has loaded - I don't like the way it looks and I think it'll probably change in future.

    When the page has fully loaded, it changes to a Refresh button:

    This leaves a minimalist and compact toolbar:

    I should actually remove the Home button - I don't ever use it.

    To try out the new stuff coming in Firefox, download a Firefox nightly build now.

  • Rendering plists from your Rails app

    Jeena emailed me recently about his Rails plugin, plistifier, that allows you to render plists (property lists).

    It allows you to render your ActiveRecord objects as plists (among other things):

    def show
      @post = Post.find(params[:id])
    
      respond_to do |format|
        format.xml   { render :xml => @post }
        format.plist { render :plist => @post }
      end
    end

    Jeena was kind enough to let me know he "stole" my old Jsonifier plugin (github) code in plistifier.

  • Railscasts Xcode theme

    For those of you who use the excellent Railscasts TextMate theme and want to replicate the theme in Xcode, you can grab my version from Github. This is what it looks like:

    Railscasts Xcode theme

    Save it into ~/Library/Application Support/Xcode/Color Themes/, restart Xcode, and open its Preferences. You will be able to pick the Railscasts Color Theme in the Fonts & Colors tab.

    I dug this theme up after realizing that I haven't actually saved the Railscasts theme I'd replicated from several months ago and was too lazy to set up another one in my other Mac.

    Koen Van Der Auwera did a similar theme too so if you find that mine doesn't work so well (I've only used it for Cocoa Touch development, not at all for developing Mac apps), scoot over to his blog post.

  • Setting up virtualization on Ubuntu with KVM

    These instructions have been tested on Ubuntu 9.10 (Karmic) 64-bit. Skip right to the instructions if you're short on time.

    After being a happy Xen user for several years now, I've recently had to switch to an alternative virtualization solution. My colleague Arun (@iamclovin) actually struggled for a week with Xen VMs that locked up on Hardy; we've had much success with Hardy and Xen before, so we attributed it to a hardware problem since these were our first blade servers.

    Out of ideas, we tried Karmic (Ubuntu 9.10) only to discover that Xen support via the apt package system is gone. I went down the path of compiling a paravirt_ops Dom0 kernel (this article was very useful) but ended up deciding the process took far too long despite being successful.

    With KVM gaining official support from Ubuntu as the virtualization solution, I ended up ditching Xen and switching to KVM for these new servers on Karmic. The rest of the entry is a step-by-step guide on setting up KVM VMs on a Ubuntu server; I'm putting this down because like all wikis, the Ubuntu KVM wiki has grown a little too organically to be useful.

    Preparing a host server for KVM

    1. Update and upgrade apt packages (use your own discretion on whether this is necessary):

      aptitude update && aptitude dist-upgrade
    2. Check whether CPU supports hardware virtualization:

      egrep '(vmx|svm)' --color=always /proc/cpuinfo

      You should see lines with either "vmx" or "svm" highlighted.

    3. Install these packages:

      aptitude install kvm libvirt-bin ubuntu-vm-builder bridge-utils

      If you see a FATAL: Error inserting kvm_intel message during installation, it means that virtualization is not enabled in your machine's BIOS. You'll need to reboot your machine, enter the BIOS setup and enable virtualization (you'll have to hunt for the option).

      After enabling virtualization in the BIOS and rebooting, run:

      modprobe kvm-intel

      There should be no error shown (in fact, no console response).

    4. Optionally, install virt-top, a top-like tool for your VMs:

      aptitude install virt-top
    5. Verify that you can connect to the hypervisor:

      virsh -c qemu:///system list

      You should see something like this:

      Connecting to uri: qemu:///system
       Id Name                 State
       ----------------------------------
    6. Setup a network bridge on the server for VMs. Edit /etc/network/interfaces so it looks like this (use your own IPs):

      auto lo
      iface lo inet loopback
      
      auto eth0
      iface eth0 inet manual
      
      auto br0
      iface br0 inet static
       address 192.168.1.222
       netmask 255.255.255.0
       network 192.168.1.0
       broadcast 192.168.1.255
       gateway 192.168.1.167
       bridge_ports eth0
       bridge_stp off
       bridge_fd 9
       bridge_hello 2
       bridge_maxage 12
       bridge_maxwait 0
    7. Make sure that you have a direct console to the server because you're going to restart networking:

      /etc/init.d/networking restart
    8. Verify that your changes took place with ifconfig. You should see 2 entries like these:

      br0       Link encap:Ethernet  HWaddr 00:11:22:33:44:55
                inet addr:192.168.1.215  Bcast:192.168.1.255  Mask:255.255.255.0
                inet6 addr: fe80::223:aeff:fefe:1f14/64 Scope:Link
                UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
                RX packets:1099 errors:0 dropped:0 overruns:0 frame:0
                TX packets:50 errors:0 dropped:0 overruns:0 carrier:0
                collisions:0 txqueuelen:0
                RX bytes:74665 (74.6 KB)  TX bytes:6223 (6.2 KB)
      
      eth0      Link encap:Ethernet  HWaddr 00:66:77:88:99:00
                inet6 addr: fe80::223:aeff:fefe:1f14/64 Scope:Link
                UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
                RX packets:4939 errors:0 dropped:0 overruns:0 frame:0
                TX packets:39 errors:0 dropped:0 overruns:0 carrier:0
                collisions:0 txqueuelen:1000
                RX bytes:532798 (532.7 KB)  TX bytes:5585 (5.5 KB)
                Interrupt:36 Memory:da000000-da012800

    Setting up a VM

    1. Setup a VM with vmbuilder:

      vmbuilder kvm ubuntu \
          -v \
          --suite=karmic \
          --libvirt=qemu:///system \
          --arch=amd64 \
          --cpus=2 \
          --mem=2048 \
          --swapsize=4096 \
          --rootsize=20480 \
          --flavour=server \
          --hostname=billiejean \
          --ip=192.168.1.240 \
          --mask=255.255.255.0 \
          --net=192.168.1.0 \
          --bcast=192.168.1.255 \
          --gw=192.168.1.167 \
          --dns='202.157.163.157 202.157.131.118' \
          --bridge=br0 \
          --mirror=http://archive.ubuntu.com/ubuntu \
          --components='main,universe' \
          --addpkg=openssh-server \
          --user=administrator \
          --pass=icanhaspasswd \
          --dest=/root/vm-billiejean \
          --tmpfs=-

      The options you need to care about are:

      1. suite: Version of Ubuntu to install (e.g. karmic, hardy).
      2. cpus: Number of CPUs to assign to VM.
      3. mem: Amount of RAM in MB to assign to VM.
      4. swapsize: Size of swap in MB of VM.
      5. rootsize: Size of root filesystem in MB of VM.
      6. flavour: The "flavour" of kernel to use in the VM. Either "virtual" or "server".
      7. hostname: Hostname of VM.
      8. ip: IP address of VM.
      9. mask: Netmask of VM.
      10. net: Network of VM.
      11. bcast: Broadcast address of VM.
      12. gw: Gateway of VM.
      13. dns: DNS server(s) for VM.
      14. addpkg: APT packages to install in the VM. openssh-server is needed so that we can login to the VM to setup the virsh console.
      15. user and pass: User account that's setup for you to access the VM.
      16. dest: Destination directory on server where VM disk image will reside.
    2. If your VM is created successfully, there'll be a config file for the VM in /etc/libvirt/qemu/ (e.g. /etc/libvirt/qemu/billiejean.xml), and a disk image in the directory specified in the --dest option (e.g. /root/vm-billiejean/disk0.qcow2).
    3. You can verify that it works by starting the VM and SSHing into it (virsh console will not work yet).

      virsh start billiejean

    Converting Disk Images to LVM Logical Volumes

    Now, we have the VM setup but it's running off a disk image. For better performance, running the VM off a LVM logical volume will optimize disk IO.

    vmbuilder is supposed to support the --raw option to write the VM to a block device (such as a LVM logical volume), but I've had no success with it (as does Mark Imbriaco, sysadmin of 37signals: http://twitter.com/markimbriaco/status/7437688341 and http://twitter.com/markimbriaco/status/7437699338). We're going to convert the disk images using qemu-img and write the bits into a LVM logical volume instead.

    1. Stop the VM if it's running:
      virsh shutdown billiejean
    2. Convert the VM's qcow2 (QEMU image format) disk image to a raw disk image:
      qemu-img convert disk0.qcow2 -O raw disk0.raw
    3. Create a logical volume to house the VM, making sure it's big enough for the VM's rootsize and swapsize options:
      lvcreate -L24GB -n <logical_volume_name> <volume_group_name>
    4. Copy raw image into the logical volume:
      dd if=disk0.raw of=/dev/<volume_group_name>/<logical_volume_name> bs=1M

      This will take awhile (the bigger your image, the longer it takes).

    5. Edit the VM's config so that it uses your new logical volume:
      virsh edit billiejean

      Change <disk> to point to the logical volume:

      <disk type='block' device='disk'>
        <source dev='/dev/<volume_group_name>/<logical_volume_name>'/>
        <target dev='hda' bus='ide'/>
      </disk>
    6. Startup the VM. You might wanna rename the original disk0.qcow2 image first just to make sure your VM isn't still using it.
    7. Once you're sure your VM is running off your LVM logical volume, you can delete or backup the original qcow2 disk image.

    Getting a console to your VM from the Host Server

    Now, we have to setup the VM so that virsh console works. This is a console to the VM from the host server that works even when the networking in the VM is not.

    1. Edit the VM's settings:
      virsh edit billiejean

      In the <devices> block, add:

      <serial type='pty'>
        <target port='0'/>
      </serial>
    2. Startup your VM:
      virsh start billiejean
    3. SSH into VM and create a file /etc/init/ttyS0.conf:
      start on stopped rc RUNLEVEL=[2345]
      stop on runlevel [!2345]
      
      respawn
      exec /sbin/getty -8 38400 ttyS0 vt102

      Start the tty with:

      start ttyS0
    4. Still in the VM, install acpid so that the VM will respond to shutdown commands from the server:
      aptitude install acpid
    5. Reboot the VM.
    6. Verify the console works by opening a console to the VM from the server:
      virsh console billiejean

      You may have to hit "Enter" before you see any console output.

    Miscellaneous VM Setup

    That's it, your VM is ready! You'll probably want to do these:

    • Set a root password and possibly delete the user you setup using vmbuilder.
    • Set the timezone with dpkg-reconfigure tzdata.
  • Stop and Reload buttons merged in Firefox

    A minor UI improvement to Firefox has been made to Firefox and it's currently only available on nightly builds of Firefox: the Stop and Reload buttons have been merged into a single button. This change should make it into Firefox 3.6.

    While a page is loading, the button acts as a Stop button:

    When it's done loading, it becomes a Reload button.

    I'm always up for 1 less unnecessary button. I think this feature is copied from Safari (I might be wrong).

  • Firefox quick tip: view images in a new tab quickly

    A mini-tip on viewing images quickly in a new tab in Firefox. Useful for web devs who want to look at the URL of an image quickly.

    As an example, here's the Youtube webpage of the owner of Maru the Cat:

    ff3-middle-click-step-1

    To open the thumbnail image of Maru in a new tab, right-click the image, and then middle-click the View Image item from the context menu:

    ff3-middle-click-step-2

    It should open up in a new tab:

    ff3-middle-click-step-3

    You can also do the same for background images (middle-click View Background Image from the right-click context menu).

  • How my simple iPhone app made it to the top of the App Store

    Slightly more than a month back I wrote this little iPhone app (App Store link), almost a toy app really, to check the usage of my data plan with my local telco (Singtel). I wanted to scratch an itch and I also really didn't want to exceed my data plan - the fees are excessive.

    Anyway, I submitted it to the iPhone App Store for approval as a free app and named it Singtel Data Usage. No points for creativity with the name but I thought it'd be nice to know exactly what the app did from just reading its name. After one keyword rejection (which was unfounded but I wasn't going to argue with the app reviewers), it finally got approved a few days ago.

    Singtel Data Usage iPhone app screenshot

    Yeah that's what it looks like. I stole part of the design from another iPhone app - can anyone guess which one? I think I spent 80% of development time changing the design over and over again, tweaking font sizes, and agonizing over which icons to use.

    Thanks to folks on Twitter (I'm @chuyeow - do follow me if you're reading my blog), word got around and by the 3rd day of it being approved, my app was the Top Free app on the Singapore App Store. Going to take a time out for the vanity shot:

    A similar app, iBBOM, was approved around the same time but I believe I got a head start because of all the awesome people on Twitter who retweeted my tweet and the kind folks who gave glowing reviews on the App Store (thanks especially to Andy Croll for the 1st review). iBBOM actually looks much better than my app (I can't help tweeting about it) and works perfectly (only thing I don't like are the ads). I believe I couldn't have gotten to the top of the App Store without the guys and gals who spread the word on Twitter.

    On to the not so fun part of this post. Yet another similar app, SG BBOM, was pulled from the App Store by Singtel (the telco) recently so I'm expecting to hear from Singtel really soon. We shall see.

    In the meantime, if you own an iPhone and have a Singtel data plan, do check out my app (App Store link) and give an honest review on the App Store. If you've already downloaded it, I'd like to know what you think in a review as well. Thanks!

  • Blog redesigned

    After 5 long years of using the same old blog theme, which I'd handcrafted from scratch way back in 2004, I've finally got down to refreshing it to a more contemporary look. I've also updated the woefully outdated About page.

    Here's a before and after shot:

    Blog theme - before and after

    I think it's much better, definitely more modern and minimalist.

    The new blog theme is basically a heavily modified version of the
    wonderful and free Compositio WordPress theme -
    you can check out a demo of Compositio here to see how different it is.

    The only thing I kept from my old theme, which I affectionately called "Clover", was the same clover logo. I did that with the shape tool in Photoshop - it's a simple logo and no skill was required to create it, obviously!

    If you're reading this in a feed reader or one of those aggreggator-type sites, you can help me out by checking out the blog itself and giving me some good old constructive criticism.

  • Upgraded to iPhone OS 3.0 and running out of battery fast?

    After upgrading my girlfriend's iPhone 3G to OS 3.0 recently, I noticed that the iPhone's battery was getting depleted really quickly. I'd left it overnight with Wifi, 3G, Bluetooth, Push Notifications all off, and the battery went from 80% to a jaw-dropping OMGWTFBBQ 10%.

    I attributed it to the recent jailbreak (I've already removed the jailbreak trying to figure out this problem) at first. I only realized something was really wrong when the iPhone's battery started draining while it was charging via USB!

    Looking around on teh interwebs, I managed to solve it (easily!) by following the suggestions from this thread at the Apple support forums. Here's what worked for me:

    1. Go to Settings on your iPhone.
    2. Turn Notifications off. I'm not sure if this is necessary but did so just in case.
    3. Go to Mail, Contacts, Calendars and delete all your accounts (I deleted even Calendar accounts just in case). You may want to write down or backup your account settings first.
    4. Go to Fetch New Data (also in the same Mail, Contacts, Calendars settings) and make sure Push is Off.
    5. It's probably not necessary but you may want to restart your iPhone (power it on and off) here.
    6. Add back all your accounts. You can turn Push back on Fetch New Data now.
    7. You can also turn Notifications back on if you wish.

    It seems like the iPhone 3.0 OS update has a bug where email accounts with active push notifications were set to keep fetching new email even if push is turned off explicitly. Hopefully this post will help solve your battery problems, if not, you've gotta keep looking - the Apple iPhone Support forums is a good place to start.

  • A curl-ier Curb - better cookie support in Curb

    As of Curb 0.3.7, Curb comes with slightly better cookie support that makes it more "curl-like".

    It probably sounds like shameless self-promotion that I'm blogging about these changes since I'd contributed them. Well, yeah that's true, but I'm also doing so because I doubt these changes will ever be made known since Curb doesn't publicize any updates or changelogs.

    Curb is my current number 1 HTTP client for Ruby so the more love it gets, the better.

    Passing cookies as a string in Curb requests

    Curl veterans will probably know how to do this with the curl binary:

    curl -b "auth=abcdef; ASP.NET_SessionId=lotsatext;" example.com

    This sends a request to example.com with 2 cookies named "auth" and "ASP.NET_SessionId" (I hate those big-ass ASP.NET cookies btw). There wasn't a way to set this in Curb, so I looked up the libcurl C API docs and replicated the same option in Curb (commit on Github). An example:

    curl = Curl::Easy.new('http://example.com/')
    curl.cookies = 'auth=abcdef; ASP.NET_SessionId=big-wall-of-text;'
    curl.perform

    Of course, the cookies will more often be retrieved/constructed rather than a literal like in the example above. In my case, I was proxying cookies while trying to wrap an API around a site that doesn't have one.

    Passing cookies as a file via the "cookiefile" option

    The second change is the new cookiefile option. This replicates curl commands like these:

    curl -b cookies-to-send.txt www.example.com

    with something like this in ruby:

    curl = Curl::Easy.new('http://example.com/')
    curl.cookiefile = '/path/to/cookies-to-send.txt'
    curl.perform

    The cookies file looks like this (you can get a sample with the --cookie-jar option to curl, e.g. curl --cookie-jar cookies.txt www.wego.com):

    www.wego.com	FALSE	/	FALSE	0	lang	
    www.wego.com	FALSE	/	FALSE	0	user_country_code	SG

    Check out the commit on Github if you're interested.

    Why would I use these?

    Now you might be wondering how these 2 changes are useful - well, they are totally irrelevant to you if you're not expecting any cookie support in Curb. However, if you're accessing or scraping a site that uses cookie-based authentication, these changes allow you to keep your Curb client authenticated across sessions, even when doing HTTP POSTs (Curb doesn't send cookies properly in POST requests even if curl.enable_cookies is set).

    I've found these changes to Curb particularly useful since I vastly prefer Curb to most HTTP clients for its speed and lightweight implementation, and heavier scraper-type HTTP clients like Mechanize are a last resort.

  • Don't kill your app when using ActiveRecord in Rails Metal, release your database connections

    Rails Metal has been available on Rails since version 2.3 - it's old news. But if you haven't used it or heard about it, you can find out more about Rails Metal on the RoR weblog and on Jesse Newland's blog.

    So anyway, I am one of those laggards and only wrote a Rails Metal piece not too long ago for a Rails app on Wego.com in an effort to optimize some high volume requests. It looked something like this:

    class Adamantine
      def self.call(env)
        if env['PATH_INFO'] =~ ROUTE_REGEX
          location = Location.find(1) # Use ActiveRecord.
    
          [200, { 'Content-Type' => 'text/html' }, [location.to_json]]
        else
          # Leave it to Rails to deal with the request.
          [404, { 'Content-Type' => 'text/html' }, ['Not Found']]
        end
      end
    end

    Notice the use of an ActiveRecord model. It ran for quite awhile, almost 30 minutes, in production until I started getting notification emails about "Too many open database connections" to the MySQL server! The change was promptly rolled back and there was no cheezburger for me.

    dam it!

    As it turns out, Rails doesn't take care of certain things in Rails Metal pieces, including the releasing of connections back to the database connection pool. A bit of googling turned up this bug in Rails' bug tracker (see Josh Peek's comment about ActiveRecord::Base.clear_active_connections!).

    I rewrote the Rails Metal piece to always ensure it clears any active database connections with ActiveRecord::Base.clear_active_connections!:

    class Adamantine
      def self.call(env)
        if env['PATH_INFO'] =~ ROUTE_REGEX
          location = Location.find(1)
    
          [200, { 'Content-Type' => 'text/html' }, [location.to_json]]
        else
          # Leave it to Rails to deal with the request.
          [404, { 'Content-Type' => 'text/html' }, ['Not Found']]
        end
      ensure
        # Release the connections back to the pool.
        ActiveRecord::Base.clear_active_connections!
      end
    end

    Moral of the story: Don't forget to release your database connections if you're using ActiveRecord in your Rails Metal. Or even better, don't use ActiveRecord in Rails Metal - you're aiming for raw speed anyway right?

  • I'm still alive, and on FriendFeed and Twitter

    Wow, it seems like I haven't posted anything this year. Don't worry, you aren't rid of me yet. I'm still alive and posting updates to FriendFeed and (less often) to Twitter.

    If you belong to what I imagine must be now the microscopic population of loyal readers of my blog, please do hook up with me on FriendFeed or Twitter. I'll follow you back if you're a reader!

  • Dragging tab to a new window coming to Firefox

    Completely by accident, I discovered that you can now drag a tab out from its current window to a new window in a recent Firefox nightly. A short video 24-second better explains what I'm talking about:

    This tab tearing capability is a pretty neat feature - I know you can already do this in Safari, Opera and Galeon. It's really well done in Safari, which I think is what Firefox is emulating. Nice to see Firefox follow suit!

    If you can't wait for Firefox 3.1, try it out in a recent Firefox nighty build (remember to use a new profile unless you are willing to risk corrupting your daily profile).

  • Optimize Firefox's memory usage by tweaking session preferences

    I'm a heavy tabbed browsing user - I have around 30 tabs open in my day-to-day Firefox profile all the time. Since the day Firefox 3 was released, I've noticed Firefox progressively getting slower with this particular Firefox profile (I use a different profile for web development). When it got to the point where changing tabs took a noticeable pause of 1-2 seconds, I tweaked some of Firefox's session store and history preferences and now things are blazing fast again.

    Here's what you can do:

    1. Go to about:config in Firefox.
    2. Type in "session" in the "Filter" box.
    3. Edit browser.sessionhistory.max_entries - this is the number of pages stored in the history of your browsing session. Basically these are pages that can be reached using your Back and Forward buttons. The default is 50 - I reduced it to 20.
    4. Edit browser.sessionhistory.max_total_viewers - this is the number of pages that are stored in RAM so that they aren't re-processed by Firefox's rendering engine. This is what allows you to go Back to a page in Firefox and have it load almost instantaneously. The number of pages stored actually depends on the amount of RAM on your machine (see this). I reduced this to 4 (I have 2GB RAM).
    5. Edit browser.sessionstore.max_tabs_undo - the number of tabs you can restore after closing them (you can do this with Ctrl/Cmd-Shift-T). The default of 10 is more than I really need, so I reduced it to 3 tabs.
    6. Edit browser.sessionstore.interval - Firefox saves your session after every 10 seconds by default. I changed this to a more conservative 30000 milliseconds.

    You can read more about these preferences and more at the MozillaZine Knowledge Base. If you've any tips on how to improve Firefox's performance, be sure to share!

  • Sass with Rails - avoiding disappearing stylesheets in production

    A few days ago I noticed that some of the pages on the Hotels app on wego.com were completely unstyled. They turned out looking rather Jakob Nielsen-istic:

    Wego.com Hotels - no CSS


    But we were attached to our ugly shade of green to leave those pages in their naked glory. Preliminary CSI work told me that some cached stylesheets generated by Rails were empty files. Why is this is happening?

    stylesheet_link_tag and the :cache option

    Was I overriding the stylesheets generated by Rails in different pages? Because we have a lot of cobranded sites and country sites on wego.com, I use the :cache option when using stylesheet_link_tag very often.

    For example, the main wego.com site's layout template has a stylesheet_link_tag like this (in reality there are a whole lot more stylesheets):

    <%= stylesheet_link_tag 'yui/reset-fonts', 'search',  :cache => 'cache/search/listings' %>

    When I need to make a new page for a cobranded site, I'll create a new layout template with this:

    <%= stylesheet_link_tag 'yui/reset-fonts', 'search', "sites/#{current_site}/cobrand", :cache => "cache/#{current_site}/search/listings" %>

    Oftentimes I'd copy and paste (boo and hiss all you want!) the stylesheet_link_tag from one layout template to another and forget to update the cache path (the :cache => '/path/to/stylesheet' part). Two different stylesheet sets being cached to the same path is naturally a very stupid thing to do. So this wasn't it, but it's good to point this out because I have made this mistake at least 2 times!

    Don't check in generated CSS files by accident

    Next, since I was using Sass, I was by now pretty sure that was it. First things first: did I check in a generated CSS file into source control (we use Git)? It's another amateur mistake, but unsurprisingly, I've done this a couple of times. I think I'd wasted about an hour hunting down the reason for a style change that just wouldn't show up. Yeah, I could have just added *.css to .gitignore, but I'm still using a mix of pure CSS and Sass templates.

    The problem

    In the end, I found this blog post by Ari Lerner on the CitrusByte blog about similar woes with Sass in production that set me on the path to a solution. It seems that when Rails encounters stylesheet_link_tag calls, it starts to pull together all the stylesheets and sometimes Sass is unable to generate the CSS files fast enough. Rails then throws an exception about not being able to find the CSS files and outputs an empty CSS file to the cache path.

    The solution

    The solution? Generate all the CSS files from Sass templates prior to restarting Rails when deploying. I added a rake task for updating all the Sass stylesheets:

    namespace :sass do
      desc 'Updates stylesheets if necessary from their Sass templates.'
      task :update => :environment do
        Sass::Plugin.update_stylesheets
      end
    end

    Then, I created a mirror of this as a Capistrano task:

    namespace :sass do
      desc 'Updates the stylesheets generated by Sass'
      task :update, :roles => :app do
        invoke_command "cd #{latest_release}; RAILS_ENV=#{rails_env} rake sass:update"
      end
    
      # Generate all the stylesheets manually (from their Sass templates) before each restart.
      before 'deploy:restart', 'sass:update'
    end

    Now, whenever I do a cap deploy, the stylesheets are generated before the Rails processes are restarted, ensuring that Rails' stylesheet_link_tag helper is always able to find the pure CSS files when trying to merge them together and caching them to a single file.

  • Reveal currently open files in Mac OS X

    Something I noticed completely by accident today when I clicked on the titlebar of QuickTime Player today with the Cmd key held down. The "titlebar" is this thing here - I'm not sure that's the right name for it:


    Anyway, if you hold down the Cmd key (aka the Apple key), a menu pops up that shows the folder hierarchy of where the currently opened QuickTime movie is in your filesystem:


    This works in all Mac apps that display the filename of the currently open/focused file in the titlebar.

    It's useful for me since my NADD means I try to close as many unused windows as possible to adhere to my Cmd-Tab diet - now I can close Finder windows after opening files and be sure that I can get back to them quickly. What about QuickSilver? Yup, I do use (and love) QuickSilver but I don't let it catalog every single file!

    Another nice thing about this is that I can easily reveal files in Finder in my favorite text editor (TextMate) without needing to use the project drawer:


  • Living on the Edge (of Rails) has a new home at the Official Ruby on Rails weblog

    You may have heard of it, but just in case, Living on the Edge is now going to be published on the official Ruby on Rails weblog. Big thanks to Gregg Pollack for getting me the new "gig", and more importantly, for reviving and freshening up the content on the official Rails blog.

    Catch the new first edition - this one's about the API changes since Rails 2.1.

  • RailsConf 2008 Day 1 - pics and a summary

    Day 1 of RailsConf 2008 was basically tutorial day (schedule) and started with my colleague here with me, Arun, missing out on Yoga on Rails and me sleeping until the first tutorial session. Anyway, I snapped some photos while trying to remain the unobtrusive tourist.

    Here's a shot of Portland Convention Center where it's all happening:

    Portland Convention Center - twin peaks outside


    There're queues for collection of badges after registering your attendance:

    RailsConf Day 1 - registration queue


    Patience in the queue rewarded me with a RailsConf badge/name tag:

    RailsConf badge


    I was at the Meta-programming Ruby for Fun & Profit tutorial in the morning. I think when I selected the tutorial it was before I'd seen Neal Ford and Patrick Farley's (the speakers) presentation videos from elsewhere - I know Patrick presented at MWRC and enjoyed that video.

    RailsConf day 1 - metaprogramming tutorial


    So anyway, after the break I went over to the Refactoring Your Rails Application tutorial. Was pretty good, but I didn't learn much I didn't already know.

    Lunch came in the form of a pretty box:

    RailsConf day 1 - lunch


    After lunch was the 2nd tutorial session and I went to both CI for the Rails Guy (or Gal) (by Chad Woolley) and Developer Testing Tricks (by Brian Takita). There were some scathing comments about how the tutorials were rather underwhelming so far in #railsconf on IRC. While I agree that the tutorials were rather underwhelming, I think I should have expected it. Oh well, I'll know to skip them next time.

    Later that night, at the Birds of a Feather session, after stealing a Pivotal Labs t-shirt (they're launching a bug tracker, project management type app called Pivotal Tracker at RailsConf), Yehuda Katz (Merb and jQuery ninja) gave a presentation on Merb (geared towards Rails folks). It was a pretty interesting talk though there wasn't much above what Ezra had presented previously at GoRuCo 2008 and at MWRC 2008 (I think Yehuda did one too but I can't remember where now). Yehuda pointed out a (heated) discussion that happened recently on keeping Merb syntax as Rails-friendly as possible. I have no objection against a different syntax really, especially since Merb looks pretty well-documented in the source itself - would be nice if someone could point out an up-to-date Merb tutorial though.

    Anyway, that's it from me - as always, if anyone who reads my blog recognizes me at RailsConf, do say hi (#railsconf works too).

  • Living on the edge (of Rails) #22 - pre-Railsconf 2008 edition

    No mind-blowing changes in Rails this week prior to RailsConf - as Gregg mentioned last week in the Rails Envy podcast, it's pre-2.1 days (Rails 2.1 will probably be released at RailsConf) so it's pretty easy to see why. Oh and all Rails tests now pass in Ruby 1.9 after a long-standing #module_eval bug got fixed in Ruby 1.9's trunk (see thread for more details).

    I'll be at (my first) Railsconf 2008 in Portland, Oregon this Thursday onwards - if anyone sees me and recognizes me from my Facebook picture please come and say hi (no head butting though).

    This week’s report covers changes from 19th May 2008 to 25th May 2008 (the day the corresponding Rails Envy podcast was recorded).

    first and last methods now work with associations and named_scope

    Remember how the merging of the has_finder gem into Rails allowed you to do things like Post.first and Post.last?

    Now you can go one step further and use the same methods on your ActiveRecord associations. For example:

    post = Post.find(1)
    first_comment = post.comments.first

    If you have a named_scope named recent defined, you can even do this:

    post.comments.recent.last

    This neat little enhancement is courtesy of Ryan Bates (yes, that Ryan Bates of Railscasts fame).

    Related changeset: http://github.com/rails/rails/commit/73c59638549686fccc749ffd3ac53cb533c5fd61

    Cache stores now have an exist? method and controllers get fragment_exist?

    The cache stores in Rails (Memcache, file stores, etc.) now have an exist? method that checks whether a cached value exists given a cache key. This allows Rails controllers to expose a fragment_exist?
    method that allows you to check for existence of a cache fragment:

    fragment_exist?('example.com/foo/bar')

    This little enhancement is courtesy of José Valim.

    Related changeset: http://github.com/rails/rails/commit/99860b72aebe0348f41e82d4710343498d89a84b#diff-2

    Create association records with block argument

    You can now create records for associations like so:

    post.coments.create!(:title => 'Techcrunch') do |c|
      c.body = "Rails can't scale"
    end

    This is in keeping with the ActiveRecord::Base.create change previously mentioned.

    Credit for this patch goes (once again) to Ryan Bates.

    Related changeset: http://github.com/rails/rails/commit/6cba97d2a449faf21aec9fe9d4434067e414226f

    As always, let me know of any suggestions or how I can improve the Living on the Edge (of Rails) series.

  • Living on the Edge (of Rails) in Spanish

    Juan Lupión wrote in to inform me that he's also translating my Living on the Edge (of Rails) series of blog posts to Español. Thanks Juan!

  • Living on the edge (of Rails) #21

    It's another slow week (just 2 changes of note imho) after the release of the 1st Release Candidate (RC1) of Rails 2.1. Follow that link for installation instructions - though if you're reading this blog post you probably don't care! (because you're, you know, "living on the edge"). Cheesiness aside, be sure to report any bugs you may encounter when upgrading to 2.1 RC1 or edge at the Rails bug tracker - it is an RC so any bug reports would be very welcome and useful!

    This week’s report covers changes from 12th May 2008 to 18th May 2008 (the day the corresponding Rails Envy podcast was recorded).

    caches_action can has conditionals

    caches_action now takes an :if option (just like caches_page does). For example:

    caches_action :index, :if => Proc.new { |c| !c.request.format.json? }

    This little enhancement is courtesy of José Valim.

    Related changeset: http://github.com/rails/rails/commit/7708650f73ddb4db300ea2059c60c1d907a4384e

    Bugfix: :select option is now scanned in ActiveRecord finders to ensure needed tables are included in generated SQL

    Post.find(:all, :include => :author, :select => 'posts.*, authors.id as "author_id"', :limit => 2)

    Would generate an SQL statement like this:

    SELECT posts.*, authors.id as "author_id" FROM "posts" LIMIT 2

    Notice how the authors table is not joined. This oversight is now fixed.

    Thanks go to John Devine for this bugfix.

    Related changeset: http://github.com/rails/rails/commit/b28b54cab090bed8f099ef375b419a8f92390dd4

    As always, let me know of any suggestions or how I can improve the Living on the Edge (of Rails) series. Also, if anyone has any recommendations of (non-ruby-related) things to do in Portland, do let me know!

  • Deploying with Capistrano via a gateway - some notes

    Some notes on setting up a gateway server for deploying via Capistrano that I couldn't find at the source.

    • You can specify the user you want to use to login to your gateway server like so: set :gateway, '[email protected]'. This logs in to your gateway server as the "deploy" user.
    • You can specify an alternate SSH port for both your gateway and your deployment servers. E.g. setting ssh_options[:port] = 11111 will make Capistrano SSH to your gateway server with on port 11111. For your actual application (and web and database) servers, you can specify the SSH port like so:
      set :gateway, '[email protected]'
      role :app, '192.168.1.113:22222'
      role :web, '192.168.1.113:22222'
      role :db,  '192.168.1.113:22222', :primary => true
    • If you're using public key authentication, you should put the public keys of your users (those who be deploying your app) on the gateway server and on the actual servers. I thought Capistrano would use the key of the "deploy" user but nope.
  • Living on the edge (of Rails) #20 - script/dbconsole and flash.now now test-able

    This week’s report covers changes from 5th May 2008 to 11th May 2008 (the day the corresponding Rails Envy podcast was recorded).

    script/dbconsole

    A script/dbconsole script has been added that allows you to connect to your database using its console client.

    If you needed to connect to your production MySQL database (you better know what you are doing!), for example, you can run RAILS_ENV=production script/dbconsole or simply script/dbconsole production (thanks to Ryan Bates for pointing this out!) and it will login to your database server using the command line MySQL client. This also works with the PostgreSQL and SQLite databases.

    To use this script in your Rails app, remember to run rake rails:update:scripts after updating to edge Rails.

    This nice little enhancement courtesy of Steve Purcell, who originally had a similar database console plugin.

    Related changeset: http://github.com/rails/rails/commit/4a07103687084496b773e18a03b1f2f5e686f7ad

    flash.now is now accessible in tests

    This is something that many of us Rails developers have probably come across when writing tests for flash messages being set with flash.now, myself included. Basically, you couldn't test the contents of your flash.now because they were always being emptied before your test could get to them.

    # In your controller:
    flash.now[:notice] = 'You gotta be kidding me!'
    
    # In your test:
    assert_equal 'You gotta be kidding me!', flash.now[:notice]
    # FAILS because flash.now[:notice] is nil

    Andreas Neuhaus took a good look at how it works and figured out how to make testing flash.now work without resorting to assert_selects.

    Related changeset: http://github.com/rails/rails/commit/74eed6290e63111d1aad2b181692a84f4f040aea

    There isn't much else of note so far but if you'd like to know every gritty detail, you'd probably want to peruse the Rails commit logs. As always, let me know of any suggestions or how I can improve the Living on the Edge (of Rails) series.

  • Living on the edge (of Rails) #19 - change_table for migrations and more

    This week’s report covers changes from 29 April 2008 to 4th May 2008 (the day the corresponding Rails Envy podcast was recorded).

    change_table for ActiveRecord migrations

    Thanks to Jeff Dean, who also blogged about the new change_table feature in ActiveRecord migrations, you can now change a table with a block like so:

    change_table :videos do |t|
      t.add_timestamps
      t.add_belongs_to :goat
      t.add_string :name, :email, :limit => 20
      t.remove_column :name, :email # takes multiple arguments
      t.rename :new_name
      t.string :new_string_column # executes against the renamed table name
    end

    Some key things to note:

    • add_XXX would add a new column for you, e.g. add_string would add a new string field.
    • Of course, add_timestamps would add the magic created_at and updated_at datetime fields.
    • remove_column now takes multiple arguments.
    • rename would rename the table.

    Very nice, DRY enhancement, props to Jeff Dean once again.

    Related changeset: http://github.com/rails/rails/commit/96980bd561d79824b6cb6efbcbecdcbf8785d452

    ActiveRecord::Base.create takes a block like ActiveRecord::Base.new

    Yup now you can also create ActiveRecord objects with a block argument just like you could for ActiveRecord::Base.new:

    @person = Person.create(params[:person]) do |p|
      p.name = 'Konata Izumi'
      p.age = 17
    end

    Credit goes to Adam Meehan for this patch.

    Related changeset: http://github.com/rails/rails/commit/dd120ede53eaf71dee76894998a81626b7a689fc

    Bugfix: change_column should be able to use :null => true on a field that
    formerly had false

    You can now use change_column in your migrations to alter a column as nullable if it was previously NOT NULL.

    This bugfix is courtesy of Nate Wiger.

    Related changeset: http://github.com/rails/rails/commit/10ef65a3b054270ed3d458ec8eb7c2b9a3e638f7

    As always, let me know of any suggestions or how I can improve the Living on the Edge (of Rails) series.

  • Announcing Wego.com - our travel meta-search engine in beta (Ruby included)

    So we've been terribly busy at Bezurk these last 2 months or so working on our new re-branded site with a bunch of new features. We were lucky enough to secure a 4-letter domain name - wego.com (and it came at a price too!) so it was all worth it. Trust me, with a name like Bezurk you're going to be terribly hard to remember and to brand. We've had partners and friends who spell Bezurk in a variety of ways: berserk, bezerk, buzerk. Don't even ask us about our encounters with non-native English speakers. Don't.

    So yes, the new site: http://www.wego.com/.

    Wego.com logo


    We've decided to push it "live" as a beta product (and we know it is very much in beta, we're not kidding or trying to be Web 2.0-ish) prior to our launch party next week. Give it a few hits or so and let me know of any feedback (leaving a comment here is good), especially what annoys you. You might even want to use it to search for airline tickets or to book hotel rooms for your next trip and let us know if we managed to somehow epic fail at the task. If you liked it or booked a trip somewhere, we're glad to have helped you on your way.

    I'd write more about wego.com but what I really wanted to focus on, being a Rubyist and all, is what is really cool to me about wego.com - we've rebuilt our Hotels product from the ground up (it was a mostly Java before, and it is still live on http://bezurk.com/hotels/) in 85% Ruby, 8% JavaScript, 5% Erlang, and 2% Java. Well, those are rough figures anyway. We also made a strong effort to keep our mostly AJAX application as usable as possible without JavaScript but I'll admit, we had to cut some corners due to deadlines.

    I'd love to be more specific about the design and architecture of our Hotels application and on the cool stuff (CouchDB, Rack, Solr, jQuery, libmemcached, and of course, Ruby) we've used that have made our lives as developers much easier than when we were in Javaland - but I'd leave that for a later post. I'm sure my friend Arun Thampi also has a war story to tell so we'll come up with something entertaining. Stay tuned!

  • Living on the Edge (of Rails) in French

    It looks like Cyril Mougel has translated my 2 latest posts to French: LotE #17 and #18. Thanks Cyril!

  • Living on the edge (of Rails) #18

    This week’s report covers changes from 21 Apr 2008 to 27 Apr 2008 (the day the corresponding Rails Envy podcast was recorded).

    Not much interesting to report this week - there were mostly a bunch of bugfixes and Ruby 1.8.7-compatibility commits.

    Introduce ActiveResource::Base.timeout and rescuing from Timeout::Error in ActiveResource::Connection

    These are 2 changes that I'd talked about earlier this week so I won't repeat myself - take a read through ActiveResource timeouts and why it matters.

    Smart integer datatype for the MySQL adapter in migrations

    The MySQL adapter in Rails now maps the integer column type in your migrations to either smallint, int, or bigint depending on the :limit option.

    This means that a migration like this:

    def self.up
      create_table :searches do |t|
        t.integer :foo, :limit => 2
      end

    will create your foo column as a smallint(2) MySQL datatype (instead of int(2) before). (More information MySQL numeric datatypes.)

    Credit goes to DHH for this patch.

    Related changeset: http://github.com/rails/rails/commit/a37546517dad9f6d9a7de6e1dba4d960909d71e8

    As always, let me know of any suggestions or how I can improve the Living on the Edge (of Rails) series.

  • New in Rails: ActiveResource timeouts and why it matters

    If you've ever used ActiveResource or for that matter, coded anything that involves interacting with a remote API, you've probably had the misfortune of having that API go down or become unresponsive. The experienced old geezers would already have forseen the problem and set a timeout on those API calls. For the rest of us, it's a good time to get acquainted with the idea of fail-fast and how no response is better than a slow response.

    So what happens when the API you're accessing via ActiveResource or Net::HTTP (or whatever, really, so long as there's a network socket involved!) becomes unresponsive?

    Unfettered outbound remote connections can kill your server

    ActiveResource (or Net::HTTP or whatever you're using) would block while waiting for the API to return. What did we learn about blocking IO in Operating Systems 101? That your process or thread gets stuck waiting for a response that's what. So now your Mongrel/Thin/Ebb/X web server process basically stuck waiting for a response from an API that might return a response in 30 seconds, or maybe it'll never return at all. It won't be long before all your web server processes get blocked and its time to "kill dash nine" (kill -9) some processes and bring out the 500 error page. Yes, your server has been incapacitated and your website is now basically offline.

    im in ur serverz making thingz better


    Timeouts and you

    If you're using Net::HTTP you can easily safeguard yourself against errors like this by setting a timeout on the socket connection. Like so:

    def fetch_something_from_some_api(uri)
      http = Net::HTTP.new(uri.host, uri.port)
      http.read_timeout = timeout
    
      response = http.start { http.request(Net::HTTP::Get.new("#{uri.path}?#{uri.query}")) }
      response.is_a?(Net::HTTPSuccess) ? do_something_with(response.body) : nil
    rescue Timeout::Error
      logger.warn("Timeout accessing #{uri}: #{$!}")
      nil
    rescue
      logger.warn("Error accessing #{uri}: #{$!}")
      nil
    end

    Take note that you really do need to explicitly rescue from the Timeout::Error (or Interrupt) since it's a child of the Interrupt class and not a StandardError.

    Any good client library should also allow you to specify a timeout on the socket connection so read the API documentation.

    So what's this about ActiveResource?

    So back to ActiveResource: Michael Koziarski was kind enough to commit my 2 patches (this and this) to ActiveResource so now on edge Rails, you can do this:

    class Person < ActiveResource::Base
      self.site = 'http://api.people.com:3000/'
      self.timeout = 5
    end

    This sets the timeout to 5 seconds on the underlying Net::HTTP instance that ActiveResource uses. The default timeout (to be exact, the read_timeout) for Net::HTTP is 60 seconds on most Ruby implementations (that includes MRI). That's far too long if your ActiveResource call is in a public-facing controller!

    If you are using ActiveResource anywhere and haven't safeguarded your application with a timeout, I think it's a good idea to fix it by using edge Rails or at least applying the patch!

    The 2nd ActiveResource patch introduces the ActiveResource::TimeoutError exception that you should rescue from whenever you make an ActiveResource call. When this happens, it's up to your application to decide what to do (like show a placating we're sorry message). At least your application is still able to do something!

  • Living on the edge (of Rails) #17

    Not much going on this week on edge Rails. It does seem however that we do have a new Rails core member, Joshua Peek. Also, the new Rails' bug tracker hosted on Lighthouse is ready for use, so be sure to submit your patches and bug reports there.

    This week’s report covers changes from 14 Apr 2008 to 20 Apr 2008 (the day the corresponding Rails Envy podcast was recorded).

    Conditional caches_page

    The caches_page now takes an :if option for specifying when a page can actually be cached via a Proc. You can now do this, for example:

    caches_page :index, :if => Proc.new { |c| !c.request.format.json? }

    That will only cache your index page if the requested format is not JSON.

    Credit goes to Paul Horsfall for this enhancement.

    Related changeset: http://github.com/rails/rails/commit/14a40804a29a57ad05ca6bffbe1e5334089593a9

    New ActionView::TestCase for testing view helpers

    Remember how you can now use specialized TestCase classes for testing controllers and ActionMailer classes? Now you can do the same with your Rails view helpers with the new ActionView::TestCase class.

    Here's a quick example:

    module PeopleHelper
      def title(text)
        content_tag(:h1, text)
      end
    
      def homepage_path
        people_path
      end
    end
    
    class PeopleHelperTest < ActionView::TestCase
    def setup
      ActionController::Routing::Routes.draw do |map|
        map.people 'people', :controller => 'people', :action => 'index'
        map.connect ':controller/:action/:id'
      end
    end
    
    def test_title
      assert_equal "<h1>Ruby on Rails</h1>", title("Ruby on Rails")
    end
    
    def test_homepage_path
      assert_equal "/people", homepage_path
    end

    Credit goes to Josh Peek for this sweet little enhancement.

    ActiveSupport::Cache's mem_cache_store accepts options

    Even though Memcache-client was added to ActiveSupport recently, it didn't allow you to specify any configuration options beyond just the IP of the memcached server. Now you can pass along more configuration options like so:

    config.action_controller.fragment_cache_store = :mem_cache_store, 'localhost', { :compression => true, :debug => true, :namespace => 'foo' }

    This patch is courtesy of Jonathan Weiss.

    Related changeset: http://github.com/rails/rails/commit/9e1d506a8cfedef2fdd605e4cbf4bf53651ad214

    As always, let me know of any suggestions or how I can improve the Living on the Edge (of Rails) series.

  • Command history meme

    See this.

    history | awk '{a[$2]++}END{for(i in a){print a[i] " " i}}' | sort -rn | head

    4605 cd
    3576 svn
    2141 ls
    1701 ruby
    1308 cap2
    1066 ss
    978 rake
    867 sshb
    688 rm
    635 mate

    cap2 is just my alias for Capistrano 2 (I use both 1.x and 2.x, since I'm lazy to upgrade some old Rails applications, ss is my alias for script/server. sshb is a shortcut for ssh with a certain port number for SSH daemons (i.e. -p 22) that we use in Bezurk (you know, security through obscurity).

  • Living on the edge (of Rails) #16 - Github edition

    A slight change this week since Rails has moved to Git - all changesets are now referenced to Github.

    This week’s report covers changes from 7 Apr 2008 to 13 Apr 2008 (the day the corresponding Rails Envy podcast was recorded).

    A helpers proxy method for accessing helper methods from outside of views

    A helpers method has been introduced to ActionController - you can now access your helpers like so:

    ApplicationController.helpers.simple_format(text)

    Credit goes to 19 year old Josh Peek, one of the more prolific Rails contributors (and most likely to join Pratik in Rails Core imo).

    Related changeset: http://github.com/rails/rails/commit/917423d664038d6791738a73ad1446437dbb71df

    json_escape for escaping HTML entities in JSON strings

    A json_escape (aliased as j, similar to how html_escape is aliased as h) has been introduced by Rick Olson for escaping HTML entities in JSON strings. This is useful if you find yourself emitting JSON strings in HTML pages (e.g. for documenting a JSON API):

    json_escape("is a > 0 & a < 10?")
    # => is a \u003E 0 \u0026 a \u003C 10?

    Of course, since json_escape is aliased as j, you can do this in your ERB templates:

    <%=j @person.to_json %>

    Related changeset: http://github.com/rails/rails/commit/0ff7a2d89fc95dcb0a32ed92aab7156b0778a7ea

    Automatically parse posted JSON content for Mime::JSON requests

    Rails will now accept POSTed JSON content! For example, you can now send a POST to "/posts" of your Post resource like this:

    POST /posts
    {"post": {"title": "Breaking News"}}

    And your create action will automatically parse the POSTed JSON into the request params so that this code below just works:

    def create
      @post = Post.create params[:post]
    end

    Credit goes to Rick Olson.

    Related changeset: http://github.com/rails/rails/commit/4d594cffcfc93b37fad4e423ec8593299e50133c

    schema_migrations table replaces the schema_info table, allowing for interleaved migrations

    A new schema_migrations table for storing which migrations have been run has been introduced, and this replaces the existing schema_info table (which simply keeps track of the last applied schema version). This change makes it possible to add migrations (for example, after you've merged from someone's branch) - when you migrate your database via rake db:migrate, those never-applied "interleaved" migrations will be run. Similarly, when migrating down schema versions, never-applied "interleaved" will be skipped.

    See http://dev.rubyonrails.org/ticket/11493 for more information.

    This useful patch is the brainchild of Jordi Bunster (WWR profile). I'm sure many Rails developers who branch extensively will thank Jordi for this excellent patch!

    Related changeset: http://github.com/rails/rails/commit/8a5a9dcbf64843f064b6e8a0b9c6eea8f0b8536e

    gems:unpack:dependencies Rake task

    Remember how you can now run the gems:unpack Rake task to unpack a gem into your vendor/gems/ directory (this was mentioned in an earlier Living on the Edge)?

    David Dollar (Github profile) has firmed up that patch quite a bit by adding the gems:unpack:dependencies Rake task that unpacks the dependencies of the gems as well. So running:

    gems:unpack:dependencies GEM=activecouch

    would unpack the full Rubygem dependency tree of gems that the activecouch gem depends upon.

    Related changeset: http://github.com/rails/rails/commit/4364c361b599f99bc2345ce4eb2d145b07ed8a0f

    As always, let me know of any suggestions or how I can improve the Living on the Edge (of Rails) series! Now go out there and start forking Rails!

  • Living on the edge (of Rails) #15 - the early edition

    I'm gonna post up the Living on the Edge a little earlier this week because I've found I just can't keep up (when things get busy) with 2 separate sets of notes for the Rails Envy podcast and another for my own blog posts.

    From this week onwards, I'll be following Pratik's suggestion to post about changes as they happen instead of after an entire week so expect a slight change in the format of Living on the edge for next week!

    This week’s report covers changes from 31 Mar 2008 to 6 Apr 2008 (the day the corresponding Rails Envy podcast was recorded).

    Rails.env, Rails.logger, Rails.cache, Rails.root

    This has been a long time coming: You can now use Rails.env instead of RAILS_ENV, Rails.root instead of RAILS_ROOT, Rails.logger instead of RAILS_DEFAULT_LOGGER, and Rails.cache instead of RAILS_CACHE. Not having to use constants is nice because it always felt a bit dirty to have so many hard-coded constants in the Rails namespace.

    Credit goes to Pratik Naik, the newest Rails core team member for this cleanup.

    Related changeset: http://dev.rubyonrails.org/changeset/9180

    Render :partial now works with a collection of heterogeneous elements

    You can now use render :partial to render a collection of different elements and Rails will figure out which partial template to use. For example:

    render :partial => [ monkey, donkey, dramatic_chipmunk ]

    will render the monkey with the 'monkeys/_monkey.html.erb' partial, donkey with the 'donkeys/_donkey.html.erb' partial, and last but definitely not lest, dramatic_gopher with the 'dramatic_chipmunks/_dramatic_chipmunk.html.erb' partial

    This is especially useful if when using STI, where your ActiveRecord model would return objects of different types. The collection [ monkey, donkey, dramatic_chipmunk ] could very well be something returned from your Animal model that has subclasses of Monkey, Donkey and DramaticChipmunk via STI.

    Credit: Zach Dennis of ar-extensions fame.

    Related changeset: http://dev.rubyonrails.org/changeset/9177

    ActiveModel work revived

    It seems like work on ActiveModel (think unified ActiveRecord and ActiveResource behaviors) has been revived, if the latest commits (http://dev.rubyonrails.org/changeset/9171 and http://dev.rubyonrails.org/changeset/9173) by David (DHH) is anything to go by.

    Bugfix: Assigning an invalid has_one association now causes #save to fail on parent

    You could do this before:

    firm = Firm.find(:first)
    firm.account = Account.new # Associate INVALID account
    firm.save # This doesn't fail

    Now that's fixed thanks to Pratik Naik.

    Related changeset: http://dev.rubyonrails.org/changeset/9232

    As usual, any feedback is respectfully appreciated!

  • Living on the edge (of Rails) #14 - the extreme edition. Extremely late.

    This week’s report covers changes from 24 Mar 2008 to 30 Mar 2008 (the day the corresponding Rails Envy podcast was recorded).

    There're lots of big exciting changes this week on edge. Smells like 2.1 soon!

    has_finder gem merged into Rails

    Ryan Daigle has the scoop on the new has_finder-like functionality in Rails so head on over (I won't repeat it here in the interests of DRY).

    Credit goes to Nick Kallen for coming up with the has_finder gem.

    Related changeset: http://dev.rubyonrails.org/changeset/9084

    ActiveRecord: Unsaved attribute changes are now tracked

    You can now ask an ActiveRecord model whether its attributes have been changed (and is unsaved) using the {attr_name}_changed? (magic) method. The {attr_name}_was method will return the original value of the attribute, and the {attr_name}_change method will return an array with 2 members, the 1st being the original value, the 2nd being the changed (and unsaved) value.

    # Change person's name.
    person.name = 'Jason Seifer'
    person.changed?       # => true
    person.name_changed?  # => true
    person.name_was       # => 'jseifer'
    person.name_change    # => ['jseifer', 'Jason Seifer']
    person.name = 'Ceiling Cat'
    person.name_change    # => ['jseifer', 'Ceiling Cat']

    Credit goes to Rails core team member Jeremy Kemper (bitsweat) for this patch (which originated from Jeremy's very own Dirty plugin).

    Related changeset: http://dev.rubyonrails.org/changeset/9127

    ActiveRecord#Base.all/first/last

    Now you can do:

    Post.all.each { |post| # something }
    Post.first
    Post.last

    Kinda like how some other ORMs do (like DataMapper).

    Credit: thechrisoshow and again, Nick Kallen for has_finder.

    Related changeset: http://dev.rubyonrails.org/changeset/9085

    Timestamped Migrations

    This is more useful than it sounds: migrations are now timestamped instead of using incremental numbers in your db/migrate folder. This eliminates the problem of migrations having the same version number when working on a multi-developer Rails app.

    Two new Rake tasks, rake db:migrate:up/down, have been added to allow running of the up and down methods of individual migrations.

    Credit goes to John Barnette, the guy who gave you foxy fixtures.

    Related changeset: http://dev.rubyonrails.org/changeset/9122

    New config.gem option for specifying which gems are required by the application + rake tasks for installing and freezing gems

    Specify which gems are required by your Rails app in your Rails environment.rb! "Vendor everything" supported right in Rails :). An example:

    Rails::Initializer.run do |config| 
      config.gems 'chronic'
      config.gems "hpricot", :version => '0.6', :source => "http://code.whytheluckystiff.net"
      config.gems "aws-s3", :lib => "aws/s3"
    end

    There are also new Rake tasks:

    # List required gems. 
    rake gems 
    
    # Install all required gems: 
    rake gems:install 
    
    # Unpack specified gem to vendor/gems/gem_name-x.x.x 
    rake gems:unpack GEM=chronic

    Credit goes to the prolific Rick Olson for this gem of a patch.

    Related changeset: http://dev.rubyonrails.org/changeset/9140

    ActiveResource #clone

    You can now clone an existing resource:

    gregg = Person.find(1)
    borg = gregg.clone

    This doesn't clone child ActiveResource members though, just the straight up attributes of resource.

    Contribors: Ryan Daigle and thechrisoshow.

    Related changeset: http://dev.rubyonrails.org/changeset/9121

    Big documentation patch

    A big doc patch consisting of all the efforts going on in the doc-rails repository on Github has been committed. Check out Pratik's post for more info on what doc-rails is. Though with Rails moving to Git soon, doc-rails may no longer be necessary!

    Related changeset: http://dev.rubyonrails.org/changeset/9093

  • Installing libmemcached and memcached gem on Ubuntu

    Quick notes for installing libmemcached and Evan Weaver's memcached gem, in case I forget (again):

    1. Get libmemcached source from http://tangent.org/552/libmemcached.html. Configure, make, make install. Make sure you get a version compatible with the Ruby memcached gem by opening the COMPATIBILITY file in the memcached gem files.
    2. gem install memcached
    3. Create a symlink ln -s /usr/local/lib/libmemcached.so.2 /usr/lib/.
    4. Try it out with irb -rubygems, require 'memcached'
  • Living on the edge (of Rails) #13 - Git script/plugin install, has_one :through

    It's time again for some edge Rails love. If you haven't listened to it yet, be sure to check out this week's Rails Envy podcast

    This week’s report covers changes from 17 Mar 2008 to 23 Mar 2008 (the day the corresponding Rails Envy podcast was recorded).

    script/plugin install works for Git repositories

    Yup you can now install plugins from Git repositories simply by running:

    script/plugin install git://github.com/foo/kung_fu

    What this really does is to clone a git repository (sans the entire git history by passing the —depth 1 option) into your plugins directory. If you're looking for a Piston-like way to manage external Git repositories though, you'd still be better off with the development version of Piston (François Beausoleil, is posting frequently on his progress with adding Git support).

    Credit goes to Jack Danger, a long-time Rails contributor for this patch.

    Related changeset: http://dev.rubyonrails.org/changeset/9049

    has_one :through

    It's been a long time coming - now you can do has_one :through (like has_many :through) for your join models. Ryan Daigle has example code on how to use it if you're curious (though really, it's just like has_many).

    Credit goes to the Art of Mission guys and Chris O'Sullivan for pulling it all together.

    Related changeset: http://dev.rubyonrails.org/changeset/9067

    rake tasks time:zones:all, time:zones:us and time:zones:local

    Some timezone-related rake tasks have been added to list the names of time zones recognized by the TimeZone class for the config.time_zone option you can put in your environment.rb. For example

    rake time:zones:local

    * UTC +08:00 *
    Beijing
    Chongqing
    Hong Kong
    Irkutsk
    Kuala Lumpur
    Perth
    Singapore
    Taipei
    Ulaan Bataar
    Urumqi

    Credit: Geoff Buesing (core team member)

    Related changeset: http://dev.rubyonrails.org/changeset/9074

    #number_to_currency helper now supports a :format option

    Now you can do something like:

    number_to_currency(123.50, :unit => "£", :format => "%n %u") 
    # => 123.50 £

    Instead of being stuck with the {currency_code} {amount} format which doesn't work in some locales (such as "42 pounds").

    Related changeset: http://dev.rubyonrails.org/changeset/9052

  • One reason Firefox 3 is going to be awesome - Auto-completion in the Location Bar

    After seriously giving Firefox 3 Beta 4 a try due to Safari 3.1 breaking the Shift key in Gmail (update: as kindly pointed out by some of my readers in the comments, it was Gmail that was broken, not Safari 3.1, doh), I've converted to using Firefox 3 Beta 4 as my primary browser. Before, I was using Firefox 2 as my main web browser, with a Safari window open for Gmail (because I tend to have around 50 tabs open in Firefox 2, and having Gmail among those tabs just kills Firefox 2's performance). So yes, I was using Safari 3 as a "Gmail browser", so to speak (in case you'd thought I'd defected from Firefox despite my history).

    Anyway, enough with all that self-indulgent background - I'm writing this today to rave about how cool this one particular feature, auto-completion in the Location Bar (or the Address Bar, you know, where you type in URLs), in Firefox 3 is. Yes, you probably have already read about it and all the other neat new features and changes in Firefox 3, but I'd love to single out this one because it was the one thing I noticed that made browsing so much better when switching from Firefox 2.

    So what is it? First, a screenshot of it in action:

    Firefox 3 autocompletion in the Location Bar


    Yup, typing into the Location Bar brings up a list of matched webpages from your browsing history. You can see from the screenshot that I entered "javascript" and it brought up not just URLs that started with "javascript" (which is the current Firefox 2 behavior), it also brought up URLs that contained "javascript" anywhere and even pages with titles containing "javascript".

    What's the big deal? Well, one of the worst ("worst" being a relative adjective, relative to my opinion) things that can happen while looking for a page you once visited is forgetting its URL. If I'm lucky I can find it by bringing up the History sidebar in Firefox 2 and searching for it by some keyword or dredging through my browsing history.

    In Firefox 3, I just need to type in some keywords related to the page and most likely it'd come up! Here's how I start posting a new blog entry to this very blog:

    Firefox 3 search history by title in Location Bar


    No need to type "blog.c" and then selecting the correct page from the dropdown while remembering that the new post page is "post-new.php". Less typing, no need to remember URLs. Just page titles and website names. If that isn't useful for you, "you're doing it wrong". What's more, Firefox 3 uses adaptive learning to keep an eye of what you've typed and what you select. After some time Firefox will learn from your choices and provide better suggestions in the autocomplete list. Sweet.

    If you haven't tried out Firefox 3 yet, go grab the latest beta. And then come back to this blog to get Firefox 2 and 3 running at the same time. I'm quite sure you'll thank me for it (because this is not the only great feature in Firefox).

    Update: Phil Wilson pointed to this extension for Firefox 2 that has the same auto-completion functionality.

  • Living on the edge (of Rails) #12

    This week’s report covers changes from 10 Mar 2008 to 16 Mar 2008 (the day the corresponding Rails Envy podcast was recorded).

    Custom JavaScript and stylesheet symbols

    Remember how you can do something like:

    javascript_include_tag :defaults

    and Rails would load all the Prototype JavaScript files and your application.js?

    You can now register your own custom expansion symbol too:

    # In a Rails initializer.
    ActionView::Helpers::AssetTagHelper.register_javascript_expansion :yui => ['yahoo', 'autocomplete', 'calendar']
    
    # In your view.
    javascript_include_tag :yui

    would result in:

    <script type="text/javascript" src="/javascripts/yahoo.js"></script>
    <script type="text/javascript" src="/javascripts/autocomplete.js"></script>
    <script type="text/javascript" src="/javascripts/calendar.js"></script>

    You can do the same with the stylesheet_link_tag by registering a custom expansion symbol via register_stylesheet_expansion.

    This is useful for anyone but in particular plugin developers who have a multiple asset files would appreciate being able to tell users to include JavaScript or stylesheets using a single symbol.

    Warning: This patch currently breaks the default symbols like :all (check out the ticket for more info).

    Related changeset: http://dev.rubyonrails.org/changeset/9016

    Sexy default timestamps in migrations

    Remember how you can say timestamps in a migration and Rails will create the 'created_at' and 'updated_at' columns for you? You can now also do add_timestamps :table_name and remove_timestamps :table_name in your migrations if you decide to add these columns later to a table:

    def self.up
      add_timestamps :posts
    end
    
    def self.down
      remove_timestamps :posts
    end

    Related changeset: http://dev.rubyonrails.org/changeset/9014

    ActiveRecord::Base#find(:last)

    Just like Comment.find(:first), you can now do something like Comment.find(:last). There's some controversy over whether this is bloat, but DHH makes a good case for it with this example:

    class Person
      has_many :comments, :order => 'created_at'
    end
    
    @some_person.comments.find(:last) # => Returns the most recent comment.

    Related changeset: http://dev.rubyonrails.org/changeset/9012

    Database rake tasks fixes

    rake db:create used to ignore the 'charset' and 'collation' options in your database.yml configuration file. This has been fixed so that your created databases now respect those options.

    rake db:drop and rake db:migrate:reset also no longer crash with an unhelpful exception if the database has already been dropped, and instead shows a proper error message.

    Related changeset: http://dev.rubyonrails.org/changeset/9004

    Rails' logger now creates the log directory if it doesn't exist

    This is a blessing to those of us who use version control systems that don't support empty directories (like Git). Rails' default logger (the BufferedLogger), now creates a log/ directory if it doesn't already exist. This should save you the step of creating/symlinking a log/ directory (or symlinking) on deploy.

    Related changeset: http://dev.rubyonrails.org/changeset/9013

    String#squish is faster

    A faster implementation of the String#squish (and String#squish!) core extension has been committed.

    Related changeset: http://dev.rubyonrails.org/changeset/9015

    The #excerpt TextHelper no longer includes 1 character too many

    Turns out that the #excerpt helper method was consistently including an extra character. This has been fixed.

    Related changeset: http://dev.rubyonrails.org/changeset/9030

    As usual, let me know of any inaccuracies or any suggestions you may have in the comments!

  • UTF8 encoding with Ruby Sequel (and MySQL)

    Just a quick note to self. If you need to store Unicode and are using Sequel, make sure you:

    • Create the database with a default charset of UTF8: CREATE DATABASE my_db DEFAULT CHARSET utf8;. I think then any tables you create uses the default charset of utf8 unless otherwise specified.
    • Pass an :encoding option to Sequel's MySQL adapter:
      DB = Sequel.mysql 'my_db', :user => 'root', :password => '', :host => 'localhost', :encoding => 'utf8'

    The Sequel documentation and wiki were quite unhelpful but reading code (see the connect method definition) always helps!

  • Living on the edge (of Rails) #11

    This week’s report covers changes from 3 Mar 2008 to 9 Mar 2008 (the day the corresponding Rails Envy podcast was recorded).

    Improve performance on :include/:conditions/:limit queries by selectively joining in the pre-query

    This is another ActiveRecord performance boost related to the pre-loading any eager-loaded :includes mentioned previously. Basically what this patch does is to only join referenced tables when needed. It does this by checking the :conditions and :limit options to determine whether a table should be joined or can be left out and pre-loaded instead. You can find out more in tickets #9560 and #9497.

    Credit goes to Gabe da Silveira (dasil003 on Trac) for this awesome performance patch.

    Related changeset: http://dev.rubyonrails.org/changeset/8977

    Better error message for type errors when parsing request parameters

    Now when you pass an incorrectly formed request (e.g. GET, POST) parameter to your controller, it will raise an exception that includes a friendlier error message that indicates exactly what you passed to it. This is helpful when trying to debug whether you constructed your form correctly.

    Contributors: Chad Humphries and matt.

    Related changeset: http://dev.rubyonrails.org/changeset/8986

    Make MimeResponds::Responder#any work without explicit types.

    Here's something I didn't know: you can actually use any in your respond_to blocks as a catch-all response. E.g.

    respond_to do |format|
      format.html do
        redirect_to :action => 'login'
      end
      format.any(:js, :xml) do
        request_http_basic_authentication 'Web Password'
      end
    end

    Contrary to its name, using 'any' actually requires you to pass a list of types to respond to. This has been enhanced now so that if you don't pass any arguments, it'll function as a real catch-all.

    respond_to do |format|
      format.any do
        request_http_basic_authentication 'Web Password'
      end
    end

    Credit goes to Joshua Wehner for this patch.

    Related changeset: http://dev.rubyonrails.org/changeset/8987

    Add readonly option to has_many :through associations

    Turns out the :readonly option for associations mentioned earlier was left out for has_many :through associations. This oversight has been fixed.

    Thanks to Emilio Tagua for this bugfix.

    Related changeset: http://dev.rubyonrails.org/changeset/8989

    As usual, let me know of any inaccuracies or any suggestions you may have in the comments!

  • Living on the edge (of Rails) #10

    Another awfully sleepy week on Rails edge. Though by the time I had sent over the notes to Gregg Pollack and Jason Seifer of the awesome Rails Envy podcast, there has been some nice changes (that I'll be mentioning next week, but there's absolutely nothing stopping you from checking those out yourself).

    In other interesting news, Pratik Naik (lifofifo), a long-time Rails contributor has been given commit rights to Rails. Congrats Pratik, well-deserved and it has been long overdue in my opinion! Pratik keeps an interesting blog at http://m.onkey.org/ (where he's not afraid to say "fuck" in his posts and his code) and hangs out an awful lot on #rubyonrails and #rails-contrib on IRC.

    As a sidenote, Capistrano 2.2.0 was released by Jamis last week.

    This week’s report covers changes from 25 Feb 2008 to 2 Mar 2008 (the day the corresponding Rails Envy podcast was recorded).

    Time#end_of_XXX methods

    A bunch of Time core extension methods have been added. These are: Time#end_of_day, Time#end_of_week, Time#end_of_year, and Time#end_of_quarter, which all return exactly what you expect them to return:

    Time.now.end_of_week # => Sun Mar 09 00:00:00 0800 2008

    Credit goes to Juanjo Bazán (a former Rails Hackfest winner) and Tarmo Tänav for contributing this patch.

    Related changeset: http://dev.rubyonrails.org/changeset/8934

    Date helpers now accept HTML options

    ActionView's date helpers (such as date_select, time_select, select_datetime) did not support any HTML options, unlike the other helpers (like f.text_field(:name, :class => 'my_css_class', :size => 20)). This inconsistency has been fixed and you can now finally do:

    <%= date_select 'user', 'birthday', :order => [:day], :class => 'my_css_class' %>

    Murray Steele (h-lame on the Rails Trac) and Jakob Skjerning contributed this patch.

    Related changeset: http://dev.rubyonrails.org/changeset/8968

    No need for explicit respond_to for RJS templates

    ActionController has been changed so that JS requests will automatically render action.js.rjs files without the need to specify an explicit respond_to block. This means that your .rjs files work the same way as your .html.erb files - just put them in the right place and Rails will use it.

    Related changeset: http://dev.rubyonrails.org/changeset/8956

    Bugfixes

    • http://dev.rubyonrails.org/changeset/8937 - Prevent Rails from crashing when trying to deserialize an XML representation of a model named "Type" (using Hash#from_xml). Contributed by Juanjo Bazán and Isaac Feliu.
    • http://dev.rubyonrails.org/changeset/8942 - Fix eager loading so that it doesn't pull in duplicate records in some cases. Contributed by Catfish.

    As usual, let me know of any inaccuracies or any suggestions you may have in the comments!

  • Living on the edge (of Rails) #9 - the sleeper edition

    It's been a slow week on the Rails trunk this week in terms of exciting changes.

    This week’s report covers changes from 18 Feb 2008 to 24 Feb 2008 (the day the corresponding Rails Envy podcast was recorded).

    ActiveResource::Base now accepts user and password configuration

    You can now set the user and password for HTTP authentication in your ActiveResource models:

    class Person < ActiveResource::Base
      self.site = 'http://example.com/'
      self.user = 'konata'
      self.password = 'password'
    end

    This is a better way to specify authentication credentials than the current way of doing it by putting it all in the site (e.g. self.site = 'http://konata:[email protected]/').

    Related changeset: http://dev.rubyonrails.org/changeset/8891

    script/plugin install now supports SVN export

    You can now pass the -e/--export option to script/plugin install to do an Subversion export of the plugin. This allows you to then check in the plugin's files into your own repository. Though seriously, if anyone really wants to do this, I'd suggest they use Piston. That is, for those of us still using Subversion rather than Git.

    Related changeset: http://dev.rubyonrails.org/changeset/8921

    Bug fixes for associations preloading

    There've been some bug fixes the associations preloading change mentioned in Living on the edge #5:

    • http://dev.rubyonrails.org/changeset/8894 - Fixed a bug where associations that were preloaded (using nested :includes) errors out when the associations return nil (i.e. when there are no associated records).
    • http://dev.rubyonrails.org/ticket/11154 - Fixed a bug where nested includes were assuming that all records are from the same class. This is not true for polymorphic associations, causing problems when they were being used.

    Yup, that's it for this week. As usual, let me know of anything I might have missed or any suggestions you may have in the comments!

  • Shokudo Japanese Food Bazaar (in Singapore) - don't bother

    Subscribers who read my blog for Ruby- or Mozilla-related posts should ignore this post, it's another of those blogging as catharsis posts. To my defence, I haven't done one of those for a really long time!

    If you're staying in Singapore and looking to try out the food at Shokudo Japanese Food Bazaar after it was featured in the local newspaper, my advice to you is, "don't bother!" Why? In 2 words, horrible service.

    Despite some positive reviews (yes, the decor is not too bad), my dining experience at Shokudo consisted mostly of getting the service crew's attention (and failing spectacularly despite standing right at the counter) and being scowled at or avoided by most of the staff (which is not good, since being a bazaar-style restaurant, you needed to place your order at each stall manned by different members of the service crew).

    My friends and I were speculating that either the staff got scolded that day (and reacted negatively to that), or they were just too stuck up because business was good. Either way, it was just terrible. When leaving, I even got rudely reminded that I'd left that one of those things they gave you to reserve a table. Did I mention the service was horrible? (Granted, there were 2 counters where the staff were attentive and actually quite friendly - this was at the macha drink stall and the katsu curry counter.)

    What about the food? With such bad service I would say no one shouldn't really care about the quality of the food - thankfully enough, the food is of a consistently mediocre level to even bother!

    Has anyone had a similar experience at Shokudo, or was it just our unlucky day?

  • Living on the edge (of Rails) #8 - the code optimization edition

    Here's this week's update of the important changes that have been happening on edge Rails. There were lots of performance patches this week, credit mostly due to the guys from Acunote and their latest blog post on optimizing Rails.

    This week’s report covers changes from 11 Feb 2008 to 17 Feb 2008 (the day the corresponding Rails Envy podcast was recorded).

    ActiveRecord::Base#attributes-related optimizations

    The ActiveRecord::Base#attributes_before_typecast no longer clones attribute unnecessarily.

    Related changeset: http://dev.rubyonrails.org/changeset/8858

    ActiveRecord::Base#attributes now no longer supports options. The ActiveRecord::Base#attributes method used to support :only and :except options, which are actually unnecessary since it returns a Hash. You can use Hash#slice or Hash#except (both ActiveSupport core extensions) directly on the hash.

    Related changeset: http://dev.rubyonrails.org/changeset/8863

    ActiveRecord associations optimized by avoiding named block arguments

    Alexander Dymo is at it again optimizing ActiveRecord performance, this time by avoiding passing named block arguments. Turns out that it is faster and more memory efficient in Ruby to pass blocks like this:

    def foo
      bar { |*block_args| yield(*block_args) if block_given?
    end

    instead of passing the block around like so:

    def foo(&&block)
      bar(&block)
    end

    Occurrences of the more expensive way of passing blocks have been replaced in the ActiveRecord associations code so associations are lighter to use.

    For a more detailed write-up, check out Alexander's blog entry.

    Related changeset: http://dev.rubyonrails.org/changeset/8865

    ActiveRecord associations optimized by using symbol callbacks instead of string callbacks

    Again from Alexander Dymo, this patch also improving ActiveRecord association performance. Internally, ActiveRecord associations code uses a lot of string callbacks (e.g. before_save "some_ruby_code_to_eval"). It turns out that that is significantly more expensive compared to simply using Module#define_method because when the callback string is evaluated, the binding needs to be passed along. Passing the binding takes up memory (which is bad, of course).

    The internal associations code has been changed to use that define_method now, and if you're using a lot of associations, you would save megabytes of memory! (Alexander's blog post has more detailed benchmarks.)

    Remember, when writing your own before and after filters, not to use a string that needs to be evaled!

    Related changeset: http://dev.rubyonrails.org/changeset/8867

    Improve ActiveRecord::Base#save performance by avoiding repeated calls to ActiveRecord::Base#connection

    Another Alexander Dymo performance patch. This particular patch improves performance by getting the connection object just once and calling it directly, instead of needlessly calling several times. This should improve ActiveRecord::Base#save performance.

    Related changeset: http://dev.rubyonrails.org/changeset/8871

    ActiveRecord associations now support the :readonly option

    ActiveRecord::Base#find supports the :readonly option to retrieve records that were read-only (i.e. you couldn't save them). Now you can do the same for associations, like so:

    has_many :comments, :readonly => true

    This allows you to protect associated records from being saved.

    Related changeset: http://dev.rubyonrails.org/changeset/8864

    String#squish and String#squish! to remove consecutive chunks of whitespace

    A String#squish extension has been added that trims whitespace from both ends of a string, and compressing consecutive whitespace groups within the string into 1 space each.

    For example,

    @description = %{ String#squish() allows me to write long strings
      inside my Ruby code, without having to worry about indentation,
      the page margin, spaces, tabs or newlines. "double" or 'single'
      quotes are both fine, and #{interpolation} works. }.squish
    

    Related changeset: http://dev.rubyonrails.org/changeset/8878

    As always, let me know of any inaccuracies or any suggestions you may have in the comments, see you guys next week!

  • Living on the edge (of Rails) #7 - improved ActiveRecord::Base#attributes, time zone support

    It's that time of the week again (every Wednesday for the overly observant, coinciding with the release of the Rails Envy podcast) for some choice updates of what changes have been happening on edge Rails. This week's report covers changes from 4 Feb 2008 to 10 Feb 2008 (the day the Rails Envy podcast was recorded).

    More efficent ActiveRecord::Base#attributes

    ActiveRecord::Base#attributes returns a hash of all attributes with clones of their objects as values. This unnecessary cloning is expensive, and has now been removed. ActiveRecord::Base#attributes now returns a hash of values of the attributes.

    Thanks to Juanjo Bazan, there have been 2 further patches (after the podcast was recorded) related ActiveRecord::Base#attributes that further improves ActiveRecord's performance: removal of cloning from ActiveRecord::Base#attributes_before_typecast, and removing options (:only and :except options, specifically) from ActiveRecord::Base#attributes.

    Related changeset: http://dev.rubyonrails.org/changeset/8824

    ActiveResouce::Base#exists? now uses HTTP HEAD instead of a GET

    A HTTP HEAD request is really all that is needed to check for existence (200 == exists, 404 == doesn't exist). This has been changed to HEAD from a GET request, which is more efficient since all we retrieve are the HTTP headers and there's no unnecessary instantiation of the ActiveResource object.

    Related changeset: http://dev.rubyonrails.org/changeset/8827

    Time zone support for ActiveRecord, and config.time_zone property for specifying a default Time Zone

    ActiveRecord models now have time zone-aware attribute readers and writers. Times returned from ActiveRecord attribute readers now have a time_zone attribute themselves, allowing time zone-aware calculations.

    You can now configure the default Time.zone value with config.time_zone in your environment.rb. Setting this will enable time zone-awareness for ActiveRecord models.

    Related changeset: http://dev.rubyonrails.org/changeset/8806

    A Template class has been introduced to ActionView

    As part of the ActionPack refactoring that's still ongoing, a ActionView::Template class has been introduced to encapsulate all the data relevant to the currently rendered template. This is largely an internal refactoring so Rails users shouldn't be affected.

    Related changeset: http://dev.rubyonrails.org/changeset/8805

    Concrete sweeper classes

    You can now use an explicit class when declaring a cache_sweeper. E.g.

    class ListsController < ApplicationController 
      caches_action :index, :show, :public, :feed 
      cache_sweeper Sweepers::List, :only => [ :edit, :destroy, :share ] 
    end

    Related changeset: http://dev.rubyonrails.org/changeset/8819

    As always, let me know of any inaccuracies or any suggestions you may have in the comments - this week's report is a little rushed because I'm a little late in posting it up!

  • Living on the edge (of Rails) #6 - better performance, Git support, and more

    Here's another week of noteworthy updates on the Rails trunk. Thanks once again to the Rails Envy podcast for featuring my updates, and even bigger thanks to the Rails contributors and committers who make things happen. This week's Living on the Edge was covered in Rails Envy podcast #17.

    The company I work for, Bezurk (yeah we know some of you hate the name "Bezurk" - that's going to change), is currently looking for a Ruby developer to join our ranks. This person will be working mainly on Ruby-based web development projects. As we all know, "Rails doesn't scale" (delivered in Jason Seifer-style - meaning it's just a joke!) so we'd love anyone who's up for a challenge and who's also willing to work outside the Rails comfort zone (our current favorite: Ramaze). Our Ruby team currently comprises 3 experienced developers and 1 up and coming Rubyist.

    We're active Open Source contributors (Arun released ActiveCouch recently), while I am a Rails contributor (I'm 'chuyeow' on the Rails Trac) myself.

    If all this sounds interesting enough to you, do check out the full job description and drop us an email. Yes, we're serious when we said "+1 for DotA/Counterstrike skills)". There's a catch though - you have to work on location at our office in Singapore.

    Anyway, let's get back on topic - this weeks’ report covers changes from 28 Jan 2008 to 3 Feb 2008 (the day the Rails Envy podcast was recorded).

    Saving, creating and updating ActiveRecord objects is more efficient

    Thanks to Gleb Arshinov and Alexander Dymo with their blog post and patches about improving Rails performance by approaching it via minimizing garbage collection, saving, creating, and updating ActiveRecord objects is now significantly more efficient.

    The expensive ActiveRecord::Base#attributes method is no longer called internally, so there's no unnecessary cloning of attribute values. Less memory usage means less garbage collection, which means better performance!

    Related changeset: http://dev.rubyonrails.org/changeset/8770

    SQL calls and rendering are faster

    This patch is courtesy of Gleb Arshinov and Alexander Dymo too.

    Rails surrounds every SQL call and ActionView::Base#render with the ruby method Benchmark#realtime so that it can print out some benchmarking numbers. Benchmark#realtime allocates an unnecessary 45k of memory per call (who knew!). This has been monkey patched in Rails to a leaner implementation, reducing memory usage and increasing performance.

    Related changeset: http://dev.rubyonrails.org/changeset/8771

    MySQL adapter slight optimization

    Table and column names in the MySQL adapter are now cached in instance variables, reducing memory allocations (and thus the need for GC).

    Related changeset: http://dev.rubyonrails.org/changeset/8794

    script/generate does Git!

    script/generate now allows you to pass the '--git' (or '-g') option to add the generated files to git (much like how you could do the same with Subversion with '-c' or '--svn'). This patch is the work of Steven Soroka so nice work Steven!

    Related changeset: http://dev.rubyonrails.org/changeset/8772

    map.resources :products, :as => 'something_else'

    Here's something that will make non-english Rails developers happy: you can now do, for example,

    map.resources :comments, :as => 'comentarios'

    so that you can keep having your model names independent of what URLs you'd actually like to appear on the site.

    The above map.resources means that you can keep your resource and model names in English (Comment), while having URLs like this: /comentarios/123.

    Related changeset: http://dev.rubyonrails.org/changeset/8785

    :index option for form_for and fields_for to simplify multi-model forms

    Inspired by a Railscasts episode where Ryan Bates mentions how ugly it is to have to pass :index => nil to prevent #text_field (and other form helpers) from inserting the model's ID in the input name (e.g. project[task_attributes][123][name] when what you really want is project[task_attributes][][name]).

    Meaning instead of

    <% fields_for "project[task_attributes][]", task do |f| %>
      <%= f.text_field :name, :index => nil %>
      <%= f.hidden_field :id, :index => nil %>
    <% end %>

    you can now do:

    <% fields_for "project[task_attributes][]", task, :index => nil do |f| %>
      <%= f.text_field :name %>
      <%= f.hidden_field :id %>
    <% end %>

    Related changeset: http://dev.rubyonrails.org/changeset/8786

    No more emails in your production logs

    You'd probably have noticed that ActionMailer logs entire email messages in the log, even in production. Well, it's been changed to be less verbose now. At the :info log level, only the recipients are logged. You can still see the entire email at the :debug log level (which is the default for development environment).

    Related changeset: http://dev.rubyonrails.org/changeset/8781

    As always, let me know of any inaccuracies or any suggestions you may have in the comments. Oh, and if you liked this, do me a favor by submitting it to Ruby Reddit.

  • ActiveCouch - a CouchDB library in the spirit of ActiveResource and ActiveRecord

    My fellow Rubyist at work, Arun Thampi, has released ActiveCouch, a Ruby library for CouchDB (if you don't know what CouchDB is, read this). Check out the official blog announcement of ActiveCouch.

    ActiveCouch tries to be an ActiveResource and ActiveRecord (yes, there is even an ActiveCouch::Migration class for creating CouchDB views) equivalent for representing CouchDB documents in Ruby. I wish I could write some coherent code examples here but I am not that up to date with the latest changes in ActiveCouch (despite my name being tagged as a committer to ActiveCouch, it is largely Arun's awesome little baby).

    Never fear though, since Arun and I are chowing down our own dog food - we're currently porting several gigabytes worth of hotel data from our hacked together XML and Ferret-based library to use the ActiveCouch library. I'm sure we'll get a more coherent API after this exercise, since we are no longer relying on specs and mocks, but working on a Real Application.

    If you decide to try out ActiveCouch, do let us know of any feedback or bug reports at the ActiveCouch Google Code site. ActiveCouch is Open Source, so if you've patches that's even better!

  • Frequent disconnects with your Linksys router? Try custom firmware

    I have a rather old Linksys WRT54G (v2.2) wireless router that used to disconnect rather frequently for no apparent reason. It is almost guaranteed to get disconnected when I go crazy with using Axel to download files.

    The worse part of the entire "router disconnected" thing is how it takes down my DSL connection (provided by my ISP, Singnet Broadband) with it - trying to reconnect the DSL modem takes upwards of 3 minutes (I imagine it takes awhile for the ISP's network to realize that my DSL connection is really down).

    A few months ago, I finally couldn't stand it any longer and decided to install custom firmware on the router to see if it fixes the problem (which is quite prevalent). I was unwilling to go down this route considering my bad luck tinkering with firmware and drivers (having broken a cable modem and a near-functional Linux install before).

    Linksys WRT54G


    Anyway, long story short: I installed Tomato Firmware and am enjoying rock solid stability! I can run Axel (10 simultaneous connections) with 2 torrents running and still surf comfortably and even download more stuff through the plain old web browser interface.

    Long story, er, long: I tried out DD-WRT first (since we use the same at work), didn't like its bloated interface, and especially didn't appreciate reading (old) allegations about how DD-WRT's author tried to screw the Open Source community over. So I flashed the firmware to Tomato Firmware and ended up loving its minimalist interface and faster restart cycles (most of the time, since Tomato tries to restart as few services as it needs to when you make configuration changes, rather than reboot the entire router like DD-WRT does).

    So, if you're experiencing the same troubles with your Linksys router and haven't had taken the leap of faith with custom firmware, I really recommend you do so. Big warning: Read the installation instructions! Not all WRT54G routers are compatible with Tomato Firmware.

    More reading: Lifehacker has a walkthrough of Tomato Firmware's features (and also one for DD-WRT).

  • Living on the edge (of Rails) #5 - better eager loading and more

    Another week of edge Rails changes, featured on the Rails Envy podcast. This weeks’ report covers changes from 21 Jan 2008 to 27 Jan 2008 (the day the Rails Envy podcast was recorded).

    Eager loading :includes does pre-loading

    The current gem Rails behavior when loading associations with something like

    Author.find(:all, :include => [:posts, :comments])

    is to make a big query with multiple joins that fetches all the associations in the same query (for you RDBMS geeks, the "cartesian product").

    Frederick Cheung (fcheung on the Rails Trac) has contributed an impressive optimization to this by pre-loading the associations rather than trying to eager loading with one big query. So a find like this:

    Author.find(:all, :include => [:posts, :comments])

    would first load all the authors, then all the posts, and then all the comments in 3 smaller, faster queries.

    The main benefit of this (depending on your data): Running X simple queries (to fetch authors, then posts and comments) rather than one mega-query that joins all the associated tables is faster most of the time. The result set is often smaller as well.

    More details can be found at ticket #9640 on the Rails Trac.

    Related changeset: http://dev.rubyonrails.org/changeset/8672

    composed_of aggregations can now be used in finder conditions

    You can now use value objects that you've previously specified that your model is composed_of in the finder conditions hash. E.g. if you have a Customer that has a balance aggregation:

    class Customer < ActiveRecord::Base
      composed_of :balance, :class_name => "Money", :mapping => %w(balance amount)
    end

    You can now pass a Money value object to Customer#find:

    Customer.find(:all, :conditions => { :balance => Money.new(20, "USD") })

    Convenient.

    Related changeset: http://dev.rubyonrails.org/changeset/8671

    New helper: label_tag

    A label_tag helper has been missing for quite awhile since the label helper was added. No longer:

    label_tag 'name'
    # => <label for="name">Name</label>
    
    label_tag 'name', 'Your name'
    # => <label for="name">Your name</label>

    Related changeset: http://dev.rubyonrails.org/changeset/8685

    New ActiveSupport class: ActiveSupport::TimeWithZone

    A new ActiveSupport::TimeWithZone class has been introduced to make timezone support in Rails easier. Ryan Daigle has a good writeup on this.

    A word of warning though: this is still work in progress, as Geoff Buesing, the core team member responsible for these timezone changes, has more to add on how his plans for the timezone puzzle in the comments.

    Related changeset: http://dev.rubyonrails.org/changeset/8696

    map.root can be easily aliased

    map.root now accept a single symbol as an argument to declare an alias. Just a little something to keep your routes a little more DRY, e.g.:

    map.new_session :controller => 'sessions', :action => 'new' 
    map.root :new_session

    Related changeset: http://dev.rubyonrails.org/changeset/8738

    As usual, let me know of any inaccuracies or any suggestions you may have in the comments!

  • Trying code blocks in Ruby (aka Prototype's Try.these)

    Prototype's Try.these is a really useful bit of code (though I doubt it sees much application outside of JavaScript libraries). I have this evil bit of code somewhere, for example:

    var results = Try.these(
      function() { return response.responseText.evalJSON(true); },
      function() { return eval('(' + response.responseText + ')'); }
    );

    Evilness personified in its evaled glory, but that's besides the point here.

    Just for fun, I tried to do this in Ruby, just to see how easy it was, and came up with this try method:

    module Kernel
      def try(*these)
        raise ArgumentError, 'try requires at least 2 arguments' if these.size <= 1
        fallback = these.pop unless these.last.respond_to?(:call)
        these.each { |candidate| begin return candidate.call rescue next end }
        fallback || raise(RuntimeError, 'None of the given procs succeeded')
      end
    end

    Which you can (ab)use like this:

    try(
      Proc.new { open('http://finance.yahoo.com/foo/') },
      lambda { open('http://finance.google.com/bar/') },
      proc { open('http://finantiq.com/flomp/') },
      :fallback
    )

    Real world applications would be, like the example above, retrieving content from several unreliable websites to retrieve currency rates.

    You can find the specs for Kernel#try here: http://pastie.org/144339.

    So the question now is, is there a better way to implement this, or a more idiomatic Ruby way?

  • Songza - finally an easy way to listen to boy band music

    Songza's quite nice, especially after us non-US users lost access to Pandora. Now I can finally listen to some boy band music when I feel like it (because I don't have any in my library).

  • Rails and penis enhancements

    Funniest bug report I've seen on the Rails issue tracker: http://dev.rubyonrails.org/ticket/10919. Be sure to read the comments, and the resolution:

    I appreciate that penis enhancements are the norm for most of the commenters here, but their use is definitely not widespread enough to justify fixing this.

  • Living on the edge (of Rails) #4 - faster routes, easier form partials

    It's time again for your weekly dose of what's new in edge Rails. This weeks' report covers changes from 14 Jan 2008 to 20 Jan 2008 (the day the Rails Envy podcast was recorded).

    Route recognition is faster

    Rails' route recognition has been optimized and is significantly faster especially for applications using many resources (i.e. via map.resources in your config/routes.rb). Big thumbs up to Oleg Andreev (aka oleganza) who also wrote a detailed post about it. You can also find out more about how this works in the code comments.

    Related changesets: http://dev.rubyonrails.org/changeset/8674 and http://dev.rubyonrails.org/changeset/8652.

    render :partial and forms

    Instead of doing:

    <% form_for(:person) do |f| %>
      <%= render :partial => 'form', :locals => { :form => f } %>
    <% end %>

    you can now do:

    <% form_for(:person) do |f| %>
      <%= render :partial => f %>
    <% end %>

    Convenient. This also works with your custom form builders:

    <% form_for(:person, :builder => LabellingFormBuilder) do |f| %>
      <%= render :partial => f %>
    <% end %>

    The partial template rendered is then people/_labelling_form and the local variable in the partial is labelling_form.

    Related changeset: http://dev.rubyonrails.org/changeset/8646.

    Callbacks refactored into ActiveSupport::Callbacks

    All that duplicated callback code in ActiveRecord, ActionController::Dispatcher, and in test case setup and teardown methods have been extracted into the ActiveSupport::Callbacks module.

    Less duplication means more robust code so yay!

    Related changeset: http://dev.rubyonrails.org/changeset/8664.

    ActiveRecord test suite gets a makeover

    The ActiveRecord test suite has become something of a maze jungle in recent times, with no clear hierarchy of how things are organize. It's not that easy to find what you are looking for especially for newcomers trying to contribute to ActiveRecord. Thankfully, someone (John Barnette - the foxy fixtures guy) has stepped up and cleaned things up quite a bit. Fixtures, test cases, fixture models, etc. are now in their own directories and from looking at the directory hierarchy one should be able to know where to put what at a glance.

    Related ticket: http://dev.rubyonrails.org/ticket/10742.

    Bugfixes

    * redirect_to nil no longer explodes (but raises a friendlier 'cannot redirect to nil' exception) (related changeset: http://dev.rubyonrails.org/changeset/8633).

  • Seen in the Rails source - Orcl nds shrt indx nms

    Oracle needs short indexes


    Hah.

  • Living on the Edge (of Rails) #3 - X-Sendfile and many other sexy enhancements

    Edge Rails saw a barrage of refinements and enhancements this week and there's even talk about Rails 2.1 being just a little around the corner. There's also been a flurry of contributions to making Rails more thread-safe and performance optimizations (all still work in progress at the moment) - it's really nice to see how the Rails community is looking seriously at performance post-Rails 2.0 (not that they weren't serious about performance before Rails 2.0!).

    This week’s report covers changes from 7 Jan 2008 to the day the corresponding Rails Envy podcast was recorded (13 Jan 2008).

    X-Sendfile gets easier with send_file :x_sendfile => true

    If you only have a vague idea of what it is (like, X-Sendfile is that thing that lets you send files, right?), you'd do well to read this helpful explanation. (In short, X-Sendfile allows you to send static files directly and more performantly to HTTP clients and bypassing your app server process.) You don't have to set those headers yourself or install the plugin mentioned in that article though, since edge Rails has an augmented send_file helper that does all the legwork for you.

    All you have to do now is to this:

    send_file '/path/to.png', :x_sendfile => true, :type => 'image/png'

    Rails will set the X-Sendfile header for you (i.e. response.headers['X-Sendfile'] = '/path/to.png') and sends a HEAD response (empty response body) with the file path, allowing your web server (Apache, Lighttpd) to serve the static file and not involve your Rails processes (such as your precious, perpertually resource-starved mongrels).

    This is really nice if you are serving lots of static files (downloadable images and documents come to mind) and are not already using X-Sendfile or doing it the "long" way (by setting the response header and then render :nothing => true).

    Related changeset: http://dev.rubyonrails.org/changeset/8628

    ActionController::Base.asset_host proc now takes the request object as an optional 2nd argument

    Rails 2.0.2 allowed you to set ActionController::Base.asset_host to a proc that took a single source argument (as detailed in my earlier post). People were still running into problems with asset hosting though, particularly while trying to serve assets from an SSL-protected page (i.e. HTTPS requests). When serving SSL-protected pages, you'd need to either have an SSL certificate for each asset host(!) or live with a mixed media warning (which browsers report when you have SSL and non-SSL content on the same page).

    The asset_host proc now takes the entire controller request instance as an optional second argument. This allows you to either use a single asset host or to disable asset hosting for SSL requests. For example, this proc below practically disables asset hosting:

    ActionController::Base.asset_host = Proc.new { |source, request| 
      if request.ssl? 
        "#{request.protocol}#{request.host_with_port}" # Disable asset hosting.
      else 
        "#{request.protocol}assets.example.com" # Use asset host.
      end 
    }

    Related changeset: http://dev.rubyonrails.org/changeset/8578

    Nicer way to access request headers

    There's now a nicer way to access headers - instead of request.env["HTTP_CONTENT_TYPE"] you can now do request.headers["Content-Type"] (or request.headers["content-type"] (note all lowercase) or even good old request.headers["HTTP_CONTENT_TYPE"]. How convenient - I often worry about getting the casing right (was it 'Content-Type' or 'Content-type'?) and now I can be sure it doesn't matter!

    Related changeset: http://dev.rubyonrails.org/changeset/8625

    ActiveSupport::TestCase and friends now support declarative setup and teardown callbacks

    ActiveSupport::TestCase (and the ActionController::TestCase and ActionMailer::TestCase subclasses) now support declarative setup and teardown callbacks that are called before setup/after teardown. For example,

    class FooTestCase < ActiveSupport::TestCase
      setup :run_this_first, :then_run_this do
        # Run this last.
      end
      teardown :remove_tmp_files, :undef_constants
    
      def run_this_first
      end
    
      def then_run_this
      end
    
      def remove_tmp_files
      end
    
      def undef_constants
      end
    end

    will run run_this_first, then_run_this, and finally the stuff that's in the given block to 'setup' before running the actual setup method. The teardown callbacks are done in reverse order, meaning undef_constants is called, and then remove_tmp_files (and then the actual teardown method).

    Related changeset: http://dev.rubyonrails.org/changeset/8570

    TMail updated to 1.2.1

    The bundled TMail library (that ActionMailer uses) has been updated from 1.1.0 to 1.2.1. This new version currently maintained by Mikel Lindsaar promises bugfixes and greater test coverage. For more details, see https://rubyforge.org/frs/shownotes.php?group_id=4512&release_id=18049.

    Mikel has been working with Rails core to get Tmail Ruby 1.9-compatible. Good work Mikel!

    Related changeset: http://dev.rubyonrails.org/changeset/8620

    Bug fixes

    • ActionController::UrlWriter respects the relative_url_root. For those of you who don't know, the ActionController::UrlWriter module is a convenient mixin that allows you to use url_for and your named routes in arbitrary classes (such as in your mailers or in a BackgrounDRb worker).

      class SitemapWorker < BackgrounDRb::MetaWorker
        include ActionController::UrlWriter
      
        # Access your named routes and the url_for helper.
        url = page_url(:permalink => 'foo')
      end

      And now it respects your relative_url_root instead of blissfully ignoring it.

      Related changeset: http://dev.rubyonrails.org/changeset/8616

    • render :text => nil and render :text => false work properly now instead of inexplicably trying to render a file. This was a big gotcha when using render :text => some_variable where some_variable could potentially be false or nil in some cases - Obie Fernandez has a writeup on this long-standing bug. Related changeset: http://dev.rubyonrails.org/changeset/8577

    Optimizations

    There're significant performance improvements for classic fixtures with HABTM data, due to some slightly clever caching model classes, and instantiating fixtures from the model class, instead of expensive constant lookups from the name of the class.

    Related changesets: http://dev.rubyonrails.org/changeset/8560 and http://dev.rubyonrails.org/changeset/8561

  • Retrying code blocks in Ruby (on exceptions, whatever)

    I am not certain whether the ability to retry a code block when encountering exceptions was a feature available in Ruby, but I certainly couldn't find anything on that topic (what I did find were mostly about the retry keyword for iterator loops).

    Before you ask why I need this, the motivation for this was because I was getting intermittent HTTP errors (503s mostly) trying to connect to a web service. Turns out it's really easy in Ruby to implement a retryable method that does something like this:

    retryable(:tries => 5, :on => OpenURI::HTTPError) do
      open('http://example.com/flaky_api')
      # Code that mashes up stuff for your "social networking" site.
    end

    Here are the Kernel#retryable specs (pastie).

    And the code:

    # Options:
    # * :tries - Number of retries to perform. Defaults to 1.
    # * :on - The Exception on which a retry will be performed. Defaults to Exception, which retries on any Exception.
    #
    # Example
    # =======
    #   retryable(:tries => 1, :on => OpenURI::HTTPError) do
    #     # your code here
    #   end
    #
    def retryable(options = {}, &block)
      opts = { :tries => 1, :on => Exception }.merge(options)
    
      retry_exception, retries = opts[:on], opts[:tries]
    
      begin
        return yield
      rescue retry_exception
        retry if (retries -= 1) > 0
      end
    
      yield
    end

    It's not hard to implement the same for checking return values as well, i.e.

    retryable_deluxe(:tries => 5, :on => { :return => nil }) { puts "working..." }
    
    retryable_deluxe(:on => { :exception => StandardError, :return => nil }) do
      # your code here
    end

    Ruby is nice.

  • My very own X of the Year 2007

    It's about 9 days too late, but I figure it'd be fun to just throw these out and see if anyone else enjoyed the same things I did in 2007.

    PC Game of the Year

    This has definitely got to go to Call of Duty 4: Modern Warfare. The single-player campaign was short but visually impressive and amazingly realistic. It was like watching a movie in most parts. And really, sneaking by Spetsnaz just a few metres away in sniper ghillie suits and was totally pimp. Multiplayer is real good fun too, I'm still playing it now (usually on GamingSA.com servers as 'konata').

    Special mention: World of Warcraft (yes, I quit it ages ago, but it's still good for the early part of 2007 when I was still playing casually).

    PSP Game of the Year

    Yes, I got a PSP just this year. And yes, Jeanne d'Arc is the best game I've played and completed in a while. For those who don't know, Jeanne d'Arc is a very accessible Strategy RPG (SRPG) that is very loosely based on Joan of Arc. It's much easier to play than Final Fantasy: Tactics or Disgaea: Hour of Darkness and doesn't leave you constantly wondering about missing out on secrets. Highly recommended if you're looking for an RPG-like game on the PSP.

    I haven't had the chance to play many other PSP games, but look forward to getting deeper into Disgaea: Hour of Darkness (and highly anticipating Final Fantasy VII: Crisis Core in March 2008).

    Anime of the Year

    2007 is the year I switched my anime tastes from mostly shonen anime (like Naruto and Bleach) to the seinen genre.

    My favorite anime for 2007 (this is a tough one) has to go to Lucky Star. I even went through a prolonged Konata-ism phase and even had a printout of Konata saying "Relax" pasted right above where I sit in the office.

    Konata says 'Relax'


    It's therapeutic :)

    2007 was a good anime year. Tekkon Kinkreet had an edgy, surreal drawing style but had a fantastic plot. I highly recommend it if you've missed it - it's about 10 times better than your run-of-the-mill Princess Mononoke or Spirited Away type anime movies, in my humble opinion.

    Potemayo's moeblobs were just too funny to watch. Gochuko (below) had me LOL when she went around using tape to fix everything she sliced with her big scythe. At first appearances Potemayo may look like a kid's anime but it's really a work of comic genius for adults.

    Gochuko from Potemayo


    Genshiken 2 picks up where the 1st season left off and while the ending left quite a bit to be desired, on the whole it was enjoyable watching otakus cope with life (who knew it was so tough to get a job in Japan).

    What about you?

    What did you enjoy in the last year and more importantly, do you have any recommendations for an ani-otaku like me?

  • Living on the Edge (of Rails) - 1st week of the year edition

    Yup, it's time for your weekly dose of the changes on edge Rails, more or less covered in the latest Rails Envy podcast. Using edge Rails is neither arcane nor terrifying, and hopefully weekly reports like these will allow you to take control of your own release schedule with your Rails apps.

    This week's report covers changes from 31 Dec 2007 to the day the podcast was recorded (6 Jan 2007).

    Caching changes

    Looks like most of the changes from the 2.1 caching branch have been
    merged into the trunk. Some key points:

    1. memcache-client has been vendored (included in Rails directly). MemCacheStore works out of the box in Rails now, no need to install the memcache-client gem!
    2. The caching code has been refactored and moved into ActiveSupport (ActiveSupport::Cache::*).
    3. Added ActiveRecord::Base.cache_key to make it easier to cache Active Records in combination with the ActiveSupport cache libraries introduced in this changeset.
    4. Fragment cache keys are now by default prefixed with 'views/'.
    5. Deprecation: ActionController::Base.fragment_cache_store is now ActionController::Base.cache_store

    Fragment caching now works in RJS and Builder templates

    Yup, you couldn't do fragment caching in non-erb views before - now you can.

    Freezing Rails now automatically updates your Rails app

    If you're using edge Rails and use the rails:freeze:edge rake task, you probably usually forget to run (or maybe you're not even aware of) rake rails:update to update your Rails app with the latest config/, scripts/ and javascript files from the version of Rails you just froze to. On edge Rails, the rake rails:freeze:edge task runs the rails:update task for you. +1 for convenience!

    I prefer to use Piston so I'm gonna have to keep remembering to run my rake rails:update now and then!

    Check out the related changeset.

    Optimizations

    Only 1 optimization in the past week worth talking about: the ActiveRecord::Base#exists? method is faster. It now uses ActiveRecord::Base#select_all instead of a more expensive ActiveRecord::Base#find that unnecessarily instantiates AR objects. (Check out the related changeset.)

    Bug fixes

  • self.plug :with => 'Rails Hackfest'

    Yes, it's a plug for myself and it's shameless:

    Dec 2007 Rails Hackfest


    Whatever Zed Shaw may say about Rails its community, Rails has done a huge part in making web development fun again for jaded web developers (first-time web developers probably won't be able to tell the difference), and more importantly, raising the profile of Ruby more than any other Ruby project/library/framework/tool ever did (and I'm sure we're all constantly rolling our eyeballs at the new web frameworks that're so terribly familiarly Rails-like). Hell, I bet at least 90% of Rubyists now wouldn't even be Rubyists if they hadn't come across the web framework that could 2 or 3 years ago. So yes I'd still be contributing to Rails so that it can make my life as a web developer easier, thank you.

    I think I had about 18 accepted patches in December 2007 Rails Hackfest (a few of the patches were not attributed properly in the Hackfest due to technical issues with the Rails Hackfest site).

    Probably the only significant patches were:

    • Allowing a proc to be set for ActionController::Base.asset_host (changeset 8421).
    • to_xml should not automatically pass :procs to associations included with :include (changeset 8258).

    Of course Ruby 1.9 compatibility was a big deal in December 2007 since that Ruby 1.9 was targetted to be released on Christmas (changesets 8369, 8309, 8398, 8397, 8412). Yes, I know, Ruby 1.9 is a development release.

    There were also broken tests to be fixed (changeset 8271), bugs to be fixed (fixing Array#to_sentence), and some refactoring (changesets 8343 and 8522).

    And what would patching Rails be without random documentation fixes (changesets 8457, 8471, 8472, 8280, 8279, 8278, 8521). The API documentation always needs a hand, so if you see any outdated or incorrect documentation, or think you can improve them with better examples or whatever, just submit a patch (it's easy and should take you all of 3 minutes).

    This month's hackfest has a terribly attractive top prize of a RailsConf ticket. Too bad I won't be able to take part in this month's hackfest since winners of past month's hackfest are automatically excluded from the next's. But maybe you can ;).

  • Living on the Edge (of Rails) - the pilot

    Gregg Pollack of Rails Envy (the guys behind your favorite Ruby on Rails vs everything commercials) asked me recently whether I could provide them with weekly updates of changes on edge Rails for the Rails Envy Podcast (which is often hilarious besides being informative), seeing as I am already keeping up with developments on the Rails trunk while doing my small part contributing to Rails. Of course, I responded with a +1.

    The podcast is now available and contains only abbreviated notes about the changes that I'd sent over to Gregg, so here's the detailed version of changes on edge Rails from the release of Rails 2.0.2 (17 Dec 2007) to the day the podcast was recorded (30 Dec 2007).

    Noteworthy changes on edge Rails: 2007-12-17 to 2007-12-30

    Native mongrel handler

    A native Mongrel handler has been introduced. This is so that it can be worked on independent of Mongrel's release cycle (the Rails handler is currently in the Mongrel codebase). Jeremy Kemper (bitsweat) has already made a minor performance improvement by moving the mutex from the handler itself into the dispatcher. This means that as Rails gets more threadsafe, the Rails team can push the mutex further down so that it's not a (scary) giant lock (the existing Mongrel Rails handler has a synchronized block around Rails' dispatcher).

    Note: The native mongrel handler is not used by default at this time on edge Rails.

    Caching branch for Rails 2.1

    A caching branch has been introduced, targeted for Rails 2.1. Other than being a big refactoring, there's also native cache key management (e.g. Product.find(5).cache_key # => "products/5") and memcache integration is also being looked at.

    Ruby 1.9 compatibility continues...

    Yup, work on Ruby 1.9 compatibility for Rails continues. Rails is fortunate to have committers and contributors who are so keen on being 1.9-compatible - free performance gains are hard to turn down after all. There's been lots of patching going on to deal with the changes in Ruby 1.9 (Unicode changes, new syntax, deprecations, etc.).

    Yes, the Rails committers and contributors working on 1.9-compatibility are fully aware that Ruby 1.9 is a development release, but nothing's lost in making sure Rails is compatible with a version of Ruby that will eventually be a production version of Ruby. Besides, I think most of the 1.9-compatibility contributors have a dream that sometime soon, when Ruby 1.9 becomes "stable" enough, we'd be running our Rails applications on Ruby 1.9 for a free performance boost.

    Too bad running Rails on Ruby 1.9 is not really possible at the moment since other projects like Mongrel need to be updated for Ruby 1.9 as well, first.

    Bugfixes

    • The new TestCase subclasses (ActionController::TestCase,
      ActionMailer::TestCase) introduced in Rails 2 have a gotcha. This has been fixed quickly by Josh Peek.
    • Fixed a bug where trying to :include for a belongs_to association inferred the foreign key incorrectly from the class name, rather than from the association name (changeset contributed by Jonathan Viney). Code is probably easier to understand:
      class Post < ActiveRecord::Base
        belongs_to :main_category, :class_name => 'Category'
      end
      
      Post.find(:first, :include => :main_category)
      # => ActiveRecord::StatementInvalid: Mysql::Error: Unknown column
      'posts.category' in 'on clause'

    Optimizations

    I can has feedback nao plz?

    Please leave any suggestions for improvements you may have - about anything really: format, level of detail, what you'd like to see, whatever. Until next week...

  • Why I love RSpec nested example groups

    Prior to the introduction of nested example groups in RSpec, I'd always felt that descriptions got a little unwieldy when trying to describe the different cases and disliked specifying the controller_name repeatedly. For example (and these are real examples from real projects at work, with actual code removed for conciseness):

    describe 'POST HotelDealsController#create with a valid hotel deal' do
      controller_name :hotel_deals
    
      before(:each) do
        # ...
      end
    
      it "should create and save a new hotel deal" do
        # ...
      end
    end
    
    describe 'POST HotelDealsController#create with an invalid hotel deal' do
      controller_name :hotel_deals
    
      before(:each) do
        # ...
      end
    
      it "should not perform any redirection" do
        # ...
      end
    end

    Nested example groups allow me to do this instead:

    describe SearchController do
      before(:each) do
        @search = mock_model(Search)
      end
    
      describe "POST 'create'" do
    
        it "should render 'new' when creating an invalid search" do
          # ...
        end
    
        describe "with a valid search that has a location_id" do
          before(:each) do
            @search.stub!(:valid?).and_return(true)
            @search.stub!(:location_id).and_return(1)
          end
    
          it "should save a new search that has a location_id, location_code and location_name, and redirect to 'show'" do
            # ...
          end
        end
    
        describe "with a valid search that doesn't have a location_id" do
          before(:each) do
            @search.stub!(:valid?).and_return(true)
            @search.stub!(:location_id).and_return(nil)
          end
    
          it "should ask the Location model for possible locations if the search doesn't have a location_id" do
            # ...
          end
        end
      end
    end

    What's the difference you say? Well, while it may sound trivial, I can do describe SearchController only once and nest all examples for the different actions and scenarios inside without breaking it up into separate top-level example groups where I'd need to say controller_name :search multiple times.

    Another (more important) benefit is I can progessively specify different scenarios I want to test by nesting them and providing a before block to setup mocks and stubs for that specific scenario. Let's look at what I mean with some code. Over here, I'm specifying the create action of my SearchController:

    
    describe "POST 'create'" do
      it "should render 'new' when creating an invalid search" do
        # ...
      end
    
      describe "with a valid search that has a location_id" do
        # ...
      end
    
      describe "with a valid search that doesn't have a location_id" do
        # ...
      end
    end

    There're 3 possible scenarios here: an invalid search, a valid search with a location_id, and a valid search without an location_id. I can write examples for invalid searches within the top-level example group itself (it "should render 'new' when creating an invalid search"). Now, to test valid searches, I break out 2 nested example groups with their own before block to setup mock searches, one which has a location_id, the other doesn't. What's the big deal? It just feels much more organized than the first, non-nested example where a top-level example group is created for each scenario.

    And I know it may not seem like much, and I don't think there's anything particularly wrong with non-nested examples - it's a matter of personal preference. Nesting allows me to write examples for controllers in a saner fashion where I can say confidently to myself that "all examples for XXX action go here". I'm a big believer in readable tests (if you're on the Rails Trac much and have seen my reviews and patches you should know), so being able to write specs where I feel they belong makes me happier, and the examples read really nicely too.

    I guess what I'm really trying to say is this: when trying to add a new example to legacy specs (legacy being anything that was written more than 5 mins ago) I instantly know where to place it - gone are the days of going through different example groups looking for the right place to put my new specification (and often settling on just simply creating a new top-level example group). I like to think we've all been there.

    That said, I'm not saying that deeply nested examples are a good thing. When testing Rails controllers I find that you don't often have to go more than 3 deep to allow for all the possible scenarios. Any more would suggest that your controller is doing too much work.

  • DRY in your functional and ActionMailer tests - Rails 2.0 a feature a day #7

    That's right, Don't Repeat Yourself in your functional and ActionMailer tests. If you're a Test::Unit user, this will probably look familiar to you when writing Rails functional tests (for your controllers):

    class PostsControllerTest < Test::Unit::TestCase
      def setup
        @controller = PostsController.new
        @request    = ActionController::TestRequest.new
        @response   = ActionController::TestResponse.new
      end
    
      def test_should_not_explode
        # Invariably sexy test code.
      end
    end

    You'd probably have noticed how setup always sets the @controller, @request and @response instance variables in all your controller tests. In Rails 2.0, you can instead subclass ActionController::TestCase (in turn a subclass of Test::Unit::TestCase) in your test cases and avoid repeating the instance variable assignments.

    class PostsControllerTest < ActionController::TestCase
      # No need to define setup.
    
      def test_should_not_explode
        # Invariably sexy test code.
      end
    end

    Isn't that much better?

    Watch out for a gotcha though, when you actually do need to define a setup method (like when you need to setup several other things before your tests run), as Jeffrey Allan Hardy reports. The issue has been resolved on edge Rails. If you're not using edge Rails, you should remember to always call super in your setup method should you define one:

    class PostsControllerTest < ActionController::TestCase
      def setup
        super
        @user = users(:konata)
      end
    
      def test_should_not_explode
        # Invariably sexy test code.
      end
    end

    A similar subclass of Test::Unit, ActionMailer::TestCase is available for your ActionMailer unit tests as well. That means you can replace this:

    class UserMailerTest < Test::Unit::TestCase
      include ActionMailer::Quoting
    
      def setup
        ActionMailer::Base.delivery_method = :test
        ActionMailer::Base.perform_deliveries = true
        ActionMailer::Base.deliveries = []
    
        @expected = TMail::Mail.new
        @expected.set_content_type "text", "plain", { "charset" => "utf-8" }
        @expected.mime_version = '1.0'
      end
    
      def test_signup
        # Test code.
      end
    end

    with this:

    class UserMailerTest < ActionMailer::TestCase
      def test_signup
        # Test code.
      end
    end

    For the geeks among you, check out the related changeset.

    About the contributor, Michael Koziarski

    Michael Koziarski, better know as nzkoz, is a long-time Rails core committer. Michael has done a signficant amount of work improving the performance of ActiveRecord for Rails 2.0 so we have him to thank for a good portion of the optimization work done on ActiveRecord. He also keeps a fairly new personal blog.

  • Ruby 1.9.0 has been released

    Just in the nick of time for the Christmas target, the Ruby core team has released Ruby 1.9.0 - Matz announced the release on the ruby-talk mailing list not too long ago.

    Rubygem and Ruby library developers, now's the time to get down to making your Ruby code compatible with 1.9.0.

    Rails is not too far from being compatible with 1.9, thanks to the efforts of Jeremy Kemper and a few other contributors. Mongrel is not quite there yet though (I use mongrel for my Rails applications, and I think at least 50% of you do too). So while running your Rails apps on Ruby 1.9 is not an easy task right now (unless you enjoy lots of monkey patching), let's hope the full 1.9.0 release will inspire more Ruby developers to get their libraries compatible so we can all enjoy the big speed boost in Ruby 1.9.

    Very nice (if somewhat geeky) Christmas present from the Ruby core and contributors!

  • Generating migrations gets easier - Rails 2.0 a feature a day #6

    You may not have noticed this yet, but Rails 2.0 has a new convenient syntax for generating ActiveRecord migrations. Go ahead, run the migration generator with script/generate migration. I'll wait.

    Yup, you can now specify the columns you want to add in your migration by passing attribute/type pairs to the migration generator. Ergo,

    script/generate migration AddMoreToAnime episodes:integer licensed:boolean

    will generate a migration like so:

    class AddMoreToAnime < ActiveRecord::Migration
      def self.up
        add_column :animes, :episodes, :integer
        add_column :animes, :licensed, :boolean
      end
    
      def self.down
        remove_column :animes, :licensed
        remove_column :animes, :episodes
      end
    end

    This ties in pretty nicely with the syntax for the model generator (script/generate model), which also accepts attribute pairs. Tiny change but "it's all about consistency".

    For the geeks among you, check out the related changeset and ticket.

    About the contributor, Pratik Naik

    Pratik Naik (WorkingWithRails profile), probably better known as lifofifo or just lifo, is an active Rails contributor and can be found on #rails-contrib and #rubyonrails nearly 24/7. Almost everyone who's used Rails has used code that lifo has written. Oh, and lifo also won one of the monthly Rails Hackfests.

    He also keeps a pretty good blog where he posts mostly on Rails hacking and tips.

  • Rails 2 a feature a day - update

    In case anyone is wondering about the pause in Rails 2.0 - a feature a day posts, yes, I know, I've not been writing any posts for a week or so. I'll be posting more actively again from tomorrow - just taking a short break due to general lethargy these few days. And submitting patches > blogging.

    On a somewhat related note, I'll be posting weekly updates of edge Rails changes in a tie-up with a particular Rails podcast from the beginning of next year - not gonna say which one!

  • Rails 2.0.2 released, so what's new?

    Rails 2.0.2 was tagged not too long ago and will hit the gem servers soon I expect. Being a minor point release there aren't many changes, but here're the ones I feel are worth mentioning:

    ActionPack changes

    Configurable asset hosts: You can now pass a proc to ActionController::Base.asset_host. This allows you to get by the hard-coded assumption that you have 4 asset hosts numbered 0-3 when using the '%d' wildcard (introduced in Rails 2.0.1).

    I contributed this patch so I'm gonna be lazy and copy and paste most of the documentation I wrote for patch.

    The example proc below generates http://assets1.example.com and http://assets2.example.com randomly.

    ActionController::Base.asset_host = Proc.new { |source| "http://assets#{rand(2) + 1}.example.com" }
    image_tag("rails.png")
      => <img src="http://assets2.example.com/images/rails.png" alt="Rails" />
    stylesheet_include_tag("application")
      => <link href="http://assets1.example.com/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />
    

    The proc also takes a single source parameter which is the path of the source asset. This can be used to generate a particular asset host depending on the asset path. This is useful if you have, say, images hosted elsewhere, or you want an SEO-friendly URL for your images:

    ActionController::Base.asset_host = Proc.new { |source|
      if source.starts_with?('/images')
        "http://images.example.com"
      else
        "http://assets.example.com"
      end
    }
    image_tag("rails.png")
      => <img src="http://images.example.com/images/rails.png" alt="Rails" />
    stylesheet_include_tag("application")
      => <link href="http://assets.example.com/stylesheets/application.css" media="screen" rel="stylesheet" type="text/css" />

    Asset cache directories are automatically created: In Rails 2.0.1, if you tried to cache your assets (javascripts and stylesheets) in a subdirectory (example below), you either need to check it into your source control or have your deployment tool (Capistrano, etc.) create it for you. Josh Peek contributed a patch that created the subdirectory instead so you can do something like:

    javascript_include_tag(:all, :cache => "cache/money")

    and not have to bother about creating the 'cache' subdirectory.

    Railties changes

    Default database is now SQLite3: The default database is now SQLite3 instead of MySQL. Running 'rails takeover_the_world_app' will now come with a database.yml that's setup for SQLite3. To get the old behavior (where it's preconfigured for MySQL), run rails -d mysql takeover_the_world_app instead. This is mostly to make this "just work" on Mac OS X Leopard, where SQLite3 is already installed with the OS.

    Template loading is faster: DHH has turned on ActionView::Base.cache_template_loading by default in the production.rb environment config file that basically means that Rails no longer does file system stat calls when loading templates. The downside is that you have to restart your Rails application to see templates changes in production mode, but it's not really that big an issue since no one should be editing templates anyway 'live' in a production application.

    New rake tasks for migrations: rake db:migrate:redo to undo your last migration and re-run it - very useful when developing migrations (when you screw up, that is). rake db:migrate:reset drops the database, re-creates it, and then runs all migrations.

    rake task for generating secret keys: rake secret to generate a secure key that you can use for cookie sessions. This is useful for updating Rails applications from 1.x to 2.x, which uses cookie-based sessions by default and requires a secret key.

    config.action_controller.session = {
      :session_key => '_your_app_session',
      :secret      => 'some super long string that you can generate with the rake secret task'
    }

    Personally I just use super long quotes from Family Guy.

    That's it! For the curious, expect Ruby 1.9 compatibility, improved caching, and big ActionPack and rendering refactoring changes in Rails 2.1.

  • Sensible GROUP BYs in ActiveRecord::Calculations - Rails 2.0 a feature a day #5

    Doing GROUP BYs in ActiveRecord has always been tricky. There's a lot of "magic" in ActiveRecord and room for untested edge cases. Thankfully, Rails also has a squadron of eagle-eyed contributors who are on hand to fix unforeseen errors.

    For example, if you wanted to do this:

    class Click < ActiveRecord::Base
      belongs_to :site  # site_id foreign key is a String, e.g. 'www.rubyonrails.org'
    end
    
    # Now, try counting the number of clicks, grouped by site.
    Click.count(:all, :group => :site)
    # => [[nil, 1], [nil, 3]]
    

    This is a surprisingly common thing to do especially when you are producing reports or statistics of any kind and unfortunately decided to go with a non-(auto)numeric foreign key. As you may have noticed, the sites were returned as nil which is totally unhelpful. Thankfully, this has been fixed in Rails 2.0:

    Click.count(:all, :group => :site)
    # => [[nil, 1], ['www.site, 3]]

    GROUP BY and :foreign_key don't mix

    This change is NOT in Rails 2.0.

    If you wanted to do GROUP BYs in your ActiveRecord models (via the :group option), you had to take note of one particular gotcha. For example, with an ActiveRecord model like this:

    class Anime < ActiveRecord::Base
      belongs_to :japanese_studio, :class_name => 'Studio', :foreign_key => 'studio_id'
    end

    Note that we specified the :foreign_key option since it can't be inferred from the association name of 'japanese_studio'. If we wanted to counting the number of anime grouped by studio though, we're in big trouble:

    Anime.count(:all, :group => :japanese_studio)
    # ActiveRecord fails and mumbles about an unknown "japanese_studio_id" key.
    

    Basically what happened is Rails did not use the specified foreign key column of 'studio_id', inferring it instead from the association name of 'japanese_studio'. This is not yet fixed but there's already a patch ready for it at the Rails issue tracker. In fact, I'm gonna verify it right after posting this.

    About the contributor, Kamal Fariz

    I thought it was time to cover Rails 2.0 contributions by someone closer to home ("home" being my home, Singapore) so I singled out Kamal Fariz (WorkingWithRails profile), a Rails developer working in Malaysia for a Rails shop. Kamal's also a former Rails Hackfest winner (read the post-Rails Hackfest interview). Kamal is also an active member of the Malaysia Ruby Brigade (I guess it's not called a "Ruby User Group" since "RUG" sounds rather unglamorous).

    Wow look at the number of parentheses I used in the last paragraph. For no reason whatsoever here's a lolcats picture:

    Parenthese cats are parenthetic


  • ActiveRecord::Base.with_scope { :only => 'in your model' } - Rails 2.0 a feature a day #4

    Today's (un)feature covers the oft-abused and misunderstood with_scope method. The excellent err.the_blog had a post on how you should be using with_scope (yes, only use it in your ActiveRecord models, not in your controllers or filters). Yes that blog post may have been written a long time back but it's still applicable to Rails 2.0.

    With Rails 2.0, the with_scope method is now protected, meaning that you can no longer use it in your controllers (unless you use the #send(:with_scope), or #send!(:with_scope) in Ruby 1.9). I'm not even going to show you how you could use it in your controllers since you really shouldn't (unless you know what you are doing).

    Here's what with_scope allows you to do:

    class SiteOwner < ActiveRecord::Base
    
      # Returns list of distinct active sites.
      def active_sites(*args)
        with_scope :find => { :select => 'DISTINCT(site)', :conditions => { :active => true } } do
          find(*args).collect(&:site)
        end
      end
    end

    And even better example, stolen from the err.the_blog post mentioned above, is re-using scopes:

    class Movie < ActiveRecord::Base
      def self.find_playing(*args)
        with_playing do
          find(*args)
        end
      end
    
      def self.find_playing_by_id
        with_playing do
          find_by_id(*args)
        end
      end
    
      def self.with_playing 
        with_scope :find => { :conditions => [ state = ? AND visible = ?, NOW_PLAYING, true ] } do 
          yield 
        end 
      end
    end

    I tend to use that a lot combined with method_missing magic (also from the err.the_blog post) to allow me to do things like Movie.find_playing_by_name:

    def self.method_missing(method, *args, &block)
      if method.to_s =~ /^find_(all_)?playing_by/
        with_playing do
          super(method.to_s.sub('playing_', ''), *args, &block)
        end
      else
        super(method, *args, &block)
      end
    end

    If you prefer to get these for free, check out the the Rails scope-out plugin implements many of the ideas in the err.the_blog post.

    About the contributor, Josh Peek

    Josh Peek (WorkingWithRails profile) was the man behind the "protection" of with_scope. This is not Josh's greatest contribution to Rails, of course - Josh has been a very productive and active Rails contributor since the very beginning. A winner of one of the monthly Rails Hackfest, you can read up on the ever-elusive (I say "elusive" because he doesn't have a blog - it's just patch after patch after patch) Josh Peek in this post-Rails Hackfest interview.

    Josh is also the man behind the deprecation and "plugin-izing" of dynamic scaffolding and pagination (yes, those are gone from Rails 2.0) among a great number of other patches and test coverage improvements. Right now, Josh appears to be working on refactoring ActionView.

    Outro

    Apologies for the late-on-arrival Rails 2.0 a feature a day posts, but such is life. Feature #5 follows immediately after :)

  • Concatenate your stylesheets and JavaScripts in 3 seconds - Rails 2.0 a feature a day #3

    I don't think I need to explain how concatenating your 5 stylesheets and 12 JavaScript files into single files each makes your webpages load faster. Unless you're using multiple asset hosts (Rails 2 allows for multiple asset hosts), then it becomes tricky to say for sure which method is better, but I digress.

    There're a bunch of really useful plugins that help you bundle your JS and CSS files, most of them offering minifying/packing of JS and stripping of comments and whitespace in CSS as well. I was a big fan (still love it) of Asset Packager, but newer plugins like PackR and Bundle-fu work great too.

    With Rails 2.0 though, unless you want the minifying/packing/comment-stripping features, you can do away with installing a packager plugin. Remember your trusty javascript_include_tag and stylesheet_link_tag? They now accept a :cache option:

    # :cache => true creates a cached javascript named all.js.
    <%= javascript_include_tag :all, :cache => true %>
    # This becomes:
    # <script type="text/javascript" src="/javascripts/all.js"></script>
    
    # You can name your javascript too, by passing a name instead of true.
    <%= javascript_include_tag 'jquery', 'swfobject', 'application', :cache => 'base' %>
    # This becomes:
    # <script type="text/javascript" src="/javascripts/base.js"></script>
    
    # Same deal for stylesheets...
    # :cache => true creates a stylesheet named all.css.
    <%= stylesheet_link_tag :all, :cache => true %>
    # This becomes:
    # <link href="/stylesheets/all.css"  media="screen" rel="Stylesheet" type="text/css" />
    
    # You can name your stylesheet like for javascript_include_tag.
    <%= stylesheet_link_tag 'yui/reset-fonts-grids', 'application', :cache => 'styles' %>
    # This becomes:
    # <link href="/stylesheets/styles.css"  media="screen" rel="Stylesheet" type="text/css" />
    

    Of course, this only takes place if caching is turned on (ActionController::Base.perform_caching), which is on by default in the production environment and off in the development environment.

    For those who want to look at some code, check out the changeset.

    About the contributor, DHH

    Well, DHH hardly needs any mention. David has_one Wikipedia entry. Nice little touch on this new feature though, I love the convenience.

    I promise, like I'd said before, to cover contributions by someone less "mainstream" in the next Rails 2 "feature a day" blog post.

  • gchartrb = Ruby + Google Chart API

    Google Chart API looks quite useful. I've used Gruff to generate graphs before but this looks way easier (and much lighter on the server without all that ImageMagick magic!) I'll have to play with it someday - if only I didn't have to mess with those URL params!

    Well, Deepak Jois from singapore.rb (the local Ruby user group) recently announced a Ruby wrapper around the Google Chart API: gchartrb (rdocs). You can get it as a rubygem, just run:

    sudo gem install gchartrb

    Looking good so far and I dig the familiarity - Deepak says the API is Gruff-inspired and it tells. If you need a charting solution (for reporting or whatever), this is a gem to consider.

    Ah yup, there'll be no Rails 2 feature today - just submitted a couple of Ruby 1.9 compatibility patches for Rails and feeling a little bummed out now (dependency handling is not that pleasant in Rails). Rails' Ruby 1.9 compatibility needs you!

  • Declarative exception-handling in your controllers - Rails 2.0 a feature a day #2

    I know I said I was going to try and keep the features in "a feature a day" to those not mentioned in popular places, but this feature is one of the few nicest features in Rails made by a contributor outside of the Rails core so I couldn't resist mentioning it.

    First, an example

    class CommentsController < ActionController::Base
      rescue_from Comment::Spammy, :with => :moderate_comment
      rescue_from ActiveRecord::RecordNotFound, :with => :redirect_if_not_found
    
      protected
        def moderate_comment
          # Handle the exception, like placing the comment in a moderation queue
          # and then rendering a different action.
        end
    
        def redirect_if_not_found
          # Redirect somewhere...
        end
    end

    Compare this to what you had to do with Rails 1.2.6 and earlier:

    class CommentsController < ActionController::Base
    
      def rescue_action_in_public(exception)
        case exception
          when Comment::Spammy
            # Handle the exception as above.
          when ActiveRecord::RecordNotFound
            # Redirect somewhere...
          else
            super
        end
      end
    end

    Much clearer and reads almost like English. I don't think I even need to explain what the code in the 1st example is trying to do.

    Let's try another example

    Here's another common use-case: dealing bad create/update actions. With rescue_from, you can simply do this:

    class ApplicationController < ActionController::Base
      rescue_from ActiveRecord::RecordInvalid do |exception|
        render :action => (exception.record.new_record? ? :new : :edit)
      end
    end

    No need for actions to be coded like this anymore!

    def create
      @comment = comment.create!(params[:comment])
    rescue ActiveRecord::RecordInvalid
      render :action => :new
    end

    This patch was committed in revision 7597 after a short discussion on the syntax and implementation over at the ticket. (More improvements on the implementation over at ticket #9645 for the interested.)

    About the contributor, Norbert Crombach

    Norbert Crombach (Rails Trac username: norbert, WorkingWithRails profile) is another regular Rails contributor hailing from the Netherlands. The rescue_from is, in my opinion, one of his more significant contributions (and what a great one it is too).

    Some final words

    While the new declarative rescue_from macro is useful, I've found myself liking the merb way of dealing with exceptions, where exception handling is delegated to an Exception controller. You can use layouts, templates, helpers and filters just like in ye plain olde controller. I'm still not so sure if it's worth making a patch for Rails to emulate this behavior yet though!

    Update: Rails 2.0 a feature a day #3 is now available Concatenate your stylesheets and JavaScripts in 3 seconds

  • Time#advance now uses :weeks, :hours, :minutes, :seconds options - Rails 2.0 a feature a day #1

    Time#advance and DateTime#advance just got, erm, more betterer in Rails 2.0.

    In Rails 1.2.6 and earlier, Time#advance and DateTime#advance only accepted :year, :month and :day options. You'd think :hours, :minutes and :seconds would work too but due to a bug in the code (see ticket #9818), you get something like this instead:

    time = Time.utc(2000,10,1,10,30,45)
    # => Sun Oct 01 10:30:45 UTC 2000
    
    time.advance(:days =>3, :hour => 2)
    # => Wed Oct 04 02:00:00 UTC 2000
    

    Notice how the hour is changed instead of being advanced.

    Now, thanks to Geoff Buesing's efforts, passing :hours, :minutes and :seconds options work as you'd expect:

    time = Time.local(2005,2,28,15,15,10)
    # => Mon Feb 28 15:15:10 0800 2005
    
    time.advance(:hours => 5, :minutes => 20, :seconds => 25)
    # => Mon Feb 28 20:35:35 0800 2005

    Hell, you can even #advance by :weeks (see ticket #9866):

    time = Time.local(2005,2,28,15,15,10)
    # => Mon Feb 28 15:15:10 0800 2005
    
    time.advance(:weeks => 2)
    # => Mon Mar 14 15:15:10 0800 2005

    +1 for consistency and properly behaving code. Thanks to this #advance patch, Geoff was also able to re-use #advance to refactor some other parts of ActiveSupport's time-related extensions.

    About the contributor, Geoff Buesing

    Geoff Buesing is a regular Rails contributor and I've known him to be the date/datetime/time guru - just look at his submitted patches if you're not convinced. Not too long ago Geoff was given well-deserved commit rights (congrats Geoff)! You won't find him on the Ruby on Rails Core team page yet though - it's probably out of date.

    Check out his blog at mad.ly where he writes mostly his adventures with on unobtrusive JavaScript in Rails.

  • Rails 2.0 - a feature a day

    Now that Rails 2.0 has been baked to near-perfection, I'm sure all you Rubyists with Rails applications are considering updating or have already updated your applications to be Rails 2.0-compatible. And you've probably already pored through the massive list of new features and changes in Rails 2.0 or seen it on Ryan Daigle's Scraps. Ryan also wrote a Rails2 mini eBook that's been getting good reviews.

    I've been keeping track of the Rails Trac timeline myself since my 1st committed Rails patch mostly to keep track of Rails development (we run several apps on edge) and to monitor any comments on my submitted patches. Turns out keeping an eye on the timeline and the rubyonrails-core mailing list gives you a pretty decent "on the ground" view of new features and changes to the Rails trunk.

    So I'm gonna try something and see if helps anyone: I'll be running a near-daily series of blog posts about new Rails features or changes. I'll try not to repeat anything that's already been mentioned, and instead focus more on lesser-known changes. I think it's also a great opportunity to raise the profile of the core committers and contributors to the Rails community so look out for an "About the contributor" section in each post.

    Hop on over to the 1st post - Time#advance now uses :weeks, :hours, :minutes, :seconds options.

  • Rails 2.0 released

    Just go to the release announcement and major features blog post by DHH already!

    If you're having trouble installing the Rails 2.0.1 gem, do a gem install rails -y --source http://gems.rubyonrails.org

    Now that the obligatory link to the Rails 2.0 release announcement is done, I'd like to urge anyone with any interest in improving Rails or seeing new features or fixes in Rails to try and contribute to Rails. I'm sure everyone of us Rails users have had "what if Rails could do this" or "why is Rails doing this, that's just wrong" moments. Well, roll up your sleeves and help by contributing instead of complaining about how "they" won't/haven't/are too stupid to fix it..

    A good starting point is to read the Rails Trac wiki, and after that be sure to check out John Susser's excellent guide to contributing to Rails (read the presentation slides, it won't take long). You should have enough information to start. I recommend just opening the base.rb classes for ActiveRecord, ActionController and peeking around the ActiveSupport classes just to get a feel of things. After that crack open the relevant test for your issue and write your test-driven patch!

    It's really easy, since Rails is written purely in Ruby - if I can do it I'm sure you can too :) Your contribution doesn't even have to be a bona fide patch file with tests, documentation and bits of Ruby code - if you notice a bug and report it not knowing how to fix it, that's good enough! Or even something as trivial as a new style (CSS and layout) for the Rails error pages (there isn't such a patch yet but I'd love to see prettier default exception pages instead of the current rather plain-looking ones).

    Anyway, that's enough of the sermon from me - enjoy your Rails 2.0! Congrats to the Rails Core team and contributors on another solid release!

  • Rails 2.0 - it's landed

    Update: Looks like it's going to be released as Rails 2.0.1 instead due to a bugfix post-2.0 tagging.

    Just a quick one - seems like Rails 2.0 has been tagged for release. Rails 2.0 milestone has just been completed.

    Expect a post on weblog.rubyonrails.com soon.

  • Time.now.obsession = PSP Slim

    My PSP Slim


    Jeanne d'Arc is a great little SRPG I'm enjoying right now - I don't even want to start Final Fantasy Tactics until after I've completed Jeanne d'Arc.

    I have a Nintendo DS Lite as well and I have to say that I'm more of a PSP gamer where portable consoles are concerned. There just weren't that many involved games in the DS (except Puzzle Quest) and coming from a big fantasy CRPG background kinda made the DS a poor companion on train and bus rides. Being able to play videos on a large screen (relative to the iPod video I carry around all the time) is awesome. It's not an iPhone but it's good enough for watching videos!

  • Try the new Mac OS X UI for Firefox 3

    The Firefox 3 developers have been working on a visual refresh that integrates more tightly into the OS, and the Mac OS X version is looking pretty sweet. Well, public opinion varies greatly - from people who feel that it's a rip off of Safari to those who really like the Brushed Metal look (myself included).


    Well, if you wanna check out the proposed Mac OS X Firefox 3 UI for yourself, grab yourself a copy of Firefox 3 Beta 1 and go get the Proto theme.

  • Firefox 3, Beta

    Firefox 3 Beta 1 is out! Check out the pretty sweet list of changes.


    If you want to try it, you'll probably want to run Firefox 3 and Firefox 2 at the same time. Only this time you should remember to rename Firefox 3 so that it doesn't override your install of Firefox 2.

    I'd blogged about some of the changes previously if you care for some screenshots of some of the new features (Mozilla Links covers a good number of new Firefox 3 changes too):

  • Best JavaScript library project roadmap I've seen

    The mootools developers look like they are having fun defining their project roadmap:

    mootools 1.3 roadmap


    MooTools Plugin to uninstall Internet Explorer from any machine within network range

  • New in Rails: a request profiler for profiling your app

    Jeremy Kemper (aka bitsweat) committed a very useful tool into the Rails trunk not too long ago: a request profiler! It's a human-friendly wrapper script around the ruby-prof library, a nice ruby code profiler, that lets you run multiple requests against a URI in your application and get a detailed code profile report in text and HTML.

    Wanna try it out? Update your vendor/rails to at least revision 8016, run rake rails:update to copy the new script/performance/request script into your app's script directory, install the ruby-prof gem (sudo gem install ruby-prof), then let it loose on your app.

    The options are pretty simple, just pass the script the URI you wanna profile. There're options for specifying how many requests you want to process, and for profiling POST requests, there's one for
    specifying a POST fixture file.

    USAGE: script/performance/request uri [options]
        -u, --uri [URI]                  Request URI. Defaults to http://localhost:3000/
        -n, --times [0000]               How many requests to process. Defaults to 1000.
        -b, --benchmark                  Benchmark instead of profiling
            --method [GET]               HTTP request method. Defaults to GET.
            --fixture [FILE]             Path to POST fixture file
            --open [CMD]                 Command to open profile results. Defaults to "open %s &"
        -h, --help                       Show this help

    At the end of the run, you get a text and HTML report with the methods called and the time spent in them. If you haven't seen a code profile before, it looks something like this:

    Rails request profiler text output report


    Very nice stuff! It's extremely convenient to have profiling like this built into Rails itself - personally I've not even run any profiling on my Rails code because it seemed like a hassle (though I do run httperf benchmarks and take note the number of requests per second). With code profiling, you can easily see which parts of your code are the bottlenecks and optimize away - it's a godsend that it's so easy to do it in Rails!

    Anyway, this is all still new stuff I imagine and subject to change (and improvements), but still very exciting for those of us who are hitting performance bottlenecks in our Rails apps (and are not already doing code profiling).

  • Konata Izumi in Edge Rails

    You wouldn't think that Konata Izumi (a character from the anime, Lucky Star) and Ruby on Rails (a web MVC framework) would somehow be associated, but they are!

    I took the opportunity to fill in the missing ActiveRecord::Base#to_json documentation with examples using Konata Izumi and it got committed unchanged in revision 7905. Cute. I hope no one changes them.

    Update: Managed to get more Konata Izumi documentation into Rails ;)

  • Harajuku Crepe arrives in Singapore

    One of my favorite foodie moments from my trip to Tokyo was at Harajuku where I had a crepe with cheese cake and ice cream from Angel's Heart (need to dig that photo out). It was THE best crepe I've ever had. I'd even thrown around the idea of bringing it back to Singapore as a commercial venture.

    Well anyway, I found that one of the crepe shops in Harajuku, Marion Cafe, that was just opposite Angel's Heart, has opened a branch right here in Singapore at the IMM Building.

    Crepe from Marion Cafe in IMM Building


    I tried their mango crepe (it had mango and ice cream in it) which set us back about SGD 5.80.

    Mango crepe from Marion Cafe in IMM Building


    It was alright, not as good as the one I had in Harajuku of course, but still a nice treat. They have 105 different types of crepe - anything ranging for your run-of-the-mill strawberry and ice cream crepe, to tuna crepe, and even crepe with Japanese curry.

  • New on edge Rails: JSON serialization of ActiveRecord objects reaches maturity

    The last time I wrote about ActiveRecord#to_json on edge Rails, it was missing some key functionality. For one, you couldn't include any associations. Another thing was you couldn't do something like this in your controller:

    @authors = Author.find(:all)
    
    render :json => @authors.to_json(:only => :name)

    Oh and did I mention it wasn't emitting valid JSON by default?

    In the last few weeks, several patches to the Rails trunk have fixed all these issues (most of these patches are already on the Rails 2.0 Preview Release). JSON serialization of ActiveRecord objects is finally on par with XML serialization, and that old Jsonifier plugin I wrote can finally be sent off to the plugin retirement home (though, I am thinking of porting all the JSON patches into Jsonifier to add Rails 2.0-like JSON serialization to Rails 1.2.4, but who knows, I am lazy).

    Anyway, back to the new JSON stuff on edge Rails.

    No more invalid JSON from Rails

    Yes it's true. No more changing of module attribute to get Rails to produce valid JSON. Thanks to Choon Keat with his detailed SVN history snooping, we finally managed to get a patch to fix JSON encoding to quote all hash keys into Rails.

    :include option can be used to include associations with ActiveRecord::Base#to_json

    At DHH's urging, I submitted a patch that added the :include option to ActiveRecord#to_json. Of course, it was a sloppy first submission and DHH pointed that out - a little bit of DRY love and it was really great to see it get committed (I have a soft spot for JSON serialization from Rails, since I'd been trying to achieve the same thing with Jsonifier).

    So yes, now you can do:

    json = @david.to_json(:include => :posts)
    # or even...
    json = @david.to_json(
      :include => {
        :posts => {
          :include => {
            :taggings => {
              :include => {
                :tag => { :only => :name }
              }
            }
          }
        }
    })

    Enumerable#to_json and Hash#to_json now accept options

    One problem with Jsonifier that I often get emails about is how the to_json options don't work for lists (Enumerables). And why is this important? Well, we often retrieve collections of ActiveRecord objects at a time with AR::B#find so it's not too much to ask to be able to do this in your controller actions:

    @authors = Author.find(:all)
    
    render :json => @authors.to_json(:only => :name)

    But of course you couldn't, so I patched away by changing the to_json methods of Enumerable and Hash (and other types as well) to accept an optional options argument. Enumerables will pass on any options it receives to its elements. Hashes will respect :only and :except (as well as passing on the options to its elements).

    Unambiguous (non-US-centric) dates and times

    While Rails 1.2.3 (and 1.2.4) never supported encoding of Dates, Times and DateTimes, edge Rails has been happily supporting Date, Time and DateTime conversions to JSON since revision 6673. Unfortunately, the date string format is in MM/DD/YYYY format:

    Time.now.to_json
    => "\"09/21/2007 12:15:02 UTC\""

    Geoff Buesing suggested that Rails should change this to the more unambiguous YYYY/MM/DD format. Finding the right date format is important not only for readability reasons, it's also important that the date string is directly parsable by the many browser JavaScript implementations using the JavaScript new Date(dateString) function. There is no formal spec of how dates should be represented in JSON, but it's most convenient that strings representing dates can be directly converted into JavaScript Date objects.

    Testing in several browsers and on different platforms, albeit not extensively, we settled on the %Y/%m/%d %H:%M:%S %z (strftime) format (more details in the comments of the Trac ticket).

    Now it is:

    Time.now.to_json
    => "\"2007/09/21 12:15:02 +0800\""

    Much better.

    What's left?

    Documentation, for one thing. ActiveRecord::Base#to_json is still undocumented (docfix patch). The to_json methods for Enumerable and Hash need documentation as well (will patch that soon).

    ActiveRecord::Base#to_json also doesn't deal with binary attributes, which can be easily resolved by Base64-encoding them (patch coming soon, it gets slightly complicated when trying to do a from_json). And of course, optimization improvements are always nice (decoding from JSON especially).

    So, if you use JSON at all, please come in and help patch the remaining bits before Rails 2.0 rolls out. If you can't (or more realistically, don't have the time to) patch Rails yourself, do help out by verifying patches :).

  • So apparently I'm a Rails Hackfest winner...

    Got an email not too long ago from Working With Rails (my profile) entitled "WWR September Hackfest Winner - Congrats!", and almost deleted it thinking it was spam. I read it in the end (after all, how often do you see spam with "Rails" in the subject title), and turns out that my recent patches to Rails pushed me up to 6th position in the September Rails contributors rankings.

    Rails Hackfest September 2007 winners


    I vaguely remember seeing Rails Hackfest mentioned somewhere but only just realized it's a "contest" run monthly. The winners are the top 10 contributors to Rails (contribution being determined by the number of accepted patches, docfixes, and even comments). That's really nice of the sponsors and Working With Rails, since that wasn't really what I'd expected in return for contributing to an Open Source project (that would be a sense of satisfaction, personally-speaking).

    I'm just really glad that the JSON patches went into Rails before Rails 2.0 and it is satisfying to see my code get into Rails (I'll blog about the patches in a separate post). There's still some more work needed though - please help to verify and give suggestions or +1s ;).

  • What's new in Firefox 3: Pasting text into search bar to be 100% less annoying

    Now if you're a regular user of Firefox, you've probably already had the opportunity to be dismayed by how you cannot copy and paste text with newlines into the search bar. All you end up with is the first line of text. I just end up typing in the search terms myself or copy and paste line by line.

    Try it for yourself with the text here:

    First line.
    Second line.

    Well, all that is gonna go away in Firefox 3, which replaces newlines with spaces. Small change perhaps, but definitely needed in this fanboy's opinion.

    Oh and it works for the Address bar too, only the newlines get removed instead of getting replaced with spaces. Could be useful for multi-line URLs which appear quite often in emails!

  • New in Edge Rails: helper for creating form labels

    Edge Rails now comes with a helper for creating form labels, so in your views, instead of doing this:

    <% form_for(:user, @user, :url => user_path(@user)) do |f| -%>
    <label for="user_login">Login</label>
    <%= f.text_field :login %>
    <end>

    where the <label> is given a for attribute of user_login (since the convention for form input ids is {MODEL_NAME}_{ATTRIBUTE_NAME}), you can instead now do:

    <%= label(:user, :login) %>

    Quite a bit neater. Makes it easier to explain to beginners how they should use <label>s appropriately since the syntax is similar to the other form helpers. Now give your <label>s proper for attributes if you're not doing so already.

    This makes plugins like this form label helper plugin obsolete.

  • New in Edge Rails: Serialize ActiveRecord objects to JSON with ActiveRecord#to_json

    Converting ActiveRecord instances to their JSON representation has always been a topic that has been dear to me. I'd tried to get a patch that adds to_json to ActiveRecord into Rails (but the quality of my patch was lacking in a few respects). The Jsonifier plugin that I wrote tried to address the lack of built-in JSON serialization by adding a ActiveRecord#to_json method that acts much like ActiveRecord#to_xml.

    So it is with mixed feelings when I discovered a couple of days ago when DHH committed a changeset that added a native ActiveRecord#to_json method. Of course, I'm elated that JSON serialization is given first class attention now, since the raison d'être of Jsonifier and my Rails patch was to do just that. A little bit of me is selfishly disappointed since it means that Jsonifier has become quite obsolete!

    But it is an excellent change, I look forward to patching it up (if DHH doesn't beat me to it - there've been so much activity on the Rails trunk recently) so that the JSON serializer supports an :include options for including associations much like for ActiveRecord#to_xml and Jsonifier's mixed in to_json.

    Update: I've submitted a patch for adding :include to ActiveRecord#to_json. Please test it and give feedback or +1 ;).

  • ActiveRecord database :default "gotcha"

    Note to self:

    When a table column has a default value, such as for the type_category column/attribute like so:

    t.string :type_category, :limit => 20, :nil => false, :default => 'tv'

    a new instance of your ActiveRecord model will try and set the defaults from the database. Meaning:

    Anime.new => #<Anime id: nil, type_category: "tv"...

    Courtesy of this in ActiveRecord::Base:

    def attributes_from_column_definition
      self.class.columns.inject({}) do |attributes, column|
        attributes[column.name] = column.default unless column.name == self.class.primary_key
        attributes
      end
    end

    So, don't get confused when your model validations (validates_presence_of :type_category) don't seem to work when testing:

    it "should require a type_category" do
      @foo.attributes = valid_foo_attributes.except(:type_category)
      @foo.should have(1).error_on(:type_category)  # doesn't work since @foo.type_category = 'tv'
    end

    Explicitly set it to nil or a blank value.

    Sounds silly, but it's a true story.

  • What's new in Firefox 3: Download Resume

    This is not exactly a new change to Firefox 3's nightly builds since the bugs have been setting in my browser tab bar for about a week but I figured I'd write about them anyway since it's quite a nice improvement.

    What's new? Download Resume - now you can pause and resume your downloads in the Firefox Download Manager. Implemented by a Summer of Code student, this brings Firefox's Download Manager up to speed with Safari (which has a really nice download resume feature that leaves .download files that you can just double-click to resume the download).

    Firefox 3 download resume


    You'll notice the insane speeds (986GB/s) I'm getting too in that last screenshot. Nope it's not a feature, it's a bug. And a known one that's probably fixed if you have a recent nightly.

    For Firefox 1.5 or 2 users, don't despair, extensions like DownThemAll! allow you to resume downloads. If you use an external download manager, you'd probably be more interested in FlashGot.

  • Moar wiurd SSIDs

    Following my previous post on SSIDs, I saw this today when I fired up my MacBook on the bus:

    SSID = Jesus Redeemer


    Some of the ones I liked from the comments are:

    • broken
    • youneedbettersecurity
    • INTERNETS THROUGH THE AIR

    Have you seen any amusing SSIDs today?

  • "Open in Tabs" fixed in Firefox 3, at last

    The last time I blogged about Firefox's "Open in Tabs" behavior was 3 years ago in September 2004. Open in Tabs is quite a cool bookmark feature where you can open a folder of bookmarks.

    Unfortunately, some of us felt that there it had a flawed implementation. Here's how you can see it for yourself:

    Here, try this in a new Firefox window. Open up 4 tabs. Make sure that there’s nothing in these tabs that you want to remember to come back to later, especially the last 2. Now try using "Open in Tabs" on a bookmark folder with just 2 bookmarks. What just happened? Firefox has closed the last 2 tabs and loaded the first 2 tabs with the first 2 bookmarks. Well, actually this is not so bad for the first 2 tabs, because you can use the "Back" button to go back to your page should you want to, but the tabs that were closed are lost.

    It was then rather unfortunate that this bug was closed as WONTFIX. I was thus surprised to see a patch for a similar bug land in the Firefox 3 trunk about a week ago (I haven't had time to blog about it until now!)

    This change in Firefox 3 means that "Open in Tabs" is no longer a destructive proposition - a "use existing tabs and append" strategy is employed.

    Here's how it looks like graphically (which I expect would be easier to understand compared to aforementioned "use existing tabs and append" strategy). Let's start with 4 tabs, with the Google, Ruby on Rails, Facebook and jQuery websites loaded:

    Open in tabs, initial


    I have a bookmark folder with 2 bookmarks (to the Firebug and Prototype websites) in them:

    Open in tabs, bookmark folder


    If I click on "Open in Tabs" in Firefox 3, I end up with this:

    Open in tabs, initial


    Notice how the Back button is enabled on the Firebug tab (allowing us to go back to the Google webpage), and the Prototype bookmark is loaded in a new tab after the Firebug one.

    Small little tweak you may say, but it's all these small usability tweaks (like the recent Password Manager improvement) that promise to make Firefox 3 a much better browser.

  • Dugg? No problem with WordPress

    An old post I wrote on a spoof MMORPG named "Outside" was Dugg not too long ago and I was quite pleased to find that my server and the blogging software (WordPress) that I use was handling the load extremely well. Quite obviously I was getting more hits in a day than entire months:

    Blog stats after getting Dugg


    My setup is a Virtual Private Server (VPS) with 256MB RAM hosted at SliceHost and this blog is served off Nginx and PHP FastCGI processes to handle PHP scripts. The wonderful (because it just works and is really easy to setup) WP-Cache WordPress plugin keeps a cache of pages that's swept at logical times (i.e. whenever there are any updates or comments).

    I may be a Rails/merb fanboy, but this awesome piece of blog software that can stand up to the Digg Effect with ease is great. WordPress FTW!

  • jQuery 1.2 goodness

    jQuery 1.2 was released yesterday and while it's unfortunate that it doesn't work with some of the fantastic jQuery plugins I am already using, there're some really cool new features. In particular (this is my favorite), built-in cross-domain script loading via the getScript is just too convenient.

    This would be awesome for loading heavy scripts later and only when they are "activated", like Google Maps (which was exactly what I was trying to do earlier before realizing I'd need to setup a reverse proxy to workaround the cross-domain problem).

    Oh, and the easy way to turn off browser caching in AJAX calls is a nice convenience as well (no need to append random numbers or timestamps manually).

    Yup, I'm in love with jQuery right now. Protoype and YUI have fallen by the wayside like Chinese first and second wives of yore.

  • What's your SSID?

    After seeing this amusingly-named SSID, I thought it'd be fun to post the SSID of my wireless network at home:

    AssAssID / SSID?


    The name is completely not my idea so I claim no credit - I stole it from my girlfriend's brother, who conceived the SSID of "AssAssID".

    What's your SSID and have you seen any amusing ones? Or is yours one of the bajillion 'linksys' or 'DLINK' SSIDs in existence?

  • Smarter (and less-annoying) Password Manager in Firefox 3

    This is really nice: trunk builds of Firefox 3 since 1 Sept 2007 have a smarter Password Manager that asks you whether you want to save your passwords in a non-modal way. (The relevant enhancement ticket is replace modal pre-submit save password dialog with post-submit bar for those of you who want to read the bug report.)

    Anyway, in simpler words (and some screenshots to follow), what this means is that whenever you login to a site that requires your password, Firefox will no longer wait for you to tell it whether it should remember your password. I bet this looks familiar:

    Firefox 2 modal 'remember my password' dialog


    Its usability is lacking because of 2 things:

    • if I entered the wrong password told Firefox to remember my password, I'd have to go to the Password Manager to delete it, and
    • Firefox doesn't submit your login until you tell it what to do with your password, annoying for 2 reasons:
      1. Sometimes I'm not sure if I entered the right password - but I'd be pretty sure of that if I get to the next page (i.e. if my login request was submitted).
      2. It's just plain faster if Firefox submitted the login and asked about my password together/later - blocking the submit is just a waste of time (i.e. a non-modal dialog would make more sense).

    So, especially since I've used used Firefox for 156 hours at work since a month and a half ago (my most used application apparently), it's pretty cool to find out that Firefox 3 will come with a non-modal "Remember my password" dialog. Here's what it looks like:

    The new non-modal 'Remember my password' dialog in Firefox 3


    The dialog appears on top right after Firefox submits the login form. One more thing to look forward to in Firefox 3!

  • How to have Firefox 3 and Firefox 2 running at the same time

    Firefox 3, or Gran Paradiso, is really shaping up with some pretty cool new features being implemented in the recent months. That's not including the cool FUEL (Firefox User Extension Library) JavaScript library that promises to make extension development a bajillion times more productive, Places for data storage, and the use of Cairo for graphics.

    The problem with playing around with Firefox 3 is (by default) you can't run both Firefox 2 and 3 at the same time since they both insist on using the same profile and insist on running only 1 copy of Firefox at any one time.

    Want to get in on checking out the new features but still want good old Firefox 2? Here's how:

    • First, you should create a new profile just for testing Firefox 3. You should do this to keep your precious Firefox profile safe from any problems in Firefox 3. Otherwise, Firefox 3 will use your current Firefox 2 profile by default. You can create a new profile easily with the Profile Manager, which you can access by running:

      /path/to/firefox -profilemanager -no-remote

      (Thanks to Jeff for pointing out that there is no need to close Firefox first before creating a new profile.)

      Firefox profile manager


      Create a new profile (I gave it an obvious name, 'minefield', which is the codename of the current Firefox trunk). If you get confused, the official Firefox website has more detailed instructions on how to create a new profile.

    • Now, you should already have one of the Firefox nightly builds. If not, you can download the latest nightly build from the Firefox nightly build directory. You can start up Firefox 2 normally (just so you can see Firefox 2 and 3 running side by side). For Firefox 3, we're going to start it differently.
    • We're gonna run Firefox 3 using our newly created profile. I'd written earlier on how you can run two Firefox profiles at the same time, and you can still do the same with Firefox 3:

      /path/to/firefox3 -P minefield -no-remote &

      The 'minefield' in the command is the name of the profile we created earlier. Windows users should leave out the '&' (on UNIX-based systems, this just runs Minefield in the background so that you can exit your terminal).

    • Firefox 3 should start up with your new clean profile!

      Firefox and Minefield icons in my Mac OS X dock


      That's the Firefox 3 (Minefield) icon living right next to my Firefox 2 icon in my dock (yeah, I use a Mac).

    Andy Croll (who works at Bezurk too) has written a convenient little AppleScript so you don't have to repeat these steps.

    Just so I can put up another screenshot (as if I needed to prove that this really works), here's Firefox 2 and one of the recent Firefox 3 nightly builds running side by side:


    Have fun! I think I'll get back to watching the development of the trunk (now that it's getting more exciting) like I used to in the build up to Firefox 1 and 2. I may even make some posts!

  • Most sensible post on the Odex controversy in Singapore

    RE: the recent anime and IP controversy in Singapore, everyone affected in Singapore should read this sensible post by Dark Mirage. He offers really good advice on what you should and shouldn't do should you find yourself the recipient of one of Odex's letters. Be sure to read the comments too.

    More reading:

  • Don't trust web apps (or how Google Reader and my ISP conspired to lose all my feeds)

    About a month ago, I logged in to Google Reader to see a shocking thing - all my subscriptions were gone and I was subscribed to some feeds that I've never heard of.

    All my feed subscriptions gone from Google Reader


    I would say I'm a pretty heavy Google Reader user, checking my several hundred subscriptions several times a day, so it was pretty distressing. I posted a thread at the Google Reader Google Group (the link doesn't work now because they've moved the Google Group somewhere else for some reason) and several other people responded that they were seeing the same weirdness.

    I had the same problem, and here's the really weird part: I got YOUR feeds! If you look at the top of the page you can see the account you're supposedly logged in with. Well, I saw your account there. And this was before I read this message. I also saw some other names every so often. Right now it says email censored.

    Maybe it's some weird mix up with the ISP. Do you use SingNet?

    Some time later I started seeing other people's accounts:

    Seeing other people’s accounts in Google Reader


    The problem affected only Singnet (a major local ISP) users in Singapore (about 7 other users in Singapore using Singnet responded with the same problems). No one really knew what the problem was and neither the Google Reader team nor Singnet (someone sent them an email) responded.

    My guess was Singnet started caching Google Reader at a proxy and somehow managed to bypass all the credentials that was needed to modify any feed subscriptions. Sounds like a pretty nasty security bug to me.

    The aftermath:

    • Google Reader no longer has my feed subscriptions (I didn't manage to recover them so it's still in the sad state of having only 1 feed subscription to some UK property shite).
    • I've manually rebuilt my OPML file (containing my feed subscriptions).
    • I'm now using NewsFire. It's not decentralized so I can't sync it between machines, which really sucks since I have 2 primary machines, but I'm wary of using online feed readers right now.
    • I regularly export my OPML file and check it into Subversion.
    • I'm looking for a reliable decentralized feed reader once again (having used Bloglines and of course, Google Reader prior to the catastrophe).
  • ActiveRecord attribute-related improvements on Edge Rails

    I just noticed a very fresh changeset committed into the Rails trunk that's pretty damn cool: changeset 7315. Michael Koziarski (aka nzkoz") has refactored ActiveRecord's attribute-related methods that, among other things:

    • caches typecasted attribute values in hashes. This avoids expensive typecasting (such as for Time-related attributes) which means access to these fields is significantly faster!
    • moves generation of attribute methods to the class, instead of the instance.
    • generates attribute reader and writer methods (before, only reader methods were generated and writer methods relied on method_missing magic).

    The Ruby on Rails: Core Google Group thread on this patch has more info on the changes and some of the rationale behind them.

    Here's a simple (read: totally unscientific) benchmark I ran to access a DateTime attribute 10000 times (I ran it that many times to reduce the effect of the City.find call) with pre-revision 7315 edge Rails (I was using revision 7314):

    >> Benchmark.bmbm do |x| 
    ?>   x.report('test') { c = City.find(:first); 10000.times { c.created_at } }
    >> end
    Rehearsal ----------------------------------------
    test   1.500000   0.010000   1.510000 (  1.568782)
    ------------------------------- total: 1.510000sec
    
               user     system      total        real
    test   1.500000   0.010000   1.510000 (  1.592264)

    Post changeset 7315:

    >> Benchmark.bmbm do |x| 
    ?>   x.report('test') { c = City.find(:first); 10000.times { c.created_at } }
    >> end
    Rehearsal ----------------------------------------
    test   0.100000   0.010000   0.110000 (  0.129514)
    ------------------------------- total: 0.110000sec
    
               user     system      total        real
    test   0.000000   0.000000   0.000000 (  0.009831)

    Quite a bit faster!

  • MMORPG called "Outside"

    MMORPG called ‘Outside’


    Seen at http://chickencrap.com/c.php?c=980.

    I thought it was funny (I was a hardcore World of Warcraft player). All the features are so true too. Fully PvP rawr! The penalty of death sucks though - if you thought the experience point penalty in EverQuest was bad, think again.

    This post got Dugg! I had quite a few giggles reading the comments there and those posted here. And yes, I think that is KL (Kuala Lumpur) Tower in the picture.

  • How to install Rails, the wrong way

    I've always thought the Rails installation instructions on the official Rails website were pretty straightforward, so I couldn't help laughing when I saw this chat transcript a friend copied and pasted for me:

    [5:23:05 PM] X says: try so many times finally can le
    [5:23:23 PM] X says: it is not about the version or wat
    [5:23:46 PM] Y says: then what is the error?
    [5:24:22 PM] X says: the gem install rails--include-dependencies
    [5:24:37 PM] X says: between rails and --include got spacing

    I know it's mean to laugh but I'm not a carebear! I think it took him a day to install it despite having installed and uninstalled it for 2 whole months (don't ask me why, but his environment kept getting borked).

  • Adium themes use plain old XHTML, CSS and JS

    Just a quick note of something I found out while reading Transcending CSS: all the themes (message styles, contact list styles, etc.) in Adium are crafted with our old friends XHTML, CSS and JS. I opened up a few AdiumXtras to see for myself and true enough, <div>s and CSS rules make up the style. Very smart decision by the Adium developers.

  • Some of the world's hottest startups - Bezurk on Business 2.0

    Business 2.0 recently published an article on the world's hottest startups outside the US and Bezurk was picked. Nice to know that ;)

    Bezurk on Business 2.0


  • YSlow - Firefox extension that helps to optimize your web pages

    YSlow is a Firefox extension that works with Firebug to grade your web pages' performance and gives you advice on how to fix any problems. Awesome. It has JSLint built into it too. And yes I got a big freaking F. Via Arun (IRL).

  • MySQL command line "secret"

    Tom Preston-Werner (of Gravatar and Chronic fame) wrote about this neat MySQL command line "trick" where instead of ending your SELECT statements with the usual semi-colon, using a "\G" gives you a very readable output.

    mysql> select * from locations where location_code = 'SIN'  limit 1\G
    *************************** 1. row ***************************
              location_code: SIN
              location_name: Singapore Changi Apt 
    multi_airport_city_code: SIN
              location_type: A
          location_sub_type: A
               country_code: SG
                 state_code: 
                     active: 1
                   latitude: 01.22.00N
                  longitude: 103.59.00E
    1 row in set (0.10 sec)

    Very useful, especially when SELECTing multiple fields where the output becomes unfit for visual consumption (you know what I mean, just go SELECT some stuff in your MySQL command line and you'll see if you don't).

  • God and your web server

    God is a new process monitoring Ruby framework with a rather apt name (no, it's not that God). The config files looks something like that:

    God.meddle do |god|
      god.watch do |w|
        ...
      end
    end

    Interesting code to write. And feels safe too to have God watching over your mongrels. Oh and you can use it to clean up stale mongrel PID files without patching mongrel_rails. Right now I use monit a lot and it's really rather unDRY to have repeated configs for each mongrel on the server (is there a better way of doing this in monit?). God would be able to DRY it up.

  • logrotate and mongrel (mongrel_cluster)

    I've never actually explored log rotation tools in Linux and merely knew of the existence of logrotate and left it at that until log sizes really became a problem. Turns out logrotate is amazingly easy to configure, and thanks to this Logrotate and Mongrel blog post by Corey Donohoe I have it setup for my Rails applications that are running on Mongrel. Here's what my /etc/logrotate.d/mongrel_cluster looks like:

    /var/railsapps/APP_NAME/shared/log/production.log {
      daily
      rotate 28
      missingok
      compress
      sharedscripts
      postrotate
        for i in `ls /var/railsapps/APP_NAME/shared/log/*.pid`; do
          kill -USR2 `cat $i`
        done
      endscript
    }
  • Better JSON output from Rails with the Jsonifier plugin

    Update: Edge Rails now does whatever Jsonifier does (and more). Check out my blog post on JSON serialization maturity in edge Rails. This means that this plugin is virtually obsolete if you're using edge Rails.

    If you've tried to output JSON from your Rails applications before, you'd probably have noticed how inadequate it all seems. Let's look at the kind of output (pretty-printed for easier reading) you get from calling to_json on your typical User model:

    {
      attributes: {
        id: 1,
        name: "Konata",
        awesome: true,
        created_at: "07/01/2007"
      }
    }

    Some of the common problems developers who write applications that speak JSON have with this are:

    • The attributes part is often unnecessary cruft when consuming ActiveRecord objects as JSON.
    • There's no obvious way to control which attributes are shown in the JSON (e.g. you may want to hide private or lengthy attributes). The way to workaround this is either to write your own to_json instance method for your ActiveRecord model, or to use a Rails plugin (like acts_as_json) or a gem.
    • Including 2nd (or higher order) associations is tricky (as above, you can achieve this by defining your own to_json instance methods).

    Under the hood of the current ActiveRecord#to_json

    The to_json method for ActiveRecord objects falls back on using that defined in ActiveSupport's JSON classes. The way it's currently done, there are a bunch of JSON encoder classes defined for various types - when you call to_json on an ActiveRecord object it simply uses Object#to_json.

    By the way, see that created_at: "07/01/2007" bit in the example above which is the JSON output for a Date attribute? That's in MM/DD/YYYY format. Yup, 1st July 2007 not 7th January 2007 you "rest of the world you". Ouch, but at least you can use JavaScript's Date.parse() on it directly as detailed in this patch. And before you ask, yes, Date.parse() doesn't understand the YYYY-MM-DD format.

    Can has Rails patch?

    So about 2 days ago, I wrote a Rails patch to boost ActiveRecord#to_json with an options hash. Yep much like what you can do with ActiveRecord#to_xml.

    I hope it gets committed into the core some day. I mean, gosh, isn't Rails all about Web 2.0 mashups. It even has that fancy render :json thing. I do believe JSON output needs to be treated more seriously in Rails rather than the half-hearted attempt at JSON encoding. No? Well, if the patch doesn't get accepted I'll still have my plugin (see below).

    Anyway, let's see some examples:

    Some examples, see?

    Assuming User and Post models where User has_many Posts:

    david = User.find(1)
    david.to_json  # {id: 1, name: "David", awesome: true, created_at: "07/01/2007"}
    

    No more attributes cruft!

    :only and :except work the same way that as for ActiveRecord#to_xml:

    david.to_json(:only => :name)                 # {name: "David"}
    david.to_json(:only => [:id, :name])          # {id: 1, name: "David"}
    david.to_json(:except => :created_at)         # {id: 1, name: "David", awesome: true}
    david.to_json(:except => [:id, :created_at])  # {name: "David", awesome: true}

    You can use the :methods options as well to include any methods on the object.

    david.to_json(:methods => :permalink)
      # {id: 1, name: "David", awesome: true, created_at: "07/01/2007", permalink => "1-David"}
    david.to_json(:methods => [:permalink, :interestingness])
      # {id: 1, name: "David", awesome: true, created_at: "07/01/2007", \
      #   permalink => "1-David", :interestingness => 666}

    The :include option lets you include associations.

    david.to_json(:include => :posts)
      # {id: 1, name: "David", awesome: true, created_at: "07/01/2007", \
      #    posts: [{id: 1, author_id: 1, title: "Welcome to the weblog"}, \
      #            {id: 2, author_id: 1, title: "So I was thinking"}]}

    :only, :except, and :methods works on the included associations as well:

    david.to_json(:include => { :posts => { :only => :title } })
      # {id: 1, name: "David", awesome: true, created_at: "07/01/2007", \
      #    posts: [{title: "Welcome to the weblog"}, \
      #            {title: "So I was thinking"}]}

    Of course, 2nd level (and higher order) associations work too:

    david.to_json(:include => { :posts => { \
                                  :include => { :comments => { \
                                                  :only => :body } }, \
                                  :only => :title } })
      # {id: 1, name: "David", awesome: true, created_at: "07/01/2007", \
      #    posts: [{comments: [{body: "1st post!"}, {body: "OMGWTFBBQ!"}], \
      #             title: "Welcome to the weblog"}, \
      #            {comments: [{body: "Don't think too hard"}], \
      #             title: "So I was thinking"}]}

    Please do give any feedback at the Rails patch for ActiveRecord#to_json or here. I wanna know what you feel about adding this functionality into the Rails core!

    Jsonifier plugin

    I've rolled the patch into a plugin: Jsonifier. The Jsonifier Trac is at http://trac.codefront.net/jsonifier/.

    Git repository:

    git://github.com/chuyeow/jsonifier.git

    Github project page:

    http://github.com/chuyeow/jsonifier/

    To install the plugin:

    ruby script/plugin install git://github.com/chuyeow/jsonifier.git

    or git clone the repository manually into the vendor/plugins directory.

    A note on valid JSON

    The JSON Rails spits out by default is not strictly valid JSON since the JSON specifications require keys to be double quoted. To get strictly valid JSON, add

    ActiveSupport::JSON.unquote_hash_key_identifiers = false

    in your environment.rb (or in the Rails initializers directory if you're on edge). See my blog post on how to get strictly valid JSON from Rails for more info.

  • Haruhi Season 2 announced!

    Season 2 of The Melancholy of Haruhi Suzimiya was announced in the August 2007 edition of NewType Japan - check out the scans. The announcement isn't official, but there'll be one on the 7th - that's 7/7/2007!

  • WTF is this? (Or how NIS and portmapper on Ubuntu Dapper Drake drove me nuts)

    Not too long ago a couple of the Ubuntu Dapper Drake (6.06 LTS) VPSs we use abruptly became inaccessible. These VPSs were NIS clients that suddenly could no longer connect to the NIS master. Great. And as far as I could tell I didn't do anything to those servers. I even managed to replicate the problem on another similar VPS (that was still accessble) simply by restarting the NIS daemon.

    Thanks to Rimuhosting's fantabulous support, I managed to regain access to the VPSs. Oh yeah, big lesson learnt here: when configuring NIS clients, be sure to leave at least one user account with sudo privileges or set the root account's password (Ubuntu doesn't come with a root account enabled by default - run sudo passwd to set it and enable the root account as a side effect). Big big lesson learnt. Don't let this bite you in the ass if you're a sysadmin-ish person.

    Anyway, /var/log/syslog was complaining this:

    ypbind[1026]: Unable to register (YPBINDPROG, YPBINDVERS, udp)

    Which turned out to be a portmap problem. So yeah portmap wasn't running! For a moment there I thought we had an easy solution. Sadly, /etc/init.d/portmap start was throwing this inexplicable error (i.e. Google wasn't of much help):

    [email protected]:/sbin# /etc/init.d/portmap  restart
     * Stopping portmap daemon...
       ...done.
     * Starting portmap daemon...
    dhclient.4.x 2.*.5, December 30th, 2000
    Compiled on Dec 12 2003 09:55:54
    Features: DBG, SEE, ALS, SEF
    init: Couldn't open the file ./libuuid.so ->
       ...fail!

    It sure isn't caturday. After hours spent scrambling to find out why portmap can't find libuuid.so, reinstalling the NIS .deb, comparing configuration files between working NIS clients and these busted ones, it finally dawned on me that I have yet to re-install portmap (not that it was logically sound that I'd have to reinstall it, but at this point I was willing to try anything). Annoyingly enough, it worked!

    apt-get remove portmap nis
    apt-get install nis

    I still don't know WTF happened there. If anyone has any clue please please point me to it.

  • Installing Xen on Ubuntu Feisty Fawn - the complete newbie's guide

    I had to spend some time setting up Xen on one of the new Dell servers we bought and while there was some documentation around, I had to constantly refer to the different sources since there wasn't a complete page which had everything I needed for my particular situation - I have a host machine with Ubuntu Feisty Fawn installed and wanted to install a couple of Xen guest domains on it. One other thing I found lacking in the documentation is the explanation of commands or answers to the question "so why the hell am I doing this?". I'm not considering LVM at the moment and am happy with loopback disk file images too.

    So I think it's a good idea to write this down somewhere for the next time I have to do the same bloody thing over again on any new servers.

    Some caveats before we start:

    • Like I said, I'm not gonna talk about LVM for disk images since I know next to nothing about configuring it at the moment. The host machine I have isn't setup with LVM and I haven't got the time to look into LVM either.
    • I'm quite the networking newbie, so my setup uses the default Xen bridge installed via apt-get. Check out Xen networking on the Xen wiki for more complex network setups.
    • I'm not gonna talk about putting a Linux distro other than that of the host machine into the Xen virtual machines. In other words, I have Feisty Fawn for both dom0 and all my domUs.
    • I probably did some completely stupid things. Stuff like xen-tools exist to simplify Xen administration, but I haven't had a chance to use them. Let me know if you spot an error or have a better way of doing something please!

    Some terms that deserve explaining (most guides out there just use "DomU" and "Dom0" without any ceremony, and assume you know what they mean):

    • Domain-0 or Dom0: This refers to the host machine OS. You know, the OS of the actual physical server that you have.
    • DomU: This refers to a Xen guest domain. A DomU is a single Xen virtual machine. The "U" stands for "unprivileged" I believe.

    OK let's go!

    Installing the Xen Server

    First off, we need to install the Xen server on the host machine (or Dom0). Thankfully, there is a Xen server package in Feisty Fawn's apt-get repository.

    • Make sure 'universe' is enabled in /etc/apt/sources.list and run: apt-get install ubuntu-xen-server
    • Reboot the machine. After boot, running sudo xm list should show you Domain-0 (which is the host machine that's running Xen).
    • Edit /etc/xen/xend-config.sxp and uncomment this line:
      (network-script network-bridge)
    • Comment out this line:
      (network-script network-dummy)
    • Restart xend:
      sudo xend restart
    • When you run 'ifconfig', you should see the 'xenbr0' interface. This is the Xen network bridge.

    Creating a base image

    Now let's create a base Xen image. We'll use this as a template for any future images (domUs).

    • Use 'dd' to create the images (I place them in /xen-images/):
      dd if=/dev/zero of=/xen-images/feisty_base.img bs=1024k count=4000
      dd if=/dev/zero of=/xen-images/feisty_base_swap.img bs=1024k count=1000

      These commands create image files of 4GB and 1GB for your virtual OS and its swap respectively. 'count' is the number of blocks (block size = 1024k).

    • Create the filesystem (we're using ext3 in this case) within the virtual OS image file.
      mkfs.ext3 /xen-images/feisty_base.img

      Answer 'yes' to proceed anyway when it complains about the file not being a block device.

    • Do the same for the swap file:
      mkswap /xen-images/feisty_base_swap.img
    • Now, restrict the permissions on the images so no one else but root can screw around with them.
      chmod 640 /xen-images/feisty_base*
    • Create a mount point for the image. We need this to mount the image to install and configure Ubuntu Feisty Fawn the way we like it. We can then replicate this base image for any future virtual machines.
      mkdir /xen-images/mnt
    • Mount the base image via the loop device:
      mount -o loop /xen-images/feisty_base.img /xen-images/mnt

      Now we have full access to the base image's filesystem. What we need to do now is to put a base install of Feisty Fawn on it.

    Installing and configuring the base image

    Next, we have to put Ubuntu onto the image and configure it to our liking.

    • Install debootstrap, a handy tool that bootstraps a basic Debian (Ubuntu in this case) system.
      apt-get install debootstrap
    • Bootstrap the base image with 'feisty':
      debootstrap feisty /xen-images/mnt

      After this is done you should see the basic Ubuntu file hierarchy:

      ls /xen-images/mnt
    • Copy your sources.list into the base image:
      cp /etc/apt/sources.list /xen-images/mnt/etc/apt/
    • Copy your kernel modules as well (replace 2.6.19-4-server with whatever kernel version you have):
      cp -a /lib/modules/2.6.19-4-server/ /xen-images/mnt/lib/modules/
    • Install the libc6-xen package, which is a Xen-compatible glibc that allows applications to use Thread-Local Storage (TLS).
      apt-get install libc6-xen

      Taken from the Xen FAQ:

      Some modern distributions ship with a 'TLS' version of glibc that is not fully compatible with Xen. To use Xen reliably and with maximum performance you must disable the incompatible glibc.

    • Configure networking by editing /xen-images/mnt/etc/network/interfaces:
      auto lo
      iface lo inet loopback
      
      # Uncomment this and fill up with your networks settings.
      #auto eth0
      #iface eth0 inet static
      #  address 192.168.0.201
      #  netmask 255.255.255.0
      #  broadcast 192.168.255.255
      # gateway 192.168.0.1
      #  dns-nameservers 192.168.0.1

      Notice how the network settings are commented out - this is because we are leaving the base image as a template from which other images would be made from.

    • Edit the hosts file (/xen-images/mnt/etc/hosts):
      127.0.0.1       localhost localhost.localdomain
      127.0.0.1       guest
      
      # The following lines are desirable for IPv6 capable hosts
      ::1     ip6-localhost ip6-loopback
      fe00::0 ip6-localnet
      ff00::0 ip6-mcastprefix
      ff02::1 ip6-allnodes
      ff02::2 ip6-allrouters
      ff02::3 ip6-allhosts
    • Give the base image a generic hostname by editing /xen-images/mnt/etc/hostname. In this case I'm just naming it 'guest' - actual virtual machines would have sensible hostnames.
    • Edit the filesystem table (/xen-images/mnt/etc/fstab).
      proc            /proc           proc    defaults        0       0
      /dev/hda1       /               ext3    defaults,errors=remount-ro 0       1
      /dev/hda2       none            swap    sw              0       0

      Don't worry, the /dev/hda* would be configured to point to the images you created, not to your actual physical harddisks.

    • You're done with the initial configuration and have enough to bootup the image. Let's unmount the image:
      umount /xen-images/mnt
    • Now, we need to create a Xen config for the base image so we can boot into it. Create a file named /etc/xen/vm_feisty_base.config.example.sxp (you can name it whatever you like of course):
      name = "feisty_base"
      kernel = "/boot/vmlinuz-2.6.19-4-server"
      ramdisk = "/boot/initrd.img-2.6.19-4-server"
      root = "/dev/hda1 ro"
      memory = 512
      disk = ['file:/xen-images/feisty_base.img,hda1,w','file:/xen-images/feisty_base_swap.img,hda2,w']
      
      # Network.
      hostname = "feisty_base"
      vif = ['bridge=xenbr0']
      dhcp = "off"
      ip = "192.168.0.201"
      netmask = "255.255.255.0"
      gateway = "192.168.0.1"

    An explanation of the configuration options:

    • name - a descriptive name of your image.
    • kernel - path to the kernel.
    • ramdisk - path to the initrd (initial RAM disk) image.
    • root - point this to your host machine's harddisk where your image files are.
    • memory - the amount of memory assigned to the virtual machine in megabytes
    • disk - specify a list of block devices that will be export to the virtual machine. In this example, we're mapping the /xen-images/feisty_base.img image file to /dev/hda1 in the virtual image (same for the swap image). This should match whatever config you did in /xen-images/mnt/etc/fstab.
    • hostname - hostname of your virtual machine.
    • vif - virtual network interface that your virtual machine will use. In this case, we're using the default 'xenbr0' bridge that Xen installs. For alternative configurations, check out the networking page on the Xen wiki.
    • ip - specify your virtual machine's IP here.
    • netmask and gateway - netmask and gateway that your virtual machine should use.
    • Now, start the base virtual machine:
      xm create /etc/xen/vm_feisty_base.config.example.sxp -c

      You should see a console (the '-c' option connects you to the console immediately after booting up the virtual machine. Login as root (empty password) - you'll want to change the password (with 'passwd').

    • Enable shadowed passwords.
      shadowconfig on
    • Create a backup of /etc/network/interfaces and edit /etc/network/interfaces to put in correct values so you can access the Internet from the virtual machine:
      cp /etc/network/interfaces /etc/network/interfaces.bak
      vim /etc/network/interfaces
    • Start networking:
      ifup -a
    • Install the Ubuntu standard system after running an update:
      apt-get update
      apt-get install ubuntu-standard
    • I also like to upgrade any packages that have been updated, if any:
      apt-get upgrade
    • Install the OpenSSH server so that you can access the virtual machine via ssh:
      apt-get install ssh
    • At this point, do any configuration or installation of packages that you want this base image to have. Remember that we're going to replicate this base image for future Xen virtual machines so don't go overboard and install stuff you're not gonna use.
    • Stop networking and put back the original /etc/network/interfaces:
      ifdown -a
      mv /etc/network/interfaces.bak /etc/network/interfaces
    • Exit the guest console with Ctrl-]. This will bring you back to dom0's shell.
    • Shutdown the base Xen domU (-w means "wait for the domU to shutdown completely"):
      xm shutdown feisty_base -w

    You're done setting up the base image! Now you have a nicely configured base image.

    Creating your first virtual server

    The fruits of thy labor are almost at hand - with the base image, we can now make a copy of it for our first Xen virtual machine.

    • Make copies of the base image and the swap image (let's call our virtual server "yuki"):
      cp /xen-images/feisty_base.img /xen-images/yuki.img
      cp /xen-images/feisty_base_swap.img /xen-images/yuki_swap.img

      You may want to resize the base image depending on your disk space needs. (And, of course, you can create your own swap image like we did earlier instead of copying the base image's swap file.)

    • Make a copy of the base Xen configuration file that we created for our base image earlier (you can use whatever name you like):
      cp /etc/xen/vm_feisty_base.config.example.sxp /etc/xen/vm_yuki.config.sxp
    • Edit the /etc/xen/vm_yuki.config.sxp file, changing the values to suit your virtual server (you probably want to change at least the 'name', 'disk', 'ip, and 'hostname' values).
      name = "yuki"
      disk = ['file:/xen-images/yuki.img,hda1,w','file:/xen-images/yuki_swap.img,hda2,w']
      hostname = "yuki"
      ip = "192.168.0.202"
    • Now start up your cloned virtual server and log into it like we did earlier with the base image (if you haven't changed the password, you can login as root with an empty password).
      xm create -c /etc/xen/vm_yuki.config.sxp
    • The first thing you should do is to secure your root password (if you haven't done so in the base image earlier).
      passwd
    • Now, you'll want to change these files:
      • /etc/network/interfaces
      • /etc/hostname
      • /etc/hosts
    • With the proper network settings, you should be able to bring up networking to connect to the internet (assuming that your dom0 is connected to the internet).
      ifup -a
      ping google.com
    • At this point, you are free to customize your virtual machine as you see fit - install nginx for use as a static file server, Rails, MySQL server, whatever.
    • But first, let's make sure this Xen virtual server starts up automatically on boot. Exit the virtual machine console with Ctrl-]
      ln -s /etc/xen/vm_yuki.config.sxp /etc/xen/auto/

      Any Xen domain configuration placed in /etc/xen/auto/ will be started up automatically on boot (and shutdown when the host machine is shutdown too).

    That's it! You have a functional Xen guest domain that starts up on boot and that can connect to the Internet. Oh before I forget, some useful commands when dealing with the Xen images:

    • xm create /path/to/config_file - Starts up the guest domain with the given Xen domU configuration file. (xm create -c to login to the console immediately after booting the image.)
    • xm console yuki - Open the console for the 'yuki' Xen domain.
    • xm shutdown yuki - Shutdown the 'yuki' domU.
    • xm list - Lists all the Xen domains you have running.

    Resizing your virtual machine disk

    Let's say you wanna add 1GB of disk space to your virtual machine (named 'yuki').

    Note that LVM is usually recommended for Xen setups, so you may wanna take a look at that instead.

    • Create a temporary 1GB file:
      dd if=/dev/zero of=/tmp/temp_expand bs=1024k count=1000
    • Bring down the virtual machine (I've found that a shutdown is needed so that the virtual machine recognizes the increased disk space):
      xm shutdown yuki
    • Append the file to the disk image of the virtual machine (/xen-images/yuki.img in this example). Be sure to use '>>' (which means append) - using a single '>' would overwrite the disk image with your temporary file, which you most definitely don't want!
      cat /tmp/temp_expand >> /xen-images/yuki.img
    • Resize the file system:
      resize2fs -f /xen-images/yuki.img
    • Startup the virtual machine again:
      xm create -c /etc/xen/vm_yuki.config.sxp
    • Remember to login to your virtual machine to verify that the disk space has increased (df -h)
  • Prototype 1.5.1 is baaaaad for Safari 1 and 2 users

    Read the bug fix announcement. Upgrade! This was causing extremely puzzling crashes in Safari on Macs (pre-Safari 3 beta) on one of our applications and I'm glad (in some ways) that the problem lay with the Prototype library.

  • Bezurk featured on Channel NewsAsia

    The company I work for was featured on a short "Surf Time" segment on Channel NewsAsia's TV channel. Catch the Bezurk Surf Time video on YouTube.

  • How to get strictly valid JSON from Rails

    Update: Edge Rails (and the forthcoming Rails 2.0) will emit only valid JSON. Read JSON serialization of ActiveRecord objects reaches maturity for more details.

    If you've worked with JSON long enough in Rails, you'd probably have noticed that the JSON the convenient Object#to_json method spits out is not strictly JSON-compliant (according to the RFC 4627, which states that object keys should be "strings", and "strings" should be double-quoted). Here's an example of what to_json produces when called on an ActiveRecord instance:

    {
      id: 6589,
      code: "SIN",
      name: "Singapore",
      type: "City"
    }

    Notice how the keys (id, code, name, type) are not surrounded with double quotes. While this is generally not a problem if working purely in Rails, since Prototype and Rails itself can decode JSON like this, you'd run into problems with more finicky JSON parsers and decoders. PHP's json_decode is a perfect example - it demands that the object keys be double-quoted (or it fails to decode your JSON, silently returning NULL).

    For a while I couldn't figure out why Rails was doing it and Google was of no help. I decided to look at the Rails code since this can't be a huge oversight on the Rails community, can it? Sure enough, I found the "solution" in the encoding portion of the ActiveSupport::JSON module:

    
      # When +true+, Hash#to_json will omit quoting string or symbol keys
      # if the keys are valid JavaScript identifiers.  Note that this is
      # technically improper JSON (all object keys must be quoted), so if
      # you need strict JSON compliance, set this option to +false+.
      mattr_accessor :unquote_hash_key_identifiers
      @@unquote_hash_key_identifiers = true

    Doh! All that's left to do is to put ActiveSupport::JSON.unquote_hash_key_identifiers = false in your environment.rb (or in the Rails initializers directory if you're on edge) and your Rails app will start producing strictly valid JSON (and is friendlier to other non-Rails applications):

    {
      "id": 6589,
      "code": "SIN",
      "name": "Singapore",
      "type": "City"
    }

    unquote_hash_key_identifiers was added in changeset 5486.

  • Spam, the Dungeons and Dragons flavor

    Now that's how you get a Dungeons and Dragons fan to read your spam:

    Laurana's in Qualinesti now, attending the funeral of her father and also trying to arrange an agreement with that stiff-necked brother of hers, Porthios, and the Knights of Solamnia.

    Dragonlance spam email


  • So I just got back from PLAY! A Video Game Symphony...

    I just got back from PLAY! A Video Game Symphony over at The Esplanade Concert Hall (in Singapore) and man, it was good! I had doubts at first about buying the tickets since I didn't play most of the games on their programme, but listening and watching to the gameplay of those games that I did play was pleasantly nostalgic. Here's what was played, with the games that I did play highlighted:

    • PLAY! A Video Game Symphony - this is just a fanfare piece composed by the great Nobuo Uematsu (composer of, most notably, many Final Fantasy game soundtracks).
    • Super Mario Bros - what a game to open with! Being a fan-favorite, it came as no surprise to hear the collective gasps of recognition in the audience when game footage of the original Super Mario was played on the video screens. A suite of various in-game music was played, each time with much audible amusement from the audience.
    • Shenmue and Shenmue II
    • Lost Odyssey
    • Sonic the Hedgehog - oh man, I didn't realize how much I used to enjoy this game until I recognized every single tune they played in this suite. I played the very first Sonic the Hedgehog on a Sega Mega Drive (as well as a few others after this one - I remember Tails and Knuckles) way back in the day.
    • Metal Gear Solid
    • Blue Dragon - the much-hyped game had a few pieces of its soundtrack performed. I think I'd have been much more excited if they'd shown some actual game footage instead of scripted cutscenes.
    • Kingdom Hearts - a bunch of obvious console gamers near us made loud excited noises when the conductor mentioned this.
    • Final Fantasy VI
    • Daytona USA - the composer Takenobu Mitsuyoshi performed this piece himself!
    • The Elder Scrolls IV: Oblivion
    • Chrono Trigger and Chrono Cross
    • World of Warcraft - I have the Collector's edition of the original World of Warcraft which contains the game soundtrack (mostly consisting of music composed by Jason Hayes), so these were familiar tunes. My only gripe was that they didn't play the Undercity background music - it's my favorite.
    • Halo
    • Castlevania - I played the NDS version if that counts for anything. Can't say I recognized the music though.
    • The Legend of Zelda - yes, I really didn't play Zelda. I never owned a Nintendo before I got my NDS but I am eagerly anticipating the release of Phantom Hourglass! The conductor was expecting a great cheer from the crowd but it never came heh.
    • Final Fantasy VII - for the ending, they played One-Winged Angel from the game soundtrack which was pretty memorable since it was played during the boss fight against Safer Sephiroth and more recently in Final Fantasy VII: Advent Children. A very nice grand ending piece I must say. Still, I do wish they played game footage and more FFVII music than just a single piece. There're so many memorable songs in the game, and the chocobo tunes would have been amusing.
    Play! tickets and programme booklet


    There was also an encore which was very very cool, because the music was from Super Shinobi (also known as The Revenge of the Shinobi). This old Sega Mega Drive game was one of the coolest games of the time (you get to play a ninja!). The composer of the soundtrack, Yuzo Koshiro, was there himself. Super Shinobi's theme music was chosen as the song to play for an encore on Game Axis - I must say, good choice folks! Game footage was played and I recognized every single part of it - I didn't even realize I had memories like that. I'm sorely tempted to see whether my old Sega Mega Drive still works so I can play Super Shinobi again.

    Shinobi vs. Batman boss in Super Shinobi


    After this, the audience demanded another encore and we were rewarded with another rendition of One-Winged Angel (I'd have liked to hear Super Mario Bros again).

    All in all, an enjoyable performance - if you're a gamer and missed it, I feel bad for you! If Play! is coming to your location, be sure to get tickets.

  • LOLCATS + email

    I IZ BORED: MY LOLEMAIL POST, LET ME SHOW YOU IT.

  • So what exactly are you looking forward to in Leopard?

    So there's this big Leopard thing that's the talk of Mac town (well, that and Safari) with all the new features being revealed (officially, at least, to the general public) at WWDC 2007.

    Screenshot of Apple Leopard homepage


    Some of the features are pretty cool, most of them are mundane. For me, I am really looking forward to:

    • the new Finder - Finder sucks so bad as a file system interface. I use Path Finder, which incidentally had an upgrade recently to 4.7. The new Finder looks pretty (Cover Flow for files, sexy!) but what I'm really hoping for is a Finder with which you can actually be productive.
    • Ruby and Rails baked right into Mac OS X - while installing Rails and upgrading Ruby is a breeze on Mac OS X as it is right now, having these installed by default is pretty sweet. Even Capistrano will be included. Now, how one upgrades Ruby is another thing though...
    • Time Machine - Even though I already own a licensed copy of SuperDuper!, I'm still eager to use Time Machine. I mean, who isn't hooked on the time travel metaphor yet? It's like System Restore done right (with the advantage of hindsight, of course).

    What are you looking forward in Leopard?

  • That "Safari" thing...

    So what exactly is new in Safari 3 (other than the big OMGWTFBBQ over Safari for Windows)? TUAW has the best writeup (with screenshots of course) of the new features in Safari 3 I've seen so far. Check it out if you're lazy to try it out - I did install the beta and took it for a test drive, but was never much of a Safari user to recognize what exactly did change in Safari 3 beta, so this helped. I must say the new Find interface is really slick and the draggable tabs has been a long time coming (one of my biggest annoyances with Safari and Camino).

  • Nginx, PHP and a PHP FastCGI daemon init script

    One of the things us ex-Apache httpd or ex-Lighttpd users have to get used to when installing nginx is how there isn't built-in FastCGI support for process spawning. While these means nginx is more lightweight and faster, it does mean that you have to manage your FastCGI processes yourself.

    Having just upgraded to Ubuntu Feisty Fawn, I set to installing nginx and tried to get WordPress (which needs PHP, of course) running on it. Thanks to the new nginx package in the Feisty universe repository, getting nginx up and PHP (CGI version) and running is really straightforward. There're lots of tutorials to help you out as well.

    So that's that. The part that bugged me though, was how the tutorials always provided a script to start your PHP FastCGI processes, but never provided a way to ensure your FastCGI processes started up on a reboot (i.e. an init script). Once again, Google doesn't disappoint, turning up this php-cgi init script. Tweak the init script a little, put it in /etc/init.d/php-fastcgi, sudo chmod +x /etc/init.d/php-fastcgi, run sudo update-rc.d php-fastcgi defaults and place the configuration for the script in /etc/default/php-fastcgi, and you're done. You now have a PHP FastCGI init script that spawns and kills your PHP FastCGI processes.

    Note: Lighttpd does come with a spawn-fcgi binary that does the same, but I'm having trouble finding a suitable init script for spawn-fcgi (only the Gentoo emerge seems to have the init script). Still, this script works and I didn't really want to install lightty on my starving VPS.

    The init script: /etc/init.d/php-fastcgi

    #! /bin/sh
    ### BEGIN INIT INFO
    # Provides:          php-fastcgi
    # Required-Start:    $all
    # Required-Stop:     $all
    # Default-Start:     2 3 4 5
    # Default-Stop:      0 1 6
    # Short-Description: Start and stop php-cgi in external FASTCGI mode
    # Description:       Start and stop php-cgi in external FASTCGI mode
    ### END INIT INFO
    
    # Author: Kurt Zankl <[EMAIL PROTECTED]>
    
    # Do NOT "set -e"
    
    PATH=/sbin:/usr/sbin:/bin:/usr/bin
    DESC="php-cgi in external FASTCGI mode"
    NAME=php-fastcgi
    DAEMON=/usr/bin/php-cgi
    PIDFILE=/var/run/$NAME.pid
    SCRIPTNAME=/etc/init.d/$NAME
    PHP_CONFIG_FILE=/etc/php5/cgi/php.ini
    
    # Exit if the package is not installed
    [ -x "$DAEMON" ] || exit 0
    
    # Read configuration variable file if it is present
    [ -r /etc/default/$NAME ] && . /etc/default/$NAME
    
    # Load the VERBOSE setting and other rcS variables
    . /lib/init/vars.sh
    
    # Define LSB log_* functions.
    # Depend on lsb-base (>= 3.0-6) to ensure that this file is present.
    . /lib/lsb/init-functions
    
    # If the daemon is not enabled, give the user a warning and then exit,
    # unless we are stopping the daemon
    if [ "$START" != "yes" -a "$1" != "stop" ]; then
            log_warning_msg "To enable $NAME, edit /etc/default/$NAME and set START=yes"
            exit 0
    fi
    
    # Process configuration
    export PHP_FCGI_CHILDREN PHP_FCGI_MAX_REQUESTS
    DAEMON_ARGS="-q -b $FCGI_HOST:$FCGI_PORT -c $PHP_CONFIG_FILE"
    
    
    do_start()
    {
            # Return
            #   0 if daemon has been started
            #   1 if daemon was already running
            #   2 if daemon could not be started
            start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON --test > /dev/null \
                    || return 1
            start-stop-daemon --start --quiet --pidfile $PIDFILE --exec $DAEMON \
                    --background --make-pidfile --chuid $EXEC_AS_USER --startas $DAEMON -- \
                    $DAEMON_ARGS \
                    || return 2
    }
    
    do_stop()
    {
            # Return
            #   0 if daemon has been stopped
            #   1 if daemon was already stopped
            #   2 if daemon could not be stopped
            #   other if a failure occurred
            start-stop-daemon --stop --quiet --retry=TERM/30/KILL/5 --pidfile $PIDFILE > /dev/null # --name $DAEMON
            RETVAL="$?"
            [ "$RETVAL" = 2 ] && return 2
            # Wait for children to finish too if this is a daemon that forks
            # and if the daemon is only ever run from this initscript.
            # If the above conditions are not satisfied then add some other code
            # that waits for the process to drop all resources that could be
            # needed by services started subsequently.  A last resort is to
            # sleep for some time.
            start-stop-daemon --stop --quiet --oknodo --retry=0/30/KILL/5 --exec $DAEMON
            [ "$?" = 2 ] && return 2
            # Many daemons don't delete their pidfiles when they exit.
            rm -f $PIDFILE
            return "$RETVAL"
    }
    case "$1" in
      start)
            [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$NAME"
            do_start
            case "$?" in
                    0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                    2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
            esac
            ;;
      stop)
            [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$NAME"
            do_stop
            case "$?" in
                    0|1) [ "$VERBOSE" != no ] && log_end_msg 0 ;;
                    2) [ "$VERBOSE" != no ] && log_end_msg 1 ;;
            esac
            ;;
      restart|force-reload)
            log_daemon_msg "Restarting $DESC" "$NAME"
            do_stop
            case "$?" in
              0|1)
                    do_start
                    case "$?" in
                            0) log_end_msg 0 ;;
                            1) log_end_msg 1 ;; # Old process is still running
                            *) log_end_msg 1 ;; # Failed to start
                    esac
                    ;;
              *)
                    # Failed to stop
                    log_end_msg 1
                    ;;
            esac
            ;;
      *)
            echo "Usage: $SCRIPTNAME {start|stop|restart|force-reload}" >&2
            exit 3
            ;;
    esac

    Config file for init script (the init script looks for this): /etc/default/php-fastcgi

    START=yes
    
    # Which user runs PHP? (default: www-data)
    
    EXEC_AS_USER=www-data
    
    # Host and TCP port for FASTCGI-Listener (default: localhost:9000)
    
    FCGI_HOST=localhost
    FCGI_PORT=9000
    
    # Environment variables, which are processed by PHP
    
    PHP_FCGI_CHILDREN=4
    PHP_FCGI_MAX_REQUESTS=1000

    nginx config file: /etc/nginx/nginx.conf

    location ~ \.php$ {
      fastcgi_pass   127.0.0.1:9000;
      fastcgi_index  index.php;
      fastcgi_param  SCRIPT_FILENAME  /var/www/blog.codefront.net$fastcgi_script_name;
      include        /etc/nginx/fastcgi.conf;
    }

    My fastcgi.conf file (I'm not sure I need everything here...)

    fastcgi_param  QUERY_STRING       $query_string;
    fastcgi_param  REQUEST_METHOD     $request_method;
    fastcgi_param  CONTENT_TYPE       $content_type;
    fastcgi_param  CONTENT_LENGTH     $content_length;
    
    fastcgi_param  SCRIPT_NAME        $fastcgi_script_name;
    fastcgi_param  REQUEST_URI        $request_uri;
    fastcgi_param  DOCUMENT_URI       $document_uri;
    fastcgi_param  DOCUMENT_ROOT      $document_root;
    fastcgi_param  SERVER_PROTOCOL    $server_protocol;
    
    fastcgi_param  GATEWAY_INTERFACE  CGI/1.1;
    fastcgi_param  SERVER_SOFTWARE    nginx;
    
    fastcgi_param  REMOTE_ADDR        $remote_addr;
    fastcgi_param  REMOTE_PORT        $remote_port;
    fastcgi_param  SERVER_ADDR        $server_addr;
    fastcgi_param  SERVER_PORT        $server_port;
    fastcgi_param  SERVER_NAME        $server_name;
    
    # PHP only, required if PHP was built with --enable-force-cgi-redirect
    fastcgi_param  REDIRECT_STATUS    200;
  • Toilets and Linux evangelism

    Pardon the self-promoting cross-linking: Toilets and Linux evangelism (or what we use to build Bezurk) is an entry I just posted on the brand new Bezurk Blog about how the toilet is a good place to spread the word.

    If you've found other neat ways to use the free Ubuntu stickers (or even the Apple stickers that comes with iPods), do post it!

  • ImageMagick port "broken" temporarily

    Just in case anyone runs into this problem (which, I might add, would probably go away when either the portfile is updated or the source mirror fixes the problem) while installing ImageMagick via MacPorts...

    While trying to install Rmagick on my spanking, new, glossy, kakkoii MacBook, MacPorts reported a checksum error with the ImageMagick source tarball:

    Target com.apple.checksum returned: Unable to verify file checksums

    At first, I thought it was an outdated portfile but even after a sudo port selfupdate and sudo port -d sync, the checksum error was still occurring (checksum of the file: 4bcb4264c2170fe562b10a732f43e7af, expected checksum in the portfile: 9469ce1b1b645f8c728158cc434b0ff8).

    Turns out, the first listed master site (where MacPorts gets its source files from), http://imagemagick.linux-mirror.org/download/ is hosting a source tarball with a bad checksum. Digging around the man page for port a bit, and switching the order of the master source sites solved it, so it was just a case of a bad file on one of the mirror sites.

    sudo port edit imagemagick to edit the portfile and change the source mirror to a legit one. You should see something like this:

    master_sites \
      http://imagemagick.linux-mirror.org/download/ \
      http://ftp.surfnet.nl/pub/ImageMagick/ \
      sourceforge:imagemagick \
      ftp://ftp.imagemagick.net/pub/${name}/ \
      ftp://ftp.fifi.org/pub/ImageMagick/ \
      ftp://ftp.nluug.nl/pub/${name}/

    Just move an alternative master_site to the top of the list (I used the SourceForge one). There probably is a way to specify the master_site on the command line with port install but I've had just about enough of reading man pages and the now nearly unfindable MacPorts documentation (whatever happened to the old Darwin Ports site that had great documentation?)

    Anyway, I've written to the webmaster of the mirror site linux-mirror.org, so this would probably be fixed for all two of you who are gonna be installing ImageMagick via MacPorts within these few days or so. Still, it was a good exercise in debugging bad port installations.

  • 'Mac vs PC commercials'.gsub(/Mac/, 'Rails').gsub!(/PC/, 'Java')

    The Rails Envy guys have come up with a spoof of the Mac versus PC commercials. Yes, it's Rails vs Java. Go watch it or catch it straight on YouTube.

  • Lucky Star: Konata-ism

    I've never much blogged about anime before so it's probably a little known fact around here that I'm a huge anime fan. One of the anime series I'm watching now is Lucky Star, an anime adaptation of a 4-panel comic strip by from Kyoto Animation. This being the same studio that produced the excellent The Melancholy of Haruhi Suzumiya, I knew I had to watch it.

    Despite first reading about the numerous negative comments on blogs about the first few episodes of Lucky Star, when I watched it I actually found it pretty funny (there are some good reviews, though rare, such as this one). Four moe school girls, one of them an otaku? KyoAni's really pandering to the "niche" crowd here ("niche" meaning just about every single anime gamer).

    It was not until later (about 3 episodes in) that I realized the main character, Konata Izumi, was voiced by Hirano Aya (who played Haruhi in The Melancholy of Haruhi Suzumiya). Once afflicted by Ayaism, this fact enhanced the appeal of Konata.

    Although, by the time I watched episode 5, I was hooked on Konata-ism. I'll let the screen captures speak for themselves:

    I am a tank

    So she plays an MMORPG and plays a tank? Back when I was a World of Warcraft junkie, I had a level 60 warrior (yeah, pre-Burning Crusade n00b, I am) and had too much fun with it (to use a cliche, it's like crack).

    Teacher only AOEs

    Hahaha... Fair enough. I had a mage in World of Warcraft before switching to a warrior and sometimes I do think that way about my ex-fellow mages ;) Though I've had the honor of playing with some of the best mages in my old guild.

    I'll pull one

    "I'll pull one." "lolololo." Classic.

    Teacher logged on

    Konata's teacher (who plays with her often) logs on.

    Rare drop!

    Konata gets a rare drop. Woo... Good times.

    LOL gratz

    Roflcopterskates

    "LOL gratz", "roflcopterskates" and "bbq" - I'm not sure if the fansubbers took some liberty here with the translation of the MMORPG-speak.

    Ahh, and did I mention Konata watches a lot of anime as well? Konata is the new Haruhi.

    Now go watch some anime!

    Screencaps taken from a.f.k.'s excellent fansubs.

  • Sexy migrations on Edge Rails

    So it seems DHH was inspired by the Sexy Migrations plugin (which in turn was inspired by Hobo) and committed changeset 6667.

    Now, you can do this:

    
    create_table :products do |t| 
      t.integer :shop_id, :creator_id 
      t.string  :name, :value, :default => "Untitled"
      t.timestamps
    end

    Succinct!

  • Growl integration in Firefox 3

    Some of the latest Firefox 3 aka Minefield trunk builds include Growl integration! This is probably a non-event for anyone other than Mac users, but hey Growl notifications without needing to install an extension? Pretty nice.

    Firefox 3 Growl notification for downloads


    Get a nightly here: https://ftp.mozilla.org/pub/mozilla.org/firefox/nightly/latest-trunk/

    To test Firefox 3 nightlies without possibly messing up your existing profile (preferences, extensions, what have you), use the Profile Manager to create a new profile just for testing. Way less stupid then trying to load up 48 tabs from your current profile in Firefox 3 and then seeing most of the extensions fail to work (which was what I tried to do, once).

    Source: Bug 362685 – Growl Integration for Mac OS X (nsIAlertsService)

  • Firefox Recommendation #1: Clearing download history automatically

    It's been ages (well, almost 2 years, but that's a lifetime in Internet time) since Firefox Secrets was published and I figured I should probably live up to being the "Firefox book" guy (in Singapore). I'm gonna start posting Firefox tips and pointers at least twice a week (hopefully I don't run out of material!) And instead of using the word "tip", I'll call em "recommendations" instead, seeing as Robert Accettura already has an excellent Firefox Tips thing going on his blog.

    Today's recommendation: get Firefox to clear the list of downloaded items in the download manager to keep your Firefox speedy. You've probably read about how cleaning up your download history can help speed up Firefox (on Of Zen and Computing or on Mac OS X Hints), but do you really want to remember to clear up your download history manually?

    Firefox download manager


    Unsatisfied with a manual solution, I went looking for an about:config preference (not familiar with about:config? Enter 'about:config' into your Firefox address bar and hit Enter - read more). Sure enough, there is a browser.download.manager.retention preference when I used 'download.manager' as a filter:

    Firefox download manager retention preference


    But what value should I set it to? The default value is 2 but there's no clue what another value would do. There's only one place to find out: the about:config entries page in the MozillaZine Knowledge Base. Search for retention and there you go:

    When to remove downloaded files' entries from the Download Manager
    0: Upon successful download
    1: When the browser exits
    2 (default): Manually

    Set it to 0 or 1, whichever behavior you prefer. I like 0 since I use the Download Statusbar extension and never get to see the Download Manager.

    That's it for today's tip (and a pretty low-key tip at that)!

  • Google Reader, so pretty...

    Today being RSS feed backlog-clearing day, I came across Jon Hicks' Google Reader Theme, installed the Stylish extension for Firefox, added his user style for Google Reader to Stylish, and have been wow-ed ever since. Here's a rough idea how it looks (click the thumbnail for a bigger picture):

    Google Reader with Jon Hicks' theme


    If you're a Google Reader user like me, you'd probably like Jon's theme. I don't have to say that it does look very much better than the original Google Reader theme (oops, I said it). Amazing what a talented designer can do with user style sheets.

  • Axel - lightweight command line download accelerator

    I never really fancied download accelerators but Axel is different - it's a command line application and is naturally significantly more lightweight then those graphical download managers I've stopped using since 1996 (teh intraweb was slower then, and I was sucking bits of it through a state of the art 33.6kbps dial-up modem).

    I've been using wget for its auto-resume support, but have switched to using Axel since one of my colleagues at Bezurk introduced me to it. If you're a wget or curl fan, Axel is almost a drop-in replacement (although it doesn't handle multiple redirects or broken connections too well).

    Install it:

    
    # On a Mac, with Darwin Ports.
    sudo port install axel
    
    # On Ubuntu.
    sudo apt-get install axel
    

    Windows users would require cygwin to get Axel to work for them (what, a Windows user and you don't have cygwin installed already?).

    Now go download some files. If you need a good place just to test the speeds, go to YUI Theater and download some videos (watch them too, most of them are pretty good, like Douglas Crockford's and the Firebug videos). Run it on the command line by typing:

    axel -n 10 http://example.com/some_file.mov

    The -n 10 option tells Axel to use a maximum of 10 simultaneous connections when downloading the file. Another useful option is -a, which outputs a wget-like report of download progress in a few lines rather than filling up your screen with download progress messages.

    Check out the speeds I managed to get:

    Check out the 1MB+/s download speed with Axel


    250MB in 3 minutes, with an average download speed of 1339KB/s. That's pretty damn fast. Comparatively, I could only get speeds of around 40KB/s using Firefox. It's hard not to love this raw speed and I think you might too.

    Oh, and to go off-topic here, it's nice to know we are steadily chipping away at the Windows user base in Bezurk. The colleague who introduced me to Axel recently switched to Ubuntu (he's been waiting for Ubuntu 7.04). It was painful to set it up correctly (the KDE part of Kubuntu, not Ubuntu itself), but I think he's much happier working on Linux for some inexplicable reason.

  • Thunderbird 2.0 and Gmail integration

    I haven't been following Thunderbird development like I used to so I was presently surprised to see how far Thunderbird has come when I tried a beta of Thunderbird 2 a while back (of course, Thunderbird 2 has been released for over a week, but I'm still clearing a backlog of blog drafts).

    One of the things I noticed was how Gmail POP integration is baked right in when creating a new accounts:

    Thunderbird - Gmail mailbox creation step 1


    Step 2: tell Thunderbird your Gmail ID.

    Thunderbird - Gmail mailbox creation step 2


    Step 3: there isn't really a step 3, just confirm your Gmail settings.

    Thunderbird - Gmail mailbox creation step 3


    How's that for time-saving? Gone are the days of looking for POP client configuration documentation on Gmail. My only gripe is that there isn't a similar wizard interface for Gmail for your domain.

    Oh and there're tons of other nice features in Thunderbird
    2
    : Advanced Folder Views lets you configure "Favorite Folders" and has a view for unread folders as well, tags (a la Gmail labels or GTD organization) and of course, a well-tuned Saved Searches feature (which has been there since version 0.9).

    I'm back to using Thunderbird again after a brief fling with Apple Mail, for no particular reason actually. Well, actually, I did reinstall Mac OS X Tiger (it got really crappy in terms of performance - must be the tons of software I was installing) and found it much less work to get a satisfactorily working email client with Thunderbird than with Apple Mail.

  • If you're having problems with script/console on edge Rails...

    If you're on edge Rails and are having problems starting script/console, it may be caused by spaces in your Rails working directory (see ticket 7955: Console environment load broken when RAILS_ROOT contains spaces). Basically the lack of quoting on RAILS_ROOT causes console.rb to pass incorrect paths to IRB (it gets split on the spaces in your directory path).

    Just in case anyone runs into this problem and doesn't want to start debugging the problem like I did. Changeset 6512 fixes things. If you do update Rails to revision 6512 (or higher) though, be aware of changest 6507. Ahh the joys of living on the edge.

    
    /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `gem_original_require': no such file to load -- /Data/Working (LoadError)
            from /opt/local/lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:27:in `require'
            from /opt/local/lib/ruby/1.8/irb/init.rb:252:in `load_modules'
            from /opt/local/lib/ruby/1.8/irb/init.rb:250:in `each'
            from /opt/local/lib/ruby/1.8/irb/init.rb:250:in `load_modules'
            from /opt/local/lib/ruby/1.8/irb/init.rb:21:in `setup'
            from /opt/local/lib/ruby/1.8/irb.rb:54:in `start'
            from /opt/local/bin/irb:13
  • Get your Rails tests results via Growl notifications

    I've been using Autotest (part of the ZenTest package) while testing my Rails applications and never thought of asking for more. After all, getting my tests (well, specs actually) run automatically whenever I make a relevant change and having diff-level granularity on which tests Autotest re-runs is pretty damn useful enough. That is, until I came across this RSpec and Autotest with Growl notifications blog entry (on JavaBlogs, no less!)

    How're these for red/green test results?

    Tests (specs) passing!

    Growl notification of tests passing


    Tests (specs) failing :(

    Growl notification of tests failing :(


    Much better! Now I don't have to glance at my terminal window (I recently realized how painful it is while coding with a single monitor) and get unobtrusive notifications.

    Unfortunately, this is Mac-only (or wherever Growl is supported). I had to follow links to several blog entries to get it setup nicely (see Growl + Autotest Rails with Zentest 3.4.0 and Green goodness with autotest + growl - those are my references).

    Basically you need Growl installed, and the growlnotify tool in your path. growlnotify can be found in the Extras/growlnotify directory in the Growl dmg (for example, after you've mounted the Growl-0.x.x.dmg, you can find it here: /Volumes/Growl/Extras/growlnotify). Just copy the growlnotify executable into your path (I copied it into /opt/local/bin since I'm using Darwin Ports and don't have a /usr/local/bin directory). Verify it's running by entering:

    growlnotify -m "Testing growlnotify" Boo Ya!

    into a terminal window. You should see a Growl notification.

    Now, you need to create an Autotest hook by creating an autotest init file in your home directory named .autotest, and copy this chunk of text in:

    
    module Autotest::Growl
      def self.growl title, msg, img, pri=0, sticky="" 
        system "growlnotify -n autotest --image #{img} -p #{pri} -m #{msg.inspect} #{title} #{sticky}" 
      end
    
      Autotest.add_hook :ran_command do |at|
        output = at.results.last.slice(/(\d+)\s.*specifications?,\s(\d+)\s.*failures?/)
        if output =~ /[1-9]\sfailures?/
          growl "Test Results", "#{output}", "/Data/Pictures/Icons/rails_fail.png", 2, "-s" 
        else
          growl "Test Results", "#{output}", "/Data/Pictures/Icons/rails_ok.png" 
        end
      end
    end

    Replace the paths to the pass/fail icons (as you can see, mine are in /Data/Pictures/Icons/) with the correct paths to any images you want to display (grab them from Green goodness with autotest + growl or roll your own). You can change the parameters passed to growlnotify however you want (use growlnotify -h to see what parameters it acccepts). The pri parameter sets the priority level of the message (test failures come up with a priority of 2, hence the red background of the Growl notification). Passing -s to sticky tells Growl to keep the notification sticky. If you're running your (auto)tests on a remote server (e.g. in a Continuous Integration system), you can even use growlnotify to send remote notifications (see the -H switch) to your development workstations!

    Lovely! I love you, MacBook Pro serial no. W86280NAVWW (despite your broken DVD drive and you randomly freezing up on me when you get too hot).

  • Setting up svnsync-ed (mirrored) SVN repositories on Ubuntu (part 2 of 2)

    Note: if you haven't already, you may want to read part 1 of this article first.

    Phew, and that was all the work you needed to do to migrate your Subversion repository to another server. We've barely touched that other server that we wanted to use for mirroring the repository!

    "Bootstrapping" your mirror SVN server for svnsync

    This mirror SVN server also needs Subversion 1.4.x installed, so go ahead and do the (almost) same things we've done in part 1 to get Subversion installed. You should be able to use the .deb package generated by checkinstall on your main Subversion server to install Subversion 1.4.x on the mirror SVN server. Just scp it to the mirror SVN server and install it (dpkg -i subversion-1.4.3.deb).

    With Subversion installed, create a new Subversion repository (using svnadmin create, remember?), but don't load the repository dump like we did on the main SVN server in part 1 (of course, since we are going to be mirroring the repository!).

    Setting up svnsync to mirror your repository

    First, create a SVN user for svnsync to use - let's call this the 'svnsync user'. The easiest (and best) way to do this is edit the svnserve.conf and passwd files:

    conf/svnserve.conf

    
    # Uncomment this line.
    password-db = passwd
    

    conf/passwd

    
    svnsync = secret
    

    This gives read and write access to the 'svnsync user'. The svnsync program will authenticate with our repositories as this user via the svn:// protocol (i.e. via svnserve).

    Next, we need to create a pre-revprop-change hook for the destination repository. The svnsync documentation has a detailed explanation. Create a hooks/pre-revprop-change file under your destination repository's directory.

    
    #!/bin/sh
    USER="$3"
    
    if [ "$USER" = "svnsync" ]; then exit 0; fi
    
    echo "Only the svnsync user can change revprops" >&2
    exit 1

    Make it executable, and then initialize the sync:

    
    chmod +x hooks/pre-revprop-change
    svnsync init file:///var/svn/repositories/destination_repos svn://source.host/source_repos

    Don't worry, this only sets up the sync - there's no actual data copying yet. Syncing your repository data may take a long time if you have a big source repository, so I suggest using nohup to run the code overnight (or something), or at least saving the output in a log. Either way, the command to start the sync is:

    svnsync sync --username svnsync file:///var/svn/repositories/testsync/

    You should start seeing svnsync committing in changes from your source repository. Instant gratification (well, almost)! Should your svnsync process get aborted or killed, you can remove the hanging lock by running:

    svn propdel svn:sync-lock --revprop -r 0

    Setting up 'on-the-fly' syncing

    So now you have your source and destination repositories synced, but what happens when you start committing changes to your source repository? Nothing! That's because svnsync is merely a passive syncing tool (meaning you have to run it to sync, instead of it knowing when to sync automatically).

    There are two ways you can setup 'real-time' syncing:

    1. Use cron (or a similar scheduler) on the destination repository server. Add something like this to your crontab:

      * * * * * /usr/local/bin/svnsync --non-interactive sync svn://source.host/source_repos

      This basically runs svnsync on your destination repository server every minute to pull down any changes to your source repository.

    2. Add a post-commit hook to the source repository. I found this svnsync entry by Paul Querna that has a sample post-commit hook. If I recall correctly I tried it but it didn't work for me, so I settled on using cron to sync up my repositories.

    Things that I skipped

    There're some things that I skipped over while writing this, mainly to do with SVN authentication.

    • If you're accessing your repository via the svn+ssh:// protocol, you've to manage the (group) permissions of the repository files in the filesystem appropriately (basically the repository should be group writable by your users). chmod and chown are your friends, as is NIS (or something similar) to manage your users. I use these steps to create a new SVN repository that gets access via the svn+ssh:// protocol:

      
      sudo mkdir /var/svn/repositories/funky_project
      mkdir /tmp/funky_project
      mkdir /tmp/funky_project/trunk
      mkdir /tmp/funky_project/branches
      mkdir /tmp/funky_project/tags
      sudo svnadmin create /var/svn/repositories/funky_project
      sudo svn import /tmp/YourProjectNameHere file:////var/svn/repositories/funky_project -m "Initial import."
      rm -rf /tmp/funky_project
      sudo chown -R www-data:www-data /var/svn/repositories/funky_project
      sudo chmod -R g+w /var/svn/repositories/funky_project
          

      As you can see, my SVN users are part of the www-data group, and the repository directory is made group-writable.

    • The svn:// protocol has authentication configuration files in the conf/ directory of your repository. The SVN book has a section explaining how to configure authentication for svnserve.
    • Apache httpd can be used to expose your SVN repositories via the WebDAV protocol. This allows for the very commonly seen http:// repository URLs (especially for Open Source projects). Configuration is a little more involved and you would probably have to install Apache from source as well. The SVN book has the details.

    Wrapping up

    I hope someone found this entry useful - I know I could have used one when I was setting up Subversion and svnsync.

  • Testing rescue_action_in_public with RSpec

    After overriding rescue_action_in_public in the ApplicationController to deal with ActiveRecord::RecordNotFound exceptions (a very common exception to rescue in the canonical 'show' actions of your controllers), I decided to test it. I've been getting used to BDD with RSpec (and the Spec::Rails plugin), so I stumbled a bit when writing the spec.

    I finally settled on this:

    
    class DummyController < ApplicationController
      def index
      end
    end
    
    context 'A child class of ApplicationController' do
      controller_name :dummy
    
      specify 'should render a 404 error for ActiveRecord::RecordNotFound,
        ActionController::UnknownController,
        ActionController::UnknownAction,
        ActionController::RoutingError exceptions (in public)' do
    
        exceptions_404 = [
          ActionController::RoutingError.new('test'),
          ActiveRecord::RecordNotFound.new,
          ActionController::UnknownController.new,
          ActionController::UnknownAction.new]
    
        exceptions_404.each do |exception|
          controller.eigenclass.send(:define_method, :index) do
            raise exception.class, 'some message'
          end
    
          lambda {
            get 'index'
          }.should_raise(exception.class)
          controller.send :rescue_action_in_public, exception
    
          response.should be_missing
        end
      end
    end

    Notice the use of a dummy controller so that we can actually make a request to it (and get all the Rails magic and environment set up ready for testing). Also, I had to use instances of the exceptions rather than their classes because I'm sending a rescue_action_in_public message to the controller without knowing how to instantiate the exceptions (for example, ActionController::RoutingError actually has a constructor which requires at least 1 argument). So I create the exceptions first.

    The eigenclass method simply returns Ruby's canonical singleton class or metaclass, depending on who you talk to (i.e. class << self; self; end;) and I modify the dummy 'index' action to raise the exception. And here's the stinky part:

    
    lambda {
      get 'index'
    }.should_raise(exception.class)
    controller.send :rescue_action_in_public, exception

    Make a GET to the 'index' action, make sure it raises the exception and catch it (with should_raise - the assertion is unnecessary since I did override 'index' to raise the exception), and then force rescue_action_in_public to be called. Something's fishy here - why isn't the exception caught by default by rescue_action_in_public? I've set these to make sure that rescue_action_in_public is called but it seems like it never is called:

    
    ActionController::Base.consider_all_requests_local = false
    controller.eigenclass.send(:define_method, :local_request?) do
      false
    end

    I traced the code into ActionController::Rescue and everything seems to be in order. I'm stumped and weary, I think I'll look at this again tomorrow. Anyone see any obvious mistakes?

  • Setting up svnsync-ed (mirrored) SVN repositories on Ubuntu (part 1 of 2)

    This is a 2-part journal on setting up migrating and upgrading a Subversion repository, and then using svnsync to mirror the newly created repository. (Part 2)

    Initial setup

    Ever since Subversion 1.4 was released, I'd been eying the new svnsync tool because we had a single repository that was not, erm, really backed up (we had daily server backups and occasional manual repository dumps but that was it). svnsync promised to make repository mirroring simple, and after doing some repository migration and upgrading, I can assure you it really does make things easier than any other (more manual) repository backup solutions I had seen before. This is a walkthrough of how you can upgrade your pre-1.4 SVN repositories to 1.4.x, and setup svnsync to mirror your repositories. It's going to be very biased to Ubuntu but I'm sure you can translate any Ubuntu specific steps to your favorite distros.

    Here's our initial setup:

    • A pre-1.4 (it was version 1.2.3) SVN repository that needed to be upgraded and migrated to another server.
    • 2 cleanly installed Ubuntu 6.06 LTS VPSs, one of which is the intended target for the repository migration. The other would mirror the new 1.4.x repository (using svnsync).

    An un-installable Subversion 1.4.x?

    I wish I could have simply ran sudo apt-get install subversion and have Ubuntu pull down the latest 1.4.x .debs. Unfortunately, the version of Subversion in the Ubuntu apt-get repository is still 1.3.1 (which doesn't have svnsync). If anyone knows a reliable way to install Subversion 1.4.x via apt-get, let me know! I looked around for a good edge sources.list but came back empty-handed.

    I balk at installing stuff from source because I never did figure out how to easily clean out the stuff that gets installed. All thanks to this reluctance, I went digging around and found checkinstall. This thing is awesome - I wonder why I didn't manage to find it earlier.

    What checkinstall basically allows you to do is, instead of running the usual make install after the usual configure and make steps, it creates a Debian package (it also does RPMs and Slackware packages) for you that is easily un-installable with dpkg, and then proceeds to install the files just as it would have for any other deb.

    On Ubuntu it's really easy to install checkinstall, just:

    sudo apt-get install checkinstall

    Now, you no longer should type 'make install' - always use the 'checkinstall' command instead:

    
    ./configure
    make
    sudo checkinstall # instead of "make install"

    checkinstall will ask you a bunch of stuff but you can just go with the defaults for most of them - I did name my packages 'XXX from source', like 'Subversion 1.4.3 from source' so it's easier to check which packages are checkinstall-generated with a simple grep to dpkg -l.

    checkinstall generates a .deb (Debian) package before it actually installs your software (Subversion, in this case). It should tell you right at the end of its installation process about where to find this .deb and how to uninstall your newly installed (from source!) package (something like dpkg -r subversion-1.4.3). Don't delete this .deb yet as we will be using it to install Subversion 1.4.x on our mirror SVN server.

    Installing an un-installable Subversion 1.4.x on Ubuntu

    Now, blessed with our new checkinstall-granted powers, we can install Subversion from source without any qualms. Before we start, be sure to purge any existing Subversion packages you may have installed (do note that if you're using any packages that depend on the official Ubuntu Subversion packages, you may run into library version problems).

    
    dpkg -l | grep svn
    dpkg -l | grep subversion
    
    sudo dpkg --purge subversion
    sudo dpkg --purge libsvn0

    Now, it's time to get the source. Get it from the official Subversion website. Look for the source code download - the file should be something like this: http://subversion.tigris.org/downloads/subversion-1.4.3.tar.gz. Remember to get SVN dependencies (something like this: http://subversion.tigris.org/downloads/subversion-deps-1.4.3.tar.gz) as well as these are needed for access to 'http://' scheme SVN repositories. If you want to use Subversion to connect to a server via a http:// or https:// URL, you will require these dependencies (more specifically, the Neon library).

    Use something efficient like wget, curl or Axel (love Axel) to get the sources on the server where you want to install Subversion. Unpack them to the same directory. configure. make. checkinstall.

    
    tar zxf subversion-1.4.3.tar.gz
    tar zxf subversion-deps-1.4.3.tar.gz 
    cd subversion-1.4.3
    ./configure  # Be sure to read the INSTALL file for any options you may want to set (such as SSL)
    make
    sudo checkinstall

    If you get a warning "configure: WARNING: we have configured without BDB filesystem support" during your configure step, you'll get by just fine. Unless you specifically want your Subversion repositories in Berkeley DB format, we can ignore the warning (Subversion will use FSFS filesystem for your repositories) - see FSFS notes and Choosing a Data Store if you want to make an educated decision.

    Anyway, now with a brand new Subversion 1.4.x installed, we are finally ready for the real work - migrating your Subversion repository!

    Dumping and importing a repository

    Dumping a Subversion repository is dead easy:

    
    svnadmin dump /path/to/repository > repository_name.dump

    Depending on how big your repository is, you could end up with a pretty large dump file. gzip it, then scp it over to your new server, then gunzip it. Use svnadmin to load the repository dump.

    
    cd /var/svn  # I like to keep my svn repositories under /var/svn
    mkdir repository_name
    svnadmin create repository_name
    svnadmin load repository_name < /path/to/repository_name.dump

    If you have a good pipe between the source and destination servers, you can do this in a one-liner:

    svnadmin dump /path/to/repository | ssh -C [IP/domain of destination server] svnadmin load /path/to/new_repository

    Of course, all this dumping would require a temporary suspension of any repository write actions otherwise you're just going to have an inconsistent dump - just send out an email to your fellow developers and disable svn access.

    Setting up access to your new repository

    Now, you have a Subversion repository that is only accessible via the local filesystem (file:// 'protocol'), which isn't very useful. We'll need to setup remote access. Your Subversion repository can be accessed in a variety of ways, including:

    • svnserve standalone daemon (svn://)
    • svnserve with inetd (svn://)
    • svnserve over a SSH tunnel (svn+ssh://)
    • over the HTTP protocol (http:// and https://)

    The svnserve documentation details how to deal with the first 3, and setting up http:// and https:// access to your protocol is really a subject that deserves its own tutorial. Try the SVN book or Google.

    Personally I prefer svn+ssh:// access for internal projects since it allows me to unify authentication for my Subversion repositories with UNIX user accounts. Be wary of an angry cadre Windows developers though, since they need to take quite a good number of steps to setup public key authentication and integrate it with their svn clients on Windows machines. Integration with TortoiseSVN is quite a pain, though my Windows-using colleague at work found these useful: Putty and TortoiseSVN, Using Cygwin, Keychain, SVN+SSH and TortoiseSVN in Windows.

    svn:// access

    I also expose my repositories via svn:// (as we'll see later, this is useful for allowing access to a svnsync user without messing around with any UNIX user accounts) and use the xinetd daemon (apt-get install xinetd on Ubuntu to install) to launch svnserve process. If you're taking this path, create a file (I name it 'svn') in /etc/xinet.d to tell xinetd about svnserve.

    In /etc/xinet.d/svn:

    
    service svn
    {
            port                    = 3690
            socket_type             = stream
            protocol                = tcp
            wait                    = no
            user                    = www-data
            server                  = /usr/local/bin/svnserve
            server_args             = -i -r /var/svn
    }

    Notice that I needed to use the full path to svnserve (do a which svnserve to get the full path, making sure this is the 1.4.x version that you just installed). The server_args parameter also bears some explanation. The -i option tells svnserve to use inetd (xinetd is a variant of inetd, sorta). The -r /var/svn option tells svnserve to only expose repositories below that path. This basically translates your repository at /var/svn/my_cool_project to be accessible via svn://your.hostname/my_cool_project.

    svn+ssh:// access

    Accessing your repository this way basically logs in to the host server of your repository over SSH, invokes the svnserve process, and accesses your repository in a very file://-like manner. What this means is that your repository path is taken from the root of your filesystem. An example: a repository located in /var/svn/my_cool_project would be available at svn+ssh://your.hostname/var/svn/my_cool_project. For this reason I often symlink /svn to /var/svn (to get repository URLs like svn+ssh://your.hostname/svn/my_cool_project instead).

    Relocating working copies

    Now, all your working copies are still pointing to the old Subversion server - no need to fret, a simple svn switch fixes things:

    svn switch --relocate [from] [to]

    Replace '[from]' and '[to]' with the source and destination Subversion repository URLs.

    Remember to stop access to your old server so no one is making commits to the wrong place.

    Setting up svnsync

    I'd intended to write this entire piece in one blog post, but I'm running out of steam at this point. In Part 2, we'll actually setup svnsync for some repository mirroring goodness!

  • Checking for duplicate ActiveRecord objects

    I've been writing a database importer plugin for a Rails application that needs to data on some "legacy" production databases (well, not really legacy, but the schema differs from ActiveRecord conventions) with the intention of scheduling a cron job to run the imports. Why not connect the Rails app to the legacy databases? Hmm, let's see:

    • the records don't have to be up to date (so I can afford to, say, import yesterday's records today),
    • less jumping through hoops molding ActiveRecord models to the legacy databases,
    • the production database schema is liable to change - but this should not affect my Rails application,
    • there will be lower loads on the legacy databases which are in full-blown production use, and
    • most importantly, it gives me an excuse to figure out writing a data importer for a Rails application.

    And I am surprised that it actually was rather fun writing the importer plugin (data importing stuff is normally one of the most unexciting things a programmer can do, right next to writing lengthy requirements documentation and any kind of contact sport). It's basically a plugin that defines ActiveRecord models on the source (legacy) databases and then creates our Rails app's models from these. Importer classes allow me to then run the imports using script/runner like so:

    
    script/runner "HotelsImporter.import :start => 2.days.ago.to_date, :end => 1.day.ago.to_date" -e production

    Put that in a cron job and there you go, scheduled daily (or hourly, whatever) imports.

    But I digress. What was I actually going to talk about? Oh yes, checking for duplicate ActiveRecord objects. Now, the importers I wrote were run daily but there was the risk of re-importing the same data again (due to failed cron jobs, running the same job twice, acts of god, etc.). To be defensive, I needed to check that there were no existing records before importing them from the legacy databases.

    At this point I could decide to run uniqueness checks on any natural keys of each table (and Rails makes this really easy with AR validations, as we all know), or rely on a more convenient "the whole hog" field-by-field comparison. I settled on doing a field-by-field comparison after realizing that:

    • it's easier and I don't have to specify which natural fields constitute the natural keys, and
    • there are some tables which don't really have a natural keys (these generally belong to has_many side of an association).

    Update: As choonkeat pointed out in a comment below, I can simply use Post.find(:all, :conditions => new_post.attributes) since that stood out very clearly as the way to do it. This was actually the first way I tried to do this but it didn't work in the importer - I must have been doing something stupid! Doh! Thanks choonkeat for pointing out my blooper. Anyway you can mostly ignore what follows below but I'll keep it here to remind myself of my error.

    So I went looking for an easy way or a Railism to check whether an existing new ActiveRecord object already exists in the database. Hmm, I couldn't find anything helpful - I guess everyone is relying on AR validations. Still, I went ahead and mixed in a to_conditions instance method to ActiveRecord::Base - looks like my answer to everything nowadays is to re-open existing classes.

    
    module Bezurk #:nodoc:
      module ActiveRecord #:nodoc:
        module Extensions
          def to_conditions
            attributes.inject({}) do |hash, (name, value)|
              hash.merge(name.intern => value)
            end
          end
          alias :to_conditions_hash :to_conditions
        end
      end
    end
    
    # ...
    ActiveRecord::Base.send(:include, Bezurk::ActiveRecord::Extensions)

    So now in my importers I can easily check for potential duplicate entries:

    
    new_post.save! if Post.find(:all, :conditions => new_post.to_conditions).empty?

    Now, I just have this nagging suspicion that there is a better way to do this...

  • irb and script/console tab-completion

    Ugh, I wish I found this earlier: Tab Completion in IRb. I only went googling for this after I realized I have been tabbing to get auto-completion on script/console for a bit but it never sunk in that tab-completion wasn't ever working. Useful stuff, go set it up if you haven't already.

  • DRYing up link_to_remote for degradable URLs

    I like to keep my links in my Rails applications that make AJAX requests degradable in non-JavaScript browsers so I often find myself doing this:

    
    link_to_remote 'Show details',
      { :url => { :action => 'show', :id => post.id } },
      { :href => url_for({ :action => 'show', :id => post.id }) }

    This allows me to get HTML links like so:

    
    <a href="http://www.example.com/posts/show/123" onclick="...">Show details</a>
    

    instead of the usual (if the :href parameter is not specified):

    
    <a href="#" onclick="...">Show details</a>
    

    You've probably already noticed the duplication right there in the :url key of the 2nd parameter (options) and the :href key of the 3rd parameter (HTML options) that litters my Erb templates where link_to_remote calls are prevalent. I got tired of editing and copying and pasting the url_for parameters so I monkey patched Rails' PrototypeHelper class in ActionPack (actionpack/lib/action_view/helpers/prototype_helper.rb).

    To do this, I had to replace Rails' link_to_remote method definition. Thanks to living and breathing code examples in Rails that do this, I was able to promptly come up with this module that I placed into the lib/ directory of my Rails application and require it in my environment.rb file.

    
    module Bezurk #:nodoc:
      module ActionView #:nodoc:
        module PrototypeHelperExtensions
          def self.included(base)
            base.alias_method_chain :link_to_remote, :degradation
          end
    
          def link_to_remote_with_degradation(name, options = {}, html_options = {})
            html_options[:href] = url_for(options[:url]) if !html_options.has_key?(:href)
            link_to_remote_without_degradation(name, options, html_options)
          end
        end
      end
    end
    
    ActionView::Helpers::PrototypeHelper.send(:include,
      Bezurk::ActionView::PrototypeHelperExtensions)

    The modification to link_to_remote itself is dead simple: it checks for the non-existence of a :href key in the html_options parameter and sets the :href option in the html_options parameter hash to the url_for the :url value in the options parameter hash. It then calls the original link_to_remote to do what it does best.

    I've submitted a (trivially simple) patch to Rails in my opened ticket #7904 - DRY up link_to_remote for degradable URLs. Let's hope this makes it in so I can kill my monkey patch!

  • A Singapore MRT WTF

    The Singapore MRT has to be one of the best public rail transport systems in the world (I fancy the Tokyo train/subway system as well - the fare adjustment machine is one of those "since sliced bread" ideas), but they still have room to screw up in a Daily WTF-worthy way.

    This sign at Raffles Place MRT used to show train arrival times:

    MRT signboard rebooting


    It's been there for about 3 days, was still there when I checked this morning.

  • Validate that favicon

    The Favicon validator ensures that you have a correctly created favicon. If you're wondering why your favicon works in some browsers but not in others (like IE), it probably has an incorrect format.

    Favicon validator results


    I also learnt from validating my favicons that you can actually place more than 1 version of the favicon image into the .ico file (it seems to be a container for these images rather than being an image itself).

    Oh, and the same site also has a very handy Favicon generator - I used this to fix some of my favicons, which used to be GIFs renamed with the .ico file extension. Very handy.

  • Firefox tips sharing session at WebSG meetup

    Kevin has uploaded a video of my session on Google Video - it's kinda grainy and really soft.

    I was at the 2nd WebSG (Web Standards Group Singapore) meetup (read about the recap of the meetup) on Wednesday (28 Feb 2007) where I hosted a short session on some Firefox tips. It's all thanks the urgings and encouragement of Lucian (the organizer) that I finally got down to publicly sharing my Firefox knowledge ever since I wrote Firefox Secrets.

    Me at WebSG meetup 2


    I was asked to blog about this (since I didn't have any presentation slides), so here it is, for those of you who missed anything in my mumbling or frantic keystrokes.

    Keyboard shortcuts you should know

    Note: Mac users should replace Ctrl with the Cmd key for any of the keystrokes listed below.

    • Ctrl-L puts your cursor in the address bar, Ctrl-K puts your cursor in the search bar.
    • Ctrl-T opens a new tab and puts your cursor in the address bar, but
    • I forgot to mention this during the session, but I personally prefer using Ctrl-L or Ctrl-K, typing in the new URL or search query, and hitting Alt-Enter open the new page or search result in a new tab - it just feels more accessible than Ctrl-T. Most people I observe still use Ctrl-T though.
    • Ctrl-Shift-T re-opens closed tabs. Keep hitting it to reopen more closed tabs in your history. This is a big time saver. Newly built into Firefox 2.0.
    • Ctrl-Tab and Ctrl-Shift-Tab brings you to the next and previous tab. If you're having trouble remembering both, just remember that the usual convention is a Shift does the reverse. So all you need is to remember Ctrl-Tab, and adding a Shift modifier does the reverse (i.e. goes to the previous tab instead of the next).
    • Ctrl-F brings up the Find bar, and you can start typing in whatever you want to find on the page and in any input fields. Use Ctrl-G and Ctrl-Shift-G to go to the next and previous occurences respectively. The Find bar is good, but...
    • I forgot to mention (as Zul mentioned to me after) during the session that Quick Find is much better. Just hit / and start typing as you would for the normal Find bar, and the Quick Find bar will go away after several seconds. Much better in most cases than the Find bar which likes to stick around until you close it. And why is this important? Because the Find bar takes up screen real estate, especially for those of us using wide screens.

    Getting rid of the close button on all tabs

    I've been told how annoying Firefox 2.0 is when it insists on putting a close button on each tab.

    Firefox 2.0 - close buttons on each tab


    Accidental closing of tabs when you try to select them is one big complaint. Well, you can turn it off and go back to the good olde Firefox 1.5 days of a single close button on the extreme right of the tab bar. Enter about:config in your address bar, filter for browser.tabs.closeButtons, double click on the preference entry that shows up, and change the value from the default of 1, to 3. If you don't want the close button on the right of the tab bar, enter a value of 2 (this assumes you're going to close tabs with either Ctrl-W or middle-clicking on the tab - this is my prefered setup).

    Backing up your profile

    MozBackup is all you ever need if you use Windows exclusively. This neat little (Windows-only) application backs up your profile into a single file - all you need is to use a very simple wizard. And of course, you can restore your profile just as easily. I loved this when I was still using Windows.

    Like I said, people using other operating systems can easily DIY - just read Mozilla's official documentation on Firefox profiles. You're probably using either Mac OS X or some Linux distro - you can do this without any hand-holding ;)

    What about backing up extensions?

    Some people asked me during and after my session about backing up extensions. FEBE is one extension you can look at unless you use a Mac (I tried the beta version that supports Mac OS X, but gave up when I couldn't get it to use the correct binaries for zipping and unzipping.)

    My personal take on backing up extensions: don't back them up. I prefer to dump a list of extensions (I use the Extension List Dumper extension) that I have installed and just re-install them again. This is no big chore for me now since I have significantly fewer extensions installed than back in the day. I just like starting clean, after having had bad experiences with restoring extensions from backup before.

    Still, if you really want to back up your extensions, I say just go ahead and see if it works (MozBackup and FEBE should work).

    Profiles and "why is my Firefox so damn slow?"

    This is one thing I had planned to mention but I got kinda threw off kilter when the slowness of the wireless internet connection made me worried about quitting Firefox (didn't want to quit Firefox, load up another profile, only to find that it'd take another 5 mins to get my demo tabs loaded again).

    Anyway, some questions I often get is, "why is my Firefox so slow?" and "why does Firefox use up so much RAM/CPU?" The first thing I suspect is a busted profile. Whenever I experience intolerable slowness in my Firefox setup, I dump and restore my profile into a newly created profile, minus all the tab history information (and of course, never restoring backed up extensions). Sometimes I even start clean new profiles, importing only bookmarks. You'll very likely notice immediately that Firefox is back to its old speediness. And if anything happens, you still have your old profile with you.

    I'm not that certain of what exactly causes Firefox to get too slow over time, but my suspicions lie with:

    1. Firefox saving browsing history in all the bazillion tabs you have open - that's a lot of data to be carrying around
    2. broken/bad extensions
    3. a busted profile

    Profile management for productivity

    Firefox hides its Profile Manager from you. You can access it by passing the -profilemanager switch to your Firefox executable. For Windows users, it's actually --profilemanager.

    Firefox profile manager


    The Profile Manager is really handy for people who need to remove distractions. For me, I setup different profiles for work and for play so I don't become distracted the "play" tabs I have open while I'm working (I'm beginning to like the Tab Groups extension for segregating tabs more though). Also really handy for setting up clean profiles for running user demos (I setup a WebSG profile for my presentation and installed specific extensions I wanted to talk about) - don't really want to have your naughty browser history showing up in some autocomplete heh.

    Some extensions I dig

    I also demoed some extensions that I find essential.

    • Download Statusbar for unobtrusive downloads.
    • All-in-One Gestures for easily accessible Back and Forward gestures when you happen to have your hand on the mouse. Like I said, I don't use any of the other gestures because they all require memorization (I use too much of my memory storing keyboard shortcuts).
    • Super DragAndGo for drag and drop searching. Great time saver when you are doing online research.
    • Web Developer toolbar for its convenient Disable Cache, browser resizing, and HTML validation shortcut (Ctrl-Shift-A).
    • UrlParams for easy viewing and editing of GET or POST parameters.
    • And of course, Firebug - godsend for web developers, web designers, and especially AJAX developers like us.

    Oh, and Mousepose was the application I used to get my keystrokes to show up on the screen. It's simple yet extremely helpful for presentations, though I probably should have used the mouse spotlight feature more. Oh well, I did just buy the license for it a few hours before the meetup - goes to show how unprepared I was heh.

    It's late and I'll blog more on the event and the people I met another day.

  • Tab Groups for Firefox: best extension since sliced bread... er, tabbed browsing

    I saw a Lifehacker blog post today in my RSS reader on this Firefox extension, took one look at the screenshot, and went "Woooooow, give me some of that". Aforementioned Firefox extension: Tab Groups extension for Firefox.

    As its name suggests, it allows you to group your tabs in, er, groups. Forget the verbiage, just look at the screenshot:

    Tab Groups extension in action


    This extension is a godsend for those of us who usually have a bazillion tabs open in Firefox (personally I keep it under a bazillion tabs so all those tabs don't slow Firefox down - I usually start killing tabs once I hit 30). By allowing you to group your tabs into logical groups, such as one group for work, one for play, and one for your many Gmail accounts, it brings some order to the "tab hell", which usually happens when you have to scroll or Ctrl/Cmd-Tab through more than 20 tabs to find the tab you want. Some people may use an Expose-style extension to show thumbnails of all their tabs at once, Showcase is one example - those are quite nifty as well though I've always found them to be unmanageably slow when there are too many tabs, which kinda defeats the purpose.

    Anyway, coming back to Tab Groups extension... This is an extension that's going to be absolutely necessary for those of us who like having many tabs open at once. For those of you who are less tab-crazy, or even, god forbid, surf in single tab mode, I'd expect you'd be much less excited about Tab Groups.

    Tab Groups is pretty much early release software (the author considers it pre-alpha), but it's very much already usable at this stage. You can create groups, rename them, and start dragging tabs into whichever tab group they belong. There are several features that are missing but almost definitely planned for future releases. For one thing, you can't move tab groups around, but I'm sure that'll be baked in soon enough. Also, the tab selector (the dropdown list of tabs thingie on the extreme right of your tab bar) stops working if tab is in another group. Another thing is the Ctrl/Cmd-Shift-T to reopen closed tabs currently reopens tabs in a new tab group. All small inconveniences (that would be soon be removed once Tab Group history gets baked in) for a very nice extension.

    Via Lifehacker via CyberNet News.

  • Running two Firefox profiles simultaneously

    At yesterday's WebSG meetup, someone asked me how you can have 2 different user sessions in Firefox, and I was unable to answer as I never had the need for 2 different user sessions (though I imagine it'd be nice for testing certain types of interactive web applications).

    Anyway, I found the answer today after some Googling - Lifehacker's "Manage multiple Firefox profiles" article.

    Basically, once you have started Firefox in a profile that you want, you can start another profile by passing the '-no-remote' switch together with the name of the profile you want to start with the '-P' switch:

    /path/to/firefox -P profile_name -no-remote

    It does mean you need 2 different profiles though, for the 2 different user sessions. Oh well.

  • Debugging SSH public key authentication problems

    After a longer than desired struggle with getting sshd to accept my public key, I think a blog post is in order to remind myself not to repeat the same mistakes. Here's how you should go about debugging your SSH public key authentication woes:

    • Getting more debug info when connecting with your ssh client: Add a '-v' option to your ssh command (e.g. ssh [email protected] -v -v -v). Add more '-v' for more detailed debug (you can do up to '-v -v -v' I think).
    • Debugging on the remote host by running sshd in debug mode: Run '/usr/sbin/sshd -d -p 2222' on the remote host and connect to it. '2222' here is the port number of the sshd process you started on the remote host.
    • tail the authentication log: Run 'tail -f /var/log/auth.log' on the remotehost. You can watch the log as you try to connect via SSH with your key.
    • Make sure your ssh key agent is running: Do a 'ps aux|grep ssh-agent'. Make sure your key agent is running. If you're not using ssh-agent (I like keychain from Gentoo, or SSHKeyChain for Mac OS X), do whatever you have to do to ensure that your keychain is running.
    • Make sure your private key is added to the ssh key agent: Do a 'ssh-add -l' to check that ssh-agent has your key. Likewise, if you are using something else, check your keychain application has your private key.
    • Check the permissions on your home directory, .ssh directory, and the authorized_keys file: If your ssh server is running with 'StrictModes on', it will refuse to use your public keys in the ~/.ssh/authorized_keys file. Your home directory should be writable only by you, ~/.ssh should be 700, and authorized_keys should be 600.

    Tailing the authentication log was the clincher for me this time - my problem was the group permissions on the home folder were incorrectly set (the error message I got from auth.log was: 'Authentication refused: bad ownership or modes for directory /home/chuyeow'). Just had to fix it so it was no longer group-writable. Of course, this can also be fixed by turning setting 'StrictModes off' in your sshd config (/etc/ssh/sshd_config), but it's not really recommended. Plus, you may not always have the rights to edit that file anyway.

  • IMAP-IDLE plugin for Apple Mail

    Yup I switched to Apple Mail from Thunderbird a few months back, primary reason being back then I was barely able to run my developer applications on 512MB of RAM (Apple Mail runs lighter than Thunderbird, unfortunately). It's comforting to find that there is an IMAP-IDLE plugin for Apple Mail (Thunderbird got its IDLE support way back in 2004.)

    What's IDLE? Well, quoting myself:

    Simply put, IDLE is a command that allows IMAP email servers to transmit updates to the client in real time. This saves the client from having to continuously poll the server to achieve the effect of new mail appearing immediately. All in all, an exceptionally useful command that saves Thunderbird the work of polling IMAP servers continuously.

    So anyway, it's really simple to install and use (just simple checkboxes for enabling IDLE for your IMAP accounts).

    Preference pane for Apple Mail IMAP-IDLE plugin


    Remember to configure Mail to stop polling these mail accounts every X minutes or so now. Enjoy the geeky feeling of knowing you've just saved several CPU cycles and a few kilobytes of bandwidth.

  • Finally, Bezurk and news.com.au co-brand is up and running

    It was a few weeks later than planned, but finally (actually, about 3 days ago), we're up on the News.com.au Travel section.

    No thanks to a CSS bug in IE6, we were delayed by a week. The last thing we expected was a CSS bug hanging IE6 with all the JavaScript we were pushing out, but yeah, well, it was. At least now my conscience is clear - everything works nicely in IE6, IE7, Firefox, Safari and Opera.

  • Sharing a USB printer from Mac OS X to Windows

    Not too long ago I was asked to use hook up the Mac Mini that was collecting dust in our office to our HP Deskjet printer (HP Deskjet 1280 to be exact) and have it function as a "print server" of sorts (we have since replaced it with a NAS with print server functionality, which subsequently broke - that's how long this post has been sitting in my drafts folder). While setting it up to be shared with Mac machines was a cinch (we have a grand total of 3 Mac machines in the office, including the Mac Mini and my treasured MacBook Pro), sharing the printer from the Mac (Mini) to Windows machines was significantly more difficult. After some trial and error, first with what made the most sense, and then with stuff I could glean off the Internet, I finally arrived at something that works. Maybe this would be useful for the next unfortunate bloke that needs to do this sharing of printers from Mac to Windows machines without a print server.

    First point of reference: Print from Windows XP to a shared Mac printer tip on Mac OS X Hints. The tip suggests you select a PostScript driver in Windows after finding it on the network (which requires you to do several things first, but we will come to that later). This worked, but it was sub-optimal because you couldn't use the printer driver software to do stuff like 2-up printing (i.e. print 2 pages per side per sheet). This post will show you how to share a USB printer from Mac OS X to Windows PCs with full driver capability.

    1. On the Mac (the one that the USB printer that you want to share is connected to), go to the Sharing preferences pane and ensure Printer Sharing and Windows Sharing are both turned on.
    2. Fire up your browser and go to http://127.0.0.1:631 - this is the web interface to CUPS. When asked to enter a password, login with your Mac OS X user account (it has to be an administrator account).
    3. Go to the Printers tab and add a new printer (yes, in addition to any existing printer configuration that already exist for the same printer). Choose a name that's short and descriptive (no spaces). For the purposes of this guide, let's call it 'uberprinter'. Best to keep it under 12 characters since Windows is finicky.
    4. When asked to select a device, select USB printer.
    5. You'll be asked for a Device URI. To find out, open up a terminal and type lpinfo -v. You should see your USB printer coming up. Mine came up as 'direct usb://HP/Deskjet%201280?serial=CN516851RPUN'. Copy and paste this (without the 'direct' part - i.e., I'd have copied 'usb://HP/Deskjet%201280?serial=CN516851RPUN') into the 'Device URI' field.
    6. Select a 'Make' of 'Raw'. Keep going until the printer is added.
    7. You're done configuring from the Mac. But before you go, determine your Mac's IP address (do a 'ifconfig' in a shell or fire up System Profiler and check out the 'Network' item) - note it down somewhere. Now it's time to hook up your Windows machine to use the shared printer.
    8. OK now go to your Windows machine and add a new printer (Control Panel -> Printers and Faxes). Select 'A network printer... blah blah'. Don't browse for the printer, you will enter its IP address directly in the 'URL' field. Enter 'http://your.macs.ip.address:631/printers/uberprinter' (replacing 'your.macs.ip.address' with your Mac's IP address and 'uberprinter' with the short name you gave your printer). If you can't remember your printer's name, just scurry back to the Mac and browse to http://127.0.0.1:631/printers/. You should be able to see the printer you added listed there - its name is linked there.
    9. Now, all that's left is to install the correct Windows printer driver on the Windows machine. If you're lucky Windows already has your driver, if not do whatever you need to get the proper driver - after all, the purpose of jumping through all these hoops is to get full printer driver functionality off the shared Mac printer.

    That's it. That'll teach you for not getting a print server or one of those new printers with network functionality.

    Problems?

    Some readers wrote in with their own difficulties and have kindly allowed me to share their solutions. First off, Patrick McKrell who had a solution for cases where you still are just not able to print from the Windows machine (it involves killing a daemon so it's pretty sweet).

    Thank you for making available your instructions for sharing a USB printer connected to a Mac using OS X with a Windows PC. All of your points worked flawlessly on the Mac. My Mac is a B&W G3 running OS X 10.2.8. The printer is an HP PSC 1510.

    ...

    Back to Windows Add Printer. Is my driver there? No. I browsed the Windows file system to Program Files/HP/. No luck. Couldn't find or add my driver. Next I unplugged the printer's USB cable from the Mac and into the PC. Windows detected the new hardware, created whatever files it needed, and automatically created the USB-connected PSC 1500 series printer in Printers and Faxes. Well, that's pretty darn close. I opened the printer's properties, changed the port from USB to Internet Port (as per your configuration guidance), sent a print job....and nothing. Hmmm.

    Finally, I recalled seeing someone's web post --thanks to your point of reference. I followed the poster's instructions, and, yeah, it prints, and in color. Thanks again, Chu, for your post. It was easy to follow, and importantly, it worked with my setup (despite the color issue).

    Patrick McKrell

    The solution? Change your CUPS configuration to allow raw printing:

    Basically, I saw that every job on the windows side had "error".

    After looking at the error logs for cups on the mac box (/var/log/cups/), I noticed this line repeatedly:

    print_job: Unsupported format 'application/octet-stream'!

    Did some googling, and found a post with the answer:

    >
    > You probably need to uncomment the following lines in
    > /etc/cups/mime.types and /etc/cups/mime.convs:
    >
    >
    > /etc/cups/mime.types:
    > #application/octet-stream
    >
    >
    > /etc/cups/mime.convs:
    > #application/octet-stream application/vnd.cups-raw 0
    > -
    >
    >
    > That will allow raw printing.

    Then, kill -HUP the cups daemon, and you're good to go.

  • One stupid thing you can do with a UNIX shell and root access

    sudo chown -R dick /

    instead of

    sudo chown -R dick ./

    You won't be able to su - or sudo to chown the ownerships back properly because the sudo executable needs to be owned by root. Some people should never have unfettered sudo rights... like me.

  • Determining web page size - the wrong way

    Got an email today that disputed web page sizes I reported (diplomatically telling me to get my facts right):

    Regarding the size, if you save the homepage as webpage complete you can get the size of the iframes files individually.

    <teenage-angst>Ick... Learn to use Firebug or a Web Page Analyzer. Or anything else that does better than saving the page from a web browser.</teenage-angst>

  • Essential Firebug video for web developers

    If you're doing web development or design of any sort, you have to check out Joe Hewitt's talk on Firebug at Yahoo! - download the movie or watch it online, just make sure you watch it.

    If you aren't already using Firebug, this will convince you. If you are, you'll probably find some gems in there that you didn't know before. I didn't know that you could log events to elements on the fly, or press the up and down arrow keys to adjust numerical values (such as margins and paddings), or edit the values in the box model layout diagram, or hover over variable names to see a tooltip of their values in the JavaScript debugger, or conditional breakpoints.

    Now if only it were able to detect JavaScript syntax errors that cause an entire JS file to be ignored by the browser.

  • BarCamp Singapore Jan 2007

    I just got back from BarCamp Singapore a few hours ago. I was only vaguely familiar with the Rules of BarCamp prior to this, and actually only found out about the event from Choon Keat (of RssFwd fame - check it out if you haven't already) 2 days ago.

    Anyway, props to the organizers for putting this together (especially the free BarCamp t-shirts from Yahoo!). Personally, I felt the icebreaker game took up too much time that could have been used for the sharing sessions - the tech track of sessions were crammed up towards the end of BarCamp.

    BarCamp Singapore logo


    Notably, Harish Mallipeddi, who interned at Bezurk for a couple of months, shared with us an introduction to Django together with some working code on a site he is working on. I didn't know a whole lot about Django before and was impressed that there is a "free" admin application for Django applications. We (Choon Keat, Harish, and I) had a small debate over Django's templating system (briefly, Django has it's own templating system for its views, whereas Rails ERB is basically Ruby code mixed in HTML). To this day I am still convinced that PHP is already a templating language despite my old Smarty card-carrying days. Relating back to the Django templating system, Choon Keat and I didn't like the idea of having to learn to use a templating language when the base language itself (Ruby or Python) would suffice. Obviously Python syntax wasn't suitable for web designer consumption so Django had to come up with its own templating language.

    Michael from PetrolWatch showed us some flashy scriptaculous effects on his site and demoed the Ruby on Rails-inspired CakePHP. It's a nice framework if you had to use PHP and performance is a concern (it's true, Rails apps run more heavily than PHP scripts, and the difference in performance is very clear in servers with minimal resources). But the lack of a good testing framework was discouraging. Still, I must say I'd have used it (or a similiar framework) if Rails didn't exist or if I was tied to using PHP for web development.

    Choon Keat showed the non-believers the power of Rails' script/console as well as the flexibility of Ruby in allowing developers to make their own extensions to existing classes (aka "who needs hooks from God?"). I probably should have stepped in and showed some basic Rails migrations stuff that would have impressed (hopefully) the Django and CakePHP fanbase (all 2 of them) - but my laptop was running out of battery (and I was shy).

    Oh and I saw a good number of Macs there, and at least 3 laptops running a Linux distro of some sort. Cool.

  • RubyGems 0.9.1 - required update

    RubyGems has been updated to 0.9.1 and is a required update due to a security issue. The blog announcement reveals more:

    The most important change in RubyGems 0.9.1 is that RubyGems no longer allows files to be installed outside of the installation directory.

    It's a required update as the RubyForge gem repository will soon disallow installation of gems if you have an older version of RubyGems.

    Anyway, a new feature I found very useful is gem outdated, which lists the gems you have installed that are currently not the latest versions (I just realized that mongrel is actually already up to 1.0 RC!). And don't forget multiple gem uninstallation with gem uninstall.

    Update your RubyGems by running:

    gem update --system
  • What you can get for $6.25 in Singapore

    Found a forwarded email with this image of a receipt from a local restaurant in Raffles Hotel sitting around in my inbox while clearing my inbox (trying to achieve the state of "Inbox Zero"):

    Slut receipt from Seah Street Deli


    Yeah I'd like some nachos with my slut.

  • I brought back an abscess from Tokyo

    One of the reasons I've been rather quiet on this blog ever since my return from the trip to Tokyo is this nice medium-sized abscess I brought back with me. It appeared about 3 days into the trip, and I must have really bad luck because it was located on my posterior - moving around on foot and sitting down were particularly trying events. Considering that we got around most of Tokyo either on foot or by train, it made most experiences less pleasing than they would probably have been.

    Nevertheless, it was a great trip - had some great food, saw some breath-taking sights, bought some (or rather, not enough) anime swag. Someday soon (if not soon then probably never) I will blog about it (with pictures, of course).

    And I saw a bus advertising a The Melancholy of Haruhi Suzimiya DVD release:

    Haruhi bus in Tokyo


    Anyway, I'm glad I can say that the abscess burst yesterday, relieving me of the pain that had to be suppressed with painkillers. While the copious amounts of blood and pus that came out of the burst abscess wasn't pleasant to deal with (it's still bleeding and excreting pus right now), the relief from the pain is immeasurable. Plus, I didn't really like the idea of the surgery that would be necessary if it hadn't burst - a friend who had an abscess on his face described the procedure and while it seemed painless enough, sticking a needle up that mass of pain just didn't seem like an ideal way to deal with it.

    Abscesses suck.

  • Tokyo 4th Jan - 13th Jan

    I'll be out of town (i.e. Singapore) for the next week and a half. Going to Japan, more specifically Tokyo and nearby cities. Tentacle sex and soiled panty vending machines here I come!

    Oh and if anyone has any pointers or places/food/experiences that are not to be missed, please let me know.

  • VLC 0.8.6 - full screen controller panel

    VLC media player 0.8.6 (changelog) was released a few weeks ago so this may be old news, but I am clearing my backlog of drafts.

    Mac OS X users, if you watch videos in full screen mode (like I do most of the time), you'd be delighted to know that they added a full screen controller à la the video player in iTunes:

    VLC full screen controller panel


    That was one of my pet peeves with VLC when I was still using Windows as my main desktop OS, and I'd stayed true to Media Player Classic right to the point where I switched to OS X. Media Player Classic is Windows-only, so I had to switch to VLC as well (QuickTime player never impressed me and I wasn't willing to pay for the Pro version). The lack of a full controller in full screen mode and the inability to move to specific spots in the video timeline with sufficient granularity seems to have been fixed with this new full screen controller panel.

    And while they still haven't made it possible to pause a video by clicking on the video output (which was the case in Media Player Classic), I've mostly compensated for it by hitting the space bar. Now that they've added a right-click context menu to the video output screen, the same can be done but in 2 clicks with just the mouse - not optimal but it does feel better to see a context menu pop up when right-clicking. I guess that's the Windows user in me talking - I still feel that almost everything I see should have a context menu or it just feels wrong.

    Oh and they added support for the Apple Remote Control. Works great, I just have to remember to take out my remote control and use it next time I'm sitting back watching a movie or anime.

  • Mr. Hankey the Christmas Poo cake for sale

    Saw this on a Christmas brochure while buying some pastry from the local Delifrance cafe:

    Mr. Hankey the Christmas Poo lookalike log cake


    I immediately thought of Mr. Hankey the Christmas Poo (of South Park fame).

    Mr. Hankey original picture


    While the resemblance is not uncanny, I'm pretty sure that's one log cake I don't want to eat.

  • Seen elsewhere: Glue sniffers on Singapore train

    Tomorrow.sg has an entry with a photo of glue sniffing girls on a MRT train. This picture is tits (as Eric Cartman would say).

  • Mind your MySQL update syntax

    Just noticed an insidious "bug" in MySQL when you use "AND"s instead of commas to separate the fields you are updating in your SET clause. Being a little rusty in SQL, I entered something like the query below while trying to make a quick fix to some records:

    UPDATE characters
    SET alignment = 'Neutral Evil'
    AND last_name = 'Majere'
    WHERE first_name = 'Raistlin'

    Much to my consternation, the record appeared to have disappeared when I tried to query for it. After much bewildered cursing, I came across MySQL bug #15928: update syntax error not detected (this is not an actual bug though). As it turns out, MySQL does a logical AND and the result is that the 'alignment' field is set to '0' since 'Neutral Evil' AND last_name = 'Majere' results in false. And MySQL casts it to '0'. Grrr. Luckily I was able to revert the changes by finding the record again with the new '0' alignment value (was using an auto-committing MySQL GUI client, no ROLLBACK available).

  • Firefox Secrets on Google Book Search

    A GoogleAlert (not the Google Alert) email arrived in my inbox today alerting me to the Firefox Secrets entry on Google Book Search (that's a book I wrote, for the uninitiated). It's listed as a limited preview so only the first few pages of each chapter are available. If you're interested in previewing a Firefox book circa 2005 (virtually the Mesozoic ages in the tech world), just maybe, check it out.

  • HP laptop with an Apple sticker

    Saw this in a MacDonald's at a local shopping mall:

    Man using a HP laptop with an Apple sticker


    If you can look past the fuzziness of the photo, it's actually someone using a HP laptop with an Apple sticker. Now, if only I can get a HP sticker for my MacBook Pro...

  • Adding full text search to your Rails app in 1 hour and 10 lines of code

    Yup, it's that fast and simple, using Ferret, a Ruby port of Lucene (the de facto full text search engine written in Java) and the acts_as_ferret plugin, I added "Did you mean this?" functionality to a small Rails application (non-perma link) today rather quickly and painlessly.

    Adding full text search funtionality to your Rails applications

    Install Ferret via RubyGems.
    sudo gem install ferret

    Install the acts_as_ferret plugin for your Rails application.
    ruby script/plugin install <ACTS_AS_FERRET_SVN_REPOSITORY>

    Add the acts_as_ferret mixin method to your ActiveRecord model, specifying the fields you want indexed.

    class Location < ActiveRecord::Base
      belongs_to :country, :foreign_key => 'country_code'
      belongs_to :state
    
      acts_as_ferret :fields => [:location_name, :country_name, :state_name]
    
      def country_name
        country.country_name
      end

    If you have any associations that you want searched as well, one way (there is more than one way to do this) would be like what I did above with the country_name attribute of Country. For a more detailed discussion on Ferret and ActiveRecord associations, I found this thread in the Ruby forum enlightening.

    In your controller, use Model.find_by_contents() to perform the full text search with your query. If you need fuzzy matching, append a tilde ("~") to the end of your query - if you're here, you probably needed fuzzy matching anyway as you're most likely creating "Did you mean this?"-type functionality.

    # In the controller...
    @locations = Location.find_by_contents("#{@search_request.location_entered}~") # The "~" is for fuzzy matching.
    

    All that's left is to flash the appropriate message in your view and display your model objects as you normally would.

    Some gotchas:

    • If you change the way your model is indexed, remember to clear out the existing one. acts_as_ferret stores the index by default in a 'index/' directory in the root of your Rails application. Stop your mongrel/Webrick process to remove the lock on the index files and delete the whole directory - Ferret will rebuild it the next time you perform a full text search.
    • The first time you do a full text search, Ferret builds your index for you and this takes quite a while - don't be alarmed if your Rails application appears for freeze or returns a 500 internal server error. You can observe the contents of the 'index/' directory in the root of your Rails application to tell when Ferret has finished indexing (files stop appearing and disappearing). I found it easier to run a 'find_by_contents' call on my models via 'script/console' and waiting for some results to appear.
    • Append a "~" to your query string for fuzzy matching! This allows you to retrieve results matching phonetically or are mispelled. There are more complex matching rules - check out the Lucene documentation.
  • How to setup your Thrash/Sent/Drafts folders for IMAP on Mail.app

    How to setup your Trash/Sent/Drafts folders for IMAP on Mail.app. "Select mailbox, Mailbox -> Use this mailbox for..., Sent". (OK I misspelled 'trash' in the post title - see trackback below.)

  • My Linux snob shirt

    All I got for my birthday was this t-shirt:

    My Pink Linux Mastercard ad snob shirt


    In case the image doesn't load at some point or is too small, here's what it says:

    Two hours of broadband to ftp the Linux Package, 15 cents.

    CDs to burn the files, $1.00.

    The knowledge that nothing on your computer is from Microsoft, PRICELESS.

    There are some operating systems that you don't need money to buy.

    For everyone else, there's Windows.

    Ironically, I'm now a Mac OS X snob, but the t-shirt still kicks arse. And no, I'm not one of those Microsoft haters without a cause - it's just a t-shirt.

    (Actually I got more than the t-shirt, but I wanted to start the entry with "All I got was this t-shirt".)

  • Seen elsewhere: The Single Most Important Rule for Retaining Software Developers

    Something true: The Single Most Important Rule for Retaining Software Developers.

  • Firefox "ad" in the Singapore MRT

    I saw a familiar furry animal on one of the ads near the entrance of City Hall MRT and did a double take when I realized it was actually a firefox! I had no real choice but to snap a grainy photo and post it here.

    Firefox in an MRT ad!


    Well, the ad actually labelled it as a "Red Panda", but we all know that red pandas are also called firefoxes.

  • Hilarious Thai commercial, Bleach's new OP

    I don't normally post short entries on non-technical topics nor link to LiveJournals (just kidding!), but I'll have to make an exception for this one. Check out these Smooth-E Babyface ads, probably one of the most creative commercials you'd have seen in awhile.

    Oh and while I'm "off-topic", Bleach fans who want to find out more about the opening song (Rolling Star) in the latest episodes should check out this thread on YUI (the singer). I'm now totally hooked on Tomorrow's Way (YouTube video) and I Remember You (another YouTube video), and of course, the Bleach OP.

  • MacHeist Mission One 101

    Update: seems like several people are coming into this thread after searching for "MacHeist combination" - this post is for last year's MacHeist. For spoilers and solutions, I suggest you go to the MacHeist Backroom forum.

    If you are a Mac user you'd probably have heard of the much-hyped MacHeist. (I bet non-Mac users were probably annoyed with the coverage of MacHeist at some point on sites like Digg.) Even I was late to join up with MacHeist because of its "I'm not gonna tell you what it's about but it's gonna rock your world" press and the oh-so-exclusive invite system (which is surprisingly rather effective despite being probably the most used viral "marketing technique") - I got into the whole MacHeist thing courtesy of the registration link posted over at TUAW (The Unofficial Apple Weblog). (Oh and yes, I do know that by blogging about it I've fallen prey to the MacHeist hype, but I can't help myself. Or maybe I just need to blog something that's easy to write about.)

    MacHeist registration page courtesy of TUAW


    And so I signed up, and after being completely muddled about what this MacHeist thing is all about, came upon a mission briefing with a very well-done voiceover (love that voice, sounds just right). Turns out MacHeist is a detective (or insert snooping, heist-related adjective here) game where you (try to) accomplish missions and get "loot" in return.

    MacHeist mission one briefing


    Not what I'd expected, and I almost didn't play along until I checked out the MacHeist forums (requires a MacHeist account) and saw that the sheer volume of activity. A few thousand posts in a couple of hundred topics may not look like a lot, but it's pretty amazing for what I assume is a small user base (invite-only system) and you always have to remember MacHeist is really only a week old or so.

    MacHeist forum - look at the number of posts!


    Now that got my attention again. Lurking around the forums a bit, I found general praise of the "loot" from completing Mission One. And turns out the "loot" is a bunch of free Mac software, which I really should have guessed. "Free", "legitimate", "Mac software" - ze Mac lover in me cannot resist! I went back, re-read Mission One, and sent out an email to what I hoped was an automated email bot (it would be disturbing if someone was actually replying to all the MacHeist emails) in an attempt to dig out what I was told were much-coveted iPhone specs. It was fun for a while trying to convince the email bot to send me what I needed, but it got old when I had to, you know, get the damn answer without running in circles (with circular email).

    Lurking around the forums more when I got stuck, I came across this stickied Official Mission #1 Thread and it was all easy mode from there. So yes, the answers are all there if you're lazy or impatient (I am both).

    MacHeist safe


    After completing the mission, you get the combination code to your safe, where you can find the fruits of your labor, loot! Well, about the reward, I have to say I was disappointed - perhaps I was expecting something like free licenses of TextMate or Parallels, or even Disco (though I have actually purchased licenses for all 3 - this Mac thing is turning me into someone who actually buys software - the horror!). Anyway, the rewards were full copies of Chat Transcript Manager, Assignment Planner, and Soulver. Two out of three applications for students... I got Soulver to play with its gimmicky maths functionality, didn't bother with the rest (not to put them down, but personally don't have any use for Chat Transcript Manager being a full-time Adium user nor for Assignment Planner).

    Despite the anti-climax, I really think the MacHeist guys have done an awesome job (they got me for sure - I'm already anticipating the next heist and more loot!). It's an excellent way to promote Mac software (some of the lesser known products as well), and to get people talking. I can imagine they aren't doing too shabby with ad placements and sponsorship. And really, that's a damn polished setup they have there with the missions. Oh and before I end this post, if you need an invite, let me know (just leave a comment), still have 4 to give out. Well, if the registration link posted over at TUAW (The Unofficial Apple Weblog) no longer works that is.

  • OMG, 66 gig error log...

    So yeah today we realized one of our development servers had 0% free space left... after checking it out with

    du -h --max-depth=1

    in a few places in the filesystem, it turned out that we had a 66 gigabye Apache httpd error log.

    66 GB error log


    That's what you get for setting the log level to debug and not rolling over your error logs...

  • Muting the Mac startup sound

    One of the mildly annoying things of owning a Mac laptop is how it can draw unwanted attention with its startup chime when it boots up. There was once it went off rather loudly while I was on the train and I'd imagine I must have looked rather sheepishly embarrassed. From then on I kept trying to remember to lower or mute my Mac before shutting down, but it still slips my mind most of the time. To be honest, I think that even the Windows startup chime sounds better than the one on my MacBook Pro.

    So I was rather pleased when I saw someone had created a simple application (preference pane, rather) called StartupSound.prefPane that allows you to control the startup volume or mute it completely.

    StartupSound.prefPane screenshot


    Intel Mac owners take note: I grabbed version 1.1b1 of StartupSound.prefPane off the MacUpdate page for StartupSound.prefPane though, as I noticed it said "StartupSound.prefPane now works on an Intel-based Mac" for that version. Wouldn't want to try the stable release version (1.0.4) since it's for PowerPC-based Macs.

  • Firefox 2 - it's out

    Even though Firefox 2 is slated to be released later today, the release builds have already made it into the Mozilla Firefox release directory. The impatient (like me) can go get it right now.

    A screenshot of Firefox 2.0 on Mac OS X


    Oh, and a must have extension for those who want to enjoy the 2.0 release: Nightly Test Tools. Despite it's geeky sounding, "why the hell would I need that?" name, it is essential for preventing your transition from Firefox 1.5 (or lower, god forbid) to Firefox 2.0 from becoming an exercise in frustration caused by extensions being disabled if the authors have not updated them for Firefox 2.0. With Nightly Tester Tools, you can "Make compatible" any "outdated" extensions to force them to be usable in Firefox 2.0. Granted, this doesn't always work (if, say, an extension uses functionality that no longer exists in Firefox 2.0), but for most of the extensions I've had to "make compatible" so far function just fine.

    Nightly tester tools in action


    Oh and while you're there playing with your extensions (Firefox extensions you sicko), you'll probably find that you no longer need extensions that save and restore your browsing session (such as SessionSaver or the functionality that comes with Tab Mix Plus) and certain feed subscription extensions (such as Feed Your Reader) - these already come built into Firefox 2.0.

  • Pfft, it's back to WordPress for me

    After 2 failed experiments with Typo and Mephisto, I caved and went back to WordPress. As you can see (if you're on the site itself instead of reading from a feed reader), it's all green and so 2005-looking (the year 2005, that is) - gonna have to convert the templates from the old Mephisto setup to WordPress.

    Screenshot of the Plink theme in action on my old Mephisto blog


    What exactly went wrong with Typo and Mephisto? Well, Typo was beating the crap out of my VPS, among other reasons. Mephisto was fine until the Mongrel processes running it started dying after a few hours - the 192MB or RAM that I have on the VPS was 100% used and the Rimuhosting guys have wrote me more than once suggesting a memory upgrade as I was causing way too much disk swappage on the host machine. I put the high memory usage down to comment spammers, but hell I wonder how everyone else manages to keep their Mephisto or Typo blogs up assuming not everyone has the luxury of an excess of 192MB of RAM to play with. If you're running a Typo, Mephisto, or any other Rails-based blogging application under low memory conditions successfully, I want to know!

    Another reason I had for abandoning Mephisto was the difficulties I had with its templating system. I could generally live with the lack of an easy way to display stuff like monthly archives unless I was following the trunk, but it was the lack of pagination (such as paged monthly archives) that really annoyed me. Rick Olson has stated that pagination probably won't go into the Mephisto core and I disagree on its lack of usefulness (but this is another matter). I lacked the perserverance to finish up a pagination plugin when I realized that I had to work off the trunk which was already very much changed from the point release I was using.

    Rails is such a wonderful framework, but throwing (another) templating layer into the mix (Mephisto uses Liquid) is a mixed blessing. The whole exercise felt very much like jumping through hoops when there's already a clear path to goal - nevertheless, I'll have to give the benefit of the doubt to the lack of documentation and the fast-moving development on Mephisto trunk. I'm probably geting too old for living on the edge where blogging software is concerned.

    Anyway, I'm glad to come back to WordPress and less downtime, I hope.

  • Google Reader - a Bloglines user's perspective

    With the recent update of Google Reader, Google's shot at an online feed reader, I just had to try it out even though I was rather contented with Bloglines. I've been a long-time Bloglines user (since end 2003 I think), and even though there was little in terms of innovation and useful new features happening, Bloglines was, in my opinion, ahead of its time way back in end 2003, and it provided an unchanging interface that worked (well, my opinion on that changed after using Google Reader, as we will soon find out).

    Google Reader interface


    So, I took some time to clear the backlog of unread articles in my 289 feed subscriptions at Bloglines recently so I can "start over" at Google Reader and not have to read the same unread articles twice. Export Bloglines to OPML, import into Google Reader... It went painlessly and I noticed that Bloglines "folders" got converted into "Tags" in Google Reader - mmm, taaaags. But oh wait, what's this when I try to "manage my subscriptions" - tags, folders and labels. I'm getting confused.

    Google Reader - tags, folders and labels


    Just to make sure that they really are the same thing, I created some test tags/labels/folders, and yes they are actually the same thing (meaning if you add a new folder, it becomes available as a label and tag). I'm sure the terms used will be made consistent as Google Reader moves out of beta (or rather, gets further along as a beta).

    If you noticed how unorganized my Bloglines subscriptions are, that's because organizing feeds was a pain on Bloglines back in the day (it was clumsy to organize feeds into folders, you had to select a feed, scroll to the dropdown, and select the action to move it into a folder), but that's not really a problem now with the new drag and drop interface for managing subscriptions that Bloglines pushed out recently (I think). Thankfully, Google Reader makes managing subscriptions easy as well with the familiar Gmail-like labeling.

    Anyhoo, I started using Google Reader for a bit to read new articles in my feeds, and it wasn't long before I just found my killer feature: 'mark items as read when you scroll past them'.

    Google Reader's 'mark items as read when you scroll past them' preference


    This preference will tell Google Reader to only mark those items you have scrolled across as read. I hated it in Bloglines where clicking on a feed would mark all its articles as read, especially for those prolific blogs or those for which I have a backlog (200 entries is usually a little too much for one sitting for good blogs). I have to say it again, this is the killer functionality for me. I've been bitten by interruptions and crashed browsers once too many times. I always click on feeds (in Bloglines) with more than 50 unread articles with no small amount of trepidation, fearing that I won't be able to read them all or that I'd do something to crash Firefox (which is surprisingly common when you're working with large datasets in JavaScript). This often results in my reluctance to click on feeds with more than 50 unread posts, and with the vicious cycle 50 becomes 200 (the Bloglines limit for unread posts) and the feed rarely gets read (happens with blogs like Scoble or news websites like The Register).

    With this feature in Google Reader, I can click on "All items" even if it says there are 1 gazillion unread posts and still feel safe about not losing my place. Those of you using non-web feed reader applications may scoff at this (I'm really not sure, I haven't used one in a long time so I only assume something like this is common in applications like NetNewsWire or FeedDemon), but I've yet to see this done in a web application. Offline feed readers are not really an option for me unless they integrate to an online (i.e central) source (let me know if one exists!). For this feature alone, I decided to make a switch to Google Reader from Bloglines.

    Google Reader can get a little slow though, but then I only have 512MB of RAM right now so it probably isn't indicative of anything (the new Bloglines is slow on the Macbook Pro as well I noticed). Hopefully all this will be moot when I get the 2GB RAM upgrade that's waiting at the store. Other than that, Google Reader seems faster to respond network-wise (which I'm not surprised at, considering that it's Google (and their distribution channels)), and it's also prettier. Yeah, looking good matters to some people, like me (not that Google Reader is fantastic aesthetically, but it's far less staid than Bloglines).

    Now all that's left is to keep those fingers crossed for some sexy Gmail integration - I'm thinking something like RssFwd, a wonderfully useful creation by Choon Keat. I use RssFwd to track some important blogs and to track the latest releases of TV show torrents (Choon Keat is not gonna be happy about that though heh).

  • Native magnification in Mac OS X

    Mac OS X Tiger (10.4) had an minor update (version 10.4.8) not long ago and while the changelog seemed pretty uninteresting on the most part, there was one unannounced feature that's pretty cool - Zoom Using Scroll Wheel. If you've already updated to 10.4.8, you can try it out for yourself now by holding the Ctrl key and scrolling up the mousewheel. Cool, yeah? You can still do whatever you want in the magnified view (I took a screenshot) so it's not simply a cheap static magnification (just as Expose windows aren't static screen captures of your open windows).

    Screenshot of Digg taken with Mac OS X new zooming feature


  • Duplicating CDs/DVDs in Mac OS X

    When my dad asked me to duplicate a video CD, I decided I'd try to get it done on my new Mac (to, you know, get wow-ed by how easy it is on a Mac compared to the old way I used to do things on Windows). As it turns out, I was rather disappointed by the default Mac OS X burning functionality provided via Finder and Disk Utility, though I'd say it's still way ahead of the default disc burning in Windows XP. Burn folders? Dig them. Creating and customizing .dmg files (disk images) and then burning them? Love the UNIX toolbox-y goodness of how all this is done in Disk Utility.app. Duplicating CDs or DVDs? Not that great.

    Correct me if I'm wrong, but I couldn't get the "create image from CD, then burn the image" method to work with Disk Utility - it created a .dmg file without my files in it and I didn't burn it because I didn't want to risk getting a coaster.

    I asked a couple of Mac users and they didn't know how to duplicate CDs easily as well (one of them copied the files to disc and then burn them onto the blank disc). Some Googling turned up this thread which, while rather old, seems to show that there's no easy way to duplicate a disc with Mac OS X.

    Hmm, this just feels wrong to me, or more likely, Apple decided to intentionally leave disc duplication out (probably for digital rights issues or the like). I'm really missing the functionaliy that Nero provides with an easy "Duplicate disc" function. Does anyone know how to easily duplicate a disc in Mac OS X or can recommend a good application that does so?

  • Vibrant Ink TextMate theme for Eclipse

    Spent some time tinkering with Eclipse's preferences today as I was really missing the Vibrant Ink theme for TextMate and Eclipse is looking pretty now...


    The only thing that I still couldn't figure out was how to change the color of the folding/breakpoint bar next to the gutter. And I'm really starting to like the Monaco font that seems to be the default programmer's font on Macs (I found a Windows version of Monaco but haven't tried it).

    I guess TextMate envy still exists even when you are on a Mac with a registered version of TextMate heh.

    Here's my Eclipse preferences (I just did a File -> Export -> Preferences). It seems Eclipse only allows a full export/import so you may want to be careful to backup your existing preferences before importing mine.

  • So what's been happening?

    No I didn't "die" again... Here's what's been happening lately:

    • I got a MacBook Pro. I've been rather blown away by the whole Mac OS X experience. Yes, I do have some gripes, but it's one hell of a nice OS. I hope to write up on my whole "Switch" experience sometime soon.
    • I threw up a quick redesign of the blog... As you can tell (unless you're reading the RSS feed) it's been "in progress" for over a week - this coincides with the purchase of my MacBook Pro (*wink*). In any case, I wanted to get Windows running on the MacBook so I could test the site in Internet Explorer - to that end, I've setup Parallels (see Lifehacker article on Parallels) but it's way too slow on 512MB of RAM. Hopefully the 2GB RAM upgrade that's been out of stock happens soon.
    • I've also been working on a Rails project with Jon Tirsen and Michael Lee (who happen to work at ThoughtWorks). Jon has been busy lately (doing some really exciting stuff!), so I've only had the pleasure of working closely with Mike.
  • Job Ad: Java Web Developer for Bezurk

    We're on the lookout for a Java Web Developer (ad on Craigslist). I managed to get "Ruby on Rails" into the ad copy somewhere heh - it gets kinda lonely working in a development team of Java heads. Anyway, we're looking for a great Java developer to work with us in Singapore - if you're up for it, send us your resume. I would really like to get someone with some interest in Rails development to work together with on some upcoming (Rails) projects (you know, like after all the real work is done).

  • Excluding specific tests with rake

    Sometimes, I just want to skip just that one test when I run my tests with rake test because it takes much longer than other tests, and it gets in the way of rapid iterative cycles when coding. I looked around and haven't found a way to do that with rake, so I set down to writing a custom rake task.

    namespace :test do
      namespace :units do
        Rake::TestTask.new(:excluding => 'db:test:prepare') do |t|
          if ENV['THESE']
            def t.file_list
              exclusion_patterns = ENV['THESE'].split(',')
              unit_tests = FileList['test/unit/**/*_test.rb'].select do |path|
                excluded = exclusion_patterns.select { |excl| /#{excl}/.match(path) }
                p "Excluding #{path}" if not excluded.empty?
                excluded.empty?
              end
            end
          else
            p 'Running all unit tests (specify THESE="pattern1, pattern2" to exclude files)'
            t.pattern = 'test/unit/**/*_test.rb'
          end
          t.libs << 'test'
          t.verbose = true
        end
        Rake::Task['test:units:excluding'].comment = 'Run unit tests with exclusions (specify exclusions with THESE="pattern1,pattern2")'
      end
    end
    

    Of course, rake test:recent and rake test:uncommitted would suffice for most purposes, but there are times when I just want to not run that a particular test or tests because they just take too long (and by definition, get in the way) and I'm pretty sure they won't be broken. (Always run all the tests before a commit though, don't be naughty.)

    You can run the task like this:

    rake test:units:excluding THESE="item_test,atte\w*"

    Update: As it turns out, the task descriptions weren't being set correctly. Revision 5016 fixes this so rake -T shows the task descriptions properly.

  • Some functional testing gotchas in Ruby on Rails

    I've been writing lots of functional tests lately and came across several unexpected gotchas. Seeing as how forgetful I am recently, I'll jot them down here (and hopefully benefit anyone else who comes across the same problems).

    Testing flash.now

    Testing the contents of the flash.now, somewhat surprisingly, can't be done with code like this:

    assert_equal 'Unable to activate your account.',
      flash.now[:error]
    

    flash.now[:error] would be nil, in your functional test, and a quick lookup on Google brought me to HowToTestFlash.Now on the Rails wiki. Apparently, Rails renders the flash in the view and then proceeds to clear the contents of flash.now (so it isn't accessible in your functional test). So, you'd have to test the contents of the rendered page, instead of testing the flash itself:

    assert_tag :tag => 'div',
                   :attributes => { :class => 'flash error' },
                   :content => 'Unable to activate your account.'
    

    Asserting cookies

    This one's particularly annoying - trying to assert that cookies are set by your controllers. Unlike what was written in the Rails manual on testing (granted, it's rather well-known that the manuals are somewhat outdated), you cannot retrieve cookies using a Symbol as an index into the cookies hash in your functional tests.

    cookies[:auth_token] = {
      :value => session[:user].remember_token,
      :expires => session[:user].remember_token_expires_at
    }
    
    assert cookies[:auth_token]  # nil
    assert cookies['auth_token']  # This works.
    

    Thanks to Herry for pointing this out - it saved me an exercise in frustration.

    Yet another cookie testing gotcha is testing for the deletion of cookies (example use case: deleting a users cookie after he logs out). So you may do this in your controller:

    cookies.delete :auth_token

    And expect this to pass in your test:

    assert_nil cookies['auth_token']

    But nope, instead of setting the cookie to nil, the cookie is actually emptied.

    assert cookies['auth_token'].empty?  # This works.

    I just used wrote my own custom assertion (there is an assert_cookie plugin around but I didn't think this justified adding another plugin to the application, not just yet):

    def assert_no_cookie(cookie_name)
      cookies[cookie_name].nil?
        || cookies[cookie_name].empty?
    end
    

    Update: as it turns out, assert_no_cookie and assert_cookie_equals exist as custom assertions in ActionPack (source), but have been deprecated in favor of the cookies collection. Still, looking up the cookies collection with a Symbol but having it return nil is unexpected behavior considering the inline documentation says it should work.

  • From Typo to Mephisto

    So with the usual fickle-mindedness, I've changed my blog software from Typo to Mephisto. Well, it wasn't so much based on a whim - there were some reasons I wanted to stop using Typo:

    • For some reason, Typo is very unstable for me on my VPS with 160MB RAM. I deploy Typo on a mongrel_cluster of 2 mongrels, fronting it with Apache 2.2 + mod_proxy_balancer, and all 160MB RAM and 192MB of swap is used up whenever I post an article or sweep the cache. Comments also take noticeably longer to post on my blog than on other Typo blogs. I'm not sure what exactly I've done wrong.
    • I often get an obscure NilClass error that screws up the front page whenever pages are regenerated (often caused by someone posting a comment or an article).
    • Changing the blog's design requires one to create or edit an existing Typo theme - I kinda missed WordPress's Web-based template editing. This was a significant roadblock to me ever changing the look and feel of my Typo blog.

    Coincidentally, my urge to swap to Mephisto coincided with the release of mephisto 0.6 (Immortus) just today, and after some minor hiccups, I managed to get Mephisto up and running with my articles imported from Typo. (Still couldn't get Capistrano to play well though, which I'm guessing is due to Rails version conflicts - I guess I'll just have to do with the release version.)

    Documentation is rather sparse at the moment, especially on how to convert from Typo to Mephisto (I updated that wiki page while I was there). I dug around in Mephisto 0.6’s source and found the converters in vendor/plugins/mephisto_converter. Not sure if there is a web interface to this - either way, things worked almost perfectly just running this command:

    script/runner "Mephisto.convert_from :typo" -e production

    You need to create a typo entry in database.yml that points to your existing Typo database. Also, the conversion will fail if any of your Typo users and email addresses are the same as the default Mephisto user admin. To avoid this just change the Mephisto user(s) login and email to something else.

    So there's that, hopefully some time this year I'll update the look and feel.

  • Essential plugins for Trac

    I've been setting up several Trac installations recently, and while Trac by itself is already pretty awesome (I love it's simplicity and functionality that doesn't get in the way), I was pretty dismayed to discover that administration isn't that great. Configuring Trac is a matter of (more) hacking at the command line with the trac-admin script, and user administration is managed with htpasswd or whatever authentication scheme that you choose (point being, you can't manage users via Trac itself).

    Thankfully, there are some plugins that you can install to alleviate the situation. Trac itself publishes the WebAdmin plugin, which is slated for inclusion in Trac in Trac 0.11. This adds a web interface to trac-admin and it's pretty darned useful when used with two other plugins: Account Manager plugin and WebAdmin Users plugin.

    Account Manager plugin adds HTML form-based login instead of the default HTTP authentication with plain olde Trac (plus user registration and changing of passwords). I'll let you in on a secret - I never managed to configure my Trac installs properly to use HTTP auth, but with this plugin it just worked.

    The WebAdmin Users plugin let's you add/remove users via WebAdmin plugin.

    Managing user account with Trac WebAdmin Users


    By the way, if anyone has got a Linux binary for mod_dav_svn.so for Apache 2.2, let me know - I'm too lazy to go through the whole trouble of compiling it.

  • This blog is related to "Cartoon Porn"

    Got this in my email recently:

    Respected Webmaster,

    I found your page on Yahoo! when I was searching for Free Cartoon Porn (this topic is related to my site). You are running a great website and I would be happy to place your link on my site. If you would like to exchange text links with me, please submit your website on deleted link

    I'm sure you know that links exchange will help both of us to get better positions on search engines and have more visitors to our websites.

    I went to Yahoo! and searched for "free cartoon porn redemption" and sure enough, that link was up there. A bunch of comment spam qualified my site for a link exchange, yippee! I was pretty amused.

  • Beware of form parameters named 'submit'

    Well, that is if you are ever going to be submitting the form via Javascript. We had to generate forms on the fly and POST them behind the scenes (i.e. in hidden <iframe>s), and got a decent script going, until a particular case failed for no apparent reason. I finally found that the problem was due to a form parameter named "submit" that overwrote the submit() function of the <form>. That's, of course, after looking at all the wrong places.

    So this doesn't work:

    <form id="ninjaForm" action="/come/get/some" method="post">
      <input id="someParam" name="someParam"
        type="hidden" value="Some value" />
      <input id="submit" name="submit"
        type="hidden" value="Start search" />
    </form>
    <script type="text/javascript">
      setTimeout("document.ninjaForm.submit()", 200);
    </script>

    When the browser tries to execute "document.ninjaForm.submit()", it sees the "submit" form field (which overwrote the submit() function) instead and complains that "submit is not a function".

    Do this instead:

    <form id="ninjaForm" action="/come/get/some" method="post">
      <script type="text/javascript">
        // Alias the submit function in case there is a 'submit' param.
        ninjaForm = document.getElementById('ninjaForm');
        ninjaForm.__submit = ninjaForm.submit;
      </script>
      <input id="someParam" name="someParam"
        type="hidden" value="Some value" />
      <input id="submit" name="submit"
        type="hidden" value="Start search" />
    </form>
    <script type="text/javascript">
      setTimeout("ninjaForm.__submit();", 200);
    </script>

    That's one way to workaround, of course, and we can get away with the "ninjaForm" global in this case.

  • Application error (Rails) on Google search results

    I did a search on Google for "Rimuhosting" today, some time after I signed up with them, and hey, there's my blog in 3rd spot - is that some mad SEO skillz or what. And then "oooh, there's an 'Application error (Rails)' result coming out, that's not good".

    'Application error (Rails)' on Google


    I thought it was amusing - Google must have crawled my site just as I had deployment troubles (yeah it was a smart move leaving MySQL to it's default settings and using the InnoDB storage engine on a memory-starved VPS). All seems fine now though I'm still somewhat amazed at the hungry Mongrels (at 25-40MB per Mongrel process, these things run more like St. Bernards). (A shout out to Ryan Daigle who has been giving me some Rails deployment advice.)

    By the way, the "mad SEO skillz" comment was a joke, I wasn't at all surprised to be honest, Google just loves blogs (and they still do, after all this time of I've been not blogging).

  • Ruby on Rails satire blog

    Javac programmers are Very Slow and he is Agile

    My Rails Blob pokes fun at the cult of Ruby on Rails via satire. Recommended blog posts: Rubby Links on the W.W. w. and Upgradeing Rails: Securiety through Obscenity [sic]. I've subscribed to the RSS feed.

    NO! - Bad User!!!

    The Daily WTF has this monthly collection of hilarious pop-up error blunders, and as usual this month's a gem: Pop-up Potpourri: Announced By God. Must read.

    Developers would find this particularly funny (I did):

    Bad user!


    And this one's for my boss, Gary (remember the 'Abort/Retry/Ignore' you saw at the MRT station?)

    Next stop: null
  • Extract Any Archive with Ruby, Slimtimer, Feedalizer

    A bunch of cool stuff I found over the weekend that didn't individually deserve a blog post, but I thought would be worth sharing.

    Extract Any Archive with Ruby

    This is really neat if you could never remember what commands or command-line arguments you need to extracting archives (.zip, .tar.gz, .tar.bz2 - those kinds of archives).

    With this Extract Any Archive script written in Ruby, instead of

    tar jxvf DonkeyPr0n.tar.bz2

    you can simply do

    e DonkeyPr0n.tar.bz2

    Instant gratification!

    Slim Timer

    SlimTimer is one another of those productivity-keep-track-of-your-todo-list type applications - I like it because seeing the time ticking away gets you motivated on sticking to the task at hand. You can stick it into your Firefox sidebar and all that AJAX-y stuff makes it behave like a normal application.

    SlimTimer running in a Firefox Web panel


    Feedalizer - transform web pages into RSS feeds

    Choon Keat pointed this out to me recently: Feedalizer (backed by Hpricot). I played around with it and it was so easy (and fun) to use. Whipped up a Mongrel news feed (code) for Mongrel news (which didn't have an RSS feed). The hardest part was figuring out which Hpricot methods to use to parse the bits I needed.

    Served by Apache 2.2, mod_proxy_balancer, and Mongrel

    I got off my butt and set up Apache 2.2 with mod_proxy_balancer to load-balance a cluster of Mongrel processes, if you're still seeing this, that means it hasn't crashed, yet :p. Just kidding heh - I'm finding this setup pretty stable, though there was once the mongrel processes seem to have been over-loaded with requests (with 96MB RAM I can only run 2 processes, and even then I run out of memory real fast due to automated comment spam - does anyone have any solutions to blocking automated comment spammers before they even hit your website?).

    In any case, I learnt quite a bit as I was setting up my deployment environment for Rails, and hopefully someone will be able to benefit from that when I finish my post on the topic.

  • Moved to Rimuhosting

    If you're seeing this, that means the new DNS settings have finally resolved and you're being served by a plain old Mongrel server instance on my Rimuhosting VPS. It's so much faster than before.

    Server spy extension showing my site running on Mongrel


    Before you start lecturing me on running just Mongrel, let me just say that now isn't right time to get a proper setup - I'll get it done over the weekend. There're so many choices (combinations of Apache, lighttpd, pound, pen, mongrel, mongrel_cluster), I think I'm due for some reading before making a choice.

    If you see any bugs, let me know! Of course, any deployment-related advice will be appreciated.

  • Moving to Rimuhosting VPS

    After being plagued with performance issues running Typo (the blog software on which this blog currently runs) on Dreamhost, I decided it was time to give in and get a VPS once again. I was previously with Linode.com (they have a great control panel where you can drop in the distro you want - still have screenshots somewhere for an unpublished post), and then JVDS (good hosting, usually quick replies to my support tickets, but slow to push out a VPS control panel they've been promising), running Gentoo on both VPSs. Right now, I'm on a shared hosting plan with Dreamhost.

    Well, Dreamhost has been good to me - I got so many referral credits from them that they paid for my subscription many times over after I posted about their $0.77/month offer. Unfortunately, Typo seems to be quite a monster (compared to Wordpress), and database access is purportedly (and observably) slow on Dreamhost. Still, Dreamhost is a great host for shared hosting, I'm sticking with them for delivering more static content. A VPS just makes sense now that I've been tinkering more with Ruby on Rails, plus which geek wouldn't admit it just feels more right to be in full control of your server (well, it's root on a virtual machine, but still root).

    This time around, I chose Rimuhosting, a New Zealand-based hosting company (servers are US-based, of course), and here's why (I did some research once again on WebHostingTalk and asked some people their experiences with their webhosts):

    Pros:

    • Fairly affordable prices - I got their cheapest MiroVPS1 plan, which goes for $19.95/month and get 30GB of transfer and 96MB of RAM (which could be a problem, but upgrading seems easy enough anyway from what they say on their site). Comparing to several other VPS hosts like unixshell, Tektonic.net, ServerAxis, their plans are somewhat mid-range.
    • Their reputation is great. Yup, I've heard and read only good things about them, something that's quite hard to find in the world of web hosting. We use them at work to setup servers sometimes too.
    • Seems to be run by competent people - they have a bliki (blog + wiki) with some nice posts. The one that really caught my eye is the one on their Ruby on Rails hosting stack. Not that I'd use it since I'm on Ubuntu, but it shows that the folks at Rimuhosting are up-to-date.
    • They run Xen, which Deepak, among others, tells me is the most efficient server virtualization software. Yeah, whatever, score one for statistics (and lies).
    • Support is reasonably fast - got replies to my pre-sales queries reasonably fast (more than a few hours, but it wasn't during working hours anyway). Of course, I'll have to see how it goes now that I'm a real customer.
    • A simple web-based control panel where I can reboot the VPS. It's no Virtuozzo Power Panels, but it's enough. JVDS didn't have one and it started to get old sending support tickets to do a reboot.
      Screenshot of Rimuhosting VPS control panel


    Cons:

    • They don't support Gentoo, my Linux distro of choice. Only RHEL, Ubuntu, Debian and Fedora Core are supported. I went with Ubuntu, not having much experience with it (excluding a brief affair with Debian). I was wary of this at first, but my fears of bungling around with an unfamiliar distro have vanished after I saw how easy it is to install stuff with apt-get, and things are not placed in weird places in the filesystem hierarchy. I think I could get used to it, this "not compiling everything that moves" idea ;).
    • ServerAxis supports Gentoo, and offers much better "value for money", except that their lowest priced plan is much higher ($30). But you get 512MB RAM, 200GB RAM, and 5 IP addresses... Too bad I'd rather pay out of my PayPal funds.

    Well, less blogging, more server migration. Actually the application installs are mostly done. I've never had apt-get play so nice back when I was experimenting with Debian - just
    apt-get install [package] and things are installed quite cleanly (keyword: "cleanly"). I think this may grow on me (yeah yeah laugh and point at the Ubuntu noob) - perhaps you don't need to compile everything (gasp, I said it!).

  • Rails 1.1.5 (security patch)

    For anyone who cares or who runs a production site on Rails, DHH just announced the release of Rails 1.1.5 with an undisclosed security patch. Time to

    gem install rails

    and update your config/environment.rb to use the Rails 1.1.5

    # Specifies gem version of Rails to use when
    # vendor/rails is not present
    RAILS_GEM_VERSION = '1.1.5'
  • Easy introspection in Ruby

    Just some notes on reflection and introspection in Ruby

    
    someString = 'http://google.com'
    
    # Print the object's class, methods, superclass and
    # ancestors (mixins and superclasses),
    p someString.class
    p someString.methods
    p someString.class.superclass
    p someString.class.ancestors
    
    # Print the methods of the String class.
    p String.private_instance_methods(false)
    p String.public_instance_methods(false)
    
    # Pass true to recurse into parent classes.
    p String.public_instance_methods(true)
    
    
    # Calling instance methods with send().
    "Random text".send(:length) # 11
    -23.send(:succ) # 22
    
    # Using Method objects and call().
    length_method = "Random text".method(:length)
    length_method.call # 11
    
    # Another way, using eval().
    length_method = %q{"Random text".length}
    eval length_method
    

    Check out Distributed Ruby (DRb): it's a very neat, non-fancy way of exposing object methods as remote services.

  • From Wordpress 2 to Typo 4

    It was a slow day so I decided to cross one thing off my to-do list, and that's getting Typo installed and migrating from WordPress. I've been looking at getting Typo installed for awhile ever since I saw (and liked) its AJAX-y comments form, plus Typo 4 being just released did help.

    The install went straightforward enough, though I didn't get to try out the new installer gem. I didn't bother because I'm on a Dreamhost shared hosting account, though installing the gem without root privileges seems like it should work. I simply followed this guide to getting Typo 4 running on Dreamhost. Ran into some trouble running the Wordpress import script (found in /db/converters/wordpress2.rb), but Google turned up the solution (basically a patch to the Articles model that tries to send pings when importing posts). This should already be fixed in Typo SVN, but it's not in release 4.0.

    Anyhow, Typo seems spiffy, though I am still getting the occasional "Application Error", but that's most likely a Dreamhost thing. Liking the near-minimalist theme as well - I'm already missing the green clover though heh.

    Things I'm already liking:

    • Sidebar modules that you can add to your blog - for example, getting the del.icio.us links I have at the side to show is just a matter of dragging the del.icio.us sidebar item to the "Active" list and pointing it to my del.icio.us feed. There're sidebar items for Flickr, tags, 43things, Amazon, and even the categories and archives are sidebar items.
      Screenshot of Typo 4's sidebar items administration


    • A Blacklist section where you can enter blacklisted words and regexes! Take that texas holdem poker!
    • Live preview of your posts as you type - can get a little sluggish though.
    • The ability to search and delete all comments - Wordpress was missing that and running SQL statements in phpmyadmin stopped being fun.
    • The nice Scribbish theme that comes with the Typo package.

    Things that I don't like:

    • Linking to uploaded files was confusing. I like the whole "Attachments" idea, but I didn't know how to link to them until I browsed to the Resources section and copied the link to the image I just uploaded (by saving the post I was making - there wasn't a way to upload an attachment while writing the post).
      Screenshot of Typo Attachments feature


      I'm probably doing things the wrong way though, seeing as this is way too obtuse. Would be nice to see improvements (such as, upload file as you write your post, click on uploaded file to include into post, all on the same page with AJAX).

    • Um, nothing further!

    Need to use it more to decide whether I'll like this or not.

  • RSpec and BDD

    RSpec: Behavior Driven Development framework for Ruby. It's like writing your specifications in English that's executable. Eight tools in one, and a really nice Mock Objects framework.

  • Unobtrusive IFrame shim - a half solution

    Something that really bugged me when using YUI Calendars and YUI AutoComplete widgets was how IFrame shims are currently being managed in libraries and home-brewed code (we use IFrame shims in many places as well). YUI Calendars doesn't have a built-in IFrame shim, YUI AutoComplete has its own, and we use our own IFrame shim library.

    For the uninitiated, IFrame shims are basically IFrames dynamically inserted (hence "shim") under overlaying DOM elements so that any <select> elements do not show through in IE 5.5 and above. A common example would be dynamically generated divs that float on top of drop downs in forms, such as the Yahoo! Calendar widget example below.

    An example of the select box see through problem in IE 5.5+


    So anyway, the thing that's bugging me is the awkwardness in inserting IFrame shims where they are needed and the duplication of unnecessary code to create, show, and hide the IFrame shims. Ideally, I'd like to do something like this to attach IFrame shims and forget about them:

    var iframeShim = new net.codefront.IFrameShim('elementId');

    Or the conceptually nicer way of attaching IFrame shim "behavior":

    net.codefront.IFrameShim.attachShim($('elementId'));

    No need for manually displaying and hiding the IFrame shim, polluting display logic and event handlers. I don't care how all that shimming is done nor should I have to. I don't want to have to call displayShim() whenever I display a floating div and hideShim() when I hide it. It should be smart enough to display the shim when the overlay is showing and to hide when the overlay is no longer visible.

    As it is, I am seeing code duplicated in many places to manage these IFrame shims, and that's a bad smell. Ahh ahh, before I go further, I am completely aware that I could be completely off-base and such a miraculous time-saver of a library already exists. If so, I want to know!

    Trying to fix this problem, I came up with this bit of JavaScript, which you shouldn't bother reading. The idea is to allow me to do this:

    var shim = new com.bezurk.widget.IFrameShim('myFloatingCalendar');
    shim.manualShim = false;
    shim.manualDeshim = false;

    And I have a 'myFloatingCalendar' element that's automagically shimmed whenever it's displayed. (The shim.manualShim and shim.manualDeshim are defaulted to false, and are shown purely for explanatory purposes.)

    There is one problem though - I can't figure out an elegant way to "listen" to the display and hide events of the element that's shimmed. The way I've done it is to rely on Prototype's PeriodicalExecuter (extended to support a stop() method) to poll for the visibility of the element. Naturally this is an ugly hack and causes noticeable flicker.

    I've looked at custom events (YUI and Dojo have very nice event handling libraries) but can't see how to hook this up unobtrusively. If you have a suggestion or a solution, I want to hear it!

  • The first rule of Maintenance is - you do not talk about Maintenance

    What would Tyler Durden do if he had to bring down his Web 2.0 service for maintenance?

    Someone in the office thought this message would be appropriate.

  • When Firefox extensions annoy

    Firefox extensions are a good thing (since sliced bread and embedded ActiveX controls...), in fact some of them are my most compelling reasons for using and loving Firefox.

    Anyway, so with that World Cup thing that's going on I installed an extension called FootieFox that tracks any goals being scored in real-time and pops up a small little notification message. All fine and dandy, great unobtrusive little widget. Well, right until "FootieFox Education Day", when the extension started showing a banner that overlaid everything else on the screen, blinding up in obnoxious slowness, sitting there for a few seconds, and blinding down painfully with no way to dismiss it except to wait.

    FootieFox extension overlay annoyance


    I promptly uninstalled the extension. (Great extension, but that "I'm gonna block out everything else so you have to read me" banner thing annoyed me.)

  • The first Firefox ads are out

    Firefox Flicks has just put up 3 of the first ads that have been submitted to the Firefox ads competition. Check them out. Drama queen was my favorite of the three, though they are all surprisingly very good quality ads.

  • Wufoo - Cool AJAX form builder

    Wufoo - try the demo. Very impressive (strange how I find this more impressive than Ajax Write).

  • How to fix IE always opening Firefox instead

    It was not until recently (when I had to actually open Internet Explorer to test some cross-browser stuff) that I realized my workstation and my home PC had a particular problem in Internet Explorer: anything I typed into Internet Explorer opened in Firefox (my default browser). After some Googling, a forum thread over at Neowin.net provided a solution - just delete the HKEY_CLASSES_ROOT\CLSID\ {c90250f3-4d7d-4991-9b69-a5c5bc1c2ae6} registry entry.

    Long version for Windows Registry newbies (alternatively, you can create a .reg file and run it like someone said in the thread):

    1. Click our Start button, then "Run...".
    2. Type "regedit" into the box that appears and hit Enter. This will bring up the Registry Editor.
    3. Drill down the tree on the left pane under HKEY_CLASSES_ROOT until you find the HKEY_CLASSES_ROOT\CLSID\ {c90250f3-4d7d-4991-9b69-a5c5bc1c2ae6} entry.
    4. Delete the entry.
    5. Close the Registry Editor - you're done.

    Anyway, turns out this is a bug caused by having "IE7 Beta1 in an unsupported side-by-side configuration with a version of IE6", as officially stated in IEBlog.

    Painfully, I had to look all over again for the solution back home because I didn't bookmark the page and it didn't make sense to run over to the office just for this. Now it's bookmarked in del.icio.us and blogged so I won't forget.

  • Copy 'n' Paste and the incorrigible programmer

    We just never learn, do we?

    String fromLocationCode;
    Location fromLocation = request.getFromLocation();

    String toLocationCode;
    Location toLocation = request.getFromLocation();

    // Do stuff to toLocation

    First, my (extremely talented) colleague makes a similar copy and paste blooper, and one day later I repeat the same mistake. But, but... Select boring code, Ctrl-C, Ctrl-V, it's sooo easy...

  • Diggnation drinks Tiger Beer

    The hosts of Diggnation (Kevin and Alex), one of the few podcasts I listen too, usually have some beers as they are hosting the show, and in the most recent episode 34, they actually had Singapore's trademark Tiger Beer (a hint as to why this is remotely interesting to me: I'm Singaporean). It's rather interesting because most of us (Singaporeans) know how a Tiger tastes like (having had watered-down Tiger beer from when I was in the army didn't help), and Kevin and Alex were pretty much making polite comments about it. Of course, I could be entirely off-base, but I have watched the last episode (or was it the one before?) where they were raving over Hoegaarden (which is a great beer, this coming from one who doesn't drink more than once every few months).

    By the way, does anyone know anything about the Geek Drome (or "Drone" or "dom") podcast they were talking about on the show? Been poking around Revision3 for it but no luck.

  • Tabbed-browsing enhancements for Firefox in latest build

    Bug 308396 - UE fixes for tabbed browsing landed in the 2005-01-21 (Trunk) build of Firefox, bringing with it "tabbed browsing usability enhancements" (and I quote Ben Goodger from the bug page).

    One fix that I really like is the one related to moving focus after a tab is closed. Right now, if a link opens in a new tab and gains focus, and you close that new tab, you are taken to the right-most tab you have opened. The fix makes it much friendlier by taking you back to the "parent" tab that opened that link. Hurray!

    Another change is the addition of big "X"s (crosses) on the right of each tab which you can click to close tabs. Something like this:

    Close buttons on tabs in Firefox


    Well, rather nice for people with only 2-button mice or using specific OS ports of Firefox (I'm thinking some flavors Linux desktop environments, but I'm not sure), since they can now close tabs in a single-click (instead of right-click -> Close Tab). Personally, I prefer to middle-click tabs to close them or hit Ctrl-W, and the additional close widgets are just unnecessary clutter (so it's a good thing this can be turned off via about:config at the moment). Anyway, if you're using a 3-button mouse and are closing tabs with right-click tab -> Close Tab, stop doing that! Middle-click the tab. Middle-clicking is (or should be) the new "close me" in any environment that involves tabs - think IntelliJ IDEA and editors like PSPad. If we could just get other applications to follow this "new convention" (think Eclipse IDE, EditPlus).

  • Naming your computer

    Great conversation pieces when you name your computer "vagina".

  • How does one get in touch with the Firefox Add-ons people?

    An update: I've actually deleted the existing extension on the Firefox Add-ons site and added a new one per Patrick's suggestion below, and it is now available there. And yeah, I didn't put any of the changes you guys requested in there besides getting it to work for Firefox 1.5...

    I've seen your emails and comments about updating the Unread Tabs extension for Firefox 1.5, and I apologize if I haven't responded to your email personally.

    Anyway, yes, the fix is extremely simple, and in fact I did try to fix it once and upload it onto the Firefox Add-ons site a long time ago last year when the extra character in the GUID was brought to my attention (to explain this a bit - each extension has its own GUID which is universally unique 32-character string, and somehow I screwed it up by inserting a GUID with 33 characters). Well, the problem is, in the developer section of the Firefox Add-ons site, the "Add New Version" function enforces the rule that a new version of an extension should have the same GUID, which is perfectly fine and expected.

    So I've been trying to find a way to get in touch with the guys over at Firefox Add-ons to workaround this and to see what implications this may have, but can't find any form of contact information at the site (and no, I'm not gonna email the mozilla.org webmaster to bug him unless no one really knows how to reach the Firefox Add-ons guys). Anyone has any clue? Or maybe I should just email the mozilla.org webmaster...

    Whatever the result of that, here's an updated Unread Tabs for Firefox 1.5 with a new GUID:
    Install Unread Tabs | Download Unread Tabs

  • Gmail adds a Delete button, at last

    Remember back when you had to use a hack like a Greasemonkey script for one-click deletion of emails in Gmail? Well, Google has added a Delete button.

    Gmail's delete button


    (You'll have to log out and log back in if you don't see the button - well, at least I had to.)

  • Pop-up blunders

    The Daily WTF has screenshots of some hilarious pop-up error messages over at it's Pop-up Potpourri: Sixth Time is the Charm post. And cute that CVS servers send the string "I HATE YOU" whenever your login is incorrect.

  • Pandora is one hell of a radio station service

    This is probably old news to most of you, but I just chanced across Pandora today and I was quite blown away by it. Well, it did happen to be a time when I was rather bored of the current songs on my playlist (I still love 'The Artist in the Ambulance' by Thrice and 'Slow Motion' by Third Eye Blind, probably played them a 2^20 times on my iPod), but it is still pretty awesome even then.

    So what is Pandora? (You'll want to skip this post if this is old news to you.) With Pandora, you enter a few songs or artists that you like, and it plays similar songs that it thinks you'd also like to listen to. Nothing really special, except that the results returned are based on some Music Genome Project and it seems to do pretty well in playing great songs I like from bands that I'd never heard of before. It has a Flash interface through which you basically do everything, and it will stream and playback songs, and you can have different "stations" for different types of music/artists you'd like Pandora to match against (so I can have, say, one station that plays songs like "Stare at the Sun" by Thrice, Weezer-like songs, and heck, even throw in a little Bowling For Soup for a little pop punk flavor).

    As it plays each song, you can give it a thumbs up by indicating you like it, or if you indicate your distaste, it'll skip to the next song. It uses this to fine tune the type of songs it plays for you so it learns more about your tastes the more you use it. In practice, I haven't used it long enough to observe any noticeable improvement in the selection of songs it plays for me, but then I'm a mere 1-day old user. That said, I've liked roughly 4 out of every 5 songs Pandora has played for me - that's pretty awesome.

    Anyhow, the service is free and sponsored by ads, but you can also sign up for the paid service to remove the ads (that's the only difference between the free and paid versions, for now). There are convenient links for you to buy any songs that caught your fancy from Amazon.com or Apple. Apple really needs to set up an iTunes music store here soon, rather than late next year as was mentioned in the papers today - I'd have bought some tunes already if not for that.

    So check Pandora out if you haven't already or just read about it in passing - if you love your music and are finding a shortage of new tunes to hum or sing along to, this could be your answer to unravelling new favorite artists and songs.

  • SitePoint's New Book - Run Your Own Web Server Using Linux & Apache

    SitePoint has just released another book, Run Your Own Web Server Using Linux & Apache, which is looking to be quite a winner for those of you who want a complete guide to setting up your development environment for LAMP development. As an Advisor over at SitePoint Forums, I am getting this book for free (yay SitePoint!), though to be honest I have been neglecting my duties over there.

    Anyway, just a little publicity for SitePoint's new book (disclaimer: I have no obligation to SitePoint to promote the book) - I really think it looks promising for those of you looking to run your own LAMP setup or even budding system administrators, as it seems to cover a whole lot and SitePoint's books have been known for being immediately of practical use. It costs USD39.95 and comes with a Fedora Core 4 DVD, Linux Quick Reference Poster and a collectible SitePoint-branded bookmark(!).

    Enough said though, because the book hasn't arrived yet. If I do have time to read it (though it isn't very relevant to me now), I may give it a once-over review (no promises).

  • Moving to DreamHost

    I'm gonna be moving the codefront.net domain and the hosted services (Web, email, database) to a DreamHost account soonish. The current VPS is just too under-utilized at the moment, considering I am no longer using my SVN repository. Plus, DreamHost supports Ruby On Rails now. Expecting soothing server migration woes, which would be a relief from development stuff at work.

    Update: Migration complete!

  • An extension for a bigger Back button in Firefox

    Phil Wilson has quietly created an extension that gives you a bigger Back button in Firefox. It's charmingly named "Fitts' Back Button" too.

    Bigger back button in Firefox


    I'd blogged about this previously and this is in fact in Firefox Secrets (on page 191, with due credit given, of course).

  • Plugging Firefox Secrets

    So here it is, the inevitable "author plugs his new book" blog post. In the last months of 2004 and early months of 2005, I was spending a good number of my evenings after work writing what was to become (and now is) Firefox Secrets. "Writing" being a broad term used when tasked to churn out a software book - I'd say that almost 50% of the time was spent fiddling with Firefox, tracking elusive things down, verifying that things I intend to say actually work, and taking screenshots. For those of you who were regular readers, you know how I love to put up screenshots in blog posts, but I tell ya, it kinda gets old after awhile. (Thankfully, MWSnap is an amazing screen capture tool that's freeware to boot.)

    Firefox Secrets photo


    I'd like to thank SitePoint for approaching me with this offer to write a book on Firefox, considering my (non-)experience in book-writing. The content of the book was initially intended to be geared towards the non-technical Firefox user (as proposed by SitePoint), but we (SitePoint actually) decided to rebrand it as a "Secrets" book when they discovered that my submitted drafts weren't that much of a plain old technical manual but more of a "stuff you don't know" kinda book. Honestly, I never really warmed to the idea of writing another "blah how do I use Firefox" grimoire so the rebranding is for the best.

    So, what's in the book? Well, this table of contents should give you an idea. SitePoint also included a nice little CD-ROM with Firefox, Thunderbird, and extensions and themes that I've mentioned in the book material. I tried my best to include everything for both new and experienced Firefox users, so there should probably be something in there that interests you. For those of you who want a preview of the book, check out the You Don't Know Jack About Firefox! article over at SitePoint.com, or download the sample chapters (PDF format, requires email address).

    Acknowledgements

    I've acknowledged these same people in the book, but it bears repeating here.

    I owe it all to SitePoint for making the book come through, even though things weren't all that smooth-sailing. Thanks for giving me the opportunity to write about Firefox.

    Asa Dotzler, for being an awesome technical reviewer for the book, thank you. Asa's reviews relieved me of several misconceptions, and I even picked up a few secrets from him (and yes - these secrets appear in the book).

    Dear blog readers, I admire you for tolerating with my absence and sticking around even when at times I seem to have died and left my blog in an owner-less limbo. Thanks for reading as always.

    Last but not least, the Firefox community: developers, evangelists, users, all of you. Firefox is the success it is today (and a wonderful browser to boot) because of you guys.

    Elsewhere, on Firefox Secrets

    I'll be keeping (and updating) a list of blogs, articles, and other miscellanous web stuff that talks about Firefox Secrets here.

  • Getting back on track

    It's been a while since I last blogged huh? Well, I actually was back, until a particularly unfortunate incident with my VPS where the database tables for WordPress got corrupted (likely due to overflowing backups and running out of space) threw me off track and got me real grief-stricken that I lost my blog template/theme and a couple of posts (I've managed to restore the template/theme since).

    Excuses, all. I'm horrified that I've actually allowed my blog to fall into a state of disuse and disrepair. I'm back and I hope to stay back. But first I've to clean up all this crud that's on my blog (comment spam, Trackback spam, etc.).

    Public Service Announcement
    I lost a post entitled "Ooh look at this old website I dug up". I believe I offered something to the person who guessed correctly, but I only have Jed Brown's comment in my email. Whoever guessed first before Jed did, please do send me an email (chuyeow at gmail dot com).

  • One-click delete in Gmail, thanks to GreaseMonkey

    Thanks to GreaseMonkey and this nifty user script from Anthony Lieuallen, deleting emails in Gmail got much easier and less iffy (there were times when I selected "Add Star" when I really wanted to "Move to Trash").

    Screenshot of the added Delete button in Gmail


    Oh yes, that's Trackback spam.

    What other neat GreaseMonkey stuff did I miss?

  • Mr Ruderman has switched

    Jesse Ruderman: Switching from Movable Type to WordPress. Gotta upgrade to WordPress 1.5 soonish.

  • What's up with Stop-Sign and Firefox?

    I was browsing the documentation for the awesome Web Developer Toolbar extension today and for some reason noticed a Google ad that was headlined "Get Mozilla Firefox". It probably caught my eye because Firefox ads on Google were new to me and I wondered if it wasn't some sort of sfx initiative of some sort.

    I spotted the ad on this page (will not necessarily appear for you, of course, since Google rotates ads - try surfing around the documentation pages if you're interested in seeing the ad for yourself).

    Screenshot of the Web Developer Toolbar documentation page


    Here is the ad itself (it's the bottom one). Er, www.stop-sign.com?

    Screenshot of Stop-Sign's Firefox ad


    The ad brings you to this landing page (incredibly small screenshot below) which apparently is part of an ad campaign of eAcceleration to promote its Stop-Sign product (which from what I understand is some sort of virus scanner cum spyware remover).

    Screenshot of Stop-Sign's Firefox ad landing page


    Well, they seem to be blatantly riding on the coat tails of the Firefox name. And they even offer Firefox (a firefox.exe file) for download which I just don't have the guts to try. And their message is ambiguously misleading enough, seeming to convey the idea that Firefox is a product of eAcceleration.

    If you are new to Firefox and eAcceleration, you will be offered a Stop-Sign computer protection package with the Firefox download. Please use it to make sure your computer is virus, spyware, and adware-free.

    Very scammy. I don't know if anything should (and could) be done about this, but this is surely an unpleasant and rather underhanded way of online advertising. Whether the authorities (er, Mozilla Foundation) should be alerted is something I'm not really sure of...

  • 50 Gmail invites - open to public soon?

    Would you look at that? Google has given me 50 Gmail invites to give out. I'll bet everyone else with a Gmail account would have a whole bunch of them too. This reeks of either a stress test (though I can't really think of anyone who hasn't got a Gmail account by this time) or a public release of Gmail soonish.

    Screenshot of my 50 Gmail invites


    And for those of you who wrote to ask if I was OK, thank you for your concern. I'm rather fine, not doing too well balancing work, writing, and a World of Warcraft addiction. I'll be blogging more once I submit my final draft, but before that, blogging will remain sketchy.

    Oh, has any Community Champion got their autographed Firefox T-shirt yet? I haven't heard anything from sfx, but then again, I haven't really been keeping up.

  • WoW Rogue video - killing the Ironforge bankers

    World of Warcraft servers are down for maintenance today, so I get time to blog a little and read forum posts. Check out this Rogue video - Killing the Ironforge bankers thread (and the video of course). It's cool being a rogue.

  • The ones I read first in my long blogroll

    I have a long list of blogs/websites (233 feeds at this moment) to read in my Bloglines account. I don't have the time to read all of them nowadays (I used to), so I'm finding myself skimming through a select few blogs. Weirdly, even though bloggers like Scoble make very interesting posts, I tend to not read them because he's just too damn prolific and I don't have the time to read all the posts. Hmm... Interesting... I was just about to check whether there was a way to keep a post unread in Bloglines and there is (there's a "Keep New" checkbox at the bottom of every feed item). Well, maybe this will get me reading Scoble again.

    As I was saying, I don't read every feed I subscribe to nowadays. (By the way, Bloglines only keeps 200 entries per feed before it stops keeping track of new ones.) What I do now is go through the list from top to bottom and check out the ones that I seem to be more interested in. It's interesting to see a pattern. I tend to give feeds with too many unread items a miss, resulting in a vicious cycle of them never getting read. Feeds with 30 new items a day, these I also tend to not read. I also read too many Mozilla-related blogs. Here's my list of "To Read" blogs (arranged in no particular order) whenever I'm short on time or just too plain lazy to read everything:

    • Jon Hicks' hicksdesign
      One of my favorite designers who also happens to be a mean programmer (at least from what I can tell from Jon's hacking of Textpattern, which powers his blog and portfolio).
    • Bernie Zimmermann
      Mozilla blogger and Firefox GrayModern Theme author
    • Chris Pederick
      Chris Pederick is the author of the Web Developer Tools Firefox extension, among other cool projects.
    • Forever Geek
      Which I contribute to, but I'm on hiatus now.
    • Gadgetopia
      Lots of neat posts and links on technology
    • Gentoo Linux News
      Not a blog, but I love Gentoo!
    • Gravatar blog
      I think Gravatars are pretty sweet, this keeps me in the loop of what Tom Werner's up to.
    • Life At Ngee Ann
      This is a fellow Singaporean's blog, quite humorous. "Ngee Ann" refers to Ngee Ann polytechnic, a local academic institution.
    • life in mono
      Fellow Singaporean Adrianna's a cool techie Mac evangelist.
    • adot's notblog*
      Asa Dotzler's blog. Mozilla stuff is always interesting.
    • cheeaunblog
      Author of the famous Phoenity theme, who happens to live in Penang where I some relatives stay. Maybe we can meet up when I go there, huh, Chee Aun?
    • Neil's World
      Neil Turner blogs stuff that I would blog myself if I could. Well, mostly. Mozilla and tech blog.
    • Photo Matt
      Matthew Mullenweg's the lead developer of WordPress, and turned 21 not long ago.
    • philwilson.org
      Phil Wilson blogs mostly on Firefox and the Semantic Web.
    • vantan.org: The Daily Weblog
      Fellow Singaporean Vanessa Tan is a "devoted Netizen" who works in an environment unfriendly to Firefox.
    • The Burning Edge
      Used to follow this religiously, but a little less now because of more infrequent posts and also because much of the work now is bug fixing since the Aviary branch landing.

    Not too many A-list bloggers in that list, but that's probably because people like Simon Willison and Doug Bowman have been posting less of late. Give these blogs/sites a visit or two and let me know if you know of any related blog that I can add to my feed aggregator (yes, I'm still interested in more despite an unread list of hundreds).

  • World of Warcraft servers are always up, apparently

    When you have a server status page that doesn't work, and which tauntingly indicates that all servers are up even when they clearly aren't, you have to wonder whether it's a marketing tool for Blizzard, or just plain laziness in fixing a clearly broken script. Check out the World of Warcraft Realm status page - I've never seen a realm (i.e. a server) indicated as being down when it actually is down.

    Right now, all servers are down because of weekly maintenance that goes on for 4 hours. And guess what? The server status pages indicates that ALL servers are up, alive and bursting with players killing goretusk boars and pygmy venom web spiders. I'm not whining about the downtime, but I am whining about the perenially broken server status page!

    This page seems to be far more accurate in providing realm status.

  • Another one switches to WordPress

    Ben Milleare switches to WordPress. Don't you just love the way his Gravatars are set up? :P

  • Full disclosure (of useless Web statistics), Dec 2004

    How fun, starting a new year with a bland post on Web statistics.

    December 2004 was a record month for this blog, with record highs in visits, pages and bandwidth. I exceeded my monthly transfer limit of 20GB by just a little bit as well.

    Web statistics summary for Dec 2004


    Getting an average of 4.7K visits per day, or 10K pages per day.

    Daily Web statistics for Dec 2004


    Thanks to the "Homer Simpson uses tabbed browsing" post, transfer spiked to almost 3GB on Dec 15.

    Top referrers included SpreadFirefox.com and Bloglines. There was a referrer spammer (brushed out in the screenshot) which made it to the top 10 before I caught it and blocked it.

    Top referrers for Dec 2004


    Here's how referrers are blocked (with .htaccess), for those who are still looking for a solution. What these 2 rewrite rules do is to, respectively, rewrite the listed IP addresses, and rewrite the referrers with the following strings in them, back to the client's IP. This works extremely well, and I know because I've had my server (a VPS) brought to its knees from constant referrer spamming. The hit rates from these spammers were so high that it practically became a DOS attack. Since I've implemented these rewrite rules, all has been peaceful and I can finally see uptimes of more than 3 days (believe it or not, the VPS did go down that often).

    RewriteCond %{REMOTE_ADDR} ^195.175.37.26 [OR]
    RewriteCond %{REMOTE_ADDR} ^195.175.37.24
    RewriteRule ^.* http://%{REMOTE_ADDR}/ [L]

    RewriteCond %{HTTP_REFERER} texasholdem [OR]
    RewriteCond %{HTTP_REFERER} example\.com [OR]
    RewriteCond %{HTTP_REFERER} example2\.com
    RewriteRule ^.* http://%{REMOTE_ADDR}/ [L]

    The referrer spammers are still going at it (though they don’t appear in AWStats) - I can see them hitting me when I check my server-status page (see the mod_status module).

    I don't think I'll need to upgrade my hosting plan yet, since the bandwidth spike was a one time thing caused by the crazy Homer Simpson entry.

    And here's the top 10 browsers as requested. Unsurprising that Firefox tops the list.

    Browser stats for Dec 2004


    Well, here's to a better year ahead for everyone. For myself, I look forward to having a good time at my new job, (finally) getting published, finding religion, and finally getting a move on in life. I'm not one for "Happy New Year"s and new year resolutions though. So blah, have fun.

    Look me up in the Cenarius server if any of you are playing World of Warcraft - my main character's Lucita (gnome rogue).

  • Seen elsewhere: Life without Firefox...

    A poem on Life without Firefox, by Vanessa Tan, who has lost Firefox to her organization's software policy.

  • My favorite songs of 2004

    I like music. Well, I like the music that I like, not all genres. OK, honestly, I'm fairly picky about the songs I listen to. I only listen to alternative, alternative rock, punk rock, punk pop, and rap (mostly Eminem) music (I'm not even sure if the genres are correct). Music keeps me sane on commutes of more than an hour long to and from work, and shuts out noise at work whenever necessary (not that it happens often). As a result, I probably have a rather unhealthy dependence on my iPod mini - I can't go anywhere without it.

    Argh, I realize that recently, my thoughts don't flow in a coherent manner when blogging. I think it has to do with a combination of factors: World of Warcraft addiction (anyone interested in a Firefox guild?), book writing, and not getting enough sleep. Anyway, let's get back on track shall we?

    Now, since almost everyone is doing it (posting their top 10 albums of 2004 and the like), I thought I'd share a little on what I have been listening to over the past year. These are not songs that were released in 2004 though, just the songs I've listened to the most often in 2004.

    1. Slow Motion (Dirty Version) by Third Eye Blind
      I love Third Eye Blind. I probably played this song more than a thousand times over the year. This has such a catchy instrumental background - at first I wasn't aware of the "dirty version" (drug themes) and only heard the clean instrumental version that had minimal lyrics. I liked that a lot and after I was made aware of the version with lyrics, I liked it even more for its quirky lyrics.
    2. Hands Down by Dashboard Confessional
      With lyrics like this: "My hopes are so high that your kiss might kill me. So won't you kill me, so I die happy", what's not to like? I had this song set to repeat over and over again on my iPod quite a few times.
    3. Rough Draft by Yellowcard
      This is actually a sappy love song if you follow the lyrics, but I kinda like the way it flows off your tongue. Same for this, I had this set to single repeat as well for a period of time.
    4. Friends O' Mine by Bowling For Soup
      Damn catchy and nice to sing along to. Well, I sing along (very, very quietly and discreetly) to almost every song here in this list.
    5. Vindicated by Dashboard Confessional
      This was popular because it was in the Spiderman 2 OST. I didn't catch it on radio (I don't listen to radio anymore actually) and didn't know that it was that popular until I heard it on the radio.
    6. Inside Out by Yellowcard
      "Here. A little sympathy. For you to waste on me." Yellowcard seems to like writing songs that are self-pitying. And I seem to like those songs. Self-pitying bastard.
    7. World at Large by Modest Mouse
      I didn't actually like Modest Mouse at first when introduced to them by a good friend. They're one of my favorites now.
    8. Map Of Your Head by Muse
      This song is just good. I mean, listen to it. The guitars sound great, the Muse lead singer is at his best.
    9. El Scorcho by Weezer
      "I'm a lot like you, so please, hello, I'm here, I'm waiting."
    10. Undone - The Sweater Song by Weezer
      This is a weird semi-song. "If you want to destroy my sweater, hold this thread as I walk away."
    11. Semi-Charmed Life by Third Eye Blind
      This makes number 11. I can't leave out this one - this introduced me to look away from mainstream pop songs that I was somewhat into at the time. Granted, Semi-Charmed Life is almost a pop song, but it did spur me to check out Third Eye Blind and that led to other similar artistes.

    So, what have you guys been listening to this past year? Anyone have similar tastes? More importantly, do you have any songs or artistes to recommend?

  • Mozilla Update website gets a facelift

    Mozilla Update has gone through a facelift and now looks much better than before (and also, more importantly, it now looks and feels like the main mozilla.org site).

    Mozilla Update website cropped screenshot


    I haven't seen any announcement on this yet on mozilla.org nor on MozillaZine

  • Opera 8 beta 1

    Opera 8 Beta 1 is out. Get it from the Opera website. Changelog.

  • How would you feel if your organization forbade you from using Firefox?

    Remember I mentioned Vanessa Tan who had an IE-only policy at work? Now, the policy is official and an unbendable rule (my understanding was that Vanessa was able to use it somehow, before). Read her farewell to Firefox.

    Much has been sacrificed in the name of security.

    Of course, that bit is debatable (because IE is intrinsically insecure, though Firefox is not without its problems, as its security has been called into question recently). I shan't go there. But please, don't bring up the point of security via obscurity (because I believe there is no way you can prove it is true until Firefox is as widely-used as IE).

    Back to the blog title: how would you feel if your organization refused you the freedom of using your preferred browser (assuming that it isn't IE that you dig)? Would you take it lying down, or would you take reactive action? Remember this is different from an IT policy that, for example, forbids you from installing unlicensed software or software that is particularly vulnerable to security risks (I'm tempted to say IE here). Firefox is neither unlicensed software, nor is it as vulnerable to security risks as the software you are (hypothetically) being forced to use (IE). I know I wouldn't be happy. You might as well take away my Internet connection (which just may happen at work, soon, but for other reasons).

  • Gmail invites are back

    Looks like they're giving them out again.

    Screenshots of my Gmail invites


  • Unread tabs Firefox extension (otherwise known as the simplest extension ever)

    A request: if you intend to link to this entry, please link to http://blog.codefront.net/mozilla/unreadtabs/ instead.

    As requested (by 2 people) in my previous entry, here's the Unread Tabs extension for Firefox:

    Install Unread Tabs | Download Unread Tabs

    Also available from the Firefox Add-ons site.

    This extension italicizes the title of tabs that you have not yet read so that you can keep track of what you�ve read and what you haven�t.

    Screenshot of unread tab being italicized in Firefox


    I claim no credit for the extension: CSS comes from the Change the style of tab markers entry in the MozillaZine Knowledge Base. Thanks to an anonymous commentor, the CSS is just a single rule (rather than the 3 in the Knowledge Base entry linked to above):

    #content tab:not([selected]) {
    font-style: italic !important;
    }

    This has to be the world's simplest extension with some boilerplate XUL, an empty stub JS file, and a single CSS rule. I simply stripped almost everything out of another extension that I was working on (I've hit a snag on that one though) and did some run-of-the-mill search and replace.

  • Making unread tabs obvious in Firefox

    Update: as requested: Unread Tabs extension

    Saw this tip over at the MozillaZine Knowledge Base:
    Change the style of tab markers.

    Adding those CSS rules to your userChrome.css file will italicize the titles of tabs that you haven't read.

    Screenshot of unread tab being italicized in Firefox


    Very useful for keeping track of what you've read and what you haven't (also helps prevent you from accidentally closing an unread tab).

  • Firefox NYT ad, some disappointment

    Update: Daryl responds, and so does Rob Davis, both from the Spreadfirefox team. Please do send an email to [email protected] should you believe your name listing to be in error or if it's not there.

    So the Firefox NYT ad has gone out. Great-looking ad, great work everyone at sfx. Ooh so here's my name:

    My name in the Firefox NYT ad


    I must, however, express my dismay at how the exercise was handled. No communication with the donors as promised (we were supposed to get an email prior to the release of the ad), and no one will answer my comments on sfx nor my emails. After significant pimping and whoring, I think I got 13 donations, but no one at sfx has yet to reply to my emails when I requested that they run a check for me on whether I was a "Community Champion" (who get their names underlined, among other things that they may or may not get as promised). Surely it counts for something, insignificant as my efforts were in the greater picture.

    Is there a lack of communication somewhere? I'd say so, and not just because I wasn't replied to, but because there weren't much in terms of updates on when the ad would have been released. There also wasn't a draft of the ad put up for review, meaning some people had their names reversed or they simply weren't there. Commendable, of course, that they offer to do this:

    We will incorporate all requests for corrections we receive and, where we are responsible for the error, we will send out free, corrected posters of the ad autographed by some members of the Firefox development team. As appropriate we will also issue credits or refunds on a case by case basis.

    Still, great ad, I can't imagine how much of a nightmare it must have been to get the names in there, and go Firefox! Awesome work, awesome fund-raising campaign for an awesome Web browser.

  • Leaving a good company - muvee Technologies

    Yesterday was the official last day of my 6 months tenure as Web Developer and webmaster at muvee Technologies. It was a good 6 months, with the first 3 months being great (it is my first job after all), and slightly tapering off towards the end as I started to realize that perhaps being a Web Developer is not quite what I want to do. The bosses found out about my doubts somewhere in the middle and were totally awesome to involve me in projects that would interest me and kept me going for the remaining months. Still, it was not to be and I needed to make the hard decision to leave. It was coming all this while (my leaving), so I wasn't surprised and I'd say neither were many people. I tendered my resignation a month ago.

    muvee logo


    I remember being rather impressed with the company during the interview process because of the questions asked (these included some creative aptitude questions which I sort of floundered on), the keenness, friendliness, and plain techie-ness of the CEO (Pete Kellock), the COO (Phil Morgan), the Chief Opportunities Officer (Terence Swee) and the VP of QA/Customization (biographies) at my 2nd interview. People were working, walking around, talking in a comfortable environment and dressed in jeans - this was something really different because I never expected such a relaxed atmosphere as there was at muvee. Crazily enough, I almost didn't want to go for the 2nd interview after the 1st one because I didn't think I'd accept their offer even if muvee made me one. But I did go and I did take up their offer, rejecting another offer as a J2EE software developer in a decision that I will never know whether was right or wrong. From the company's perspective, they have lost the investment they've put into me. From my perspective, I have lost the opportunity to be a software developer for the past 6 months, which I am now discovering I want to be, much more than I want to be a web developer.

    I'll miss working at muvee, the great bosses (who never really acted that much as superiors but rather as equal colleagues), the "everyday is Friday" rule (in terms of office wear), and, most of all, the people (not everyone, I'll readily admit - in fact, just a few peeps).

  • Homer Simpson uses tabbed browsing

    I just watched the latest episode of The Simpsons and guess what? Homer uses a web browser with tabbed browsing!

    A scene in The Simpsons Season 15 Episode 5 where Homer uses a browser with tabbed browsing


    It could be the Mozilla Suite, Firefox, Opera, or <insert browser that supports tabbed browsing here>, but it sure ain't Internet Explorer.

    Since someone commented about my media player, I'd like to point out that I use Media Player Classic, which happens to be an Open Source product. It's Sourceforge project page is at http://sourceforge.net/projects/guliverkli/, but you'd do better to check out Neil Turner’s writeup instead. I hate Windows Media Player for its bloatedness, clunkiness and its tendency to be totally unhelpful or even go bust when required codecs aren't there.

  • Final Fantasy VII: Advent Children trailer

    Enjoyed Final Fantasy VII? You'll probably want to take a look at this (2nd) promo trailer for Final Fantasy VII: Advent Children, a CG movie that looks to be totally awesome. It should be out soonish (either this month or early January).

  • Cone of gayness

    Reason why linking to this post may not work: mod_rewrite rules rewrite all URLs that contain the word "gay" (and for that matter, "sex", "porn", "beastiality", "viagra" and more) to the client's IP - this is to block out referrer spam which I get a lot of and brings down the site in a fashion not unlike a DDoS.

    If you got a weird page (like a page requesting you login to a router) before, it's your own router (or your ISP's gateway). The link works now because I've removed the word "gay" from the mod_rewrite rule list. Remember, if you wish to comment on technical faults of this site, please base them on correct information (and if you are arguing from a technical standpoint, you'd do well to vaguely recognize several familar IP addresses). On the other hand, I admit my callousness in the "gay joke" and am duly chastized - for that I have no excuse.

    Also, I should have listened to the warnings and not posted this entry in the first place - homosexuality is still a touchy subject. Nevertheless, the image is still there (for, um, posterity) - I'm only not displaying it in this entry.

    Cheers, and chill out.

  • Google Labs releases LiveSearch-based enhancement

    Kevin Gibbs, software engineer at Google, has written a bit of an enhancement to Google Search that uses LiveSearch. His post on the Google Blog was probably written to make me feel jealous I wasn't working at Google. Anyway, check out Google Suggest over at Google Labs - it's kinda useful but I guess no longer cool (since this isn't new).

    Screenshot of Google Suggest in action


    And would you look at that: "muvee autoProducer crack" comes up when I enter "muvee". And of course typing "chu yeo" in there doesn't give "chu yeow". More on how it works in the Google Suggest FAQ.

  • An author, I am trying to be

    I've always kinda loved writing. Writing fictional stories was fun up to secondary school - thanks to my fascination with (medieval) fantasy fiction and gamebooks. Argumentative essays were not my forte though - I got a B3 in 'A'-level General Paper.

    So anyway, enough of trying to write a "charming" little backstory (which wasn't really going anywhere, considering the state of mind I'm in).

    These few months you'll probably see a decreased frequency of posts on this blog, mostly due to my being on assignment writing a book - my very first one. Before you ask: yes, it's a technical book, and no, I'm not sure if I want to say what it's about just yet. It's too early.

    I'm going to miss blogging and nights where I don't have to think about <Insert book subject matter here>. Not that thinking about it is bad (because it is one of my main interests and I am writing a book on it), but I'm wondering how I'm going to keep up with late nights and the inexorable temptation to play one more hour of World of Warcraft (which still hasn't arrived, but will soon, with any luck).

    Stay tuned, don't go away. I'll still be blogging.

  • On the prow for World of Warcraft Collector's Edition

    Update: I got it! I went all out in an eBay auction and bidding ended at USD130. My very first eBay purchase. Here's the item listing. I should feel guilty splurging this much but: 1) this should be worth it, 2) life's too short, 3) life's too short to waste time refreshing eBay auction pages and losing eventually, and 4) money in PayPal doesn't quite feel like real money.

    I'm looking for a copy of the World of Warcraft Collector's Edition which is incredibly sold out. If any of you chance by one of them in an obscure store, please let me know. I'll be more than happy to pay for everything (cost + shipping and handling) plus extra for your efforts. I've been bidding on eBay but keep losing the auctions and my local pre-order is uncertain to arrive.

    Here's a look at what's in the Collector's Edition. The Art of World of Warcraft coffee table book and the behind-the scenes DVD look so neat I just have to get the Collector's Edition.

    Thanks!

  • Applying Fitt's Law to the Firefox Back button

    Phil Wilson has a nice writeup on Enhancing Firefox GUI usability where he points out one good thing about IE: a bigger Back button. Of course, Phil isn't one to jibe at Firefox's back button. Rather he goes on to teach us how remedy this usability flaw by making the Back button bigger in Firefox, and this is possible thanks to the customizability of Firefox.

    All you need to do is to add a few lines to the userChrome.css file in your profile - Phil explains all. (Trivia: you can combine the 2 CSS rules into a single "padding: 0 10px;").

  • #1 Word of the Year for 2004: "Blog"

    The word "blog" is the most looked up word of 2004 over at Merriam-Webster Online. Via Dan Li.

  • Thunderbird 1.0RC is out

    Update: Thunderbird 1.0 is out. Get that instead. Asa Dotzler has the scoop. Get it from the Mozilla Thunderbird release directory. Noteworthy: much nicer icons, work offline UI for Windows, HTTP authentication for RSS feeds.

  • Widgets for your blog

    Blogbloxes - widgets for your blog (or website). Smells like Konfabulator, et al. What will they think of next?

  • Larry the cow

    Larry the cow is one domesticated animal you probably haven't heard of (though you may have heard of our beloved South Park cows). He is the mascot of Gentoo Linux and there's a headshot of him over at Gentoo's About page (see "Gentoo Linux... in a poster").

    Larry the cow being abducted


    Now, I came across this amusing bug today, bug 27727 - bug Lary (sic) can NOT be a Cow, that insists that Larry cannot be a cow but should rather have been a bull, being male. Dictionary.com says a cow is "a domesticated bovine of either sex or any age" so that was why the bug was left as WONTFIX. Still it is weird to see Larry with udders in this thread in the Gentoo forums. Larry is probably one of the first transgender/sexually ambiguous animal mascots in Linux history.

  • Job ads: Product Evangelist and Web Developer/Webmaster

    The company I'm working for, muvee Technologies, is on the lookout for a Product Evangelist in the US for our core product, muvee autoProducer. If you're a fan of muvee autoProducer (like Chris Pirillo is), and fit the requirements nicely, why not find out more about it - drop Tania (our Communications and PR manager) an email (find it over at the job spec).

    The other position is that of a Web Developer cum Webmaster. This will be a replacement for yours truly. If you're competent in PHP and SQL, comfortable with Linux and the command line, have lots of ideas on website promotion, apply for the job or write me to find out more (see also the JobStreet listing).

  • Open to ads on your blog?

    If you're open to advertisements on your blog, the enterprising Jeremy Wright is looking to gather a, um, gathering of bloggers (GOB, usage mine) for an informal blog advertising network.

    The story goes something like this:

    Jeremy knows advertisers. Advertisers want to put ads on other blogs (other then Jeremy's, that is). Jeremy gets bright idea. Jeremy posts on blog. Now he's aiming for 50 bloggers for a combined 10 million pageviews a month. Advertisers pay bloggers for ad spots. Everybody's happy because we took out the middleman ad networks (and replaced it with Jeremy, who should be a lesser evil ;-)).

    I'm pretty sure Jeremy will take a fair cut for his work and he should be able to get higher prices for ads. Frankly, I don't think I want to do BlogAds anymore, because of those annoying pictures that advertisers keep changing and because they pay late and take a 20% cut. If this goes well, we should get nicer looking, unobtrusive ads. These ads pay for my VPS hosting but recently I've been tempted to approve more ads than the number that actually covers hosting. Gah I feel like a sneaky sell-out, though I probably needn't be, because if I'm not wrong, most of you would be reading via RSS aggregators. Show of hands, how many of you hate the ads here? How many don't care?

    Ah I digress... So, if you're looking for a little something to pay for hosting or earn a little pocket money, why not hook up with Jeremy by writing him an email.

  • New iPod Updater - finally, something for iPod mini owners

    Apple has released iPod Updater 2004-11-15, the software that updates your iPod's firmware. Notable in this release is a whole bunch of features for the iPod mini that were previously only available to iPod users. Nothing for G3 iPod owners though.

    Stuff like Delete songs from On-The-Go playlists and Create multiple On-The-Go playlists should have been there out-of-the-box, while Hear Click Wheel clicker through headphones is nice to have. Can't wait to try it out tommorrow when syncing in the office.

    Ugh. You need to use the charger (the Firewire one) to flash the firmware. Now I'm stuck with a useless iPod mini for the rest of today and the long commute home.

  • Firefox avatars and a centralized forum avatar system

    rakaz, maker of some very nice Firefox wallpapers, also has a set of forum and IM avatars. Very nice if you want to show your support for Firefox and perhaps get a few IE-using friends to ask you about your avatar. Anyway, that wasn't the reason I was looking for avatars. The reason was me coming across Tom Werner's Gravatars (or Globally Recognized Avatars).

    Screenshot of Gravatar in action


    Looks like a cool thing to implement on this weblog. There's a WordPress plugin, but plugins don't work on the nightly I'm using so I'll probably wait until WordPress 1.3, or if I ever get my butt off to try to get a CVS version to work.

    (And yes, this is a concise, rushed entry because I've got my Half-Life 2. It's Half-Life 2 weekend!)

  • WordPress devs: "No, we are not dead"

    Ryan Boren, one of the developers of WordPress (which powers this blog), has an interim update on what's going on behind the scenes in WordPress development and the new features in WordPress 1.3. Pages, themes, enclosures, and a richer plugin API are some things to look forward to.

  • Solving algrebra with MSN Search

    This is pretty cool. MSN Search beta solves any polynomial equations you enter into its search box.

    MSN Search beta result for a quadratic equation


    I tried with higher degree polynomials but it seems it can only solve up to cubic equations (try x^3 -27 = 0). Quartic equations, like x^4 + 6x^3 - 5x^2 - 10x - 3 = 0, return a vanilla search result page.

    Via Neil Turner via Chris Pirillo.

  • Half-Life 2 is not here

    The whole world is playing Half-Life 2 except for those of us in Singapore who pre-ordered or decided to get the retail box versions. Apparently, and this is news from the local community forum grapevine, the sole distributor of Half-Life 2 in Singapore, iGames, has delayed the release date to 19th November (GMT +8). Lots of disgruntled people in the HardwareZone PC Games forum. (I'm one of them, but I prefer to be quietly disgruntled.)

    After I posted my previous entry, I decided to go for the retail Standard Edition and went down to the local computer hub to reserve a copy. The guy there said it'd probably be out tommorrow (unlikely, as I found out later it'd arrive probably on the 19th like I said above). Argh... More waiting. Now I kinda wish I'd bought from Steam a week or so ago and I'd be playing it right now instead of blogging about not being able to play it.

  • Konfabulator for Windows, finally

    Konfabulator has finally been ported to Windows! Now I can at least get to experience the neat little app that made such news in blogs all over some months ago. (Note to reader: I'm refraining from word-butchery, so where "blogosphere" may be more appropriate in this case, I'm not going to be tempted into using it.)

    Looks nice doesn't it?

    Konfabulator widgets on my desktop


    I'm not even sure I need the weather widget, but the What To Do, iTunes remote, and RSS feed aggregator looks to be useful.

    Mac users get all the cool-looking, drool-worthy stuff (though I've seen a mean KDE setup), and also some of the coolest technology.

    Via coresite.org.

  • Vampire: The Masquerade - Bloodlines ships the same day as Half-Life 2

    I must have missed this with all the excitement and coverage of Half-Life 2. It seems like Vampire - The Masquerade: Bloodlines will be out on November 16, the same day as Half-Life 2. That's another 40+ hours of playing time on top of the 20-odd hours in Half-Life 2. Goodbye life! No wait... I don't have a life.

  • Half-Life 2: How are you getting it?

    3 more days to Half-Life 2. The wait is killing me. I'm also mulling over how I should get Half-Life 2. Should I get the cheaper Standard Edition (SGD69.90) that comes in a whopping 6 CDs (5 + 1 for CS:Source), or the Collectors' Edition (SGD129.90) that comes in a convenient single DVD (plus a shirt and, I think, a strategy guide). Or perhaps getting it from Steam? The Gold package looks pretty sweet (and naturally, so does the price tag). But you do get a whole lot more gimmicks (because that's what they are) sans a physical copy of the game itself. I guess I'm just not so comfortable with that (and the USD89.95 price tag).

    So, take a look at the Half-Life 2 Package FAQ. Let me know what you are getting. It's something to keep you occupied while you (we) wait.

    (Anybody have any idea whether the Collectors' Edition comes with the strategy guide? The Package FAQ I posted above has conflicting information - the first post says it comes with a "Prima book sampler", the later press release says it comes with a "Half-Life 2 Prima strategy guide book".)

  • Opera 7.60 Preview 3 for Windows

    Just posted a Opera 7.60 Preview 3 entry at Forever Geek (where I have posted too seldom these days). Firefox fans, don't be offended or surprised - I still love Firefox! But really, Opera seems to have had some improvements ever since our very own Asa Dotzler ranted about the horrible-ness of its UI.

  • Gmail now accessible via POP3

    Google Brings E-Mail Client Access to Gmail. How do I enable POP (for my Gmail account)? (login required)

  • Firefox 1.0. Yes, 1.0 is out!

    Update: OK it's official.

    Go to Firefox 1.0 EN-US FTP release directory. You can also get it via BitTorrent at http://bittorrent.mozilla.org:6969/ (thanks Michael).

    mozilla.org is down right now, so you can try getting from Firefox 1.0 EN-US FTP release directory if the main Mozilla Firefox page doesn't load up for you.

    Firefox 1.0 delivered in a neat package


    News around the web and the, er, blogosphere:

    When it's officially released, you'll be able to get it at the usual place -> http://www.mozilla.org/products/firefox/

    More later. Work now.

  • A new Linux distro - for a fee

    Seen at Rent A Coder: a bid request for a Linux distribution with branding.

    I need a new linux distribution based on kernel 2.6.8 or later (2.7) with branded/customized KDE 3.3...

    Makes you wonder what the guy wants to do with this. And this is definitely the most "different" bid request I've seen in awhile.

    Oh, by the way, if you're looking for a freelance PHP or Python developer, or a Linux administrator for your web server, I may just be your man. Drop me a message.

  • Men turn into ugly things when they know they cannot get you

    Men turn into ugly things when they know they cannot get you. That's the blog of one of Singapore's top bloggers. Always quirky, full of attitude. One of the few non-tech blogs I read.

  • Version Control System Comparison

    Version Control System Comparison. Why moving to CVS in your organization/project may not be the best idea: Atomic commits - CVS doesn't have this. Renaming support - CVS doesn't have this (there are ways to hack around this, but you will lose revision history). There are better choices out there.

    Sidenote: I've been posting quite a few "Asides" recently. On my todo list is to offer an RSS feed without these "Asides" posts.

  • Simple anti-comment spam measure

    Thanks to Patrick Strang who pointed me to Steven Geen's simple anti-comment spam measure for MovableType, I managed to stem the current flow of "Please approve this comment" emails flooding my inbox. This has been happening since last Saturday! Argh! Why are they doing this even when everything goes into the moderation queue?

    Anyway, it's so simple to get this into WordPress - just edit wp-comments.php and wp-comments-post.php to add the field to the comment form (see below) and die() when the correct "letter of the day" isn't entered. (Though die()ing isn't the most elegant way, but WordPress does this for the other fields as well.)

    Screenshot of anti-spam field


    Check out the comment form if the picture above is too small. Sorry to have to put you commentors through this, but it's really for my sanity. At least it isn't one of those randomly-generated graphical thingies that really ensures you are human (or an equivalent intelligent lifeform).

  • IE-only, no Firefox

    Vanessa Tan laments the no Firefox, IE-only policy in her organization. How many of you work in organizations like hers? (Mine happens to practice freedom of choice.)

  • Mozilla Thunderbird 0.9 released

    Mozilla Thunderbird 0.9 is out. Get it at the Thunderbird 0.9 release FTP folder. If I have time later today, I'll probably write up on the new stuff.

  • Firefox NYT ad campaign covered in local paper (and I'm in there too)

    Well, Streats isn't exactly a high-profile paper (its parent company, Singapore Press Holdings, publishes The Straits Times which is in much greater circulation), but it does get around (and its website does validate as XHTML 1.0 Transitional, FWIW).

    Anyway, here's the article: S'poreans back free browser. I'm quoted in there saying (pessimistically):

    A few Singaporeans, myself included, have actually thought of doing the same (advertise) with a local paper, but prices are high and it would be a challenge considering the penetration of Firefox in Singapore.

    Of the (relatively) long reply I sent to Chris (the journalist) for the email interview, I can't say I wasn't a tad disappointed that that bit was published. But oh well, at least it got the word on Firefox out. Interestingly, someone from Singapore (I can tell from the IP) wrote a comment saying my blog "sucks shit". Thanks Kathy I bet it does. And good day to you too m'am. Another guy wrote me an email coming in from the Streats article asking me to point out the influential bloggers in Singapore and South East Asia. (Yes we do exist!)

    Streats on the web logo


    Christopher Lim (the journalist who interviewed me via email, and get this: via SMS) wrote us Singapore donors an email asking our thoughts on the campaign and why we made our donations.

    I am writing from Streats, the newspaper. Blake Ross from www.spreadfirefox.com passed me your emails after I explained that I'd like to run a story this coming Monday on Firefox's New York Times advertisement campaign, and am interested in interviewing donors from Singapore.

    The To: header of that email tells me there were 14 Singaporeans who donated to the cause. Not bad I'd think, considering our small population and tighter fists. Chris Henry, another Singaporean donor, wrote about the article too in his sfx blog. Well, if any of you donors want to discuss more on "spreading Firefox" in Singapore, do join up at http://sg.firefoxsupport.com/ (it's not my site, in case you were wondering).

  • MySQL 4.1 production-ready

    MySQL Version 4.1 Certified as Production-Ready. Still waiting for the MySQL 4.1.7 ebuild (relevant bugs - MySQL 4.1.6 ebuild, request to include mysql 4.1.6 in portage and add ~amd64).

  • How "well designed, popular and accessible" is your website?

    You'd probably already have heard of or seen Silktide Sitescore that purports to "rate how well designed, popular and accessible your website is". A while back it used to tell me I was a criminal for having an inaccessible website. I got sub-5 scores and a close-to-zero score for accessibility. I was surprised but attributed it the script doing something wrong somewhere (it is automated after all and Cynthia did tell me my site was OK in terms of accessibility).

    Recently, after reading Ingoal's post on an update to his score, I tried out Silktide again and to my surprise, got a respectable 8.6. (Ingoal's a fellow Advisor at SitePoint - this guy knows computers.) After some fixing some XHTML-validation borkage, I eked out a 10 for accessibility and managed to get a 9.2 overall score (and a 9.3 once). That puts me in the top 10 (true to form, I'm right at the bottom at number 10 below SitePoint).

    Screenshot of Silktide SiteScore rankings with blog.codefront.net in 10th position


    I'm not sure it means anything except that it goes to show what a poor boy I must be to keep posting about rankings and scores. Perhaps it's my low self-esteem working here.

    But I have to say again, thank you for your donations to Firefox and keep donating whether you do so via my donation link or not! (What am I talking about? Read more. This too.)

  • Made it into Spread Firefox Weekly Roll Call Top 5 Climbers

    Thanks to everyone's kind donations to the Firefox New York Times ad effort, I made it into the top 5 climbers (of the past week) on Spread Firefox.

    Screenshot of Weekly roll call at Spread Firefox with my name as the last of the top 5 climbers


    Once again, thank you for donating to the cause. (There's 100 points to be had per donation, and there were 12 made via my donation link.) I'm bowled over by the support for Firefox and more so by the support for me (or at least I'm glad that people are bugged enough by my constantly going on about donating to actually donate!)

  • BioWare store opening soon, Neverwinter Nights modules for sale

    BioWare is launching their BioWare store on November 10 (hey that's my birthday) and they're selling premium Neverwinter Nights modules (developed in-house by BioWare and the Neverwinter Nights community). Rather smart sales strategy. I'm considering getting one of those modules myself.

  • Opera 7.60 Preview 2 for Windows

    Opera 7.60 Preview 2 for Windows is now available. Changes since Preview 1.

  • iTunes 4.7

    iTunes 4.7 is out. What's new: 1) iPod photo support, 2) search for duplicate songs, 3) Receive an "Artist Alert" when music by your favorite artists arrives on the iTunes Music Store, 4) Minimize iTunes to the System Tray.

  • MileWideBack Firefox extension saves you from "complex hand-to-eye coordination"

    The MileWideBack extension (update.mozilla.org listing) for Firefox popped up recently on update.mozilla.org and with a name like that I had to check it out. And whaddya know, it turns out to be yet another essential extension (as far as I'm concerned). The premise of MileWideBack is simple: you use the left-edge of the Firefox window to navigate back and forth in your tab history.

    This extension allows you to navigate back and forth without requiring complex hand-to-eye coordination.

    "Complex hand-to-eye coordination" may sound amusing at first, and while I can certainly hit the back and forward buttons without a thought, but if you think of it these buttons are really, really small compared to the vast wasteland that is your maximized browser window. Particularly for my case, where I like to "Use small icons" for the buttons in the Firefox toolbar to save on vertical screen real estate.

    Screenshot of Firefox Back and Forward buttons


    These little buttons take up really minimal space in the browser, as the screenshot below shows:

    Screenshot of full Firefox window pointing out relative smallness of buttons


    Fitt's Law would suggest that it will take a long time ("long" in relative terms) for someone to find and hit those small buttons (but somehow I get by because I'm probably on the computer way too long for my own good). MileWideBack in a sense throws Fitt's Law out the window by allowing you to navigate back and forth by simply "throwing" the mouse to the left and then right-, left- or middle-clicking. (I say Fitt's Law doesn't really apply because it takes constant time to move to the extreme left of the window when no aiming is needed.) You right-click to go back, left-click to go forward, and middle-click to reload the page.

    Neat little extension, isn't it?

  • 6 more days to show your support for Firefox!

    Update 2: Mission accomplished! Spread Firefox tells me I've got 1140 points which means 10 other people have donated via my donation link. Thanks for the show of support and, more importantly, for supporting and loving Firefox!

    Update: 1 more person has donated via my donation link! Thank you, Ken Pratt! 3 more to go!

    I wrote about it before but I'll write it again now as the deadline nears (6 more days). Anyway, here goes...

    Show your support to Firefox if you love it by contributing to the expense of full-page Firefox ad that will be in The New York Times (leftover donations will be safe-kept by the Mozilla foundation to fund the Firefox 1.0 launch campaign, a non-profit organization). Donations are tax-deductible in the US. $30 for a normal donation, $10 if you're a student. If you've ever thought of donating to the Mozilla cause before, now is the best time as ever to do so.

    And I'm throwing in 2 free WordPress weblog hosting accounts (sponsored by ReviseMedia) should you make a donation (just let me know if you're going to do so).

    Firefox in the New York Times


    I'm thankful to the 5 6 of you who've already donated due to my exhortations. If you do donate, and you feel general goodwill towards me (heh), I ask that you donate via my donation link. If I manage to get 9 of you to do so, I'd get the faux title of "Community Champion" and get some extra perks (I really want the free Firefox t-shirt signed by the Firefox crew).

  • The Straits Times says Half-Life 2 will be delayed... Right

    Local (Singaporean) news publication The Straits Times has a mini-article today proclaiming "Half Life 2 Delayed". (You need to login to read the article. For that purpose, I've created a BugMeNot entry for The Straits Times.)

    Publisher Vivendi and developer Valve are suing and counter-suing each other over the rights to gaming cafe usage royalties. This battle could hold up the release date by up to six months

    Now, am I the one who's out of the loop or is The Straits Times publishing old news? (Insert snide comment on how The Straits Times has been trying to portray itself as a world-class newspaper here.)

  • Portions of Spamx plugin for Geeklog minimally based on MT-Blacklist Updater

    Part of the Spamx plugin for Geeklog (a PHP blogging script) proclaims to be based on MT-Blacklist Updater (MassDelete.Admin.class.php and Import.Admin.class.php). I hardly see the similarity though.

  • LiveSearch hack for WordPress

    I found this old link on LiveSearch that I'd wanted to implement here but never got around to doing so until now. It's really quite a bit of a hack right now, with most of the code coming from the LiveSearch page on the Bitflux Blog wiki and with direct calls to PHP's MySQL functions to get it to work with WordPress.

    Instead of replacing the default WordPress search box, I decided to add another one instead (that you can see right now on the left just below the "standard" search box) and label it a "LiveSearch" box. I do this because livesearch.js seems to crash Firefox randomly on occasion (as mentioned on Bitflux blog). Besides, it's hardly fully done, as it currently only does simple SQL LIKE matching on post titles, and the CSS is somewhat inelegant.

    Anyway, what I did was to add livesearch.js to the WordPress index template, and added the LiveSearch form.

    <form id="livesearchform" method="get" action="http://blog.codefront.net/livesearch.php">
    <input autocomplete="off" id="livesearch" name="q" onkeypress="liveSearchStart()" type="text" />
    <input id="submitted" name="submitted" value="yes" type="hidden" />
    <div id="LSResult" style="display: none;"><div id="LSShadow"></div></div>
    </form>

    Next, I hacked out a livesearch.php script (source listing for livesearch.php) that performs the relevant query on WordPress' wp_posts table (may be differently named on your installation) and returns an XML document containing the matching post titles and their URLs. The tricky part was to map the matching post titles to their URLs (since I'm using SEO-friendly permalinks). All it took was to include the wp-blog-header.php file and have the get_permalink(ID) function return the correct URL.

    The other tricky part involved preventing searches in the LiveSearch box from going to the livesearch.php page, because that'd return an XML page that the user would hardly know how to proceed from. Adding a hidden form field was a quick hack, but it works just fine and redirects the user to the search results page that WordPress spews.

    // If the LiveSearch form was actually submitted (as opposed to being requested
    // via a XMLHttpRequest, we redirect to the standard WP search page.
    if( isset($_GET['submitted']) && $_GET['submitted'] == 'yes' ) {
    header('Location: ' . get_settings('siteurl') . '/?s=' . $_GET['q']);
    exit;
    }

    Well, that's it for now. It feels really like a hackjob but it works just fine. Perhaps someone could roll this up into a WordPress plugin or we could get LiveSearch as a feature in WordPress in future (as Serendipity already does).

  • Desktop namespace shortcut for Firefox

    There's a thread in the MozillaZine forums that teaches you how to create a desktop shortcut for Firefox, replete with context menu options for accessing the Profile Manager and the Options UI. Pretty neat and convenient.

  • Firefox screensaver

    Digital Media Minute has a Firefox screensaver for Windows created with Flash.

  • My name will be in The New York Times

    Update: 7 6 days left to get your name in The New York Times! Thanks go to Ingoal, Alexandre Juneau, and one other anonymous reader for donating via my donation link (and even more importantly, for donating at all).

    You've probably heard about the full-page Firefox ad that will be in The New York Times and wholly funded by donations from the community. If you haven't, you probably don't read Slashdot, c|net's news.com, and I'd imagine a good number of news sites, in English or otherwise.

    Anyway, I was a little slow on the uptake myself, and have only just made my donation (of USD30) that goes towards a one of my favorite causes. If you're a student, you can get your name listed for just USD10.

    Screenshot of the donation completion page in the Firefox NYTimes ad campaign


    If you'd like to make a donation, and if you go through my donation link, I'd be branded a Community Champion and get some extra perks (best of which is a free Firefox t-shirt signed by the Firefox crew).

    C'mon, be a part of Firefox users' ad in The New York Times!

    By the way, would a US resident kindly inform me when the ad is taken out? I'd like to buy a copy of it for keepsakes. Would have to go hunting for a copy.

  • Half-Life 2 release date: November 16

    Half-Life 2 release date: November 16!

  • Why does VPS hosting take so long to setup?

    Update: After 3 days, I finally got my activation letter. It took a post to WebHostingTalk to get JVDS' attention. It seems that the activation email was sent, but it never landed in my inbox.

    This is the 2nd time I've signed up with a VPS provider and it's taken too long for my account to be set up. Linode took 16 hours to set it up when they promised:

    Apply now and your account will be activated within hours!

    16 hours does technically qualify as "within hours", but it's a mighty long wait. But I do understand that there's a lead time needed to setup a VPS so it's acceptable.

    But now that I'm intending to switch from Linode to JVDS (for reasons which I'll expound in an entry in the near future), and I'm again left waiting, this time for more than 34 hours (at the time of writing). And this after sending in 2 support emails asking about activation, and being told that the account details will be given to me "shortly". Sigh. Even the expensive (comparatively) The Planet dedicated server that I ordered for my company was built within 12 hours. And I signed up with JVDS because of their excellent reviews in WebHostingTalk. They may be mighty quite to reply to support tickets and emails, but what good if you can't deliver. I wonder if I'm just unlucky. Maybe the dedicated host on which they were setting up my VPS blew a hard drive.

    And so I wait... And I wait...

  • WordPress hosting up for grabs

    Update: All gone! Thanks everyone!

    Update: seeing how pathetic the (non-)response has been, I've lowered the donation amount to USD2. C'mon people. Even as a gift for your non-tech-savvy uncle/auntie/grandma who wants to blog, it's a good deal!

    Update: There are 3 of the accounts is 1 account left. Thanks Jannah and Phu for making the donations to WordPress!

    Jeff Holman of Revise Media wrote me awhile back to put up ads for his new WordPress weblog hosting service, and I gladly agreed because I was kind of a WordPress fan (caution: heavy understatement in use). His ad is on the left where you've probably been seeing it for awhile (if you're not reading via a feed reader). (On the subject of ads, well, they fund the webhosting for this blog and then a little more.)

    Anyway, Jeff offered to give me 5 Plus plans (1 year subscription) for free to give away to you, my dearest readers. There was supposed to be some contest, but I gave up trying to think of a good idea for one. So I spoke with Jeff on this other idea I had and he has agreed to let them go for a donation to WordPress (rather than for "free").

    Well, the Plus plans are going at $9.95/year each, and I ask of you to make a donation to WordPress of at least $5 $2 (this amount was of my choosing). Write me first (or leave a comment) if you're thinking of donating and claiming a free Plus plan - Jeff has 5 of them for me to give away.

  • Half-Life 2 goes gold, we think

    Voodoo Extreme: Half-Life 2 Submitted to Euro Agencies. Gamespot Rumor control: "Half-Life 2 is gold... Not bogus."

  • Search Keys Firefox extension for the accessibility and the keyboard-loving

    Jesse Ruderman has just rolled out the amazing Search Keys Firefox extension that:

    lets you go to search results by pressing the number of the search result instead of clicking

    Its premise is simple, but it's so darn useful to people like me. I'm a keyboard- and keyboard shortcut-loving person, so this is another extension I've added to my must-install list (of extensions). I can imagine this does heaps for accessibility as well.

    It works in "Google, Google News, Google Groups, Google Desktop Search, del.icio.us, and Bugzilla" at this time (most important being the Google support of course). You can see the numbers next to the links in Google search results pages, and you can open the link you want in a new tab by pressing Ctrl-1, Ctrl-2 and so on.

    Screenshot of Google search results with Search Keys in action


    It also works for del.icio.us bookmark links.

    Screenshot of del.icio.us page with Search Keys in action


    A big pat on the back for Jesse.

  • Half Life 2 review in PC Gamer (and Vampire: Bloodlines)

    I went to get some DVD+Rs today (TDK 8X DVD+R RICOHJPNR02) and went to the magazine rack to look for the issue of PC Gamer with the exclusive review of Half-Life 2 (it was all over the web and even Slashdotted). And there it was calling out to me to buy it for SGD7 (SGD1 ≈ USD0.60). Well, I used to have a subscription to PC Gamer when I was a hardcore gamer a few years back, even though it cost ridiculously high compared to the USD1.99 per issue you can get in the US. Anyway, enough with the life story... Check out the PC Gamer December issue cover (at IGN.com).

    Half-Life 2 logo


    Just finished reading the review (it got 98%, if you don't know by now), and damn I'd so like to get my hands on a copy of Half-Life 2 (no, it's not out yet) to play with the Zero-Point Energy Gun (aka the Gravity Gun). And maybe shoot down some Striders.

    The recommended system requirements are a little worrying though - the low end 3D card is an ATI RADEON 9600 PRO 128MB, which PC Gamer equated to be the same as a GeForce FX 5700 128MB. I only have a (now) crappy GeForce FX 5200 128MB. I do have a 3.0GHz HyperThreading processor, but that hardly helps when the graphics card is the bottleneck. Upgrade? Sigh.

    And what's more in this issue, there's also a hands-on scoop on Vampire: The Masquerade - Bloodlines. What a game this could turn out to be. To tell the truth, I'm marginally more excited about this game than Half-Life 2 (I am, first and foremost, a CRPG player). I thoroughly enjoyed the original Vampire: The Masquerade - Redemption and even read the Vampire: The Masquerade novels. I mean, you get to play a vampire from 1 of 7 of Camarilla clans and experience the Vampire: The Masquerade world in its Source engine glory. What's not to like?

    The waiting is the hardest part. Please let them be released on November.

  • Google Desktop is here

    The newest, coolest, next big thing from Google: Google Desktop indexes your files, emails (Outlook and Outlook Express only though), MS Office documents, and AOL IM chat history. And it really does work and that so much better than the slow (accidentally hitting F3 is no fun) and lousy Search function that comes with Windows. Download it, install it, try it out and pull out the F3 key from your keyboard with your teaspoon.

    Screenshot of Google Desktop search in Firefox when searching for "Firefox"


    Oh, and if you're trying to hide bootleg software/music/porn in obscure locations, remember to tell Google Desktop "Don't Search These Items" so none of that comes up at the least opportune moments (like when your girlfriend is looking over your shoulder).

    Via O'Reilly Network.

  • mozpartying at the big 1.0

    Worldwide parties for the big one-point-zero releases of Firefox and Thunderbird are in the works at mozparty2!

    Anyway want to organize a Singapore mozparty ? (Before you ask why don't I do it, let me say that I'm not the "planner" kind of person.)

  • muvee Technologies in Business Times

    Looking at the global picture

  • Dreamhost hosting at $0.77/month

    Update: Well, I am getting 10% referral credit from this and DreamHost has confirmed that we can keep that. Thanks everyone for the referral credit! I have $44.62 right now from referrals and am wondering what to do with the extra money. Ideas anyone?

    It's true. Dreamhost has real cheap hosting plans at USD0.77 per month. You have to pay for a year, but at USD9.24 with a free domain name, I'm surely not complaining.

    The first 777 customers to sign up for service with DreamHost using the promotional code "777" will pay just $0.77 for each month of hosting - for an entire YEAR! That's a $119.40 value - for just $9.24!

    I signed up almost immediately when I saw it (I put Daniel Glazman as my referrer because he recommended it first).

    On referrals though, it does say this:

    Referral credit will not be provided to referers of customers who sign up for this promotional sale.

    Anyway, if you do sign up, do put my ID, chuyeow, as the referrer - I suppose referral credit will accrue later when one signs up for other plans, if I read their rewards page correctly.

  • Google does it again with Google SMS

    Google rolls out Google SMS in the US. Damn, this is too cool (especially if it stays free when it gets to Singapore).

    Right now, Google SMS only works in the U.S.

    Via Google Blog.

  • Download.com asks, "When will you switch to Firefox?"

    Screenshot of Download.com Firefox switch poll


    » Download.com

    Via SpreadFirefox.com.

  • Gmail's new features - Atom feed and "inline" Contact list

    Just noticed this:

    Screenshot of Gmail atom feed button


    They've made the Contacts page "inline" instead of popping up a new window.

    Obviously the Atom feed won't work unless you are authenticated (so RSS/Atom feed readers won't be able to get anything). I wonder how this can be used. Hmm... I smell something cool.

    We'll know soon enough when Google updates the New Features page.

  • Gentoo Linux website redesign contest

    Gentoo Linux is holding a poll for their website redesign contest. I fancy Derek Gerstmann's design.

  • Tabbed browsing gets even better in Firefox

    This is perhaps my favorite bugfix/enhancement since... since they added the deletion of autocomplete results way back in March 2004.

    Remember how hard it was to get links clicked in external applications to open in new tab? We tried setting the advanced.system.supportDDEExec preference to false (it worked spottily). We tried the Show Single Window extension which worked for awhile until something broke in Firefox (this time, I didn't bother to find out which bug this was exactly) and clicked links started opening in new windows. Tabbrowser Extensions worked, but there were times when it caused Firefox to break, and it isn't officially on u.m.o (update.mozilla.org).

    Andy Mason wrote a comment recently to ask me how to get links from external applications to open in a new tab. I recommended Tabbrowser Preferences (TBP) - worked for him. Interestingly, this all happened today, just when the tabbed browsing preferences UI in bug 172962 - Options for where to open URLs from other applications (reuse tab, new tab, new window) that was mentioned in the TBP site went into the daily build. That's right, there's now a Tabbed Browsing preferences section in Firefox options as of today's daily build (2004-10-01) and this is how it looks like:

    Screenshot of Firefox's new Tabbed Browsing preferences UI


    The first thing I did was uninstall the Single Window Extension to test it by selecting the radio boxes I highlighted in red and then going crazy with external application link-clicking. The verdict: it works beautifully! (Of course, I'm not an anal-retentive QA engineer, so there could be cases where it doesn't work that I've missed - but it seems to hold out pretty well for all my needs so far.) It may not seem like much a deal, but those of us who have been struggling with this will fully appreciate and understand the magnitude of this bugfix. Especially when this is one of the most often asked questions in the MozillaZine forums. I am so glad this is fixed.

  • They say getting your site ripped off is the greatest honor

    I was going through my referrer listings (it's early in the month, so it's much easier to catch any referrer spammers). Thankfully, there were very few, now that I've blocked referrers with the words "sex", "porn", "viagra", "cialis", and so on. Anyway, I came across this referrer that had no page views and plenty of hits - an almost sure sign of hotlinking (or it could also be RSS readers, forum threads, which are legitimate).

    So I checked out http://cristal.inria.fr/~yakobows/, and almost thought I was viewing the wrong tab (it happens sometimes when you use tab-browsing) and was looking at my own blog. Of course, upon closer inspection, I realized it wasn't my site but a rip-off of the current site's design and layout. The CSS is identical, even the favicon.

    Screenshot of ripoff of redemption in a blog


    I wasn't pissed - rather, I was flattered (as this entry's title indicates). But I'm sure I want this Boris to quit using exactly the same design as mine. I kinda like what I have now (because Bart Noppen said he liked my color scheme, and I'm a big fan of Bart's design skills) and I want the blog to be (somewhat) unique.

    Boris, appreciate the honor, but please try to make it less of a rip-off would you? Thanks.

  • J2SE 5.0 is out

    New Features and Enhancements in J2SE 5.0. I miss Java. I'm having a rough time getting used to wxPython (mostly because of its lack of documentation).

  • Forever Geek redesigns

    Paul Scrivens has redesigned Forever Geek. I like it better than the old design - much easier to read (I don't really like white backgrounds). I'm proud that I blog there.

  • Another call to "fix" "Open in Tabs" in Firefox

    You may or may not have noticed this neat little "Open in Tabs" feature in Mozilla Firefox that lets you open bookmarks in a folder in tabs. Just to be clear, here's how it looks like:

    Screenshot of Mozilla Firefox "Open in tabs" menu option


    And here it is again in the context menu when you right-click on a bookmark folder:

    Screenshot of Mozilla Firefox "Open in tabs" context menu option


    "Open in Tabs" is also featured in Why You Should Switch to Firefox, the official "Why switch" (to Firefox) write-up. Well, this would be all fine and dandy and a plus point to the tab browsing paradigm of Firefox (and Opera, etc.), except for what appears to be a near fatal flaw in my eyes.

    Here, try this in a new Firefox window. Open up 4 tabs. Make sure that there's nothing in these tabs that you want to remember to come back to later, especially the last 2. Now try using "Open in Tabs" on a bookmark folder with just 2 bookmarks. What just happened? Firefox has closed the last 2 tabs and loaded the first 2 tabs with the first 2 bookmarks. Well, actually this is not so bad for the first 2 tabs, because you can use the "Back" button to go back to your page should you want to, but the tabs that were closed are lost.

    Destructive action without forewarning? I'd think so. Bug 258244- 'Open in tabs' command does not respect close multiple tabs warning, reported by Thomas Rutter (who happens to be the forum admin for SitePoint Forums), asks for this to be fixed by either displaying a warning first or opening the new tabs in addition to the current tabs. I'm not to sure about displaying a warning dialog myself, as Mike Connor feels too, but appending the new tabs to the existing ones seems like a damn fine solution. Sure, you can go to about:config and set the browser.tabs.loadFolderAndReplace preference to false. But even better would be that this was the default preference in the first place. Would have saved me a lot of cursing during those times I accidentally middle-clicked a bookmark folder. And it is easy to make those accidental middle-clicks when you're not thinking straight or not aiming properly (seriously - aren't guys notorious for not aiming properly in the toilet?).

    Bug 175124 - implement Chimera style opening tabs replace as necessary functionality also suggests another (non-destructive) way of dealing with this problem. There are also several other (at least 10) bug reports on the same issue, mostly marked as WONTFIX, with Mike Connor or some other person saying that this is "by design".

    Personally, I still feel that appending the new tabs is still the best solution, because it is what's consistent with what you'd expect from middle-clicking - something opens in a new tab when you middle-click. Does anyone feel the same way or have a better solution in mind?

  • PHP 5.0.2 and 4.3.9

    PHP 5.0.2 and 4.3.9 final are out - maintenance releases both. GIF support is back in the GD extension.

  • Google - the XUL version

    Google has a XUL version of its search page at
    http://www.google.com/mozilla/google.xul. Searching takes you to the "normal" HTML pages though.

    For the benefit of those of you not using Mozilla Firefox, here's a screencap:

    Screenshot of Google's XUL search page


    Seen at a comment at Blogzilla.

  • A furrier Firefox

    Via The Burning Edge:

    If you check out the attachments in bug 260590 - Updated Images for Windows Installer and About/Credit windows, you'd see that they've made the Firefox logo a little bit furrier.

    Here's the normal and the super-furry version placed side-by-side:

    Comparison of old and new Firefox logo


    I like it!

  • Interesting SMS message from my clinic

    Interesting. I got this SMS message this afternoon, apparently from Raffles Medical Group, a local medical establishment with a hospital and a chain of clinics.

    Thank u for using our clinic recently. Pls share your feedback to help us serve u better. Would u recommend our services to others? Raffles Medical Management

    I'm not sure whether this is legitimate (though it almost has to be), and I wonder who'd reply to a message like this. Should I say "Yes, I would recommend it to my friends and family, but your prices are exhorbitant?" (I visited their hospital's outpatient clinic because it was nearest to my office.)

    Back to trying not to scratch my rash-covered body...

  • Half-Life 2 RC, could be out in a few weeks

    According to this thread over at Half-Life 2 Fallout forums, Gabe Newell has posted saying that a release candidate of Half-Life 2 has been sent to Vivendi Universal, and is pending approval for release. This time around, it isn't a hoax:

    For readers doubt the authenticity of this post in light of the gold hoax, the IP does match up this time and many people have received e-mail confirmation from Doug Lombardi and Gabe Newell.

    Via Forever Geek.

  • Forever Geek, have you read it?

    It seems like I am not beyond a little self-promotion now and then. Well, the story goes like this. Paul Scrivens of Whitespace fame also runs Forever Geek, one of those blog/website types that posts news for geeks. Excellent stuff - I'd been reading it in my feed reader for, like, almost since I knew about it, which is around February when the site first appeared. (Of course, when I said "excellent", you will soon realize I have a vested interested in saying so.)

    Anyway, Forever Geek was looking for articles so I submitted the search article that was also posted here. That got into Slashdot. Recently, Paul posted that Forever Geek were looking for some new members to join the crew, so I applied - I find these things hard to resist, weirdly. And I got accepted. So now I am a contributing writer on Forever Geek, which I am unashamedly telling you to add to your reading list (Atom feed, RSS 2.0 feed).

    This is not my first group blog (well, it is my second). The first was ensight.org from the before time (when most of the bloggers were SitePoint forum staff).

    Anyway, read Forever Geek. Yeah.

  • Mozilla Thunderbird - changing default email sort order

    Update: Trä pointed to the relevant bug: Bug 86845 - Sort order for mail/news not configurable by default.

    In reply to my offer of free Thunderbird technical support, I've gotten a few queries from people asking how to get rid of certain annoyances they have with Thunderbird (out of the box), or whether these peeves have been "fixed". Some of these I did manage to resolve, the rest turned out to be also my own peeves with Thunderbird that I didn't realize I had until I was asked.

    One reader asked:

    Is there any hidden pref, or any way to make EVERY imap folder sort by Date with newest at the top, instead of the default which is newest at the bottom?

    Exactly the same problem I had (I have 7 IMAP email accounts in Thunderbird at work), exactly the same problem I had tried to solve a long while back (to no avail). I've checked Google, Bugzilla, and Thunderbird's pref.js but these didn't turn up anything useful. It can't be true that no one else finds this a problem. If you know that this can be changed somewhere or has already been logged into Bugzilla, let me know by leaving a nice little comment.

    Coincidentally, on the same day, a colleague asked me how to sort messages in a folder by thread and then by date. Which of course can be done by clicking on the "Thread" icon (click it again if the chronological order is not in the direction you want). But this also only applies to a single folder which is a pain in the ass if you have to do the same for each other folder.

  • Things I have to do

    Same old story - too much to do, too little time.

    To-do list:

    • Find out why the virtual server hosting this site is crashing (this explains the intermittent downtime).
    • Blog an average of 2 entries in 3 days for Forever Geek, where I am now a contributing writer.
    • Study and pass SCJP exam.
    • Learn how to play the guitar.
    • Finish reading the Vampire: Dark Ages Clan Novel series (I'm currently at Brujah, book 8 of 13).
    • Fix a client's website. Make some backend changes.
    • Get more involved in WordPress development. (Anyone know where to get WordPress from Subversion? Or is getting from CVS still current?)
    • Blog more often here.
    • Update my About page, and bring back the Archives page.
    • Get to work on this Mozilla-related website I've been thinking of doing.
    • Finish watching all this unwatched anime: Berserk, Last EXILE, Kenshin, Kiddy Grade, Hunter X Hunter.
  • Link exchange between Firefox and Microsoft bloggers

    Asa Dotzler of mozilla.org tells us how he got a link exchange from Robert Scoble, Microsoft's most popular blogger.

    ... Robert Scoble, agreed to add a Firefox button to his weblog if I would add a "I recommend installing XPSP2" link to my blog.

    Asa has already added the "I recommend installing XPSP2" link to his blog. I wonder what Robert's CEO would have to say about this if he does add the Firefox button.

    Well, whatever happens, I recommend installing Firefox, especially the freshly baked 1.0 Preview Release (grab the Windows installer or a Windows zip package - I assume other platform users will know what they are doing).

  • Mozilla Thunderbird 0.8 released

    Mozilla Thunderbird 0.8 is out. Time for you to upgrade my favorite email client or to convert if you haven't yet. Give it a quick whirl and if you meet any problems, I'll be your (free) technical support. Just drop me a note.

    All the new features I've written about one and a half months back are still there, except for Improved Quick Search.

  • Alternate stylesheet UI is back in Firefox!

    Remember the big debate over the removal of the alternate stylesheet UI? Well, the mozilla.org guys have had an Aviary meeting and approved the re-inserting of the alternate stylesheet UI back into Firefox 1.0.

    Check out Asa's re-opening of the Re-insert Alternate Stylesheet UI bug report - bug 257859:

    Setting nomination flag for PR since we'll want to get this in before
    PR if we add it back.

    Well, I'm glad it worked out this way. And even better, the new alternate stylesheet UI comes with improvements as well.

    The status bar UI looks almost the same, only the wording has been changed from "Basic" to the more understandable "Basic Page Style".

    Firefox Alternate Stylesheet UI


    There is also now a "Page View" submenu under the "View" menu that includes a "No Style" option that removes CSS styling from the webpage. Amazingly, this now strips all styles, including styles applied in HTML style attributes and even font tags! Try that on a site that uses font tags like HPB. (Before, the font tag styles will still be visible.)

    Firefox Alternate Stylesheet UI menu


    On a somewhat related note, the "Work Offline" UI has been re-added for Firefox Preview Release 1. The devs will be working to get it fully (or mostly) functional before the big one-point-oh as well.

  • iTunes plugin API - where is it?

    Anybody knows where one can find the iTunes (for Windows) plugin API and documentation? There're so many plugins available but I have no idea how the plugin authors even got started coding them - documentation on apple.com is nearly non-existent.

    Update: Found it. Thanks sryo!

  • Thunderbird "Purge" button extension

    I have said before that Mozilla Thunderbird needs a "Purge" button equivalent to that in Outlook Express. In the time since, I have been using Thunderbird despite saying that I'd stay away until a "Purge" button is implemented. Why? I guess because it didn't seem like a Purge button was going to be implemented anytime soon (and also because of increased security and protection from OE-targeted worms and viruses).

    A commentor reminded me about the existence of an extension that gives you a "Purge" button. I say "reminded" because I'd seen this extension before in the MozillaZine forums way back in the early months of this year. Somehow I forgot all about it since then and have been frantically right-clicking on each folder and selecting "Compact This folder". Which kinda sucked because that's a whole lot of clicking going on especially with multiple accounts. Now I can do the same with just 1 click (per folder).

    Thunderbird Purge and Delete Junk! buttons


    And couple that with the Buttons! extension, it's so much easier to get rid of junk mail. 2 clicks and junk mail is purged. These buttons really should be in Thunderbird itself.

  • 20 "sponsored" Gmail invites to give away

    Update: Sandip has 12 more invites.

    Update: Sandip has given away all his invites! To date he has given away all 79 of them (from multiple Gmail accounts). Nice. Thanks Sandip for you generosity!

    A kind reader by the name of Sandip Bhattachary emailed me this today:

    i have 20 gmail invites to give away. I actually have 7 gmail accounts myself. Receving so many invites per day. I have given this to everybody. Please post my thread in your website that I want to give away gmail invites to those who email me at the above mentioned email address. This is a serious email.

    You can get him at bhattacharyas AT gmail DOT com.

    Thanks Sandip.

    This also signals that Gmail invites are now actually undesirable and a burden on the soul. (Just kidding, Sandip.)

  • Why not to use MySQL

    MySQL probably is the lowest common denominator for a RDBMS in the webhosting world - you can't leave /home without it. This blog wouldn't exist without MySQL (someday WordPress will be database-independent). Sometimes I hear people say MySQL isn't a real RDBMS, or that it lacks certain features that causes it to suck™. I work extensively with MySQL and there are times when I wish for stuff like referential integrity (available with InnoDB tables, but most hosts don't provide it) so I don't have to code constraint checks in the application. I wish for transactions (again supported by InnoDB). I wish for a PL/SQL equivalent.

    Now, these guys, they've got MySQL gotchas and an anti-MySQL list listing some niggly inconsistencies and general bad behavior in MySQL. And after reading that, I'll never trust what MySQL does to my data again (silently change my data would you, mysqld?).

    (Yes, the entry title is grammatically incorrect on purpose.)

  • A Girl's Guide to Geek Guys

    A Girl's Guide to Geek Guys - "They are generally available. Other women will tend not to steal them. They can fix things. Your parents will love them. They're smart." Hey I'm a geek ;-)

  • MT Friend or Foe hack

    Ever since Movable Type started to have the comments redirection feature (since version 2.66), comment authors' URLs have been redirected via a simple redirection script that prevents them from appearing directly on blog entries. This (partially) solved the problem of comment spammers because links from comments no longer benefit from backlinks that add to their PageRank. However, legitimate commentors also have their URLs redirected, which to me seems just quite a bit unfriendly.

    Neil Turner has worked around this with his aptly named Friend or foe hack for MT. A simple but still neat bit of PHP (and a good example of MT's excellent template tags). If you use MT and want to "reward" your legitimate commentors, you won't want to miss this.

  • Upgraded to PHP5 on Gentoo

    Yup I just upgraded from PHP4.3.8 to PHP5.0.1 on the Gentoo VPS hosting this weblog. It went rather smoothly, except for an oversight where I actually forgot to compile PHP with session support (and had to re-compile it). Just unmerge the existing PHP and mod_php packages, and emerge them again from ebuilds.

    emerge unmerge php mod_php
    emerge -pv /usr/portage/dev-php/php/php-5.0.1.ebuild
    USE="curl session mysql postgres zlib ldap" emerge -v /usr/portage/dev-php/php/php-5.0.1.ebuild
    emerge -pv /usr/portage/dev-php/mod_php/mod_php-5.0.1.ebuild
    USE="curl session mysql postgres zlib ldap" emerge -v /usr/portage/dev-php/mod_php/mod_php-5.0.1.ebuild

    No configuration needed beyond commenting out the LoadModule directive (for PHP4) in the Apache configuration file and changing the Apache startup options (in /etc/conf.d/apache2):

    #APACHE2_OPTS="-D SSL -D PHP4"
    APACHE2_OPTS="-D SSL -D PHP5"

    A restart of Apache2 is all that's left.

    I'm not sure if I left any essential stuff out in the USE flags (which roughly translates into configure options) I used so if anyone would be so kind to skim through my phpinfo() and let me know, I'd be grateful and will be sure to return the favor. Some of the USE flags/configure options are pretty self-explanatory but I have no idea what the rest really do. What does excluding the truetype flag mean? Does it mean I don't get TrueType font support when dynamically generating images (with gd)? What about the pcre flag? If I don't compile with pcre, does that mean no preg_* functions for me? Point me to an explanatory page please (I have been looking).

  • Mozilla in front in Google

    Mozilla.org is on top when you Google for browser and 1. How neat is that? (Not that anybody's going to search for "1", but "browser" is pretty good.) Via Anne van Kesteren.

  • Line of the month

    If you want to destroy my sweater
    Hold this thread as I walk away

    From Undone - the sweater song. That's one of the most loaded/quirky/semi-humorous lines I've heard in a while.

    Anyway, AudioScrobbler tells me I'm listening to too much Weezer Weezer quite a bit (though AudioScrobbler erased my music entries once before so that's hardly an accurate list). It also tells me:

    Your plugin reported a submission error: Your plugin (itw version 0.0.4) is banned. Please contact the plugin author for an updated version

    Any idea what that is or anyone have the same error? I'm using the iTunes plugin - what else?

    This is because the iTunes plugin is making multiple submissions while iTunes is paused. See the relevant thread here. You can grab the latest plugin from http://www.xurble.org/iScrobblerWin_0_0_5.exe - it's not yet official, but it works. (Thanks Suman!)

    (Wondering what AudioScrobbler is? Go read the AudioScrobbler Help page.)

  • What does it take to get on Planet Mozilla?

    Which one of those should one read/subscribe to, what's the difference, and what's the criteria for getting onto Planet Mozilla?

    (Before you start to wonder whether these are angsty/whiny questions, let me say that they are not.)

  • To Matt Gough

    To Matt Gough, the guy I've been trying to contact and send a Gmail invite to:

    I've been trying to tell you that the Gmail invites that I've sent to you don't seem to get through (nor do my emails from my Gmail account, apparently). I'd sent you one before and one just today. Let me know if you don't see them (then I can just pass you the signup link).

    Figured this was the best way to get your attention when I tried, in vain, to post a comment on your weblog (it says "registration is required" and I couldn't find a registration link - what is that? TypeKey?).

  • Make your muvee contest

    The company just launched the "My Vacation muvee" contest. Interested in a real simple piece of video editing software? Check out the "I'm new" page to find out more.

  • Movable Type 3.1 released

    Movable Type 3.1 launched - since the switch, life is still good with WordPress. I do miss MT's flexible templating (like Jon Hicks), but the rest of it works great. WordPress devs are working on the templating issue though. As for me, I submitted a bad patch to WordPress - not a good start. Via Photomatt.

  • Don't know what to do with Gmail invites

    So everyone has started giving away Gmail invites again because Google has started giving them out again. I don't really know what to do with them because most people would have one already by now.

    Well, at the risk of being uncool and mainstream, I'm going to give 4 of them away to readers. Drop me an email or leave a comment.

    For a start, I'll be giving them only to people whom I've heard from before. Just in case. If all 10 (OK so Bloglines tells me there are 296 subscribers to my RSS feed) of you already have a Gmail account, I'll waive this and give to just about anyone.

  • Opera 7.60 Preview 1 for Windows

    You can now (actually, since August 24) get Opera 7.60 Preview 1 for Windows. Cool new features: something called Medium-Screen Rendering that "reformats pages so they fit better on smaller screens", and speech-enabled browsing via XHTML+Voice 1.2!

  • You are being served by Apache2 on Gentoo Linux

    OK I got Postfix and courier-imap up and running now. Plus, switching to a 2.6 kernel was as simple as changing it in the web-based Linode Platform manager and rebooting. Just select a few things and click a few form buttons. Very nice.

    The migration to the new server didn't go quite as smoothly as I'd hoped it to be, but all is good now and you are currently being served this webpage/rss feed by Apache2 web server running on a Gentoo Linux box.

    Apache signature of new server at Linode.com


    I'm using this Virtual Private Server (VPS) setup offered by Linode.com which is an unmanaged User Mode Linux hosting solution. Which means I've had to take a crash course in system administration in the attempt to transform a base Linux system with minimal services into a production web server. It's hard because of the esoteric configuration that Linux is so notorious for, though it was still easier than I thought, and very satisfying when things progressed from a state of borkage to reasonable functionality. Gentoo's Portage probably played a good hand in making things easy. emerge, emerge, emerge.

    I still need to set up a email services (the whole kahuna - MTA, LDA, AA) - any advice on this area would be very nice.

    More on Gentoo, system administration, User Mode Linux, and Linode later.

  • How to fold a shirt in 3 seconds

    How to fold a shirt. Watch the video. I need to learn how to do this.

  • Setting up AWStats on Gentoo

    Some notes to myself on setting up AWStats on a Gentoo box.

    1. First, emerge the awstats package. I added the "vhosts" USE flag because it's listed as a USE flag that awstats reads, and I need support for virtual hosts (although I don't know for sure what it does - I reckon it has something to do with the webapps-config package that's a dependency).

      emerge -pv awstats
      USE="vhosts" emerge awstats
    2. Don't use the webapp-config program that the emerge process tells you to use at the end unless you know what you are doing. There have been reports that it isn't exactly ideal in the Gentoo forums.

    3. Make a symbolic link to the Apache configuration directives we need to add to Apache in a convenient location. You may also want to make the file owner-writable (chmod u+w) since it's read-only.

      ln -s /usr/share/webapps/awstats/6.1/postinst-en.txt /etc/apache2/conf/awstats.conf
    4. I had to add this bit to /etc/apache2/conf/awstats.conf prevent a 403 error when accessing the ScriptAliased /awstats directory.

      <directory "/usr/share/webapps/awstats/6.1/hostroot/cgi-bin/">
      Options +ExecCGI +FollowSymLinks
      AllowOverride None
      Order allow,deny
      Allow from all</directory>
    5. Include the AWStats-required configuration directives in the main Apache config file (mine's /etc/apache2/conf/apache2.conf).

      Include conf/awstats.conf
    6. I'm using name-based virtual hosts, with logs for each of them (they are in the included vhosts/vhosts.conf file). AWStats by default parses the Combined log format and this is what I specify for my access logs as well. An example VirtualHost directive:

      <VirtualHost *:80>
      ServerName blog.codefront.net
      DocumentRoot /www/codefront.net/htdocs/blog
      ServerPath /blog
      CustomLog /var/log/apache2/blog.codefront.net/access_log combined
      ErrorLog /var/log/apache2/blog.codefront.net/error_log
      </VirtualHost>
    7. Now, create an AWStats configuration file for each virtual-host.

      cp /etc/awstats/awstats.model.conf /etc/awstats/awstats.blog.codefront.net.conf

      Notice that the configuration file name is of the format "awstats.domain.tld.conf".

    8. Edit the configuration file (/etc/awstats/awstats.blog.codefront.net.conf). The important things to change are:

      • LogFile (point this to your access log)
      • SiteDomain (your site's domain name)
      • HostAliases (any aliases your site may have)
      • DirData (AWStats database where your results will be stored. This has to be writable by the Apache user if the AllowToUpdateStatsFromBrowser option is turned on.)
      • DirCgi (AWStats CGI directory)
      • DirIcons (AWStats icons directory)
    9. Restart Apache.

      /etc/init.d/apache2 restart
    10. You can access AWStats from www.example.com/awstats/awstats.pl?config=www.example.com.

    11. Set password-protection for the AWStats directory.

    12. Run AWStats from the command line to update it. You'd likely want to set up a cron job for this.

      /usr/bin/awstats_updateall.pl now -awstatsprog=/usr/share/webapps/awstats/6.1/hostroot/cgi-bin/awstats.pl -configdir=/etc/awstats/

    Useful links:

  • Half-Life 2 going gold on Monday

    Gabe Newell, head of Valve Software, posts that Half-Life 2 is going gold on Monday, as GameSpot reports. We could see HL2 on shelves in a week!

  • Wired Firefox, Tired Explorer

    Seen on Wired.com: Wired (Firefox) | Tired (Mozilla) | Expired (Explorer) (via Steve Garrity)

  • Stupid mistake - fix in 9 hours time

    I made a stupid mistake while backing up my files on the old server and uploading them to the new one. That's the problem when you have 2 similar SFTP windows (1 for uploading, 1 for downloading) and taking part in multiple chat sessions. You tend to not notice that you're uploading to the server you're supposed to be downloading from.

    So this is the default WordPress template you're seeing right now. I remember making another backup copy at work so in 9 hours time, fingers crossed, things will be back to normal.

  • No alternate stylesheet switcher in Firefox 1.0

    Bug 253722: remove alternate stylesheet UI

    Daniel Glazman couldn't believe it and suggests that such incomplete features be enabled/disabled via a user option. Yes, please.

    Join the discussion.

  • JMF applet without JMF installed?

    A long shot, but do any of you Java programmers think an applet that uses JMF can be deployed and run on a client that doesn't have JMF installed?

    I think the chances would be higher if instead of an applet, a Java Web Start application was used instead. Bundling the JMF libraries could work, but there hasn't been any report of success as far as I can find. (It is possible with a full-blown Java application though.)

    Any leads at all would be good. In the meantime I will be trying this out myself.

  • Better VPS hosts according to WebHostingTalk

    I have been looking around for VPS hosting and reading customer reviews at WebHostingTalk.com. VPSColo, Dinix, Linode and TecTonic seem to be the better ones in terms of reliability, support, etc.

  • The "other" switch: From MovableType to WordPress

    Making the switch from MovableType to WordPress took a whole damn lot of time, it did.
  • Looking for hosting

    I'm on the hunt for a good webhosting package. I'm having some problems (technical and political) with the current one on the dedicated server I'm sharing. If any of you have any recommendations, do let me know by leaving a comment or writing me.

    These are my requirements:
    500MB storage
    25GB transfer (at least)
    Unlimited databases
    Unlimited subdomains
    Unlimited email accounts
    Multiple domains (on a single hosting plan)

    I'm thinking of getting a Virtual Private Server (VPS) solution and have been taking a look at some User Mode Linux (UML) hosting plans. In particular, Dinix and VPS Colo (I'm looking at their lowest priced plans) stand out after much searching and reading over at WebHostingTalk forums. $35/month isn't something I can afford though. If anyone wants to share the plan with me (we can split it 50-50 or whatever), do let me know. I'm a very nice trustworthy guy (honest!).

    UML == root access. Mmmm... I can finally get the stuff (Subversion, PostgreSQL, mod_python) I want installed.

  • The Official Gmail Notifier

    At first there was the Gmail Notifier extension for Firefox. And there was the GMailCompose extension for Firefox (made defunct by the WebMailCompose extension). The first displayed an email notification message in Firefox. The second allowed you to have all mailto: links you click automatically send you to a page to compose the email in Gmail. There was also GTray.

    And then there now is the official Gmail Notifier from Google. Purely for Windows, it appears unobtrusively in your system tray when installed.

    New message notifications appear as a transclucent pane in the bottom right of your screen. (The icon does look a little cutesy doesn't it?)

    gmail-notifier-new-msgs-notification.png


    There's not much more you can do with it.

    gmail-notifier-menu.png


    It's nice to have an official tool from Google that does what all those other homegrown tools do. It's also interesting that Google seems to have incorporated the best of all these amazing tools in their app - mail notification and mailto: link manipulation. Thumbs up to the authors of those neat little tools! Google seems to be copying your ideas!

  • McSearchPreview - a better GooglePreview?

    In response to Martey's comment and my entry (I think), Carlo has updated the McSearchPreview webpage to include an explanation of the Amazon affiliate codes mentioned below.

    When you use McSearchPreview and click on an Amazon result, you support me and the further development of the extension if you actually purchase a product.

    Support extension authors!

    Carlo Zottmann wrote me about 2 weeks back to let me know about his McSearchPreview extension for Firefox. He wrote his first Firefox extension after using the GooglePreview extension (which I wrote about) and discovering that it lacked similar capabilities for other search engines that he uses. Attending a need is indeed excellent inspiration.

    McSearchPreview adds preview thumbnails to search results for Google, MSN Search, Yahoo Search, A9 and AllTheWeb. Unlike GooglePreview, the thumbnails are retrieved from Alexa (by default - Thumbshots can also be used as an alternative). This means a larger number of results will have preview images, since Thumbshots only has thumbnail images of pages that are in DMOZ (in which very few pages would be listed).

    Screenshot of McSearchPreview options dialog


    McSearchPreview also adds an "Open in new window link" at the end of each search result - the actual usefulness of this feature is a little arguable since we are using a browser with tab browsing, after all. It also adds a "Site Info" link that takes you to the Alexa page for the search result's domain. It seems like an Amazon affiliate link but I'm not exactly sure if the author gets any benefit from it. Well, I'm fine with letting Carlo make a little profit from his neat extension, if any, but it would have been nice if he'd explained why there's (what appears to be) Amazon affiliate code in the "Site Info" links. Incidentally, the author of GooglePreview has removed Amazon affiliate code from version 0.3 of the extension.

    Anyway, this is a really nice extension and I'm using it in place of GooglePreview at the moment (more pretty thumbnails!). Thanks Carlo!

  • pickle.dump(links, blog) // Non-techie edition

  • Loving my iPod mini

    I wished I had an iPod mini. I have now got an iPod mini - a silver one.

    Image of silver iPod mini


    To clear several plausible doubts judging from the comments in that last post (well, mostly directed at the rant by jbelkin):

    • Roshambo, I'm not sure how Sumajin SmartWrap is going to be useful for me. The website isn't very descriptive so if you could explain to me what it does... :)
    • Like Luke said, jbelkin, you said you "never read (my) original post" and yet go on to say "and this is why (I'm) wrong". I liked the self-contradiction there heh. But, but, I agree with you most of your points (well, right now because I'm the owner of an iPod). Except that the iPod (mini) is overpriced. Perhaps you haven't met poor people before (OK that isn't fair, but you weren't being fair). The iPod mini cost me a week's salary. I am now willing to pay for it because I am drawing a salary.
    • I have no idea what "Fry's" is. I live in Singapore.
    • I am loving my iPod mini.

    For my fellow Singaporeans, do go get your iPod mini if you want one from SGL at Sim Lim Square (it's on the 2nd floor, and if you go by the connecting bridge from OG, it's the first shop on your right). It's $440 ($18 less the the market price of $458 at places like the AppleCentres) and the lady there is very friendly. Another thing I must say is that the Best Denki people manning their phone lines give pretty crappy service. They don’t sound very friendly and don’t bother to check for you the availability in other stores. Neither did they take the initiative to tell me when the new stocks will arrive. Harvey Norman is a completely different story because the guy who serviced my call was totally friendly, checked out the stocks in the other branches, and even told me when the stocks will arrive. Boo Best Denki.

    Anyway, more on this (the iPod mini) later.

  • Flattered

    John Stone sent me a very nice email today about his redesign.

    I've got my own blog and I used your's for inspiration, it doesn't
    look nearly as good as yours and thats good because I wouldn't want
    people to think I "stole" the design, but I just wanted to clear it
    with you first, if you think it looks to much like a clone, i have
    another design i can use.

    Someone took my crummy design as an inspiration - I am at once amazed and utterly flattered. Thanks for asking nicely John, and yes it is more than alright.

  • IE blog joke/flamebait, and get XP SP2 via BitTorrent

    Tony Chor, the Group Program Manager for the IE team, writes:

    We also came up with a very original idea – popup blocking.

    The idea was so ridiculous I knew it had to be a joke. And it was, when Tony later writes:

    For the record, I was joking about our "innovation" around popup blocking.

    But not after several not entirely unexpected flames (I found the popup blocking innovation comment from a site flamed the blog post too) from people who didn't get it.

    Anyway, if you haven't seen this yet (it was on Slashdot - I don't read it though, perhaps you do), you can get Windows XP Service Pack 2 via BitTorrent at SP2Torrent.com. Not from Microsoft (so it's not "official"), but you got to wonder why Microsoft doesn't embrace BitTorrent for distributing XP SP2 and do away with all that initial hoo-ha about staggered release schedules.

  • iWish iHad an iPod mini

    I know I pointed out 5 reasons not to buy an iPod 6 months or so ago. I take that back. I caught the iPod bug. More specifically, the iPod mini bug - don't really fancy the larger iPod, even if it's much better with the 4G version.

    I'm really getting it as something to soothe me and make my sad life a little brighter (believe it or not, pathetic as that sounds). I haven't got any gadgets yet and this will probably be the first time I'm (modestly) splurging my salary. Yes that's right. I don't have an MP3 player, no palmtop device, no 3G phone (I kinda like my trusty Nokia 8250). I don't even have a Discman that works.

    Anyway, the sadness of my gadget-deprived life is besides the point. The point is, I want an iPod mini. And I wish I can get my hands on one tommorrow against all odds. Well, someone at an Apple reseller told me that there was a national shortage of iPod mini with interminable queues. Pretty disheartening, especially when I got a couple of colleagues to go down with me to buy it. And we were arrogantly told by the sales person (I reckon he was a Mac evangelist) that "Apple does not intend to include FM radio in the iPod or in the future". (It really was more arrogant-sounding then that, but I forget the exact words to quote him verbatim.)

    Good news is, a friend told me that she saw iPod minis selling around a tech place today. Well, at least she saw the boxes. And someone at the Mac Users Group (Singapore) posted that he bought an iPod just today. It just might happen! Will be calling a few places to check on their iPod mini stocks 10 hours from now. Fingers crossed.

  • pickle.dump(links, blog) // August 6, 2004 edition

    • MWSnap - This is one bad ass screen capture utility, available at competitive pricing - it's free! It allows you to snap a selected portion of the screen (no need for resizing, cropping), and perhaps best of all, allows you to add a mouse pointer to your screen caps. Finally I can put mouse pointers in those screenshots I seem so fond of taking.
    • Ian Lloyd's Accessify.com has a whole bunch of accessibility tools and wizards to help the web developer generate code that's accessible. The Accessibility Toolbox is pretty instructive - it generates HTML code for form elements that's accessible. Are your forms not accessible yet?
    • Battle Torrent is a project that promises 'A greatly simplified BitTorrent experience'. I'm not sure if there's any active development (they're looking for a lead developer at the moment). Either way, this is something we have wanted to work on and it's great to see that we are not the only ones who think BitTorrent is amazing and it'd be even more amazing if mom and pop could use it.
    • How to Bypass Most Firewall Restrictions and Access the Internet Privately - For those of you without a proper internet connection at work. Or those of you who want privacy.
    • Punching holes into firewalls explains how tunnelling can always get you by a firewall, so long as you let a single protocol out (HTTP is ever so commonly allowed).
  • Tombstone Generator

    Via TIMELINE: Tombstone Generator

    Epitaph - Help Let me out!


    I also found the Church Sign Generator which was the inspiration(!) for the Tombstone Generator.

    Church sign - Fowler Is God


  • pickle.dump(links, blog)

    Well, I don't have a link blog and the Externals section is totally borked (I still keep my del.icio.us account going though), so I'm finding it so hard to keep track of links I'm picking up and intending to write about. Well, this is a whole genus of links that you want to link to but don't quite feel worthy of a complete blog entry (know what I mean?). Someday I will fix this broken weblog. For now, I procrastinate.

    • Lockergnome is looking for contributors and writers. Applied and accepted (for the Web Browser channel, naturally). Chris Pirillo's a nice dude. Though I could be saying that because he likes muvee autoProducer so much.
    • The Simple Guide to the A-List Bloggers is a hilarious parody of A-List bloggers (such as Dave Winer and Chris Pirillo) and their blogging styles. Of course, you'd have to actually have read these peoples' blogs regularly to get the humor.
    • Audioscrobbler is interesting stuff (OK so I'm a little late to pick this one up). My user details. Seems to go down quite often though, and still in teething stages. But promising. Via dot-totally.co.uk.
    • Free CSS menus (complete with rollovers). These look fantastic. Fully XHTML and CSS standards-compliant.
    • Webnote is an online tool for taking notes. Some mean DHTML/JS-fu going on there.
    • FreeTechBooks links to a good collection of free books in electronic form. All legal of course.
    • Conversational cheap shots. Add a varied collection of verbal weaponry to your arsenal. Get ahead in flame wars.
    • The Programmers' Stone celebrates the Art of Programming. A little wordy. I should print this out to read.
  • Testing Meme Propagation In Blogspace: Add Your Blog!

    This posting is a community experiment started by Minding the Planet to see how a meme represented by a blog posting spreads across blogspace, physical space and time. It will help to show how ideas travel across blogs in space and time and how blogs are connected. It may also help to show which blogs are most influential in the propagation of memes. The original posting for this experiment is located at: Minding the Planet; results and commentary will appear there in the future.

    Please join the test by adding your blog (see instructions, below) and inviting your friends to participate — the more the better. The data from this test will be public and open; others may use it to visualize and study the connectedness of blogspace and the propagation of memes across blogs.

    The GUID for this experiment is: as098398298250swg9e98929872525389t9987898tq98wteqtgaq62010920352598gawst (this GUID enables anyone to easily search Google for all results of this experiment). Anyone is free to analyze the data of this experiment. Please publicize your analysis of the data, and/or any comments by adding comments onto the original post at Minding the Planet; Note: it would be interesting to see a geographic map or a temporal animation, as well as a social network map of the propagation of this meme.

    Instructions

    To add your blog to this experiment, copy this entire posting to
    your blog, and fill out the info below, substituting your own
    information in your posting, where appropriate.

    Required Fields

    (Note: Replace the answers below with your own answers)

    1. I found this experiment at URL: http://www.nocertainty.com/
    2. I found it via "Newsreader Software" or "Browsing or Searching the
      Web" or "An E-Mail Message": Newsreader (Bloglines
    3. I posted this experiment at URL: http://blog.codefront.net/
    4. I posted this on date (day, month, year): 03 August 2004
    5. I posted this at time (24 hour time): 02:05:00
    6. My posting location is (city, state, country): Singapore, Singapore, Singapore

    Optional Survey Fields

    (Replace the answers below with your own answers):

    1. My blog is hosted by: Myself
    2. My age is: 24
    3. My gender is: Male
    4. My occupation is: Webmaster
    5. I use the following RSS/Atom reader software: Bloglines, Mozilla Thunderbird
    6. I use the following software to post to my blog: Movable Type
    7. I have been blogging since (day, month, year): 24 May 2003
    8. My web browser is: Firefox

    There it is, my very first participation in a meme.

  • Chee Aun has a good eye for UI bugs

    Chee Aun, author of the Phoenity theme, spotted several minor bugs in Firefox while working on a new version of Phoenity.

    The things he finds are just amazing and come complete with bugfixes (where applicable). This guy should be in there fixing and improving the Firefox UI.

  • From the creators of South Park!

    Matt and Trey have a new movie coming up! It's called Team America and will be in theatres in October. Kick ass!

    Check out the Team America trailer (requires the Quicktime plugin).

  • RSS integration enabled in yesterday's Thunderbird nightly builds

    Yup it is here - bug 253490 - Turn on the new RSS extensions and add it to the installer.

    It isn't completely working yet, but it is ready for testing. Works very much like the Forumzilla extension. It even displays the webpage in full as specified in the <link> tags in the RSS feed like Forumzilla does, which isn't ideal. Atom feeds work fine though - only the <content> is shown in plain text format.

    Well, on to the screenshots. I'm not in the mood for too much writing today.

    Creating a new RSS account:

    Screenshot of Thunderbird's UI for creating new account, including an RSS account


    Management pane for an RSS account:

    Screenshot of account management pane


    RSS account options:

    Screenshot of RSS account options


    RSS feed management dialog:

    Screenshot of RSS feed management dialog


    Adding a new RSS feed:

    Screenshot of dialog for adding a new feed


    Message pane listing items in a RSS feed:

    Screenshot of message pane for RSS feeds


    Grab the latest Thunderbird nightly build for some first-hand action.

  • A label for unread emails in Gmail

    This is a pretty nifty Gmail trick (via TIMELINE via gmailwiki): create a label named "Unread" and Gmail automatically lists all unread email when you call up that label.

    Screenshot of Gmail


    Seems very much like a half-hearted, half-implemented feature that Google hasn't decided whether to give to users. I for one would very much like to have a standard section for unread mail ("standard" meaning it goes into the same place as Starred, All Mail and co.).

  • Did you think I'd lay down and die?

    "Oh not I."

    Well, seems like I was caught off-guard when a reader (thanks Roshambo!) wrote in and alerted me that I'd exceeded the 10 GB bandwidth limit I'd set for the codefront.net domain. There were times when I thought that even 1 GB of bandwidth was a whole lot. Granted, 10 GB of transfer is a pittance, but I am a nobody with an unremarkable website (I think people come just for the Mozilla entries with them screenshots, heh), so humour me.

    Luckily I have a 20 GB reseller account with left over (or rather, unused) bandwidth from my other domains, so problem solved with a little bit of quota adjustments in WHM. Looks like I gotta get more of that bandwidth thing for next month.

  • The answer to the question 'How many of you use Internet Explorer?'

    ... is none, or at least as far as this gem of an incident goes (it happened at BlogOn2004).

    Well, at least bloggers are using non-IE browsers. I doubt this is so for the general population, but it's getting there. I'm not that anti-Microsoft. I'm just against broken browsers that don't get fixed. Especially when you have such a big market share (one could have said 'monopoly' a few years back), it becomes almost an obligation to fix whatever's broken.

  • Preview Google search results with this extension

    A reader (who didn't leave his name, though from the email address it should be a Marc or Marc-Andre) wrote me an email to tell me about this Firefox extension, GooglePreview that inserts previews of search results pages before each search result at Google. Quite neat.

    Screenshot of GooglePreview extension in action


    It also inserts product screenshots from Amazon if a search result points to Amazon and even a stock's trends (see the GooglePreview website for screenshots).

    Thanks for the heads up Marc!

    Rory asked how it works, so I took a quick look at the source of the XPI and found out how.

  • New features in Mozilla Thunderbird 0.8

    Thunderbird 0.8 is shipping soon (July, according to the roadmap) and test builds have just been announced. Here's the rundown of the new features you can expect to see.

    RSS Integration

    That's right, RSS reading capability is going to be built into Thunderbird. This has not been turned on yet in the latest Thunderbird nightlies, though bugs are being reported (mostly by Scott, the Thunderbird lead) and fixed as can be seen from bugs 250963, 251242, 252496, 252483 and 252391. Apparently, it could look and function somewhat like Opera (see Opera RSS reader screenshot) - just speculation though. The "core" bug is bug 225158 - Thunderbird should act as an RSS/Atom newsreader, too. The roadmap promises this bug (or rather RFE) will be fixed for Thunderbird 0.8.

    This promises to be a more typical news reader implementation than that that got into Firefox nightlies not too long ago (see RSS feed integration in Firefox). Most of the code is based off the Forumzilla extension, so if you want a preview of how it could look, grab and install Forumzilla.

    Blocking remote images

    Remote images in emails will also be blocked by default (this can be changed by going Tools -> Options -> Advanced and unchecking "Block loading of remote images in mail messages."). This has 2 main uses: protecting your privacy just in case someone is looking over your shoulder (though one can only imagine what images are so private!), and preventing the remote request from being sent (thereby preventing spammers from being able to detect that your account is active).

    Screenshot of blocked remote images in Thunderbird preview panel


    Global Inbox for POP3 accounts

    There will also be a single global inbox that you can use for storing messages from multiple POP3 accounts. This will come as an alternative to the current way POP3 accounts are handled - each POP3 account has it's own inbox and folders.

    The Global Inbox option will be given to you when you setup a new POP3 account:

    Screenshot of Global Inbox option when setting up a new account


    Get All Mail functionality

    And yes! The much missed "Get All Mail" functionality will be in Thunderbird 0.8.

    Screenshot of Get All Mail functionality


    This is sorely missed especially by people migrating from Outlook Express and Outlook where there is a "Send and Receive" button.

    Better quick search

    The quick search box gets a small facelift. There is now a dropdown that allows you to specify the fields to perform the search on. Very handy when you want to search text in the message body especially - no need to open up a separate dialog box.

    Screenshot of quick search box drop down


    Wrapping up

    Thunderbird 0.8 will also come with the usual bugfixes as well as Mozilla Suite Profile Migration and improved spell checker support. This is gearing up to be an excellent email client as more bugs get ironed out. I've switched. Have you?

    Get Thunderbird

  • IE development team has a blog!

    IEBlog is the official 'Microsoft Internet Explorer Weblog'.

    Some of us have our individual blogs today, but we also wanted to have one that was focused on what we do every day at work – make Internet Explorer the best way for browsing the web.

    Lookout Firefox!

  • An interim weblog report

    I was just thinking how it's been ages since I last did one of those monthly reports where I mention website statistics for this site - the last was December 2003. A look at the figures goes to show how little traffic and viewership I got (compared to, er, right now). 6 months ago I was getting 13,728 visits, or 442 visits daily. I'd got 65K visits last month and it's at 60K for the first 20 days of this month - almost 3K visits daily. Nothing to shout at but it is a significant increase (for a nobody like me especially). I'll give a shout when I get the holy PageRank 7 (which just about seems the 'cap' for conventional blogs). Right.

    Anyway, what has me most pleased is the number of subscribers to my RSS feed at Bloglines. 224 total subscribers is about a little more than I ever expected possible out of this. And Technorati says there are 256 Links from 196 Sources pointing to this site. Chief among them is kottke.org - even getting listed on his linkblog gave me 1000+ visits. Amazing! And I started noticing the spike in traffic in June, when I wrote gExodus and also quite a few posts on Firefox-related matters which were well-linked.

    Anyway, I've just begun to notice that the new ad at that I'd approved is screwing up the website layout and I think I've begun to forget the point of this post, so I'll stop now with the chest-beating. My CSS has always been unsightly and there is a case of slight divitis in the sidebar too - I've been too lazy to do anything though and probably still am.

  • MT-Blacklist Updater version 0.3

    Ed Morris wrote me earlier today regarding an insidious bug in MT-Blacklist Updater (well, not so much an insidious bug as a logical error on my part). It all started when Ed discovered an annoying (but non-critical) bug:

    I noticed that if there is an addition for a site in the changes RSS and then a deletion for that same site in the same changes file then it adds the site to the blacklist and then removes it every time the updater script is run.

    When he tried to write a patch for this bug, Ed discovered a more major bug:

    I realised that if a site were deleted and then re-added to the blacklist in the same changes file, the site would never get re-added locally (well it would briefly), as all deletions are carried out after additions in the script.

    Notice how I like to quote him instead of paraphrasing - a sure sign of... something. I don't know what. Anyway, he fixed the entire damn thing and sent the patch over to me in the same email. All I had to do was to copy over the fixes (don't worry Ed, your PHP's great).

    So, if you have an existing installation of MT-Blacklist Updater, update it to version 0.3 if you don't want it to miss any re-added blacklist entries (only those that appear at the same time in the MT-Blacklist changes feed - see Ed's explanation above).

  • Gotta love those PHP error messages

    Coding Object Oriented PHP sure has its perks, one of which is the mildly amusing error message I chanced upon below (in an implementation session that turned out to be a major debugging frenzy).

    parse error, unexpected T_PAAMAYIM_NEKUDOTAYIM


    A quick search reveals the cryptic error error message to mean 'a pair of colons' in Hebrew. You get that when you leave in an extra '$' when addressing a class method, such as:

    $SomeObject::someMethod();

    And no class-wide static variables in pre-version 5 PHP? Does not compute...

  • Can you say "Best Programming of a Weblog"?

    Dunstan Orchard has released Version 2 of his weblog and it is good. I mean really good (good enough for me to write a whole blog entry on it). Read more about the amazing plumbing job he did.

    Dunstan didn't win the 2004 Bloggies (too bad there aren't any id hooks for me to link directly to the "best programming of a weblog site" section), but he almost surely will win the 2005 Bloggies at this rate.

  • Show saved passwords option in Firefox

    Checked into yesterday's (2004-07-17) aviary (Firefox 1.0) branch: an option to show/display saved passwords.

    To check it out, grab yesterday's (2004-07-17) nightly branch build, go to Tools -> Options -> Privacy, and select the Saved Passwords option.

    Screenshot of saved passwords option


    Click on View Saved Passwords to see the Password Manager and you'll see a Show Passwords button at the bottom right. That's new.

    Screenshot of Password Manager


    You get a confirmation dialog (supposedly because showing your passwords is an important event). I think the intended use of the confirmation dialog is to save you from inadvertently showing your passwords to someone looking over your shoulder.

    Screenshot of confirmation dialog when clicking Show Passwords


    And your passwords are then shown in the plain text glory (mine are "pinked out" in the screenshot below, of course).

    Screenshot of Password Manager with passwords showing


    Looking at my list of passwords, I am beginning to think I'm not a very secure person (identical passwords for different accounts, using the same passwords for long periods, and passwords shorter than 8 characters).

  • Favicons and bookmarks now work properly in Firefox

    Checked into the Firefox aviary (codename for Firefox 1.0) branch: fixes for bugs 174265, 173762, and 228862. All bugs with favicons and bookmarks: "Wrong favicons in bookmarks", "Favicons are forgotten after shutdown", and "Store icons in bookmarks.html" respectively. All fixed by Vladimir Vukicevic with some clever JavaScript that fetches the favicons from the URL of each bookmark.

    firefox-favicons-bookmarks.png


    Pretty!

  • Split a webpage into 2 with this bookmarklet

    Cedric asked for Document-splitting in Web browsers and promptly got it (read the comment by Jeff Mesnil in Cedric's blog entry).

    Jeff Mesnil whipped up 2 bookmarklets in the space of less than one and a half hours that allows you to split a browser into 2 frames. How does it do that? It loads the current URL in a frameset consisting of 2 frames (both showing the same URL). A simple, yet genius, idea.

    Here's a screenshot of a blog entry split vertically:

    Screenshot of webbrowser split vertically

    Notice how it could be useful when cross-referencing something on the same webpage, particularly if it's a long vertical-scrolling page. A perfect use case would be referring back to a long blog entry when making a comment.

    Here's the same page split horizontally.

    Screenshot of webbrowser split horizontally

    There are probably lots of other use cases for a webpage splitter. Cedric's in particular is this:

    Just recently, I was reading an article that showed a figure at the top and which constantly referred to this figure in the rest of the article.

    Thanks Jeff for writing the bookmarklets and Cedric for thinking of it.

  • admin=false

    TheDailyWTF is great. If you develop or have developed web applications before, you will appreciate this entry: Admin=False.

  • Can't ditch IE because of Google PageRank? Now you can

    With the Google Pagerank extension! This is a Firefox 0.9.x compatible extension that adds a Google PageRank widget to the statusbar.

    it acts exactly as the real googlebar on IE: calculate a checksum, send a request to google.com (with the googlebar User-Agent string), and the reply contains the pagerank.

    Screenshot of Google PageRank extension in action


    Grab it from installation section or the author's website. (Note: Firefox should prompt and say something to the effect that the installation has been blocked. You can trust this extension, so click on the "Edit Settings" button that appears and allow the installation.)

    It updates when you switch tabs (it could take a while depending on your Internet connection), in case you were going to ask.

    Screenshot of Google PageRank extension in the extension manager


  • PHP 5 released!

    Freshly-baked newsbit: PHP 5.0.0 has just been released not too long ago. How long will it take for PHP 5 to become mainstream in webservers around the world? It's anybody's guess.

  • Google-like search engine for BitTorrent files

    bitoogle - the BitTorrent file search engine.

    Screenshot of bitoogle main page


    It even allows you to see the torrent statistics like the number of seeds and leeches. And it works!

    Hmm... I wonder how it works.

  • Seen elsewhere: New features since Firefox 0.9

    Once again, Neil Turner has the low-down on the new features in the next incarnation of Mozilla Firefox.

    Read More on new Firefox features at his weblog.

  • Better UI for blocked popups in Firefox

    Another new feature (available in nightly builds from today): a XP SP2-like yellow bar appears at the top of your Firefox window's viewport notifying you of blocked popups. Click on it and it gives you similar options on whether to allow the popup(s).

    Here's how it looks in Firefox:

    Screenshot of Firefox blocked popup UI


    And this is how it looks like in IE with XP SP2 (Release Candidate 2) installed:

    Screenshot of XP SP2 blocked popup UI in IE


    Screenshot of XP SP2 blocked popup UI in IE with context menu


  • Half Life 2 system requirements revealed!

    Via Eurogamer via Neowin.net:

    But for those who want to see the game running in all its glory, be prepared to spec up to around a 2.4 GHz machine, with 512MB RAM, Windows 2000/XP and a DirectX 9 capable graphics card.

    2.4 GHz machine - check
    512MB RAM - check
    Windows 2000/XP - Duh!
    DirectX 9 capable graphics card - check

    Looks like I don't have to upgrade (again) for Half Life 2.

    Man, could you believe I actually posted a blog entry on the specifications of a computer game? But then again, we're talking about Half Life 2 here guys.

  • Online promotion of software products

    Another call for help and advice. I can't really resist it because I've been seeing so much success in my appeals for assistance before.

    I'm looking for advice/articles/tutorials on online promotion, more specifically online promotion of software products.

    At the moment, I have these on my list of things to do:

    • Google Adwords
    • Submission to software listing sites
    • SEO

    What else is there and why?

  • RSS feed integration in Firefox

    Recently added to the Firefox feature list: Live Bookmarks (or Livemarks). Livemarks weren't initially intended to make it into Firefox 1.0, as Ben Goodger commented when he filed bug 244078. It seems like they will make it anyway, and have already appeared in nightly branch builds since 2004-07-08.

    Livemarks allow you to bookmark an RSS feed and these appear as bookmark folders, with individual items in the feed appearing as bookmarks. Just click on the "bookmark" and you will be taken to the page the item in the RSS feed is pointing to.

    Screenshot of Livemarks folder on Bookmarks toolbar


    A lightning bolt icon appears on sites which have the <link rel="alternate" type="application/rss+xml" title="RSS" href="http://www.example.com/index.xml" /> bit in the <head> tags. All you got to do is to click on the icon and choose from the syndication formats available.

    Screenshot of Livemarks icon


    There's even a "Refresh Livemark" context menu option to keep your Livemarks up to date.

    Screenshot of Livemarks Refresh context menu option


    Not anything that would take me away from the current RSS aggregator I use, but the integration with the bookmarking system is a very intuitive model that would fit into most people's conceptual model of RSS feeds and bookmarks. I mean, an RSS feed is as good as a bookmark you keep somewhere (only it feeds you live content). RSS feed -> bookmark folder, RSS feed item -> bookmark. Gels nicely for me.

    Get a nightly branch build if you're interested to see it in action for yourself.

  • "Switch to Firefox", better browser sites

    With the gaining popularity of Firefox and alternative browsers ("alternative" meaning non-IE), I've noticed a sudden increase in the number of sites advocating specific browsers. People are obsessed with the new browser wars (this time, it's a "good" war) and rightly so if your browser is the one application you use 90% of the time on your computer.

    www.switch2firefox.com/switch/ seems to be a popular one recently. Hao2lian also has one (and he's right - nobody knew about it, or at least I didn't!). www.betterbrowser.org recommends Firefox and Safari. There was also the old giantfightingrobots.com page which now appears to be defunct. Check out this Mozilla kicks ass! page too.

    Someone even bought the getfirefox.com domain as a shortcut URL for http://www.mozilla.org/products/firefox/.

    Anybody seen anymore of these?

  • Dell stops shipping Windows on all its computers

    On Newsforge:

    After being berated by Microsoft for attempting to sell Linux on some of the company's desktop systems, Dell has decided to stop selling Windows altogether.

    Gateway spokesman William Bradley said of Dell's move, "I'm glad to hear Dell will be selling less expensive computers without the Microsoft tax -- we'll finally be able to upgrade the computers around the office."

  • New Find Toolbar for Mozilla Firefox

    The Burning Edge reports on the new Find Toolbar for Mozilla Firefox that brings the much unknown Find As You Type feature to the fore, at least in terms of GUI presence. This feature has just been checked in into the branch builds of Firefox (and as such is a little buggy). To bring up the Find Toolbar, you need to press Ctrl-F or / (and then start typing).

    Screenshot of Find As You Type toolbar in action


    The "Find as you type" function is not new to Firefox and has been in there for a long time. You may not have realized it before. If you have a version (or build) of Firefox that you got hold of before today, you could just start typing the word (or words) that you want to find and Firefox will incrementally find and highlight the first instance of the word that matches what you type.

    Now you need to press Ctrl-F or / to bring up the Find As You Type functionality (and the new toolbar). Some people have already been screaming at inconvenience of the extra keystroke (or keystrokes if you use Ctrl-F) - I certainly don't like it because I'm what I'd like to call an entrenched user of Find As You Type. It's hard for me to adjust and seems painfully inconvenient. But the truth of it probably is that it's just 1 extra keystroke and should be something one can get used to. Especially if the benefit is increased visibility of the (amazing) Find As You Type feature (which, if I may add, was first seen on the Opera web browser) - if more people knew of this productivity feature, I'm pretty sure they'd stick around with Firefox. If it will get more people using Firefox, I'm ready to compromise (or if not, there'd probably be an extension in the future which disables the new Find Toolbar).

    Also new in the Find Toolbar is a Highlight button that highlights all the matching text on the webpage. No complaints against this one. Great stuff.

    Screenshot of Find As You Type toolbar Highlight function in action


  • Bloglines gets much cooler

    Bloglines turned 1 not too long ago and crikey(!) did they add a some cool new features to the plain old feed aggregator. The new look is also a refreshing change (though the old utilitarian look suited me just fine too).

    Sceen capture of new Bloglines look


    The Clip Blog feature is perhaps the very coolest. It's a convenient way to save "clippings" from blogs or sites you read in Bloglines.

    Screenshot of Bloglines Clip Board administration interface


    Bloglines explains what Clip Blogs are for:

    When you see something you want to save or share in your feeds, just one click posts it to your Bloglines Clip Blog.

    I have mine setup right here to play around with. The unfortunate thing is that there is no RSS feed to it (there is a "Subscribe" link, but it seems to be in a Bloglines-specific format). It works fine within Bloglines though, and that's the whole idea I guess.

    And finally, I can get Bloglines to sort my feeds. I have a whole lot of them (on the right sidebar) and they were sorted by the date I added them. Not the best arrangement but it's grown so big I just couldn't get down to organizing them into folders. Well, at least alphabetical sorting is slightly better. I'm not sure if this is new, but I noticed I can now check a box next to each feed and move them into folders.

    Screenshot of Bloglines feed organization


    And this citations search feature doesn't beat Technorati, but it keeps track of all citations to date (whereas in Technorati, old backlinks drop off the listings).

    Bloglines made Time Magazine's list of 50 coolest websites and it deserves to be there. If you use a RSS reader or feed aggregator of some sort, and are disatisfied with it in some way, give Bloglines a go.

  • My Firefox search article gets Slashdotted (by proxy)

    Forever Geek was looking for articles not a while back, and I thought, "what the heck" why don't I just ask Paul Scrivens (Paul is the main Forever Geek guy) if he'd like to publish my old original Outsearch with Firefox article. Even though he did say in his post that he didn't want articles that were published elsewhere before ;).

    Well, he did publish it and the Outsearch with Firefox article at Forever Geek was Slashdotted.

  • Novell gives away SUSE Linux 9.1 Pro and more

    Via Photo Matt.

    Novell is giving away the Linux Technical Resource Kit for free. It contains:

    - SUSE LINUX Standard Server 8.0 (ISO Installation Images)
    - SUSE LINUX 9.1 Professional (Bootable Installation DVD)
    - Ximian Desktop 2.0 Evaluation (ISO Image)
    - Ximian Red Carpet 2.0.2 Evaluation (ISO Image)
    - Novell Linux Services 1.0 (ISO Image & NLS Companion CD)
    - Novell GroupWise for Linux 6.5.1 - Server, Client & Messenger (ISO Images)
    - and more...

    We will contact you by e-mail as soon as your order ships. Delivery will be made via US Postal Service, first-class mail.

    Nice.

  • Looking for video editing blogs

    Help me out here. I'm looking for blogs focused on video editing, video editing software, or more generally, video-related blogs. Blogs on the topic of taking videos too.

    If you know if any of these, post a comment or drop me a line.

    Thanks!

  • Blogupdates gets a facelift

    Update: Oh no it seems that I was wrong about the timezone bug being fixed. This entry is still appearing at the top with with the time incorrectly stated (posted "5 hours 56 minutes ago" at this time).

    Blogupdates from Henrik Gemal is a Mozilla-related blog aggregator, and it has recently undergone a facelift and, more importantly for me, some reworking of the code. I say "more importantly for me" because for the past months (since my Mozilla category feed was added to Blogupdates), my entries were hogging top spot on Blogupdates because of a bug in the code that didn't properly take timezones into account. Embarassing because it would've appeard that I was a sad no-lifer who kept updating his blog entries to keep the top spot (of course I wasn't doing anything of this sort, though I am still a sad no-lifer). If anyone noticed this before, let it be known henceforth that my name is clear of guilt (or something like that, said in a self-righteous tone). Thanks for fixing this, Henrik!

    Anyway, if you're a Mozilla fan, Blogupdates is one of the must-see pages, perhaps even better than the MozillaZine Feedhouse because it notifies you which entries are new since you last visited. But it's really up to you to choose between them both (or you could read both), because there isn't a real difference between them both. Though Blogupdates did exist way before the Mozillazine Feedhouse did.

  • No time for gExodus - (Python) source released

    Ugh, I've been having less and less time to work on gExodus (version 0.2, version 0.1 - I really should put these in a consolidated area), mainly due to work and my need to relax once I get home (well, most times I just surf around, watch anime, and blearily gaze at the TV as it screens Euro 2004 matches nowadays).

    Adding support for Outlook Express (.DBX) and Outlook (.PST) email formats was something I thought would be in the offing for gExodus, but it seems that the format is just too proprietary to figure out. Arne Schloh has been working on decoding the .DBX format but only has achieved about 90% of the task. There are what seems to be very good C libraries, LibPST and LibDBX, but I don't really want to dive into using C libraries from Python - though if anyone is willing to shed some light, I'd be all ears. Python libraries for decoding the .DBX or .PST formats? Well, either I haven't looked hard enough or there really aren't any.

    So that's that, which basically means that I won't be looking at adding Outlook Express and Outlook support to gExodus barring a breakthrough. Sorry.

    Anyway, I thought it would be good to release the source for gExodus as I'd promised - just for the unlikely event that someone wants to extend what little I've done. So here's the Python source files for gExodus (0.2):

    gexodus-0.2-src.zip

    To run gExodus from source, you need to have installed Python, wxPython and PythonCard. Gordon McMillan's Installer is needed to build the Windows executable. What is written here should help if you really want to try to work on this. Either way, do feel free to drop a line via my contact form.

  • Post in forums a lot? BBCode is for you

    Came across another neat Firefox extension: BBCode. BBCode adds a list of menu choices to the right-click context menu, allowing you to quickly add BBCode (explained in the phpBB BBCode guide) to your posts (actually, the BBCode context menu appears in all textual input fields).

    One of the simplest of things it does is allow you to quickly bold, italicize, or format selected text. Just select "Bold" (or whatever formatting option you wish to use) from the context menu (right-click):

    Screenshot of BBCode bold option


    Bam! Your selected text is formatted:

    Screenshot of BBCode after using bold option


    Changing the color of the text also works as in most places, popping up a JavaScript dialog:

    Screenshot of BBCode after using color option


    Screenshot of BBCode during use of color option


    But what's a real keeper, is what's found under the "Clipboard" sub-menu. With this, you can copy a piece of text you want to quote, or a URL to an image/webpage, and then have it formatted as quoted text, or as an URL in BBCode. If you post in BBCode, you'll know how much of a hassle this can sometimes grow to be.

    Screenshot of BBCode make selection URL option


    With "http://blog.codefront.net/" in my clipboard, BBCode formats it nicely so that I don't have to.

    Screenshot of BBCode make selection URL result


    Excellent work by Jed Brown, who also recently wrote GMailCompose, which is another context menu extension that allows you to compose a reply in GMail when clicking on mailto: links.

  • Mozilla Foundation to lead open plugin initiative

    The Mozilla Foundation just announced a new open browser plugin API effort that will also consist of players like Apple, Macromedia, Opera and Sun Microsystems. The plugin API will be based off Netscape Plugin Application Program Interface (NPAPI), which is what Mozilla browsers (including Firefox) use at the moment, and will be released under an open source license. Notice how Microsoft isn't (yet?) involved - I wonder if they'd consider this a threat (which I hope not, because it's a very smart initiative that saves everyone from doing double work trying to make their plugins cross-platform and cross-browser).

    Don't confuse these plugins with extensions though - these plugins are things like the Flash plugin that allows you to play Flash and Shockwave files in your browser, and the Java plugin that lets you load applets. Maybe we'll finally have a Java plugin that isn't so iffy.

    Check out the Mozilla Plugins project if you're interested in the technical details.

  • Critique my employer's website, please

    A little background: I work for muvee Technologies as a web developer. Much of what you see there isn't my work though, since I'm only 3 weeks old in the company.

    I'm coming up with a list of things to improve with regards to the website, in terms of making it persuasive and consequently generating more sales. Of course, things like layout, accessibility, design and look and feel are important as well, but secondary for this evaluation. Truth be told, this is something my boss asked me to look at, because I've a pair of fresh eyes undeadened by years of working on the same old (same old) pages.

    Nope, I'm not going to be try and get away with hoping that everyone here will generate enough ideas for me to present to my boss. I have my own list and I'm coming up with more stuff (gradually), but I thought that while I had your attention, I might as well see what other minds can come up with and if they are so discordant from mine.

    So there, if you could help me evaluate and recommend things that can be changed/improved, I'd be grateful to you, dear mister or missus reader. Focus on how the site could be more persuasive to prospective buyers, or conducive to buyers (transactors). Recommendations on design, layout, accessibility are also welcome, but secondary (I expect plenty of these, nevertheless).

    I don't have Gmail invites now, but I promise you I'll send one over if you help me out. Those already with Gmail will instead receive my gratitude (really - a favor is worth something nowadays, right?)

  • Top 10 referrers

    In the spirit of http://weblog.wlkr.net/archives/000182.html:

    Referrer Visits
    http://gemal.dk/mozilla/blogupdates.html 983
    http://feedhouse.mozillazine.org 812
    http://www.javablogs.com/Welcome.action 569
    http://forums.anandtech.com/messageview.cfm 440
    http://www.neilturner.me.uk/2004/Jun/09/firefox_09_tour.html 414
    http://www.bloglines.com/myblogs_display 294
    http://www.thauvin.net/linkblog/ 257
    http://gemal.dk/mozilla/blogupdates-beta.html 229
    http://animetopia.com/search.php 215
    http://www.sitepoint.com/forums/showthread.php 180

    And yes, a table is entirely appropriate.

  • FoxAmp Firefox extension integrates Winamp into browser

    More and more cool Mozilla Firefox extensions seem to be popping up nowadays. FoxAmp is a Firefox extension that integrates (to an extent) Winamp into the Firefox browser. It adds several buttons into your statusbar, allowing you to play, stop, pause, and change tracks.

    Screenshot of FoxAmp


    There isn't any indication of which track is being played (umm, beyond listening and recognizing it with your sense of hearing, that is), which I got around with the Lounge skin that I'm currently using (it displays a small window for a short while indicating the track name when a new track is played).

    Still, a very cool tool added to my list of favorite extensions.

  • Danes Czeched-out

    Czech Republic 3 - 0 Denmark

    I wish I'd bothered to look for a betting station earlier today - the odds were 1.78 in favor of a Czech win. The Czechs really booked their place in the semis in style, beating the Danes by the most solid margin of all the semifinalists.

    And what a sweet chip from Milan Baros.

  • Gmail Notifier extension for Firefox

    Doron has written a Gmail Notifier extension for Firefox. It adds a button to your toolbar that displays the number of unread emails you have in your Gmail account. Like how I have 0 new emails (I'm a lonely, lonely guy) in the screenshot below.

    Gmail Notifier in action


    Neat work, and this is still early stages for Gmail Notifier. Doron has promised Gmail/Mozilla integration and this (Gmail Notifier) seems to be part of that exciting project. Check back with Doron for more later - this should lead to something really neat.

  • Sending to [email protected] labels your email

    Danyel Fisher pointed out this cool Gmail tip.

    Note that you can get around the label issue by forwarding messages to “myname+label”, and then searching for “+label”.

    (Gmail allows you unlimited “+” addresses. For example, all my mail that’s forwarded from an external account goes to [email protected])

    Aforementioned "label issue" being the issue with gExodus 0.2's new label feature which prefixes a user-specified string to the subject of each forwarded email. If I'd known that earlier... Nah it was good practice. It actually forced me to figure out how to modify an existing email's headers.

  • Gmail invites - so hard to give away

    When I last gave out a Gmail invite, I had to squeeze out 1 for my dear blog readers (actually, when I got more invites, I gave another one to Joshua too). Now it seems that even 2 invites are so hard to give away.

    It seems that the Gmail craze is finally dying down. Well, at least in the blog circles. Gmail's on almost every blog and almost every blogger has one (either through an account at Blogger or through viral means). Colleagues at work have Gmail. Friends don't see the need for Gmail. I'm actually having a hard time thinking of people who could use an invite. So from now on I'm giving away invites on my blog to readers. I'm not giving them to just anyone who searched for "Gmail invite" on Google and popped over. I happen to be listed on the first page of search results on Google for "Gmail invite" at this moment, and that has surpassed my usual high-ranking search terms of "bittornado", "uxtheme.dll", and "hotmail thunderbird" within 3 days - 1466 visitors via that search phrase.

    I was looking down the list of people who posted in response to my entry asking my readers to show themselves, looking for people to give away the invites to, but I wasn't sure who wanted them. If you posted before and want an invite, let me know.

  • Firefox 0.9.1 coming

    Ben Goodger has announced that Firefox 0.9.1 will be out soon. This will be a bugfix point release, mostly fixing Extension Manager bugs. Winstripe (the default theme) will also be updated.

  • gExodus 0.2 - some new features for Gmail mbox import tool

    I've updated gExodus (previous version 0.1) with 2 new features (straight from the changelog):

    - Added ability to set a label to prefix to the Subject of forwarded emails.
    - Added "Pause/Continue" feature.

    The "label" idea was suggested by Rory Parle (great thinking!), and it should be particularly useful for identifying which imported email messages came from which mbox. For example, if you set a label of "[yahoo-personal]" when importing your Yahoo personal email account into Gmail with gExodus, each of the emails will have "[yahoo-personal]" prefixed to their subject field. This means that an email that had the subject of "Hey dude, I heard Futurama's coming back!" becomes "[yahoo-personal] Hey dude, I heard Futurama's coming back!". It annoyed me that the imported mails cluttered up my Gmail inbox and I don’t know which ones are imported, and which are “live” emails that I’ve just received. This also now allows me to setup a filter to filter all imported mail to be tagged with a particular label.

    The "Pause/continue" feature allows you to pause the importing of messages midway through an import. Useful in situations where you have a large mailbox with lots of messages. It should rightly work even after you've disconnected (and reconnected) your Internet connection.

    Still having several issues with multithreading, so you'll notice 1 last email being imported after you pause or abort gExodus. This is because if the email forwarding thread is paused (or killed) while it is sending out an email, it will follow through with it before actually pausing (or dying). It's not a particularly catastrophic issue, though far from acceptable.

    Download gExodus 0.2

    Feedback and suggestions welcome as always. This is what's on my TODO list:
    - Pre-select where the mbox file is most likely to reside on a harddisk
    - Delay field (for specifying the delay between sending one message and the next)
    - Interactive importing ("Yes/No/Yes to all")
    - Importing of maildir format
    - Recurse subdirectories and import all the mailboxes in each directory

  • Weblog categorized using Dewey Decimal System

    Lisa Williams of Learning The Lessons of Nixon has her weblog entries categorized using the Dewey Decimal System! Nice!

    I'd do the same too, only I think most of my categories would belong under the big umbrellas of Technology and Computers.

  • Gmail invite for loyal reader

    Update: I've invited Anthony Eden - Anthony used to offer FreeRoller (a site that offers free blog hosting) for free, but has since moved on and FreeRoller is now JRoller (still free). I used to have a FreeRoller account myself.

    Note: If you haven't been a reader for awhile, I'll know, so don't try and fake it.

    I'm giving out 1 Gmail invite to a reader who has been with me for awhile. If you could show yourselves, by commenting and telling me a bit about yourself, I'll send you an invite. No, I'm not whoring out my invites (at least I don't think so) - I just thought this would help me to get to know my readers a bit better. redemption in a blog has been steadily growing with about 2,000 visits a day, but I'm not seeing many of my readers commenting. I sometimes wonder if anybody actually reads what I write or there's some bot out there periodically requesting my webpages at an approximately linearly increasing rate.

    I'm not giving it out based on who posts first, but rather genuine readers (I'd have make educated guesses, unless I know of you from the "before time").

    Initially I'd wanted to give out 2 invites, but that has quickly whittled away to just 1. Oh well, I'll get more invites soon I guess.

  • The secret behind getting more Gmail invites

    Update: Well, it seems I was wrong about this. Google seems to give out invites quite randomly. Read the comments for more info.

    OK so it'd no longer be a secret now that I've let it out into the wild. Anyway, the hypothesis goes somewhat like this: the number of Gmail invites you get (to send out) is positively correlated to your usage of Gmail. "Usage of Gmail" is hard to define, but I reckon it'd include the number of emails you send/receive, and possibly the amount of storage you use (out of the 1GB).

    Why do I say so? Um, if you've been following my inane writings on this weblog, you'd probably noticed gExodus, a Python hack job that I churned out yesterday that imports emails (in mbox format) into Gmail. It does this by forwarding your existing mbox - i.e. it parses the mbox file and sends each email via SMTP to your Gmail account. This registers as received mail in your Gmail account.

    In my testing of gExodus, I probably forwarded around a thousand emails. I just logged into my Gmail account and saw the red "Invite a friend to join Gmail!" link...

    Screenshot of Gmail "Invite a friend" link


    And I had 6 invitations (I'm left with 4 now). People don't normally get 6 invites in 6 days' time. But 1000+ (received) emails in 1 day, 6 invites the next - all that's left is to connect the dots.

  • gExodus - a graphical Gmail import tool

    Update: gExodus 0.2 is out.

    When I saw (and used) Mark Lyon's Mbox & Maildir to Gmail Loader (GML) (I wrote about it), I was inspired to write a GUI version of it (I didn't copy Mark's code, just in case, though it works on the same basis). gExodus is what came out at the end of half a day's work.

    gExodus main screen screenshot


    gExodus allows you to import your emails (in mbox format) into your Gmail account. The mbox format is the classic Unix-style mailbox format, and is used by Mozilla Mail/News, Mozilla Thunderbird, qmail, and many other Unix email applications. There currently isn't support for qmail's maildir format, which is a much better format in terms of reliability and efficiency (because there isn't a need to lock a single file, as it is for the mbox format). There doesn't seem to be any Windows email application that supports maildir, so I've left it out. Someone has also posted a feature request for maildir support in Mail/News (and consequently Mozilla Thunderbird), so vote for the bug! I'll add support for for maildir and possibly the MH mailbox formats if someone requests for it.

    Users of email clients which don't use the mbox format (such as Outlook Express and Outlook) can convert their emails into the mbox format by importing them into Mozilla Mail/News or Mozilla Thunderbird. I'll add support for Outlook Express (.DBX) and Outlook (.PST) email formats in the near future (there are many converters already available, in fact).

    gExodus is written in Python, and uses PythonCard (which is based on wxPython toolkit). PythonCard is a simpler wxPython, and is amazingly simple to use. With a WYSIWYG resource editor which allows you to place widgets and edit their properties, as well as very simple syntax rules and event handling concepts, PythonCard is Visual Studio with a better grounded language (Python). Granted, there are some inadequacies, one of the most glaring being the lack of a formal tab order (I got around that by moving widgets to the front - the nearer one is to the front, the higher it's tab order). Still, I'm glad I picked PythonCard for my graphical toolkit - I haven't used any before and was considering my options at this Python GUI toolkit "comparison" page.

    gExodus is a standalone program - Gordon McMillan's Installer was used to convert the Python scripts into executable form. The article Building 'standalone' PythonCard Applications came in very handy, especially when I didn't manage to get py2exe to work (the executable didn't work properly). The documentation for using McMillan's Installer with PythonCard didn't seem to work also. Due to dependencies on the wxPython and PythonCard libraries, the resulting executable package is pretty hefty (approx. 4MB) - 2.4MB of that is a wxPython DLL.

    The source code is currently unreleased (I'll put it out in the near future). Listed below are the binaries.

    Update: gExodus 0.2 is out. You may want to get that instead of version 0.1 below.

    Download gExodus:

    Feedback is greatly appreciated. I haven't really tested it on other machines (just my own Pentium 4 desktop running Windows XP), so bugs are expected (please let me know of them).

  • Czech-ed in

    Exhilarating match, Holland vs. Czech Republic. I'm (openly) rooting for the Czechs to win Euro 2004, so it was nice to see the Czechs come back 2 times from behind and winning it with a sweet tap-in. They played very well at that, and Milan Baros scoring was just sweet.

    Now, onto the quarter-finals!

  • Very nice app to import your emails to Gmail

    Mark Lyon has written Mbox & Maildir to Gmail Loader, or GML, which transfers emails in an mbox file into Gmail accounts. Mbox is the format Mozilla Mail and Mozilla Thunderbird uses, so if you use something like Outlook or Outlook Express, you'd have to export your emails into Mozilla Mail or Thunderbird first.

    GML screenshot


    Works nice enough, though it had some problems with emails containing attachments - those didn't import properly and Gmail couldn't parse the email, so were as good as lost. It is still good stuff because I can now archive my existing email accounts in Gmail (I do have 1 gig of space).

    Of course, GML works by forwarding the emails from your mbox file to your Gmail account, so it neccessarily becomes a possible tool for spammers to bomb your Gmail account. Not that there aren't generic email spambots in existence already.

    Other similar applications include Pop Goes the Gmail and gmail.py. GTray also seems useful, but I can't get the website to load at the moment.

  • [email protected] for sale

    Message sent to the orkut MovableType community:

    hi
    [email protected] For Sell (sic).

    If you want this ID in Gmail , Plz email to me . [email protected]

    Ugh. This Gmail name-squatting is getting ugly.

  • Thanks for the Gmail invite

    Well, a reader was kind enough to offer me a Gmail invite gratuitously:

    Want a gmail invite? I think I’ve got one left and I’d much rather give it to someone who cares about web standards (yay) than compromise what I think is my moral integrity by whoring out something I got for free in the first place.

    You can't deny there is some truth in the bit about "whoring out" Gmail invites that seem to have been happening of late heh (heard of Gmail Swap?). Well, not everyone is whoring out their invites - some of them go to a good cause (like donations to charity or non-profit organizations).

    Anyway, I'd like to thank Luke for his kind gesture. Luke wrote about his Gmail invites as such:

    I've got 2 invites left and although free stuff (or auctioning it off for cash through ebay) is tempting, it doesn't sit right with me morally, ethically, or somethin'.

    Nice.

    Luke keeps a blog at http://blog.lukema.net/ and also an anime blog with mini-reviews of anime like Naruto and Full Metal Alchemist. Visit his website sometime.

  • A for-loop? For whom?

    The Daily WTF has a very funny reader-submitted entry on for-loops (or rather, the lack of them).

    Good stuff on The Daily WTF, which has near-daily posts on programming-related things that make you say "WTF?!?". Recommended reading.

    Alex Papadimoulis, the person behind The Daily WTF, is currently giving out Gmail invites for submissions to The Daily WTF.

  • Table names and case sensitivity - bad MySQL

    This issue has wasted a good part of a day trying to figure out why my database-driven pages failed to work correctly after dumping a database from my Windows dev machine into a Linux box - I just had to whine about this.

    Following database naming conventions (actually, there are many, not all of which advocate using mixed case naming schemes), I decided to use mixed case table names when developing on my Windows box. So a table containing localization (or L10n) information for widgets would be named `Widget_L10n`, and the lookup table for widget to purchases would be named `Widget_Purchases`. No problem. Everything worked fine on the dev machine.

    When the time came to move into live testing, I dumped the contents of the database into a file and proceeded to import into the production database server. Of course, nothing worked. I checked the database contents - everything was there. I checked the scripts - everything was good to go; if it works in the dev machine, it should rightly work in the production. I then checked the dump file, thinking it unlikely that something was wrong there but checking anyway - everything seemed fine. I went back to the MySQL client (I was using MySQL Front) and it hit me - the table names were in lowercase. And the PHP scripts were (correctly) using the mixed-case table names. Gah! The database dump file contained queries with the table names in lowercase, so they were lowercase in the Linux box while I was using queries with mixed-case names. At that point I hated myself for forgetting how MySQL stores table names - as a name of the file that stores the table. The manual said so and I even read that before.

    Lesson learnt: use all lowercase identifier names in MySQL for portability across Windows and operating/file systems where lettercase matters (such as Linux and Mac OX X UFS volumes). Throw the existing database naming conventions you have out the door.

  • Mozilla Thunderbird 0.7 freshly roasted

    Mozilla Thunderbird 0.7 has just been released. Go get it (or go to the Thunderbird 0.7 release FTP directory for the zip build).

    New and noteworthy is the inclusion of the extension and theme managers (derived from that in Firefox). This is also a static build so performance has significantly improved.

    Update: just saw this Promoting Mozilla Thunderbird page pointed to on one of the TrackBack pings I received. Nice buttons.

  • Mozilla Firefox 0.9 (One Tree Hill) is out

    Mozilla Firefox 0.9 (code-named "One Tree Hill") has been released! Grab it from the main Firefox page, or from the mozilla.org Firefox FTP directory itself (where you can find the zip version, if you don't like the installer). You can also get the torrent.

    Noteworthy in this release are:

    • a new default theme, Winstripe (screenshot courtesy of Neil Turner), that replaces the old Qute theme (a subject of much acrimony, as well as dislike and praise alike)
    • extension manager
    • theme manager
    • update notification for Firefox, extensions and themes
    • a far more complete browser migration tool that imports everything from bookmarks to cookies and autocomplete data from other browsers
    • a smaller file size (for the installer version only) of 4.7MB thanks to 7-zip compression
    • ability to delete individual autocomplete results (using Shift-Delete)

    A Mozilla Update site has also been launched and lists extensions and themes that implement the new extension and theme API for 0.9. This will be the "go to" site for extensions and themes in the future, being the "official" extension and theme website.

    Firefox 0.9 is a feature-complete release, meaning that the next release, 1.0, will not contain any new major features. Bug-fixing is the key activity between 0.9 and 1.0, as evident in the Mozilla Firefox Roadmap. Interestingly, release 1.0 is code-named "Phoenix" - and so it shall be reborn.

  • Who cares about Web standards?

    The Web Standards Project asks of anyone and everyone "involved in web production in any capacity" to take a survey on Web Standards and it's relevance to you in a recent press release, Web Standards: Who Cares Anyway?.

    Most salient question in the survey: What roadblocks or challenges have affected your use of Web standards?

    "All of the above" applies to my position to a certain extent, but the biggest challenges I experience in my capacity of as a web developer for muvee Technologies (yes, I just got employed, 2 weeks ago) relate to that of lack of team and management knowledge and support. The current webpages are coded in old school tables, using Dreamweaver. Everyone else on the web team uses Dreamweaver. There's absolutely nothing wrong with that, because a WYSIWYG editor is the best for productivity. And I take nothing away from the frontend designer and media designer - they are a class act when it comes to design.

    I'm probably the oddity in the team, the only guy using a text editor to crunch out PHP scripts using the HTML webpages that are done by the creative department. And I am the only one to see invalid HTML, unclosed tags, and spacer images that Dreamweaver generates. Does it matter to the designers? Not one bit because they don't see it and the pages still turn out find in the important browsers. Does it matter to me? Yes, it is annoying because I've been coding to Web Standards wherever I can, but it is something I can and should live with. There are honestly far more important things to do, as I have been reminded, than cleaning the HTML towards Web Standards-production code. I can't argue with that. The website works (well, it doesn't work in Opera, but fixing it is going to be a touchy subject I'll bring up after I'm more settled into my position), so what's the point of wasting time making changes that don't produce any tangible benefit (to them). I can't argue with that too, yet.

    But when the time comes when there aren't more important things to do, I will be faced with an urge to propose a shift towards Web Standards. How will that turn out? One word - badly. Why? Because the designers are not going to like it. They (nor the company, I'd guess) would not be willing to toss away what they've used for years to produce their webpages. Unless Dreamweaver starts becoming a far better webpage editor in terms of the code it generates, I don't think they'd like looking at raw HTML code (and I don't blame them either). I'd probably become public enemy number one in the web team if I start proposing these changes, even if I suggest to undertake the conversion to Web Standards all by myself. I don't have a case, yet.

    Anyway, take the survey, let WaSP know what's happening in the real world, and also let me know if and how you've managed to convince your department/company to switch to Web Standards. Please do share your experiences, especially those that end in sucess, because that's where I believe everyone has lots to learn from.

  • Gmail invites for sale everywhere

    OK maybe not everywhere, but seeing threads offering Gmail invitations at a price over at the Trading Post at SitePoint Forums is new (besides the old news on Gmail invites put up for auction at eBay and also gmail swap).

    Someone is even offering a Gmail invite for logo design. Look at these threads all offering Gmail for cash or kind - 1, 2, 3, 4.

    And no, I don't have a Gmail account so don't ask me for one. If you want a 2GB email account, try AventureMail.

  • Seen elsewhere - Free eBook "Budget Design"

    Didier Hilhorst and Dan Rubin of SuperfluousBanter fame have written and released for free (for 7 days) Budget Design: Increase Profit by Improving Process. After 7 days, it'll set you back $9.

    All web developers should take a look at it (even if your budget is larger than the US trade deficit), because it's quality stuff from 2 quality web developers (and now, writers).

  • Seen elsewhere - Firefox 0.9 Tour

    Neil Turner has a comprehensive article that takes you through what's new in Firefox 0.9.

    I was going to do the exact same thing when the full 0.9 release arrives, but Neil has done an excellent job with his article and I just may forego that and just point you to his article instead (which is what I'm doing now).

    Neil also has posts frequently on Mozilla to his blog. Check out his Mozilla category archives.

  • Firefox profile directory to change again

    The profile directory for Firefox will be changed (for the last time) to Mozilla/Firefox/ (from Firefox/), as Ben Goodger announced at the MozillaZine forums. This follows up on the last profile directory change from Phoenix/ to Firefox/. This also seems to mean that the profile directory for the Mozilla suite will go into Mozilla/ while having a Firefox/ directory under that (i.e. Mozilla/Firefox/) for Firefox - a little weird.

    As usual, beware of inadvertently losing your profile if you use Mozilla Backup - be sure the check that it takes into account the directory change when migrating across nightly builds.

    Users of the Firefox 0.8 release, don't worry, because I'm sure Ben and his team will have a neat, transparent migration script for your profiles when Firefox 0.9 comes out. You probably wouldn't have to do a thing.

  • Firefox 0.9 could be out in a week

    Ben Goodger (Firefox engineering lead) has announced mozilla.org is targeting a 12th June or 14th June release date for Firefox 0.9. The Extension Manager will be fully enabled in this build, as will the new update mechanism for themes (and extensions, of course).

    When it's released, be prepared to see mozilla.org go down again (as was the case with the 0.8 release of Firefox) thanks to the Slashdot effect. Hoard your extensions and themes in advance (well, at least those extensions which are not yet 0.9-compatible). Remember that BitTorrent exists if the Mozilla.org servers go down. You have been forewarned.

  • Comment spam masquerading as a TrackBack ping

    I've been getting hundreds of comment spam but this particular one was different because it was meant to appear like a TrackBack ping at a cursory glance.

    A new comment has been posted on your blog redemption in a blog, on entry
    #156 (How to really kill Windows Messenger).
    http://blog.codefront.net/archives/2003/11/21/ how_to_really_kill_windows_messenger.php

    IP Address: 218.145.25.80
    Name: *****
    Email Address: [email protected]
    URL: http://www.*******.com

    Comments:

    Read on for the skinny... redemption in a blog: How to really kill Windows Messenger...

    And some of the things in those spam comments are just plain sick. You guys are sick, you hear that?

  • Your own private CVS repository

    To think I'd tried to set upon the path of setting up a CVS server a few weeks back to allow me and several friends sharing a dedicated server to have our own private CVS repositories. The approach I took was to setup the server for password authentication (pserver). Naturally, I failed to get it running (had some issues with xinetd), and had given up since. Little thought did I give to alternative ways of setting up a CVS repository, and probably the most common sense way, using RSH - specifically, SSH, to connect. Well, that worked, and it was amazingly simple to do too.

    I'm going to assume you have a shell account to your webserver accessible via SSH. Also, your server should have the cvs client installed - you can test it by logging into your shell account and typing 'cvs -v', whereupon it should print the CVS version and the usual blurb.

    Now, my server is codefront.net and I have a user account chuyeow. First, I'd have to create a repository on my server. I do that by logging in to my shell account (using SSH) and creating a directory where I'd have read-write access. The simplest place is to create a directory in your home directory (mine's /home/chuyeow).

    $ mkdir /home/chuyeow/cvsroot

    Create the CVS repository:

    $ cvs -d /home/chuyeow/cvsroot init

    There I'm done! I now have a CVS repository that's accessible from any location so long as I have a SSH client and a CVS client.

    To access my repository, I'd first have to set my CVSROOT to :ext:[email protected]:/home/chuyeow/cvsroot, and the CVS_RSH environment variable to "ssh". I do that by entering these lines below into my ~/.bashrc file.

    export CVSROOT=:ext:[email protected]:/home/chuyeow/cvsroot
    export CVS_RSH=ssh

    Now, all that's left to do is to import my existing sources into the repository:

    $ cvs import -m "Imported sources" sources_dir vendor start

    That's it! You can checkout a copy and then do CVS checkins, merges, etc. as you normally would from now on.

  • Singaporeans like their spam

    A recently conducted poll at The Straits Times Interactive (online version of local leading newspaper) shows 71% (at time of writing) of the poll takers preferring an opt-out scheme "because [they] don't mind knowing all that's out there." And spam? "It's useful information to me", at least for 68% of the sample size. Granted, the sample size is only around 1000+ (I got that from the print version of the poll - apparently it's not available in the online version), so it's hardly accurate, but the results still leave some niggling doubts as to whether Singaporeans are really that kiasu (colloqualism for "scared to lose") that they believe something worthwhile could be gotten from spam. Or perhaps the poll was rigged. I don't know Singaporeans anymore.

  • Mozilla recommended at Singapore's new antispam move

    So Singapore has taken a significant step against spam. I'm not going to comment on the antispam move in this entry (if at all), but I thought it interesting to check out the "free trial of anti-spam software" as reported in the article and found something interesting. The Singapore AntiSpam Resource Centre has a list of recommended antispam software, and in that list is none other than "Mozilla Browser" and a write-up on Mozilla Mail's Bayesian filtering system.

    Screenshot of Mozilla being recommended on antispam website

    Nice. I just wrote in to tell them to include Mozilla Thunderbird in that list, and also to possibly change "Mozilla Browser" to just "Mozilla" or "Mozilla Mail".

    Update: Well they've very promptly changed it to Mozilla Thunderbird. It has replaced the previous "Mozilla Browser" completely though.

  • Why BitTornado is a better BitTorrent client

    I get tons of anime via BitTorrent and one of my favorite BitTorrent clients has been BitTornado (previously known as Shadow's Experimental client - nice rebranding too). I liked it because it was simple and light, and it uses the latest official BitTorrent code that allows for incremental allocation of files (since version 3.3). Before, BitTorrent would allocate the entire required disk space for the files you're downloading prior to actually connecting to peers/seeders.

    Now, BitTornado 0.3 has just made an advocate of me. Changes include a per-file download priority system and most significantly, a fast resume feature that allows recently-run torrents to be started immediately without the need for a hash-check! I got my 4.4GB download (68% done) started in a flash, sans disk-thrashing. You can't imagine how useful that is with large torrents of several gigabytes, where hash-checking can take an interminably long time and not to mention the painful disk-thrashing involved. I get big torrents of several gigabytes quite often, and I dread the hash-checking process because it slows my system to a crawl for several minutes and also starts the processor fan spinning like mad to cool away the extra heat.

    Also very cool (and something that brings it on par with other BitTorrent clients like Azureus in this respect) is the per-file download priority system which allows you to choose the priorities of files in a multiple-file torrent. You can choose to download just a single file in the torrent if you wish, something which was sorely missing before.

    Screenshot of BitTornado file download priority system in action

    BitTornado's an excellent BitTorrent client, but to be honest I've only tried the official client, Azureus, and ABC, all of which I didn't quite grow to like. Which BitTorrent client do you use, and why?

  • Firefox Extension Manager is here! Almost...

    Ben Goodger says in this thread:

    Extension-Manager enabled builds should be available today on ftp.m.o [ftp.mozilla.org]. Please start using these and test the new EM, file bugs!

    "Today" being 24 May of course. The Extension Manager is partially functional now in branch builds (which you can get from the Mozilla Firefox nightly FTP site - just look for dates ending with "-0.9"). Get one of these builds, either 2004-05-24-0.9 build or the very fresh 2004-05-25-10-0.9 build (of course, if there are later builds by the time you read this, you should get those instead).

    Screenshot of Extension Manager installing Download Statusbar extension


    Only extensions that have been updated to work with the new Extensions API will be reflected in the Extension Manager (other extensions should install fine, but they won't be listed in the Extension Manager - they appear in Tools -> Options -> Themes as before). I tried out Content Holder and ContextMenu Extensions just to see how they work.

    Screenshot of Extension Manager just after installing compatible extensions


    Turns out they install just fine in a download manager-like kind of way. And after a restart of Firefox, they show up just fine.

    Screenshot of Extension Manager with compatible extensions installed


    The "Checking for Updates" for extensions doesn't work correctly yet - it shows a "New Updates Available" notification whether or not there actually are any new updates.

    Screenshot of new extension updates notification


    Lots of functionality is not there of course, but it helps to know that things are moving along just fine. Go Team Firefox!

  • One year of blogging

    One year ago today (well, it's actually yesterday, since it just crossed midnight), 24 May 2003, I wrote my very first blog entry. Of course, I started with good old Blogger, moved on to Blog-City, and then FreeRoller (now renamed to JRoller), before I finally settled on using Movable Type on my own hosting account. Probably going to stick around, at least until I manage to move to WordPress without losing my customizations to my Movable Type installation.

    In the one year that's passed, this weblog has undergone just 3 major redesigns (which I regrettably haven't archived for future, er, contemplation). It started out as a Java blog, before getting more involved with web development topics, and then Mozilla (Firefox, Thunderbird) news. I think I've settled comfortably on the topics of "web development, Mozilla, Firefox, Thunderbird, CSS, programming" plus any tech bits that interest me.

    Anyway, I'm glad that people are still reading :), and I am honored to have actually made a good number of blogrolls. Thanks for having me.

  • Lone Wolf RPG has landed!

    The good people at Project Aon sent out an email on their mailing list today to announce the release of the Lone Wolf RPG (RPG in the Pen and Paper sense, not the CRPG sense). Yes, a PnP RPG! Not that I've ever truly played one beyond a fleeting tryst with BattleTech PnP. Where are the people who play PnP games in Singapore hiding?

    Anyway, I digress. The rulebook seems to be very interesting reading - the usual stuff you'd expect in a Dungeons and Dragons rulebook. I suppose this sounds fantastically geeky, but reading rulebooks and sourcebooks for Dragonlance and Forgotten Realms at Borders is something I actually enjoy - and yes I don't play PnP. Go figure.

    Check out the character classes available:

    In this rulebook, we have included the following character classes to get you started - the Brother of the Crystal Star, Dwarven Gunner of Bor, Kai Lord (of course), Magician of Dessi, Shadaki Buccaneer, Sommerlund Knight of the Realm, and Telchos Warrior.

    And who doesn't remember those Crypt Spawns, Doomwolves, Drakkarim, Giak, Gourgaz, Helghasts, Kraan and Vordaks.

    Lone Wolf RPG rulebook cover art


    If you're interested in The Lone Wolf RPG, check it out at Amazon.com (there isn't much there at the moment though), and if you purchase it, affiliate earnings would help fund Project Aon.

    Note: I wrote about Project Aon while back, in case you're interested.

  • One ping (service) to rule them all: Ping-O-Matic

    Ping-o-Matic is now not only a HTML form-based service that you can use to notify multiple sites of your new weblog entry. This weblog entry let us in on the new ping URL http://rpc.pingomatic.com/ that you can use to replace the long list of URLs you're currently pinging everytime you make a new weblog entry.

    Screenshot of MT ping form section


    This means no more waiting for your weblog software (be it Movable Type, WordPress, or something else) as it pings multiple services.

  • Sorry, no more hot-linking, please

    Yup that's it, no more hot-linking of images (and CSS files as well) on this site, please. Thanks to pippo's
    How can I prevent hot-linking images?
    post, any hot-link requests to this site will be redirected to this image below instead:

    Image shown in place of hot-linked images


    I know there have been cases of legitimate hot-linking (at least in my view they are legitimate), such as from forum threads (legitimate because I don't expect your run-of-the-mill forum member to have webspace). So this "sorry" goes to you guys. If I notice hot-linking from forum threads in the future, I'll be sure to add you to the allowed list of referrers. In fact, I've already added http://gathering.tweakers.net/forum/ and http://mozillapl.org/forum/ to the "allowed list" in my .htaccess.

    I did this because there have been people who have been leeching my image files, list bullets and even my CSS(!), and I think that is bad form. If you want an image from this site, download it, and upload it on your server. So far every image and screenshot I've ever posted is available for "free" despite the Creative Commons Attribution-NonCommercial-ShareAlike 1.0 license, and should be in the future unless I turn into the Picasso of graphic design soon.

  • MT-Blacklist Updater 0.2 - user-contributed improvement

    Edward Bateman wrote me last week to suggest an improvement to MT-Blacklist Updater.

    I then realised that if large numbers of users began to use your script, then Jay's server would be under extremely high load just around the hour of each day, and especially during specific US times.

    He then went on to suggest a tiny bit of code for how to workaround this problem - it works by generating a random interval of time for the script to sleep before accessing the MT-Blacklist master blacklist changes RSS feed. Er, yes I didn't think of this at all and all credit to Edward for pointing this out.

    Go get it if you're interested - to upgrade from 0.1, just simply replace the existing files with the current ones. Of course, you have to change the configuration settings in updateblacklist.php again, so make sure you copy them out from the old file.

    Also, I haven't upgraded MT-Blacklist since version 1.62 so it would be nice to know someone could tell me if MT-Blacklist Updater breaks in the newer versions.

  • Asa (Mozilla guy) meets Opera

    Asa tried the recent Opera 7.5 release and wrote about it in his blog. I won't jump in and make a debate or express my opinion on his entry nor on the Opera die-hards' comments (in that same entry) because I'm too worn out to think. But I do think it is interesting to point out the debate for those of you who haven't seen one of these in the topic of browsers (as opposed to the PC/Mac and Windows/Linux debates).

    Asa posts an update later in response to reader comments.

    PS. to Blogupdates readers, you've probably already followed the goings on at Asa's weblog, but bear with me - this is for my non-Blogupdates readers. Oh and if I seem to be hogging the top spot in Blogupdates, it's not me constantly re-posting my entry or doing some silly stuff - I think it's a bug regarding non-GMT times in the Blogupdates script and have informed Henrik (Gemal) already.

  • Away

    Before anyone jumps to the conclusion that I've fallen into torpor or crumbled to dust in Final Death, let me assure you that I will merely be away on a short trip for the purpose of getting away from Things That Suck™ (among other purposes). Will be back Wednesday evening (my time, CCT). If I'm not back by then, assume the worst.

  • Interesting job interview today

    Fresh graduate that I am, I have been submitting applications for quite a number of jobs, mostly revolving around the "web developer" and the "software developer" careers. Of course, I am always supplementing my income with web design and development work, and have always been on the lookout for freelance work. Sometimes I wonder about work, about whether to work for someone else, or to strike out on my own... ah, ah, ah... That's a topic for another day.

    So, back on track, I went for an interview today with a SME, and the interviewer was seemingly the owner (educated guesswork involved). He was young, he was entrepreneurial (he had at least 1 major startup that crashed, the current one is the one I was seeking a position in, and it's successful enough). But what was most interesting was how the interview went.

    You see, he was looking over my resumé, and he actually made suggestions on how I should rearrange it and present individual sections, in the most friendly, I'm-here-to-help-you-friend manner. I was surprised, pleasantly. This guy was actually spending time offering very very valuable advice to me. Great stuff.

    And he was honest, he said he couldn't meet my salary expectations, he said that the job I'm applying for ("Java Developer") is a tedious, repetitive job. He also said something to the effect that starting at a small company like his is not something he'd recommend I do, "as a friend". Everything made a lot of sense. Excellent career advice.

    I didn't get the job. He said "it'd be a waste" for me to go with this job, when I should be aiming higher. I believe that this is what he felt (not that "it'd be a waste" - that's subjective), that he wasn't going through this amazing amount of trouble just to make me feel better about myself for not getting the job. Sincerity shone through. Either that or he is a sterling actor (which I doubt).

  • Microsoft now says SP2 will not install on pirated copies of Windows XP

    BetaNews reports that SP2 Will Not Install on Pirated Copies of XP, contrary to what local (Singaporean) publication Computer Times reported earlier.

    Just when I was being amazed at how thoughtful Microsoft was for everyone of us Windows XP users, pirates and legitimate users alike.

    This (in the Computer Times article) made very, very good sense:

    "Having these unsecured users means bigger worm and virus outbreaks - which also impacts the Internet and consequently, our legitimate users as well."

    Legitimate users should damn well be pissed at this announcement more than users of pirated copies. Why? Simply because there is no doubt whatsoever that when SP2 comes, cracks and keygens will quickly be released that would allow you to install SP2 anyway, if you have a pirated copy. Informed users of pirated copies won't be stopped from getting SP2. But a good majority of users of pirated copies may simply not care and live with a non-SP2 Windows XP. And what good will it do to protect only the legitimate users when vulnerable systems are legion? (Of course, this is based on the assumption that SP2 does greatly secure a system.)

    Sure, illegal users don't deserve updates, I very much agree. But when this encroaches into the security of legitimate users, I think it's very very prudent and smart to give them the updates as well, for all the reasons that Barry Goffe put forward in the Computer Times article. Illegal users don't deserve updates, but legitimate users do very well deserve to have these illegal users' Windows XP setups secured and protected as well, for their (legitimate users) sake.

  • Singaporeans found blogging

    Pardon the newsy (and awkward) title for this entry - it's hard trying to think up creative titles when that juice dries up. Anyway, this is more of a gratuitous, promotional linking entry than a real blog entry (although some of you will not be unaccustomed to blogs that are full-on linkblogs - and I'm not talking about side link-blogs).

    So yeah, recently I've encountered a couple of local (Singaporean) webloggers whom I've had to pleasure of speaking to. They are one Alex Choo and one Jaime Wong, both very different people and rather unrelated in fact, so I should speak of each in turn.

    Well, Alex is someone I've had the pleasure of speaking with at length (via IM) and will be someone I'll be working with on a clandestine project that will save the world from the Antediluvians when Gehenna comes... yes, something like that. Alex is also the owner of Google Apps, a collection of tools that use the Google API. This includes "Adsense serves...", which tells you which ads a particular webpage would serve, and also Glocal News. Glocal News lets you search for news on Google News related to a particular country:

    Glocal News does not just check for country names, it also checks for commonly used terms associated with that country.

    You may ask: "why don't I just go to Google News and key in 'USA' to find USA-related news?" The difference lies in there being other keywords related to each country, and these are entered in the search for you invisibly. That gives you a rather good set of results, if you do try it out. The bad thing, however, is there's no way you can add more keywords and the application depends on the author appending more keywords as they become relevant.

    Now, Jaime Wong is a freelance web designer who wrote me simply because you don't see many Singaporeans who know what Web Standards mean - we need to stick together, all 4 of us. We did talk a bit about starting a local interest group, but as of this time there're so very few of us. Probably needs to gather more momentum and weight first before a thrust of any sort is to do any good. If you're Singaporean and are interested in a local interest group dealing with Web Standards, do write me or Jaime, just for a chat or anything at all.

    By the way, Jaime's weblog is absolutely stunning. You have to check it out. It was even listed on CSS Vault.

    Hmm... Well, this didn't turn out to be a linkblog-like entry after all.

  • Computer problems (er, need help)

    Update: Well, the problem seems solved now, and thanks to everyone for your advice :). Rather than repeat what I said in my comment, do check it out if you've replied to my call for help. Thanks again!

    This is a computer problem that has been bugging my desktop PC and me (me mostly - the machine seems pretty happy, and sometimes I think I hear sniggers). Well, the problem is, it boots up and then freezes irrevocably (resetting is the only recourse). Nothing out of the ordinary yet, considering I'm running Windows XP, but it freezes at any point from bootup to a fully logged in session in Windows XP. Sometimes it even fails to POST. Sometimes there is just a white line across the screen and the rest is black.

    Here's what I think could be causes:
    1) The monitor (it's a Phillips Brilliance 105 - circa 1996 I think) is too old. But does an old monitor cause the system not to boot or to freeze? Anyone had experience with old monitors? Will it blow up in my face?
    2) The power supply's dying. Possible. But it's a new one and it should be way above requirements.

    I don't know how to debug (or perhaps I'm just too lazy), so I'm casting about hoping someone would bite and tell me that they have had this exact same problem and how to solve it.

    PS. I'm ready for any suggestions that my monitor is truly dead or highly dangerous in its current state - gives me an excuse to get a new, bigger (it's 15 inch) one.

  • The official Firefox and Thunderbird build threads in RSS

    If you've been a good boy or girl, you would have been going to Mozillazine Forums now and then to check out those "The Official Win32 build ... is out" threads posted daily by Peter(6) (for Firefox) and Ale (for Thunderbird). What exactly are in these threads? Well, they highlight the latest fixes, patches, bugs, regressions and related news (if any), among other things. A daily snapshot and report of the changes since the last official Win32 build, so to speak. Check out today's (2004-04-29) Firefox thread and Thunderbird thread and you'll see what I mean. Very, very useful, and all credit goes to Peter(6) for his hard work and also to Ale who followed in Peter(6)'s footsteps by doing the same for Thunderbird.

    Screenshot of part of an official Win32 build ... is out thread

    When Jacob XP asked yesterday in this thread whether an RSS feed for these official builds threads could be created, I had a feeling that tommorrow (which is today), there will be such an RSS feed. Such was my belief in the spirit of the community. And true enough, there is such an RSS feed for the official builds thread already in existence way before I logged into Mozillazine Forums today. This is possible thanks to Jay (aka Jayfromtaiwan), who's going to manually update an RSS feed with summaries of these official build threads, you have another excellent source just to keep track of all these great forum posts that appear daily.

    Now we have an alternative to the just as excellent The Burning Edge, and more importantly (to me), a quick way to find out the latest Thunderbird checkins, changes and regressions.

    Jay is also a custom builder as well, so do check out his Mozilla build site for custom builds of Firefox, Thunderbird, and the occasional Mozilla suite. Custom builds are unofficial builds created by good people, and will include things like processor-specific optimizations (such as SSE2 support for Pentium 4), optional components (such as the DOM Inspector and SVG support), and patches that haven't made (or may not make) it into the official builds.

  • Server move imminent

    Just a note in case someone wonders why this site is down or looks kinda funky in the next 24 hours (or so), I'm changing servers yet again.

    Update: after some problems with DNS (there wasn't a SOA resource record), we're finally at the new server. Write me if you notice something missing or just plain wrong.

  • Outsearch with Firefox

    This is a mini-article I wrote for the SitePoint Community Crier after a fellow staff member requested that I write some helpful tips for "next week's Crier". It's just been published in Issue 64 and I'm simpy reproducing it here.

    The SitePoint Community Crier is the newsletter of SitePoint Forums, and is put together by the Community Team lead by Saara Ord (Saz249). They do amazing work and I must say probably the most work among all the Teams (Programming, Design, Hosting, and Grow) just by producing the Crier alone. If you are not already subscribed, show them a bit of support by giving the Crier a go (subscribe at Newsletters page).

    I'll probably be writing more mini-articles for Firefox for the Crier, so if you have any suggestions on what I should cover, do write me or leave a comment. An even better idea would be to submit your own article to the Crier staff for publication - if you're already a member of SitePoint Forums, just contact Saz249, or you can write me and I'll set you up.

    Now, on to the article...

    Outfox, Outsearch with Firefox

    Searching from address bar

    Did you know that if you entered a search term into the address bar (where you'd normally enter a URL), Firefox does a "I'm Feeling Lucky" search on Google? If you don't know what that means, try clicking the "I'm Feeling Lucky" search on Google the next time you're there - basically, it takes you to the very first search result.

    Granted, not many people would consider that very useful. I'd prefer to have it perform a normal Google search instead, as I'm sure quite a number of you would too. Not to fear, because with Firefox, you have control!

    Let's see how you can make Firefox perform a standard Google search from the address bar:

    1. Type in about:config in your Firefox address bar. It should open a page like this (click for a larger image):
      Screenshot of Firefox about:config page


    2. In the "Filter" textbox, enter "keyword.URL". You should then see the keyword.URL preference with the value "http://www.google.com/search?btnI=I%27m+Feeling+Lucky&q=". This is what makes it perform a "I'm Feeling Lucky" search.
    3. All we got to do, is to double-click that preference, and enter "http://www.google.com/search?btnG=Google+Search&q=" in the textbox of the dialog that comes up.
      Screenshot of keyword.URL preference dialog


    4. Hit "OK", and you're done! Try it out.

    If Google doesn't quite float your boat, you can use other search engines as well. Use "http://search.yahoo.com/search?p=" for a Yahoo! search, "http://imdb.com/find?q=" for a IMDB search, "http://dictionary.reference.com/search?q=" for a Dictionary.com search, and so on. You just have to make sure that the search string of your search facility accepts a query string that takes in your keyword(s).

    Custom keywords

    This is an amazingly powerful feature in Firefox. With custom keywords, you can load up a webpage using with your assigned keyword. For example, I can go to the SitePoint.com website by typing "sp" into the address bar. To achieve that, all I had to do was to create a bookmark for SitePoint.com (http://sitepoint.com/) and add the keyword "sp".

    "That's it?", you say. Well, we're getting to it, the real power of custom keywords, that is. With custom keywords, I can Google search for the "best browser" by typing "g best browser" in the address bar, lookup the meaning of "promissory" on Dictionary.com by typing "d promissory", and lookup the Bugzilla bug report for bug 75138 by typing "bug 75138". Let's work through how to do that with a Dictionary.com custom keyword search.

    1. Bookmark "http://dictionary.reference.com/search?q=%s". "%s" is the placeholder that will be replaced by the word (or words) whose meaning you're looking up.
    2. Give the bookmark a keyword as described above. I use "d".
    3. Your bookmark properties should then look like this:
      Screenshot of bookmark properties and custom keyword field


    4. Click "OK" and now typing "d estoppel" in the address bar will load the Dictionary.com entry for "estoppel".

    Asa Dotzler has a good write-up on how to use custom keywords.

    Ben Goodger has also created a Firefox extension called SmartSearch that adds a Smart Keyword menu in the context menu.

    Context menu search

    Another convenient feature that boosts your search productivity - searching from the right-click context menu. Select the words you want to search, right-click, then select Search Web for "keyword". This will perform a search for your keyword on Google.

    Screenshot of Firefox context menu search


    Don't like Google? Go to about:config in your Firefox address bar as described in the "Searching from address bar" section and look for the browser.search.defaulturl preference (its value should be "http://www.google.com/search?lr=&ie=UTF-8&oe=UTF-8&q="). You know what to do! (Hint: read the "Searching from address bar" section.)

    Find as you type

    Now that you've got some search results, you may want to search for occurences of your keyword in a long page. The "Find as you type" function is your savior. Just start typing the word (or words) that you want to find. Firefox will incrementally find and highlight the first instance of the word that matches what you type.

    Screenshot of Find as you Type feature in action


    Use F3 or Ctrl-G to do a "Find next". No need to use the mouse, no need for an extra "Find" dialog that gets in the way! This is one of my favorite Firefox features (this is also in Opera) and I often lapse into simply typing to search for stuff in the occasions when I have to use IE!

    Change the width of the search bar

    The default search bar width is rather small, probably big enough for 20 characters. You can fix this if it bothers you by changing the width by editing your userChrome.css. The userChrome.css file is located in the "chrome" directory in your profile directory. The Firefox Help site has a page explaining how to get to your userChrome.css, if you're not familiar with where your profile folder is. Once you have your userChrome.css, put this bit in there to set the width of the search bar to 350 pixels:

    #search-container {
    -moz-box-flex: 350 !important;
    }

    Upcoming search improvements in Firefox 0.9

    Firefox 0.9 will come with more features that will improve your search productivity. You'll be able to drag a bit of selected text into the search bar and perform a search on that text. Firefox 0.9 will also include an auto-complete delete feature where your autocomplete entries can be removed by doing a Shift-delete. Very useful for deleting something you don't want someone else to come across, without having to clear out your entire history ;)

    Another recently added feature is the ability to add smart keywords from form fields. No more messing around with "%s" thingies and what not!

    These features have been available in nightly builds for quite some time now, so grab yourself a copy if you want them now. The Burning Edge maintains a watchlist of major bugfixes and regressions, and also links to custom builds (which are professionally packaged and come with processor-specific optimizations).

  • Firefox extension manager sneak pre-preview

    Actually, just the extension manager menu item has been turned on, and a non-working dialog for updating extensions (hence the "pre-preview" - it is not a mistake). This can be seen in today's (2004-04-21) nightly builds - I was alerted to this by this CVS comment Ben Goodger gave on a checkin of his:

    turn on extension manager menu item (testing only!)

    Just a note before the screenshots: none of this really works yet! So don't go around asking why the extension manager doesn't work if you do get the 2004-04-21 nightly build.

    An "Extensions" menu item has been added to the "Tools" menu:

    Screenshot of Extension Manager menu item


    Clicking on that menu item opens the extensions manager, which supposedly would list the installed extensions and allows you to uninstall or update your extensions. Clicking on "Update" brings up the update dialog, which of course doesn't work at this point.

    Screenshot of Extension Manager update dialog


    Also available in the options (Tools -> Options) is a new "Software Update" section in the "Advanced" section.

    Screenshot of Extension Manager options


    Firefox will check for updates to your installed extensions via a web service (Ben Goodger has a weblog entry on this). Good stuff that. Checking for updated extensions is a difficult task to keep up with when you've anything over 3 extensions installed (I have 9). And there's going to be checking for new Firefox versions as well, judging from the options. Don't suppose it will be too useful for those of us who are in tune with the developments (nightly build users included, of course), but I suppose something like this is so quite standard in most applications now, and probably trivial to implement. Good for "normal" users I guess. Nightly build followers and contributing developers will probably know by instinct that a milestone release has been released. If not news will probably spread like wildfire from sources like MozillaZine and, of course, weblogs. Not going to use that option, I guess.

    Update: Ben has just posted several screenshots of the Extension Manager.

  • Infringement of GPL - court grants preliminary injunction

    The netfilter/iptables project has been granted a preliminary injunction against Sitecom Germany GmbH for not fulfilling the obligations imposed by the GNU General Public License (which covers the netfilter/iptables code used) - Sitecom did not make the source code available nor did they include the GPL license with their products (GPL is viral). Read the press release from the netfilter project for more details.

    Reading the comments here and here had me wondering why people were so surprised by the decision made by the (German) judge. I'd think that the judge would have looked over the GPL and applied the law in accordance with it and copyright law. An anonymous commentor at Groklaw probably said this best.

  • New Thunderbird artwork checked in

    The Mozilla Visual Identity Team has completed the new artwork for Mozilla Thunderbird, as Steven Garrity (Mozilla Visual Identity Team leader) reports.

    Jon Hicks describes the process behind the design of the logo. Go check it out.

    Naturally, I'm disappointed by there not being a new default theme for Thunderbird as I'd previously thought had been hinted at. But still, it is a great-looking logo, isn't it? It doesn't look like the mythical Thunderbird in Native American art that Jon Hicks pointed to in his post to me though. That would have looked cooler and made for a less demure bird - it is a thunderbird after all ;).

    Screenshot of 32 x 32 Thunderbird icon

    The new logo is included as the application icon in today's (2004-04-20) nightly build.Looks pretty nice as a 32 x 32 icon.


    Screenshot of 16 x 16 Thunderbird icon

    The smaller 16 x 16 version, however, doesn't seem to scale well and you can't really tell that it's a bird (with a resolution of 1024 x 768).

    On a brighter note, the about dialog looks just awesome.

    Screenshot of the new Thunderbird About dialog


  • I stand corrected

    Email from an anonymous visitor:

    Just to inform you, your tagline -- "rambling's of a misfit" is misspelled. It should be spelled "ramblings."

    Ouch. I always thought I knew my apostrophes. Like how there is no apostrophe in "CDs" (it's not "CD's") - short for "compact discs". And the possessive form of "it" is "its" (no apostrophe). "It is", however, can be shortened to "it's". Blah.

    Thanks for pointing out my oversight.

  • Firefox search now works in textboxes and textareas

    The Burning Edge points out that you can now search in textboxes and textareas in a webpage when you do a "Find in this page" search. The corresponding bug is bug 58305 and this enhancement appears in builds on and since 2004-04-16. I sorely missed this, especially so because IE does have this feature - some sites use textareas to display code.

    Screenshot of Find in this page in textarea

    As pointed out in this comment, this works for both the "Find in this page" function (accessible via Ctrl-F or Edit -> Find in this page...) and the "Find as you type feature". But with "Find as you type", the text found is not highlighted due to bug 134586 - selection in text field is invisible when it doesn't have focus. This will probably be fixed by the time Firefox 1.0 is released.

  • Thunderbird to get new branding!

    Looks like Thunderbird is up for a design update with new branding like what happened with the renaming of Firebird to Firefox. Check out the teaser at hicksdesign.

    Also spotted on that page:

    You’re in luck. The same folks that made the pinstripe theme for OS X are working on a new theme [for Win32] right now….

  • Server went down

    Ugh the server hosting this site went down for at least 7 hours today with no news or emails from the hosting company. Even their own site was down.

    I'm looking at sharing a dedicated server with several online friends, so maybe I'll be rid of these problems and have less downtime.

  • Patch UXTheme.dll with Multi-Patcher to use custom themes

    I wrote a few months ago about skinning WinXP with custom Visual Styles by patching UXTheme.dll. Since then, Belchfire.net reports that a friendlier patching program called Multi-Patcher does what PatchXP does and more. It supports SP1 and non-SP1 systems, and even SP2 (beta version 2082) and Windows Server 2003. Perhaps more importantly, it allows you to easily reverse the patch by simply running Multi-Patcher again - nice safety net.

    If you're looking to skin Windows XP with custom-made themes and Visual Styles available at sites like XPThemes.com and xptheme.info, this is something you should get.

    Update: thanks to miahz for pointing out that I forgot to link to the Multi-Patcher article.

  • Getting Firefox nightlies? Be careful when using Mozilla Backup

    Mozilla Backup is an excellent, excellent tool for making backups of your Mozilla, Firefox and Thunderbird profiles. Something that should be included in those applications in some similar form or incarnation.

    That said, I just lost my latest Firefox profile from today's Windows XP clean-up-the-junk-by-reformatting operation, even after I remembered to backup my profile immediately before the reformat. Why? Well, not through any fault of Mozilla Backup. Not because Firefox was acting up. Two hints for you to make some intelligent guesswork, if you're so inclined. One, the Firefox profile folder has been changed in recent nightlies to "Firefox" instead of "Phoenix". Two, I was using Mozilla Backup 1.3a. OK probably a bonus hint is in order: Mozilla Backup 1.3a predates the change in the location of the profile folder. Dead giveaway, and I didn't see that one coming.

    Mozilla Backup 1.3a backed up my profile in the "Phoenix" folder, which unfortunately has been untouched since my use of nightlies after 2003-03-12 - it was the dud profile that was left behind after being imported into the current "Firefox" profile folder. An entire month of bookmarks all lost in digital oblivion. Ouch.

    At this point, I think it is prudent to state again that this is no fault of Mozilla Backup, more of a(n) (oxymoronic) reasonable folliness on my part. In fact, Pavel Cvrcek (the developer) has been keeping up with that profile folder change, as you'd be able to tell from recent Mozilla Backup nightlies. I love Mozilla Backup and I'll keep using it. Until the time comes, if it does come, that profiles can be imported/exported (or saved/restored) via a native browser UI.

    Bugs 22689 and 187564 had previously been filed in 1999 and 2003 respectively requesting for the same functionality. Interesting to see the initial comments that users would be able to backup profiles by saving their profile directories (and subsequently copying back into a new profile when restoring). Not something very nice for non-technical users of course, considering how getting to the profile folder itself could pose some problems. Not pointing any fingers though, because there is a different time and context when some of those comments were made. It is good to see that someone has been assigned to bug 187564. Vote for those bugs if you think this is important to you too! Submit patches if you know how to help.

  • New Firefox feature: add smart keywords from form fields.

    I haven't been getting nightly builds recently and I just did so when I noticed at The Burning Edge that 2004-04-11 builds include this new "Add keyword for this search" feature. Now that is so useful because the old way of doing this is so non-intuitive and difficult to explain to non-technical users (who really wants to do the "%s" thing other than people like us?).

    Screenshot of the right-click interface to add smart keywords in Mozilla Firefox


    I went in to Bonsai to check out the CVS checkins to the code and Ben Goodger had this CVS comment:

    provide a convenient way for adding smart keywords for web page searches by adding a context menu to form fields with HTTP-GET form actions.

    So it works only on GET form fields - I was wondering why it didn't work for just any textbox. This was checked in on 2004-04-09, so I may be a little behind, but it is too good a feature not to talk about. UI improvements are so crucial and so much more important to naive users (naive in the nice sense of the word) because all they ever think of Firefox is the interface. More please, and thank you!

  • Speaking in binary

    Via weirdbeardmt in this thread: Binary translator converts regular text to binary and vice-versa. Beware - the binary dialect of geek-speak is now easily translatable.

    Conversation occuring here.

  • Bookmarklet lays out topography of a webpage

    Joseph Pearson of Make Believe has coded a topographic page layout bookmarklet that shows the layout of a webpage. Basically, it recolors the elements of a webpage, giving more deeply nested elements a lighter color. The effect is that you can see the "topography" of a webpage, which makes it that much easier to detect unclosed divs or for those situations where something is wrongly positioned but you just don't know why. Another of those must-have web development bookmarklets.

  • 1GB of email with Gmail? SpyMac already has it

    Now this is nice stuff here. SpyMac, a community for Mac users (as its name would suggest), is offering "the Internet's first free Gigabyte email service". Of course, Google was theoretically first by running beta trials with Gmail, but pretty much SpyMac is the first to roll out such an offer to the world at large.

    Spymac agrees with the staff at Google that a 1 GB e-mail account makes sense.

    It's just POP3 and webmail at the moment, but IMAP access is in the works (which is crucial, btw, if 1GB of e-mail is going to make any sense, unless you use webmail or choose to leave your messages on the server).

    Besides this, there are also some added free services, like 100MB of web space, 250MB of picture storage, RSS feed aggregator, and even blog hosting.

    I couldn't resist and signed up. At this moment, I've been getting errors at the final registration page (seems like the sendmail program on the server's getting a little too busy). Haven't got a confirmation email yet. If you're willing to tolerate the lack of credibility (relative to Google), I think SpyMac offers a very sweet deal that's hard to resist. Pats on the back for the SpyMac staff for providing this for free. Smart move too, considering the amount of free publicity they'll get over the next few days. Oh well, you can't fault them for doing so - Google did it first.

    I won't trust SpyMac with my essential emails, but it's interesting enough to take a look-see. Gmail? I'd trust it with my list of passwords that I store in a spreadsheet - aforementioned password spreadsheet existing hypothetically, of course. Google has gained that much clout and trust, at least in my eyes.

    Source: Gadgetopia

  • Smarter referrer spam?

    Ugh more referrer spam innovation. Check out these sites [1] [2].

    Notice this bit over at both sites, where there is a link back to this website:

    If your just looking to relax take a look here. It's a good site and recommended.

    Just a hyperlink pointing to the referrer, so it seems an innocuous enough (and legitimate) backlink. What's more, the style is randomized, so if you refresh the page, you get a different-looking site.

    Blocked with mod_rewrite in my .htaccess

  • Jack I'm so jealous!

    Wrapping up my final year of study at NUS pretty soon - final thesis defence/presentation on Monday, last examination paper on April 16. Getting a job has always been there on my mind but I never really bothered about it and have been somewhat noncomittal in looking. Well, I did apply to several companies, went to 2 tests, but no interviews yet. The thesis issue is somewhat blocking everything out, because goddamnit I want to get First Class Honors. Not that I'm certificate-driven or anything, but just that since I'm in the position to get it, why the hell not? Will it even have an effect on my job prospects? I don't really know. Probably not, you know.

    Oh Jack why didn't I do better for the aptitude test?

  • Google's GMail is for real

    If you haven't yet heard of it, Google's upcoming e-mail service, Gmail, has been under contention for one of the biggest April Fool's jokes ever. People all over have either first believed it (reservedly) and then laughed it off when they saw the press release on April 1st (like good old Simon Willison, or were thoroughly suspicious and wary of being conned yet again (like me).

    The verdict? Yes, Gmail is for real. The Straits Times (local print newspaper) even published an AP article on Gmail today.

    I won't inundate you with the details on what Gmail offers, but it is interesting to note that e-mails will come with targeted ads a la Adsense. Google says "no humans read your email to target the ads".

  • Firefox Extension Manager

    Looking at the recent checkins to the Firefox source tree, you can see that Ben Goodger has checked in the initial files for the upcoming Extension Manager (see bug 170006). The Extension Manager aims to provide for extension uninstallation (currently, you can only disable via the UI - you can, however, remove extensions manually), extension updating, and (hopefully) some level of standardization of extension installation UI screens.

    Forcing extensions to have to provide a choice of whether to be installed to the profile folder or to the application folder is another thing I'd like to see (or at least warn the user that the extension will be installed to the profile folder). Probably wouldn't matter too much when you can easily uninstall extensions with the Extension Manager.

    Henrik Gemal reports on the same thing, and points out ASCII art mock screens and Ben Goodger's writeup on the new take on extensions. Mozilla.org also has a section on Application extensions, where the Extension Manager is described (some pages are empty at the moment). Interesting reading all.

    There are also plans for a Web Front End (see Ben's new take on extensions), if you were wondering how updating extensions was to be done. The Web Front End will also be the central link and information repository for compatible and verified extensions (much like what Extension Room is now) - a partial solution to the malicious XPIs problem perhaps?

  • Google's facelift is official, Personalized Web Search

    Dogtoe reports that Google's new look is official. Previously you could get the new look with a bookmarklet.

    I also came across this Personalized Web Search at Google Labs which I think is new (could someone confirm?). You can specify a profile (which is saved in a cookie), search for something, and move a slider to increase or decrease the degree of personalization of your search results.

    Google Personalized Web Search slider screenshot

    That's pretty awesome.

    Google has developed new algorithms that dynamically reorder results by weighting the interests you enter in your profile. When you move the slider, it recalculates and rearranges the results to add more or less emphasis on your profile information.

  • Default Thunderbird theme icons and usability

    allen has an excellent and constructive evaluation on the usability of the default Thunderbird theme (Qute) icons in this thread at MozillaZine forums.

    Screenshot of Thunderbird Qute theme icons

    Interestingly enough, I've used Thunderbird since before the current default theme was instituted and never noticed the usability flaws of some of the icons before I read allen's post. I tend to read the text labels, rather than decipher the meaning of an icon. Nevertheless, I can see fully how the icons are confusing and don't convey the true intention of their actions - a new user could be slightly addled without the assistance of text labels.

    Well, it all goes to show how little benefit I've derived from the HCI module I'm taking this semester.

    Note: No Qute theme-bashing, please (and none ever intended) - Qute is actually a theme that I really like, in fact.

  • Malicious XPIs run executable binaries

    Flexer recently posted his encounter with a website that tried to get him to install a malicious XPI (Firefox extension). Upon the user clicking "Install", the install.js (the script that performs the actual installation) tries to execute the contained executable, which is xxxtoolbar, as Paradox52525 reports.

    Here's a snippet of the code in the install.js:

    var xpiSrc = "istinstall_netscape.exe";
    initInstall("Adding a File",
    "addFile",
    "1.0.1.7",
    1);
    f = getFolder("Temporary");
    setPackageFolder(f);
    addFile(xpiSrc);
    execute(xpiSrc,"",false);

    Arthur_Dent breaks down exactly what the XPI and the contained executable does in his post.

    Best solution, to me? Verified and digitally signed XPIs are allowed to run without hindrance. For unverified XPIs, warn the user that of that fact, and that the XPI will directly run executable code. Require an extra step of confirmation. That's what I think at the moment, but there are some pretty good ideas in that thread. It'll be interesting to see how this is dealt with in the near future.

    Follow the discussion on MozillaZine forums.

    Update: See relevant bug 238684.

  • Deletion of autocomplete results in Firefox, Mozilla 1.7b released

    Now here's a nice feature just checked into the Mozilla Firefox source tree that everyone wants - deletion of autocomplete results (bug 171605). There's always the odd occasion where you want to just remove a certain entry, but don't want to have to clear your browser history (and consequently lose everything else that you want to keep). How does it work? Just do a Shift-Delete when the autocomplete result you want is selected.

    ff-autocomplete.png


    Another bugfix is the depiction of application-specific icons in the download manager - check out Ben Goodger's screenshot. Spiffy.

    Get the latest nightly (2004-03-18) to see these changes for yourself.

    In related news, Mozilla 1.7b has been released. Noteworthy changes include the Password Manager, IMAP IDLE command support for Mail/News, and the return of the Talkback crash reporting utility (only in installer builds). This is also very encouraging:

    When compared to Mozilla 1.6, Mozilla 1.7 Beta is 7% faster at startup, is 8% faster at window open time, has 9% faster pageloading times, and is 5% smaller in binary size.

    Get Mozilla 1.7b.

    Source: Bonsai query for mozilla/browser mozilla/toolkit mozilla/chrome in the past week

  • Thesis submitted

    Ever seen that movie where this guy has to do an Honours thesis and went through an extended period of staring at the computer trying to code and write technical reports and a 100-page thesis, and he finally went blind and led a pathetic life after that before dying alone? No? Well, I didn't either. Sounds like a pretty boring movie.

    Well, anyway, so I've finally submitted my thesis to the examiner for review. That's a veritable mountain off my back, really. What have I gained from this test of willpower, this despairing journey of initial disappointment, this obstacle-ridden trek, this inward journey (yikes!) that made me realize that I'm just not cut out for a life in academia? Well, I can write on and on about this, recounting my experiences, or I can just stop here and link gratuitously to my thesis.

    Thesis - The Incremental Genetic Algorithm with the Michigan-style Classifier System (PDF format)

    Adding a link to my (much shorter) technical paper: Technical paper - The Incremental Genetic Algorithm with the Michigan-style Classifier System (PDF format), because I see the thesis has become the number 1 result on Google for "incremental genetic algorithm". Good to provide a shorter readup.

  • Junk filter improvements in Thunderbird weekly build + new Win32 installer

    Scott MacGregor (lead engineer of Mozilla Thunderbird) has recently announced the availability of the 2004-03-12 weekly build with lots of new cool improvements. These improvements include an improved Junk Mail Algorithm, improved IDLE command for IMAP, new smilies (emoticons), and several UI improvements and sundry bugfixes. Another interesting development is the availability of a Win32 installer for Thunderbird.

    Improvements and patches to the Junk Mail algorithm allow it to produce the same message scores as SpamBayes (another Bayesian spam filter - so good it is often used as a benchmark). What does this mumbo-jumbo mean? It means Thunderbird is now better at classifying your email as spam (or not) correctly. Scott highly recommends anyone trying out the new junk mail filter to first clear out the training file and retrain it. For that purpose, a new button for clearing the training data has been added (bug 237151) for convenience (previously, we had to delete the training.dat file manually).

    Thunderbird's new Reset Training Data UI


    Thunderbird's new IDLE support for IMAP (bug 141369) also sees many improvements and bugfixes. Simply put, IDLE is a command that allows IMAP email servers to transmit updates to the client in real time. This saves the client from having to continuously poll the server to achieve the effect of new mail appearing immediately. All in all, an exceptionally useful command that saves Thunderbird the work of polling IMAP servers continuously.

    New emoticons (bug 237045) are also included in this build, and the artwork comes from Stephen Horlander of the Pinstripe theme fame. Very nice, compared to the old ones which were terribly dated (I had emoticons turned off because I didn't like them).

    Thunderbird's new emoticons


    The new Win32 installer is an early attempt at rolling out the Mozilla Thunderbird installer targetted for the 0.6 milestone release (as stated in the Mozilla Thunderbird roadmap). I tried it out and it was fully functional, but there are several nitty gritty issues to be sorted out.

    Running the installer presents you with this screen:

    Thunderbird installer setup screen


    A little messed up, but this is an early test build after all. Scott is mostly leveraging off the work put into Firefox's Win32 installer. You next get to choose a standard or custom install.

    Thunderbird installer screenshot - choose standard or custom install


    Selecting custom allows you to add additional extensions - at this moment, Scott has only bundled in the Offline extension. Plans are in store to bundle other extensions as well, including the DOM Inspector.


    Unfortunately (at least to me), the installer silently places shortcut icons on the desktop, in the start menu, and in the quick launch bar. The installer should have asked first. (I see someone else has already whined about this) I think with sufficient well-founded feedback, the Thunderbird team will come around to getting that feature into the installer.

    Get the Thunderbird 2004-03-12 build or the test Win32 installer build to try out these new improvements and changes.

  • Firefox profiles are now in "Firefox" folder

    A while back, I mentioned that Firefox profiles will be migrated over from the old "Phoenix" directory to a "Firefox" directory. Ben Goodger (Firefox lead engineer) postponed the migration from last Friday to yesterday, and the latest nightly builds now include this change (see bug 203077).

    If you are using an official nightly (or an unofficial nightly - see the MozillaZine Firefox Builds forum), you will be prompted with this screen when starting the new build if you have more than 1 profile:

    Firefox profile import wizard


    Select the profile you want, and your settings and preferences will be imported:

    Firefox profile import wizard success screen


    At this time, it appears that the saved form information is not imported. Can anyone verify this?

  • Getting .NET Button ListBar control to work

    Dave (DarkAngel) has responded to my call for help regarding the .NET Button ListBar Control and has kindly allowed me to post the solution here. Dave is the man really - he went through the trouble of grabbing the control, loading up VS.NET and working through it step by step with me in his email. All that for a stranger. I love you Dave!

    The problem I had was trying to import the .NET Button ListBar Control into my VS.NET Toolbox. The instructions said to import the ButtonListBar.vb file into the project and it should work, but it doesn't in Visual Studio.NET 2003. I think that is the reason it didn't work (that it is VS.NET 2003 instead of just VS.NET).

    Opening the ButtonListBar.vb file in the Designer threw out an error (see the call for help entry). Dave fixed that with this solution (also via email):

    ... move the region event argument classes to the end of the ButtonListBar.

    He went on to help me compile the source files into a DLL, which I'm familiar with using for importing controls. Below are his instructions (slightly paraphrased). The credit goes solely to Dave (DarkAngel) - I'm merely reproducing it for the benefit of anyone who next comes across this problem.

    1. Open up Visual Studio .NET (Visual Studio .NET 2003 to be exact)
    2. Create a new project by clicking File -> New Project (I'm using Visual Basic, so I chose Visual Basic Project).
    3. Choose "Windows Control Library".
    4. What we've done so far is just create a control library project which is ideal for putting new controls in. This will build to a DLL.
    5. Remove UserControl1.vb (the default file VS.NET creates) from the Solution Explorer (it's unnecessary).
    6. Right click the project in the solution explorer and select Add -> Add existing item
    7. Add the ButtonListBar.vb file in the control source files downloaded.
    8. Build the project (choose to build for "Release").
    9. The compiled DLL will then be in the bin/ directory of the project folder. You now have the ButtonListBar control in DLL form. All the remains is to add this control into the Toolbar of the project in which you want to use it.

    Using the control:

    1. In the project you want to add the toolbar to, right-click your Toolbox and select "Add/Remove items".
    2. Click "Browse" under the .net framework components tab.
    3. Browse to the location of the DLL we just created and double click it.
    4. Click OK to dismiss the Add/Remove items dialog.
    5. The ButtonListBar control should now be the bottom entry on your list. (You can right-click and sort alphabetically if you like).
  • Firefox mail integration UI just checked in

    The Burning Edge reports that the latest Mozilla Firefox nightly builds (2004-03-07) contain the Mail Integration UI newly checked-in by Ben Goodger (bug 214893). It adds a mail toolbar button that allows you to read your mail and newsgroups or send the current link to someone. A Ctrl-M shortcut key has also been added to allow quick access to composing a new e-mail.

    See the mail toolbar button in action with the screenshot below.

    Screenshot of the new mail button in action

    The number of unread emails is retrieved from the registry (for the Windows version), the same key that allows Windows to display "You have X new messages" at the WinXP logon screen.

    If you grab a nightly build, you have to go through a few steps to get it to appear. Go to the View menu -> Toolbars -> Customize. Drag and drop the Mail icon to any place you like on a toolbar.

  • Help needed with .NET and Java

    Update: Dave has an excellent solution to the ButtonListBar .NET control problem.

    Firstly, the .NET question: does anybody know how to get this Button ListBar Control to work?

    I was trying to get the.NET Button ListBar control into my VB project's toolbox - I followed these instructions:

    To use the control, add either the ButtonListBar.vb or ButtonListBar.cs file to your project. You should find the control automatically appears in the Toolbox; if not you'll need to force the toolbox to display the
    control: typically I find that double clicking on the control source file to open its designer causes this to occur."

    Unfortunately, it doesn't appear automatically when I added ButtonListBar.vb (via "Add existing item"). So I double-clicked the source file... But it threw out this error

    The class ButtonListBar can be designed, but is not the first class in the file. Visual Studio requires that designers use the first class in the file...

    Which I don't understand of course. Neither did it cause the ListBar control to appear in the Toolbox. I have emailed the author about this, but if anyone knows what I'm doing wrong, please let me know ASAP.

    This of course leads to the question: What is the correct way to add a user control which comes in source form?

    Now, for the Java question: is there an existing library/package that would allow me to calculate the median and, say, the 95th percentile of a population quickly? I searched to no avail, of course, before I ask this.

    I can do this myself the hard way, but I'm really pressed for time. I'd like to reuse an existing solution if there is any.

    Thanks for any leads!

  • Experience Google's new look with a bookmarklet

    Update: Google's new look is officially in effect - there is no need for this bookmarklet anymore.

    Jesse Ruderman, bookmarklet creator extraordinaire, has written a bookmarklet that allows you to view Google search results in its new look. Google has been testing the new look randomly on a small number of users (I myself have only seen it once before).

    Bookmark this link and click on it at Google.com: toggle google look

    For those of you who are redirected by Google to your regional specific Google domain, try going to http://www.google.com/intl/en/ instead.

    You can also use the bookmarklet for the regional specific Google domain you are redirected to by changing the 2 occurences of "google.com" in the bookmarklet to that domain you're at.

  • Link Toolbar for Firefox

    I can't believe I missed this Link Toolbar extension all this time. It adds a small site navigation widget in your statusbar that allows you to make use of any <link>s that a webpage may have. Opera has it, so does Mozilla. But this extension probably places it in the best and most logical section of the browser screen - in the statusbar. It doesn't reduce your viewport like the one for Opera does (it appears as a toolbar). I've been wanting something like this ever since I used it on Mozilla.

    Here's a screenshot:

    Screenshot of Link Toolbar


    And another with the More widget in action, displaying the alternate RSS format for a website:

    Screenshot of Link Toolbar with the More widget in action


  • Switching to Firefox to get easier

    In a move that will aid users migrating to Firefox from other browsers, Firefox 0.9 will feature a long-awaited import feature that allows you to import your browser settings and bookmarks from your current browser. Windows users can now import bookmarks, history, saved passwords, cookies, form history, and preferences from IE, Netscape, Mozilla and Opera. Mac users will be able to import from Netscape, Mozilla, Safari, IE/Mac and Camino. Linux users can migrate from Netscape, Mozilla, Opera, Konqueror, Galeon and Epiphany. This is definitely good news because it significantly lowers the entry barrier and also reduces the cost to users who intend to switch.

    To get an early look in at what the import feature, try a nightly build (check out The Burning Edge). Current support on Mac extends only to Netscape and Mozilla, and on Linux, Netscape, Mozilla and Opera. The Windows version supports all browsers that I've listed above. The Firefox team is working on importers for the other browsers and they will be coming online soon.

  • Firefox profiles will no longer be in "Phoenix" folder

    Ben Goodger (lead engineer of Mozilla Firefox) has announced that Firefox profiles will now be stored in a "Firefox" directory instead of the current "Phoenix" (Phoenix was the name of Firefox even before it was called Firebird) directory. This will be activated in nightly builds starting from tommorrow next week Thursday.

    This change will be in the 0.9 release of Firefox and the migration engine will automatically migrate all Firefox-related data (bookmarks, userChrome.css, preferences, etc.) to the new folder.

    There will be no auto-migration of extensions (as far as I know), so there is the hassle of reinstalling your extensions (if you installed them to the profile folder). Hopefully, the Firefox team is considering, or is already at work, implementing migration of extensions as well. Well, we'll see what's really in store when the other major extension related feature, the extension uninstaller, is implemented.

    Migrating multiple profiles doesn't sound like fun either:

    Alternatively you can import one profile, run Firefox with the profile manager and delete the profile (but make sure NOT to delete the files) then run Firefox again and import the other profile. Run Firefox with the profile manager and create a new profile in the same location as the deleted one and all the settings in it will be picked up.

    I do not know if this are only temporary measures for nightly build users, or are going to be the same for Firefox 0.9. But I am pretty sure Ben and the team will be making migration as hassle-free as possible, within the limits of what's possible and what's not.

  • Gppgle.com

    Google owns Gppgle.com.

    Apparently, typos are common enough to make them mirror Google (the search engine) on Gppgle.com. Case in point: hits from Gppgle.com in this weblog.

  • Referrer spam revisited

    Less than 3 weeks ago I wrote about referrer spam from fake blog sites. Today, as I peruse my access logs as part of my end-of-the-month ritual, I find myself again a victim of referrer spam.

    I am getting hits from [1] [2] [3] [4] [5] [6] [7] [8] [9] [10] [11] (all redirected via JavaScript so that search engines ignore them). It doesn't take a Photohunt expert to notice the similarities in each site. Amazing how this sites are appear to be purely legitimate - there is a dictionary site, a Bushido site and even an academic society's anniversy meeting website . Most are just simply facades once you try to click around - links either lead back to the webroot or take you to some external site.

    My conjecture: someone owns this collection of sites and is trying to get a high PageRank for the sites, either to later sell them off or for further abuse. Anyone else have a theory?

    Anyhow, you can view my .htaccess to see my blacklist of referrer spamming sites as well as a mod_rewrite method of blocking referrers.

  • The best list of CSS filters, so far

    Chris of dithered.com has compiled a list of CSS filters. For those not in the know, CSS filters are techniques that hide CSS rules from browsers who misinterpret, abuse or just plain don't understand them.

    In more personal news, I've been slaving away at putting out a technical paper and a thesis for my honors project. Now you know why I've been so quiet. Well, you can read my scrapped together About page (that finally has some content) to keep yourself entertained. Yeah, right.

  • Textpattern - a promising CMS sprouts its wings

    Textpattern, a content management system written in PHP by Dean Allen (of Textism and Textile fame), has just been released to the public.

    Curiousity piqued by what it promised to offer about a year ago (when it was in closed development), I made it a point to check out Textpattern once any release is made. I took a quick install and run through of Textpattern gamma 1.11 and am pleased to report that it delivers on its promises. This has the makings of the next big weblog publishing tool (bigger than MT?), or even the next big CMS. More on this after I play around with it a bit more.

  • MS Excel confounds me

    This is the error message I get when I try to open 2 documents with the same name, but in different directories, in Microsoft Excel:

    A document with the name 'CR.wine.GA.1.csv' is already open. You cannot open two documents with the same name, even if the documents are in different folders.
    To open the second document, either close the document that's currently open, or rename one of the documents.

    Emphasis mine. Why can't I? We'll never know how Excel is implemented (unless there's a MS Office source code leak), but this is pretty dumb if you ask me, considering how easy it is to get by this technical difficulty. Perhaps there is some perfectly rational explanation why there is absolutely no way that Excel would be able to work with 2 documents of the same name, or if there is, it is more work than it's worth. I'd like to hear that explanation.

    Oh wait, I believe I should Google it. Hmm... Toby Allen reports the same thing, and Joel Spolsky has this plausible explanation:

    "I think it's because when you have an external reference (of the form foo.xls!A1) they only had 8 bytes in the data structure to store the file name. It's kind of shocking that 10 years later nobody has fixed this, ...

    Well, still not fixed in Office 2003.

  • Anime version of the Firefox logo

    Jesse Ruderman points out this cute(?) anime version of the Firefox logo.

    Via Jesse Ruderman via noririty

  • Firefox Download Manager extension

    Came across this Download Manager extension for Firefox in the MozillaZine forums. It saves some screen real-estate by compacting out unused whitespace and looks better. Whether the interface is more intuitive is up for contention.

    Screenshot of the Download Manager extension in operation
  • To the people asking for an Orkut invite...

    Note: I am not inviting anyone I don't know even a bit of anymore! I've been getting a good number of requests and I have decided that it just isn't right to invite just anybody. Please do not ask for an invite. Thanks for your understanding.

    ... You forgot the most important thing - your email address!

    I've been getting a couple of people coming in from Ben's Thought Crimes asking if I was willing to invite them into Orkut when they noticed I gave Ben an invite when he asked. I don't really like to do so, but I could invite people I don't know (I know Ben, sort of, so that's different) when they ask politely. But it's hard to do so when there isn't a way I can get back to them, much less get Orkut to send out an invitation (Orkut sends invites via email).

    Not that Orkut really does anything for me at the moment. If you want to see what it's all about, fine, I may send you an invite, but I'll "delete" you as a "friend" after that.

  • Backing up your IMAP email in Mozilla Thunderbird

    Henrik Gemal has written up a clever tutorial on how to backup IMAP mail using Mozilla Thunderbird. Follow that up with the corresponding tutorial on restoring IMAP emails.

    Now those of you who're still using POP3 to get your email no longer can say "IMAP sucks because it doesn't allow me save my emails and I have to delete them otherwise my inbox will be full".

    In related news, Omar Shahine, who co-designed Microsoft Entourage, says in his blog entry on IMAP, Thunderbird, and mail clients that:

    Thunderbird is an almost perfect IMAP client for Windows. If you use IMAP, this is the product for you.

  • list-style-image alignment quirk in IE/Win and Opera?

    Take a look at this example page I set up in IE/Win or Opera, and then in a Gecko-based browser.

    Notice that the positions of the list images in IE/Win and Opera are misaligned - they're not in line with the text, but seem to be vertical-aligned to the top. After a bit of hair-pulling, I figured out that is because I applied "line-height:180%;". Setting the line-height back to 100% for the <ul> works, but then I had to resort to adding margins to the <li> elements to simulate a line-height so that it'll be consistent throughout the document.

    Does anybody have an explanation why IE and Opera does it this way while Gecko browsers render it the other, seemingly correct, way?

  • Mozilla Europe website launched

    Mozilla Europe, a non-profit organization that was recently founded by several European Netscapees and Mozilla contributors, has just launched its website. Read the press release.

    Some would say the website design has a certain Communist theme to it - I wonder if it's not because of the judicious use of red and stars. Very nice, nonetheless. My only complaint would be the accessibility links which appear when you roll over them. Somewhere below the navigation bar, there are 3 links: "skip to Navigation", "skip to Search" and "switch language", which are hidden by applying a white color to the text, but become black when hovered. Confusing to a normal user to say the least. At least it confused me a bit when for a moment I thought the site just managed to produce a Peek-a-boo IE6 bug. But of course not, this is Gecko we're talking about ;). Back on topic, I don't see the point of providing accessibility links in this manner since it appears for users who don't need them when they least expect it. Try Mark Pilgrim's advice. Not to take anything away from the designer(s) though - they have done an excellent job.

    Update: The designer of the Mozilla Europe website, Sam Latchman, has made clear several things in his comment. Most importantly, "display:none" does not pass muster on many screen readers as proven on css-discuss.

  • Interesting Google stuff

    I seem to have picked up a handful of Google-related bookmarks. I think I have way too many at the moment so it is as opportune a time as any to start making link dumps.

    • Hidden Google Tools writes on several tools you probably never knew existed. Some of these are listed below.
    • Soople provides a form-based front end to many of Google's more advanced operators. Nothing to it really - just a simple form submission to Google with the appropriate query string. But convenient nonetheless.
    • Fagan Finder is quite like Soople, though it has an IE-only version which I didn't bother to check.
    • Google WebQuotes is a beta application from Google Labs which "offers a convenient way to get a third party's opinion about each of the returns for your search, providing you with more information about that site's credibility and reputation".
    • Google Guide is an interactive tutorial on how to use Google more effectively.
    • Google has a help page on Advanced Search Operators. Nice to keep it handy.
    • Google offers special searches within specific topics, including searches for Universities and other institutes of education with university searches.

    Now that's 7 less bookmarks which makes it, erm, 1196 - 7 = 1189 more bookmarks to go through.

  • Blogzilla exposes some Windows source code

    Update: I notice lots of people coming in to this page from Google searching for "windows source code". Whoops, sorry, but this isn't the place to get the actual source or any real news on it. Do stay and look around if you're interested though :)

    Blogzilla takes a look at some Windows source code derived from "unnamed sources".

    We here at redemption in a blog were also provided with some choice bits of the source code via unnamed sources:

    if( application.language == JAVA ) {

    set_speed_step_factor( 0.50 ); // decrement clock speed to 50%
    set_crash_probabilty( 0.80 );

    if( application.toolkit == SWING ) {
    render_speed( UNBEARINGLY_SLOW );
    start_idle_task(); // start an invisible process to consume CPU cycles
    disable_hardware_acceleration();
    }
    }

    In other news, Dilbork, everyone's favorite geek comic, has a strip on Mozilla.

  • Firebird? Firefox? How about Firegoat?

    Firesomething is an extension that changes the "fox" (or "bird" if you're using Firebird) into something else. Something else which can be whatever you want.

    Screenshot of Firesomething in action

    Check out the concise feature list:

    Features: Polymorphs fire animals.

  • The new referrer spam technique - blogger beware!

    I was checking my referrer logs the other day and came across several peculiar entries. There were mostly weblogs (except for 1), and at first glance would seem to be legitimate. Until I noticed a few things:

    • There is no sign of a link back to my site.
    • There are links to smut sites which appear to be the same throughout these sites.
    • The weblogs are new - in some cases there is only 1 entry, and no sign of an archive.
    • Certain elements are repeated across the sites.

    Take a look at the sites (all redirected via JavaScript so that search engines ignore them): [1] [2] [3] [4].

    Unmistakably, these are sites owned by a single person (or group of persons) who is out to seek referral links back to their sites, what is colloquially called "referral spam". Whatever for, you say? Well, here's a hypothesis (obvious as it is, it probably should be called "the truth" rather than a hypothesis).

    First and foremost, this person (let's just say, for ease of writing, that it is 1 person and it is a guy) is clearly someone intent on capitalizing on the weblog linking phenom that allows weblogs to have high PageRanks. A discerning eye will notice how the sites have domain names that can be turned around into sites of pornographic nature. Strong evidence that these either were porn sites before or are going to be once their Google PageRank gets high enough. By putting up some seemingly legitimate content to throw off webloggers, this person hopes to have his referral links unnoticed, unblocked (and implicitly acknowleged as legitimate). Referrer spammers are probably having a hard time with the increased awareness about referrer spam among webloggers, including tools and scripts to stop them. I can almost hear the imaginary light bulb switching on in their minds - why not use blogs as well? Place referrer links from a dummy weblog, link to several sites, get linked back (illegitimately), and get away with it. Just sit back and watch the PageRank and backlinks grow. Smart, but not smart enough.

    Notice how the links to smut sites are almost consistent across all 4 sites - dead giveaway. That was what set the alarm bells ringing for me - why the hell would a weblog link to pornographic sites? In this case, this person is getting ahead of himself. The intention, as I see it, would be to make use of these fake blogs to boost search engine rankings for his existing sites by linking to them. Ah greedy, greedy. And what's more, those links go to a porn site which I've blocked. Folly, folly.

    Curiousity piqued, I Googled in hope of finding someone else reporting the same phenomenon. Here's what I found:

    So this is not new (4 months old news isn't news). I wonder, how many webloggers have heard of this or seen this before though. I'm not clear on the how high the level of awareness is, but if you do have a weblog, do warn your readers (who are probably bloggers themselves). (And no, you don't have to link back to me - hypocrite I am not ;))

    As for a solution, I use mod_rewrite to block out unwanted referrers, thanks to kasia in a nutshell. You can view my .htaccess (which contains my list of blocked referrers as well). For an explanation of how this works, check out pippo's reply to my thread at SitePoint Forums.

  • More on Mozilla - Mozilla Backup, BT tracker, updated TBE

    Ah Mozilla is buzzing (bzzz!) all over the World Wide Web. More news from yours truly, this time on some tidbits that I found to be useful to the common Mozilla Suite/Firefox/Thunderbird user.

    Get Firefox


    So, in no particular order:

  • Firebird is now Firefox, milestone 0.8 released

    It's been posted all over by now, even Slashdotted (mozilla.org was down for quite awhile earlier), Neowin-ed, blogged a thousand times over. Yes, Mozilla Firebird is now known as Mozilla Firefox. With the 0.8 Milestone release of the lightweight Gecko-based browser formerly known as Phoenix (now also formerly known as Mozilla Firebird), the Mozilla Foundation has come up with a name which shouldn't run into any legal tussles like the old Phoenix and Firebird names seemed to attract (well, the Firebird name wasn't exactly a legal tussle, more like an act of brotherhood among fellow Open Source developers).

    Ben Goodger (Firefox Engineering Lead) wrote about the rebranding of Firebird to Firefox. Steven Garrity was drafted in as the new Visual Design Coordinator who developed the new Firefox icon you see below (together with the rest his team). Jon Hicks implemented it (meaning he did the computer graphic design work), as he describes in his journal entry. Yes links galore and more to come.

    Firefox icon

    The rebranding also entailed some new spiffy icons, decals and ads which you can find in the Mozilla Firefox ad webpage. If you had a Firebird button or ad, it's time to update them! For my case, I had an 80 x 15 pixel Firebird blog button, but the new Firefox one is 94 x 15 pixel large, which was totally out of whack with the rest of the buttons. Photoshop to the rescue...

    Firefox button

    So what's new in Firefox 0.8? I won't repeat what's already been written, so you can refer to the Firefox What's New section and Jesse Ruderman's listing of new features and bugfixes. In particular, one nasty regression that wasn't fixed was the "Installing 2 extensions without restarting re-launches extension-installer for previous installed extensions" regression. Hell, one of the biggest flaws of Firebird was it's poor extension management. Extensions often break a Firebird installation, like Tab Browser Extensions (Firefox 0.8 doesn't seem to work with TBE, so I got this patched TBE extension which you can get as well). Piro Date (author of TBE) has updated TBE and it works with Firefox 0.8. See his post in his forum. And removing extensions has to be done manually (and tediously). Thankfully this will be addressed in Firefox 0.9 where an extension uninstaller is planned.

    As for zip builds (as opposed to installer builds) for Windows, there isn't an official one yet, though Ben Goodger says he'll have one by the week. Unofficial builds are also being released, and the best place to get them is to check out the Firefox builds forum at MozillaZine.org. jesus_x has a zip build which was highlighted at MozillaNews.

    More links:

  • Microsoft FrontPage ad dripping with irony

    Look at this Microsoft skyscraper ad for FrontPage 2003:

    Microsoft FrontPage 2003 ad

    Notice anything? No? Hint: zoom in on line 28.

    Source: David Tenser

  • Kuro5shin article insults MovableType and MT bloggers

    Why your Movable Type blog must die goes out of the way to spectacularly insult weblogs. In particular, the author slams Movable Type for "bad design" and being vulnerable to comment-spamming scripts. Funny. Provoking (to MT users and bloggers at least). Read it, laugh, and move on.


    Doug provides some insight into the Kuro5shin article:

    The K5 article is not a simple rant by an individual. It is part of a script-kiddie assault on MT weblogs.

    ...

    The K5 posting was written by one of the script-kiddies, and not surprisingly it provides a link to the crapflood script.

    Read Doug's entire comment.

    James Joyce, the article author, has also made a response where he says of this weblog:

    There was also the person who made two identical TrackBacks and thought it was funny.

    The article was funny, yes, but "making" 2 identical TrackBacks wasn't my intention nor was it meant to be funny. I'm not saying MT doesn't have it's flaws, erm, dude, I'm just saying your insulting tone is simply that, insulting, which makes it harder to take you seriously.

  • Test your browser security

    Scanit, a Brussels-based security company, has put online a Browser Security Test which tests for vulnerabilities in your web browser. There is a complete listing of the tests they run should you be interested.

    I ran the test on 3 of my the big browsers (for the Windows platform, that is) and collected the results.

    Results for Firebird 0.8.0+ (20040202) WinXP:

    The Browser Security Test is finished. Please find the results below:
    High Risk Vulnerabilities 0
    Medium Risk Vulnerabilities 0
    Low Risk Vulnerabilities 0

    Results for Opera 7.23 WinXP:

    The Browser Security Test is finished. Please find the results below:
    High Risk Vulnerabilities 0
    Medium Risk Vulnerabilities 0
    Low Risk Vulnerabilities 0

    Results for IE 6.0 WinXP:

    The Browser Security Test is finished. Please find the results below:
    High Risk Vulnerabilities 0
    Medium Risk Vulnerabilities 0
    Low Risk Vulnerabilities 0

    Seems like all my browsers are safe. However, as this thread at SitePoint Community Forums shows, IE needs to be patched with the latest security patches (as mine has been) to protect it from severable major security flaws. If you haven't, do so now by going to Windows Update in an Internet Explorer browser!

  • Orkut invitations for sale at ebay

    Orkut membership is by invite only. Apparently that makes it something worth buying into, as this listing of Orkut invites for sale at eBay shows.

    In case the auctions get taken down, here's a screenshot for posterity (click for a larger image):

    Picture of eBay listing of Orkut invitations on auction

    Hey I'll sell you an invite for just a dollar! Heh just kidding...

  • Mozilla Firebird out next Monday

    Ben Goodger posted this in the Orkut Mozilla Firebird community:

    It's Monday February 9 now. This is the first time I've been confident enough to give a date. This release is going to be the best Firebird yet.

    See Ben Goodger's reply in forum thread at Orkut if you're an Orkut member.


    Sorry for posting to a closed forum. This piece of news was also made known in the MozillaZine forums in this thread entitled FAQ: When is 0.8 coming out?.

    Cheers!

  • Mozilla Thunderbird 0.5 test builds are available

    Hot off the mozillazine forums: Scott MacGregor has announced that the first set of 0.5 test builds are now available for Win32.

    Expect Milestone 0.5 to be released within the week or the next as per this announcement by Scott on the updated roadmap and 0.5 plans.

    Grab the test builds.

  • My Thunderbird build 20040201

    Just for fun, I built Mozilla Thunderbird from the latest sources using Microsoft Visual C++ .NET 2003.

    Optimized for Pentium 4, Pentium M, Celeron 1.7GHz+ and Athlon64 with SSE2. I stripped out most of the stuff I don't need, and added compiler optimizations /G7 (optimize for processors listed above) and /Oxs (full optimization, optimize for size).

    Download it: thunderbird-win32-20040201-Oxs-G7-SSE2.zip

    Here's my .mozconfig:

    export MOZ_THUNDERBIRD=1
    mk_add_options MOZ_THUNDERBIRD=1

    ac_add_options --disable-accessibility
    ac_add_options --disable-activex
    ac_add_options --disable-activex-scripting
    ac_add_options --disable-auto-deps
    ac_add_options --disable-crypto
    ac_add_options --disable-debug
    ac_add_options --disable-freetype2
    ac_add_options --disable-installer
    ac_add_options --disable-jsd
    ac_add_options --disable-ldap
    ac_add_options --disable-mathml
    ac_add_options --disable-necko-disk-cache
    ac_add_options --disable-oji
    ac_add_options --disable-profilesharing
    ac_add_options --disable-tests
    ac_add_options --disable-xprint

    ac_add_options --enable-extensions=wallet,xmlextras
    ac_add_options -enable-image-decoders=bmp,jpeg,gif,icon,png
    ac_add_options --enable-necko-protocols=http,file,jar,viewsource,res,data
    ac_add_options --enable-optimize='-Oxs -G7 -arch:SSE2'
    ac_add_options --enable-strip

  • The force that is Slashdot

    You must have heard of the Slashdot effect. It is real. It is of a magnitude beyond your imagination. Yesterday I felt a teeny weeny jolt in bandwidth simply because someone linked to my "New IE vulnerability - fake URLs" entry in this Slashdot article entitled "Microsoft Advises to Type in URLs Rather than Click" in a comment. A comment that is found about 80% down a page that scrolls at least fifty times when you press the page down button.

    Lucky for me I'm just below my bandwidth allocation for this month.

  • Sorry if you came in from QueerFilter.com

    If you came to my site, either now or previously, via QueerFilter, you were obviously expecting some GLBT content. The problem is, I'm neither of those, nor does this weblog have related content. So why the heck am I on QueerFilter?

    Well, erm, the reason, you see, is a little dumb (and I'm a little dumbass). There was a period when I was looking for feed aggregators and weblog directories to add my weblog to, and when I came across QueerFilter, I added redemption in a blog summarily without checking out what QueerFilter was really all about. Duh! Apologies if I wasted your time.

    Thanks to Richard Evans Lee's blog entry on QueerFilter, I found out my mistake. I sent an email to the QueerFilter owner/webmaster to remove my feed. Let's see if this entry appears up there now shall we?

  • There is no native RSS reader panel in Firebird 0.8 - phew!

    I just read Matt Haughey's blog entry on RSS native parsing in the next Firebird.

    I was checking out the nightly builds of Firebird 0.8 betas (windows and linux, mac) and they've got an rss button and panel that parses RSS, with titles linking to the main window.

    Gasp! The first thing I thought was how could I have missed something like this? When rationality took over, I began to feel pretty sure that Matt Haughey had made an honest mistake - there is not going to be a native RSS reader panel in Firebird 0.8 (or any future releases for that matter).

    I did some Googling and satisfied myself with some answers. Firstly, there is no mention of a RSS reader in the Firebird Roadmap - even the phrase RSS didn't turn up. Secondly, there exists a Firebird extension, RSS Reader Panel, which looks very similar to Matt's screenshot. Thirdly, I trusted myself to know if there was something like this coming up in my beloved Mozilla Firebird.

    Thankfully enough. An RSS reader sidepanel included by default would be widely protested by many (including yours truly) I would surmise. Bloat is one thing, having something like that in a web browser that promises to trim off the fat surely goes against it's standing principles.

    Update: I forgot to add the reason why Matt Haughey could be experiencing this. It is entirely possible that he had previously installed the RSS Reader panel extension into his Firebird profile and summarily forgotten about such a fact. And yeah I wanted to contact him about it, but comments aren't allowed and I can't find an email address anywhere. If anyone knows how I can contact him please let me know ASAP.

    Update 2: Nevermind I found his email address.

    Update 3: I got through to Matt and he has made amendments to his blog entry. Yay!

  • XHTML/CSS/JS Scrabble

    SScrabble (Solitaire Scrabble) was created using purely XHTML, CSS and some clever JavaScript. Really neat stuff (although DHTML Lemmings had much more oomph in terms of pure wow factor - too bad it had to be taken down due to copyright issues).

    The Man in Blue speaks on his experience coding this SScrable thing up in this weblog entry.

  • IE file download extension spoofing hole

    InfoWorld reports on the new Internet Explorer security hole that allows file download extensions to be spoofed. The hole allows the site author to make it appear that a downloaded file is safe by spoofing it's extension, when in fact it could be anything, including malicious executables.

    Security company Secunia has a demo of this security hole over at their Internet Explorer File Download Extension Spoofing Test.

    The author of the InfoWorld article goes so far as to say:

    The possibilities are endless, and since both spoof issues appear to be unfixable, it must surely place a big question mark over Explorer’s viability as a browser.

    The other aforementioned spoof issue is, of course, the URL spoofing vulnerability. Some good news on this front though, with Neowin.net reporting that Microsoft will fix this with an IE update to remove support for usernames in http urls.

    Can't say the damage hasn't been done. Has it got your average non-technical Joe/Jane looking for alternative browsers? Maybe grandma is starting to ask for "a better Internet"? No one knows for sure, but I'm sure if this is publicized further in the mass media, there'll be some very pleased converts.

  • Firebird 0.8 on the horizon

    Mozilla Firebird 0.8 is almost ready to be shipped, thanks to Jesse Ruderman for pointing out that small detail in the latest mozilla.org staff minutes. Expect it Friday (today!) or Monday.

    Jesse Ruderman has listed the new features and major bugfixes over at this entry in his blog.

    Nothing much there you won't be unfamiliar with if you've been using nighty builds like me.

  • Nothing to redeem here

    I got this weird email just a few hours ago:

    Message sent via contact form by: kimberly capps
    URL:

    how do i get the redemption button for my email?

    I was confused about this "redemption button", so I asked this Kimberly:

    Hi Kimberly,

    I'm not sure what you're referring to. Could you explain a little more on
    what you're looking for?

    Regards,
    Chu Yeow

    And so she replied:

    what we are trying to find is the redemption button for our email so we can retrieve our free plane tickets!

    I think I just got my first email prank. Either that or something has gone awry with search engine results. This weblog didn't turn up on Google for "redemption plane tickets" though.

    Anyway, now is as good a time as ever to point out that the "redemption" in "redemption in a blog" is actually my online pseudonym/nickname/handle. I thought it would be a nice play of words. This is not a redemption counter. There are no free gifts.

  • Pretty in pink, and yellow and green

    Perhaps you'd have noticed the new colors and a slightly changed layout.

    Color palette used for the redesign

    Is pink too effeminate? Think the color palette is garishly eye-blinding? Do your eyes bleed from the white background?

  • VB.NET boolean operators and short-circuit evaluation

    ... short-circuit evaluation is how the logical human brain works when confronted with chained boolean conditionals in real life

    Having to pick up VB.NET for a Human Computer Interaction course this semester, I've been spending some time getting acquainted with the language through a couple of books. Every now and then I get somewhat bemused by how "different" VB syntax is - not that it's a bad thing, pray. Syntax, as almost everyone agrees, is secondary to semantics.

    Anyway, here's a particularly bemusing bit of trivia you can use to taunt/jibe/provoke your VB.NET/VB programmer friends.

    Did you know that VB.NET's default boolean operators And and Or do not take advantage of short-circuit evaluation? Take for example this snippet of conditional code below:

    Dim x as Integer 1
    If (x < 5) Or (x > 10) Then

    Now, you'd expect your run-of-the-mill compiler/interpreter to optimize by short-circuiting the 2nd boolean expression, since the 1st expression is already true (and so whether the 2nd boolean expression is true or false doesn't really matter). For some reason, probably one steeped in history, VB.NET, or rather the JIT compiler, doesn't short-circuit. It happily goes ahead and evaluates the 2nd boolean expression.

    This is logically ludicrous - short-circuit evaluation is how the logical human brain works when confronted with chained boolean conditionals in real life. If the university says I can only get a First Class Honours degree if my CAP is greater than 4.5 AND get a minimum of A- for my thesis, I won't really fret about not getting an A- for my thesis (and subsequently missing out on a yummy First Class Honours) when my CAP can never hit 4.5.

    Now to be fair, there is an alternative OrElse operator which does perform short-circuit evaluation. It is not fair though to expect such an unorthodox operator to be something your common on-the-street (read: non-VB) programmer will expect to exist (unless he reads a VB.NET book and bothers to flip to the introductory chapters).

    Come to think of it, it could be an intended design issue. Evaluating the 2nd boolean expression could be always warranted when it produces a side effect. That must be the reason why Microsoft introduced the OrElse and AndAlso operators and leaving the standard And and Or operators as non-short-circuiting to prevent confusing converting VB programmers.

    So this then is actually an issue with VB. Hence, we can conclude that they deserve to be taunted/jibed/provoked. Spare the VB.NET programmers. For now.

  • Teach yourself Java in 21 days?

    Teach Yourself Programming in Ten Years is more realistic. That just about sums up why I was never bought by the enticing promises of "Teach Yourself X in Y days" and "Learn X in Y hours" books.

  • Photoshop magic

    Check out the 1st post in this discussion forum thread. It's in Chinese but the pictures speak for themselves. 20 hours was how long it took, but check out the hair on the girl and the fur on the dog. Unbelievable.

  • Tooltips with CSS

    Pure CSS tooltips by SantaKlauss shows you how to display tooltips using purely CSS. The idea is not unlike Nice Titles, though the execution is very different - Nice Titles uses the DOM to extract the nicetitles, the tooltips use only CSS.

    The smart part is when printing: the tooltips are "inlined" and bracketed appropriately so that they show up in the normal flow of the document when printing.

    Still, I consider the execution to be semantically incorrect since the tooltip requires the anchor tag <a> to work - the text that is "tooltipped" is not an anchor. A better way would have been to use a standard <span> tag. Of course, this then breaks in IE because IE only supports the :hover pseudo-class on the <a> tag. Thanks to whatever:hover, however, this can easily be fixed. Also, there seems to be no need for the z-index fix as mentioned (tested on IE/Win 5, 5.5 and 6). The CSS is below:

    body {
    behavior:url(/htc/csshover.htc);
    }
    span.info{
    position:relative;
    background-color:#ecc;
    color:#000;
    text-decoration:none
    }
    span.info:hover { background-color:#e96 }
    span.info span.tooltip { display: none }
    span.info:hover span.tooltip {
    display:block;
    position:absolute;
    top:2em; left:2em; width:15em;
    border:1px solid #0cf;
    background-color:#cff;
    color:#000;
    text-align: center
    }

    Here's an example.

  • Funny how this site looks bad

    Take a look at Graphic Designers for Dean. Then read their tagline/motto/whatever:

    Beautifying the Dean Campaign One Pixel at a Time!

    Right. One pixel at a time it is.

    Source: Swagu

  • Get rid of low disk space popups in Windows

    Being mired in hard disk space poverty sucks. Having your operating system keep reminding you of that every minute or so sucks even more. And to add to your woes, it's promise of helping you cleanup your hard disk to make more space falls apart when there really isn't anything it can suggest you remove.

    So that's how it is recently. Look at what's in my "anime" partition: episodes of Naruto, Chrno Crusade, Full Metal Alchemist, PLANETES, Shingetsutan Tsukihime, Gunslinger Girl and newly-found Mezzo TV. And 3.33 GB invested in Neverwinter Nights together with the Shadows of Undrentide and Hordes of the Underdark expansion packs. Those and more on a 20 GB hard disk in my laptop. I live day by day, deleting anime as they are downloaded and watched, or transferred over to my desktop (which, thank heavens, has 80 GB).

    Anyway, all 3 of my partitions are often nearly full, and Windows XP keeps trying to be helpful. I don't like being "helped". So this is what I did:

    1. Opened my registry with regedit
    2. Found the HKEY_CURRENT_USER -> Software -> Microsoft -> Windows -> CurrentVersion -> Policies -> Explorer key.
    3. Created a new DWORD value named "NoLowDiskSpaceChecks"
    4. Set the value to "1" to disable the low disk space notifications

    Meh!

    Source: MyDesktopHelp.Com

  • HTTP error pages for weblogs!

    Check out the custom error pages over at Dunstan Orchard's blog as linked in his weblog entry entitled Friendly error handling. Now that is cool. One other thing is certain - Dunstan really has too much time on his hands. Heh. I think I'm just jealous.

  • The PHP Anthology just arrived

    The PHP Anthology arrived in the mail today. Shipped by USPS, I wasn't too impressed with their delivery when it came in a flimsy paper package and was subsequently stuffed into my tiny mailbox. The result - two noticeably squashed up books with dented splines and dog-eared corners.

    Picture of The PHP Anthology

    Anyway, acrimony aside, the PHP Anthology is a set of 2 books written by Harry Fuecks of phpPatterns fame. Harry is rather reputable around the PHP circles, and also in the XUL community. He also was a co-author of one of those big red Wrox Press books dealing with XML. So I'm looking forward this latest publication of his - it should be good.

    Well, I do have SitePoint to thank for sending me a complimentary copy as part of the benefits program at SitePoint Forums - being an Advisor (otherwise known as a moderator in discussion forum parlance) has it's perks. Now if I just have the time to read the books to return SitePoint the favor with a testimonial or a review...

  • Blacklist weblog.cemper.com if you don't want vaguely related TrackBacks

    I'm deliberately making a point to let everyone know of my recommendation that they should blacklist weblog.cemper.com if they do not wish to receive TrackBacks that are only marginally related from the aforementioned weblog. I wrote about this before and I haven't forgotten that I said Christoph Cemper "isn't a TrackBack spammer". That opinion still hasn't changed because he isn't a spammer in the conventional sense, as Jay Allen has mentioned. I also don't want to begin a debate on what qualifies as TrackBack spam and what doesn't.

    I do, however, want to point out to anyone who chances upon this entry that Christoph has had a series of complaints made against him for his TrackBack pings. All are dismayed to find his entries from which the TrackBack pings were sent are minimally related to their own entries. Therefore, I recommend those of you with blacklist software of some sort like MT-Blacklist to put "weblog.cemper.com" into your blacklist to save yourselves future work in removing TrackBacks you most likely would rather not have.

    At this point though, I'd like to make a disclaimer that this is solely my opinion and my own recommendation that you do so. You also shouldn't just do what I say - if this issue concerns you at all, take it upon yourself to investigate the grounds for my recommendation. To that end, I have compiled a list of related weblog entries and links to start you off with at the bottom of this entry.

    Christoph Cemper also addresses the complaints made in his entry addressing TrackBack Spam complaints. Another blogger, Camilo, wrote a nicely composed argument on Spam and censorship. In his entry, he questions the use of a clearinghouse as a form of censorship. I agree with him and have made it clear that it wasn't my intention to censor Cemper.

    To Mr. Christoph Cemper: Don't take this as a personal attack. I just don't see your side of the argument and as far as I can tell you are still doing it and annoying other bloggers.

    This is the list (so far) of bloggers I know who complained (and links to their corresponding entry, if any):

  • The monthly report - December 2003

    redemption in a blog received 13,728 visits for the month of December 2003, meaning there were an average of 442 visits daily.

    Most visited entries:

    Of note:

    • A new year, a new webhost - I've just changed my webhost to a dirt cheap webhosting plan at PinchPenny. Not that there was anything wrong with my last host, but PinchPenny had unlimited add-on domains that was just too good to miss, and there being end-year promotions (I get 10% off for as long as I'm a PinchPenny customer) as well. Besides, I was using almost my full bandwidth allocation (1,824,125KB - wait, Google can convert that to GB... It's 1.74GB) at my last host, who kindly sponsored my hosting. Just didn't feel right.
    • MT-Blacklist Updater is a simple PHP script I wrote to keep my MT-Blacklist setup updated automatically with any changes in the master blacklist at the Comment Spam Clearinghouse. What was interesting was I screwed up completely at my first try. Nevertheless, it works now, and feedback like this makes me smile.

    As always, thanks for reading, and a Happy New Year to one and all!

    Past monthly reports:

  • Changing webhosts - temporarily funky

    I'm in the process of changing my webhost for this site so some things may seem funky at the moment.

    In any case, a Happy New Year ahead to all!

  • Nice comment postbit from vantan.org

    Fellow Singaporean Vanessa Tan has a nicely designed weblog, totally deserving of being nominated for Best Designed Blog at the Asia Weblog Awards 2003. One of the nicest bits is the comment postbit ("postbit" as in "forum postbit" parlance) which I had oohed and aahed over for some time before deciding to copy it. Yup copy it I did, and left a comment for Vanessa to let her know I was using her idea. Of course I said that I would take it down if she didn't like me stealing her idea.

    Naturally, it was my bad for assuming that I could do something like this, because Vanessa actually had a no derivative works Creative Commons license and I had not attributed her. I found out when she posted an entry on her copyright notice, and have been trying to figure out how to attribute her in a way that is at once visible yet non-intrusive to readers. I think I have figured out a way by placing this bit of text in the comments section:

    The paper doll icon that precedes each comment is an idea conceived by Vanessa Tan.

    Want to see it? Post a (non-spam!) comment on this weblog.

  • Sending TrackBack pings indiscriminately makes you a TrackBack spammer

    Yesterday I received 2 TrackBack pings for my entries on MT-Blacklist Updater and my October 2003 monthly report. What is interesting about these TrackBack pings is that the entries they were sent from had almost nothing to do with what was discussed in my entries. I began to think that I have got my very first TrackBack spam. Am I really famous? The answer to that question, of course, is an emphatic no (thanks for the compliments though, Kyrah).

    Being curious and, as I'd like to think, benevolent, I decided to post a comment on one of the suspect TrackBack-happy entries:

    I've no idea why you sent a TrackBack ping to my weblog entry which has totally nothing to do with the things discussed here. I have deleted it.

    I looked through your entries and noticed that you pinged other people too.

    I hope you aren't offended if I begin to think you are a Trackback spammer! Unless you have a good explanation, I will submit your Trackbacks as spam to Jay's comment spam clearinghouse!

    It was later that I found out that at least 2 other guys, Heiko Hebig and Martin, received the TrackBack pings of the same suspect nature as I did. Hmmm... it seemed that we may be onto a spammer here. But that would be jumping to conclusions a little too early. I have been going on without saying anything about the person or the weblog which sent the TrackBacks, but I have to make it clear here that it is my view that the mentioned person isn't a TrackBack spammer, but rather a (very) misguided TrackBack-trigger-happy blogger.

    Christoph Cemper has responded to our complaints with an entry entitled TrackBack Spam complaints. He says the purpose of sending TrackBack pings is to

    notify others that you wrote about the same thing

    and later goes on to say:

    to notify Person B was my intention.

    Yes that is very much the reason for sending TrackBack pings, but I have to question how what Christoph wrote about can qualify as the "same thing" when they can hardly be qualified as vaguely related. Don't think so? Check out my entries and the pings Christoph sent (see bottom of this entry). Ahem! Just because my entry mentions "Google PageRank" doesn't mean that your entry on Google PageRank is a form of "continuing the discussion", much less writing about the same thing. At least, that is how I interpret the way TrackBack should work.

    As a last note, I leave it to readers to judge for themselves the relatedness of the entries from which TrackBack pings were sent to my entries. For my entry on MT-Blacklist Updater, this is the pinging entry. For my October 2003 monthly report, this is the pinging entry.

  • Use Bloglines subscribe to email mailing lists

    Bloglines just pushed out a new feature they call email subscriptions. I've been using Bloglines as my weblog RSS feed and newsfeed aggregator for quite a while and always been pleased with the fast response times and easy-to-use interface. This new feature just about makes wanna be a Bloglines pimp.

    So what the heck is this new feature email subscriptions? Let me quote Bloglines first:

    Email subscriptions are great for announce-only or broadcast mailing lists that don't provide RSS feeds. They are also useful as temporary email addresses.

    What Bloglines does is to setup an email account for you upon request (just click a link) and you can then use this email address to subscribe to your favorite mailing lists or newsletters (like the Mozilla Links newsletter). Any email you receive in that email account then appears in your "My Blogs" section just like any other newsfeed or blog RSS entries! How cool is that? Not cool enough for you Mr./Ms. Cynic? Think of this: you have a way of reading emails without an email client (whether web-based like Hotmail or application-based like Thunderbird or Outlook Express), AND a free "junk" email account just like those described here that you can use for any purposes. What's more, there is a measure of permanence to these accounts since the email address is yours to keep until you choose to delete it. Even better, Bloglines will be adding the ability to reply to messages soon.

    If you haven't tried out Bloglines yet, give it a shot. You may just like it. Oh and Bloglines doesn't pay me to do any pimping like this. I'm not even sure how they make money in the first place.

  • Movable Type 2.65 released, version 3.0 details dangled tantalizingly

    Movable Type 2.65 has just been released - mostly a bugfix release with a few added features. The mt-send-entry.cgi fix is included (it already was included in any MT 2.64 packages after the flaw was discovered and summarily fixed), of course. As is a security issue with the XML-RPC server, details of which are, I think, intentionally undisclosed to prevent exploitation in the interim. MT users are encouraged to either upgrade to 2.65 or grab the fixed version of XMLRPCServer.pm immediately.

    Added features include an Atom syndication template and 2 template tags <MTIfNonEmpty> tag and <$MTEntryModifiedDate$> tag. In particular the <MTIfNonEmpty> tag frees you from having to install the MTIfEmpty plugin to achieve a similar effect.

    Details about Movable Type 3.0 were also released, tantalizingly dangling nice new features in front of MT users' collective monitor-reflecting eyes/glasses.

    Support for the Atom API is promised - yummy! Read Mark Pilgrim's article on the Atom API if you're clueless about it.

    Comment registration will also be added in response to comment spammers. Nice, but I'm still thinking I'd rather not comment on someone else's weblog if I had to register first - I'm doubtful it will be well-received, but I'd like to be surprised at what Six Apart can do. Surprise me, Six Apart, you hear? What I'd really like to see is something along the lines of Jay Allen's MT-Blacklist incorporated into MT. It is by far the most indispensable MT plugin I've ever used.

    It also seems that moblogging will also make an appearance, judging from this bit in Movable Type's news article:

    Additionally, for those interested in posting from mobile devices, we expect this to be a welcome release.

    The rest of the announced features seem pretty mundane. I'm pretty sure the developers are hiding the good stuff from us.

  • Ungreeking, a color wheel, and GIFs

    Some things that could prove useful to myself in future:

    • ungreek is what you could call a lorem ipsum generator - it generates placeholder text from a variety of sources including the Old Testament, the US constitution and even the Internet RFC 1630.
    • 4096 Color Wheel is a color wheel written in JavaScript. Nice to have an instant swatch at the side.
    • Pixel2Pixel is a PHP script that allows you to change any color in your GIF images on the fly. Just upload your image and choose what colors you want changed.

  • Easy .htaccess

    Dot HtAccesser is a web-based tool that generates .htaccess files for you via a simple form. Writing the right .htaccess file isn't exactly rocket science, but we are fortunately spared reading up when in doubt, especially when the official Apache .htaccess tutorial is so sparse.

  • Email addresses for dodgy websites/people

    killMAIL and Mailinator are 2 of, I think, many free online services that give you an instant, disposable email address. Great for use on websites which want your email address for apparently no reason other than to send you product updates, offers and other emails that you never want. Or you may be worried they will sell your email addresses to spammers. Or maybe there's that insurance agent who has been bugging you for your email address so that he can send you "more info".

    Whatever your purposes and needs, I'm sure you'll find services like this useful from time to time.

  • Shooting around corners

    A cool weapon from the Israelis - the Corner Shot:

    Corner Shot picture

    Good for anti-terror and SWAT teams, but I dread the day it gets into the hands of an overzealous sniper (remember Maryland?).

    Read more about it.

  • MT-Blacklist Updater

    MT-Blacklist Updater is back up and works, this time. I'm still in need of testers so do try it out and let me know of any bugs you may find. Thanks!

    Comments, suggestions, bug reports are highly appreciated.

    I used to use a very simple PHP script which simply grabs the latest master blacklist and overwrites the local copy on my server. I didn’t think too much of having any entries I’d made myself being overwritten then. Now I do, after I received quite some spam that wasn’t caught by MT-Blacklist, and decided to rewrite it so that it uses the blacklist changes RSS feed to update my local blacklist non-destructively.

    The results of a few hours work (mostly spent writing documentation!): MT-Blacklist Updater.

    Didn't work! See this and this.

  • Google can track your FedEx delivery

    Search By Number is a new Google feature that allows you quick access to your UPS or FedEx delivery information, and easy access to patent, FAA and FCC numbers/IDs.

  • New IE vulnerability - fake URLs

    IE has a new security flaw which will be a major boon to spammers and frauds. This flaw allows spoofing of URLs via the http://[email protected] nomenclature. For example, a fraudulent spammer could well direct victims to http://wwww.paypal.com&sessionid%[email protected], but have it show up as http://www.paypal.com&sessionid%123456789 (of course, the fraudulent webpage has to be convincing enough to fool victims into believing they are actually at PayPal!). This works because by including a 0x01 character after the "@" character, IE hides the real location of the page!

    To see it in action, fire up IE and check out this demonstration.

    Source: Simon Willison

  • Easy charity with Buck-a-Hit

    Jack Bog and his wife are giving USD1 for each hit they receive today (Wednesday, December 10, 2003) from 12:01 a.m. to 11:59 p.m. PST (GMT-0800) to charity.

    Want to feel charitable without any hard commitments? Visit his blog once or twice today Or 24 times. Quoting Jack:

    If you go away and come back more than an hour later, however, that will count as two hits.

    Well, seems like a good way to promote your site too - of course I do not doubt the altruistic motives behind Jack's move.

  • Using a web browser with a phone dial

    Seems David Lu does some interesting work, like this Phone Dial Web Browser for instance. Ouch! Rotary dial? IP address only? Meh!

    This electronic etch-a-sketch simply named Etch is another (apparently) frivolous project.

    On the other hand, 3D XML Viewer (requires Flash Player) is an amazing 3D, graph-based visual representation of XML documents. Reminds me of TheBrain. Now this is something useful.

  • Just Say No to Microsoft

    Just Say No to Microsoft

    Not pretty, a little biased. But great alternatives are listed.

  • A good Mozilla Firebird nightly build

    Finally a Mozilla Firebird nightly (2003-12-07) with most severe regressions fixed (severity as determined by Jesse Ruderman).

    Builds since 2003-11-21 do not work with Tabbrowser Extensions (the fault is with TBE, not Firebird), so I'd given up on nightlies since. But this major regression-free build had me coming back and searching for a fix to this incompatibility. And guess what? There is a fix, provided kindly by me4get at the Mozillazine forums. The related thread is entitled Updated TBE for Mozilla/5.0 (Gecko/20031121) and you can install the updated TBE by clicking here.

    I'm using scragz's optimized builds - fast and works great!

    Source: The Burning Edge

  • Spam Wait for Movable Type - Humans wait, bots don't

    Spam Wait is a Movable Type hack cum plugin that forces your visitors to wait for a set period of time before being allowed to post comments. A decidedly smart move to avoid comment spamming by spam bots. The idea behind it? Human users would spend some time to read your entries/articles first. Spam bots wouldn't.

    I won't install this, yet, but it is a smart idea. MT-Blacklist has been very good at blocking comment spammers and only 2 have filtered past it since I've installed it. Besides, judging from the behavior of past comment spammer activity, the majority of spammers at this site are human spammers, not bots. And now that my PageRank has dropped from 6 to 5, I'm pretty sure I'd see a sudden drop in comment spam activity here. Sort of a mixed blessing, that.

  • Mozilla Thunderbird 0.4 - new features, upgrading, and more

    Mozilla Thunderbird 0.4 was released yesterday in quick succession to the two 0.4 release candidates. It's all good, and worthy of a point release. If the servers are too slow for you, you may want to try the Thunderbird 0.4 (Win32) torrent, or the Thunderbird 0.4 (Linux) torrent.
  • Abyss Web Server - small and really neat

    The Abyss Web Server from Aprelium Technologies is one of the neatest, tiniest web server I've come across. It's only 144 KB for the Windows version and the Mac, Linux and FreeBSD versions are around that size as well. It's also free. What's more, adding PHP support is quick and painless. Just follow the instructions for adding PHP support. I did all of it under 5 minutes (like Phil - see the source for this entry).

    Adding ASP support and Perl support appear to be a snap too from a cursory glance.

    Why is this cool? Because it is very useful as an easy-to-setup test platform for server-side scripts like PHP, ASP and Perl scripts. Being flyweight helps too, since there are no long download times for those still using dialup modems. Everything is run via CGI - there are no modules, unlike for Apache. There is a single config file, abyss.conf, which contains Apache configuration-like directives. The good thing is, there's no need to go into the file to make changes - the web-based configuration interface provided by Abyss allows a simple click-and-point interface which is a breeze to use.

    What a boon to beginners (AKA newbies) who often have trouble setting up Apache with PHP support. I'm going to recommend this as an alternative to Apache for anyone with difficulties setting that up in the future.

    Source: Phil Wilson

  • Your site in Konqueror, too!

    Remember iCapture? In case you don't, read this.

    Now it seems there's also another neat little free service called Kcapture.

    And I just noticed that IE isn't the only browser that doesn't listen to you when you tell it you want a dotted border (and NOT a dashed border you stupid piece of crap).

  • The monthly report - November 2003

    redemption in a blog received 12,262 visits for the month of November 2003, working to a rounded average of 408 hits per day.

    Most visited entries:

    Other interesting stuff:

    • I've been blogging for 6 months since my very first tentative steps into the blogosphere. I can say I'm thoroughly addicted to the weblog world and picked up a whole buncha stuff along the way. So yay for blogging!
    • My Google PageRank is still 6! OK maybe that's not so interesting. It will be when (or rather, if) I hit PR 7.

    Once again, as always, thanks for reading!

    Past monthly reports:

  • AIDS awareness

    Today is World AIDS Day.

    AIDS awareness ribbon
  • CSS selectors translator

    Confused by CSS selectors like a:not([href*="codefront"]) and body > h2:not(:first-of-type):not(:last-of-type);? SelectORacle can translate that into plain English (or Spanish) for you.

    Very neat idea that's apparently the brainchild of Eric Meyer (the CSS guy), and implemented in Python by Kevin Jacobs.

  • The beauty of 20/20 vision

    You never realize what you're missing until you experience it for yourself. I'd just bought a new yearly supply of contact lenses with the refractive index amended to the latest -6.00 (right eye) and -5.75 (left eye). Last time I had contacts made, they were -4.50 (right eye) and -4.00 (left eye). Either the last optometrist had the measurements all wrong or staring at the monitor the whole day has worsened my myopia.

    And now with the new contact lenses, everything is so clear. I thought it was a slightly blurry with my old contact lenses, but in retrospect, it was definitely more than "slightly". I can see every blade of grass and every pore in your face (that is, if you ever stuck your pretty face close to mine in real life). I can see the bus number from a greater distance away. I can tell you what the first letter is in the 20/20 row of the optometrist's eyechart.

    To those few of you who aren't myopic yet: take care of your eyes peeps! Stop staring at your monitors for too long! Hmm... What's the point? Nobody ever listens to nagging like this. I know I didn't.

  • You don't need a Mac to see your webpage in Safari

    Ever wondered how your website looked in Safari, but don't have a Mac? Well, now you can thanks to Dan Vine's automated online screen capture tool.

    For those not in the know, Safari is the default web browser in Mac OS X, which is one reason you should care how your site looks in it. Safari utilizes the KHTML rendering engine which is the same engine that powers K-Meleon and Konqueror. KHTML is to Safari what Gecko is to Mozilla (and Mozilla Firebird and Epiphany). One thing I can say about KHTML - it's fast. I tried K-Meleon just to see for myself and it's light and fast. Here's what David Hyatt had to say about the merits of KHTML compared to Gecko.

    Anyway, here's this site in Safari: screenshot. Nothing broken so far. Good.

    Source: webgraphics

  • Create your own South Park Character

    Create your own South Park Character - here's me:

    My South Park avatar


    Have fun!

  • Getting funky with Nice Titles

    Dunstan wrote up on an improved Nice Titles script in his beautiful and very interesting blog. What are Nice Titles you say? They are the brainchild of Stuart Langridge and appear on this site when you hover over a hyperlink with the title attribute set. If you're using Mozilla or Mozilla Firebird, the CSS takes advantage of the -moz-opacity to give the popup div some transparency, and -moz-border-radius for rounded corners (other browsers will render an opaque rectangle).

    Why Nice Titles? Firstly, they are kinda cool. And I like the way the URL is displayed in the popup div. Control over the delay between mouseovers and the div popping up helps to have title dialogs show up faster (or slower) than the browser default - I personally find the delay a little too long for "standard" titles.

    All this is done using purely DOM methods (DOM is a W3C standard) and will work in all correctly-implemented DOM-compliant browsers. Of course, this method degrades gracefully in the face of text browsers, limited browsers in mobile devices, screen readers, etc. Want it for your site? It's as easy as uploading the CSS and JS files to your webserver and linking to them. Any existing titles will magically become a Nice Title.

  • Movable Type spam vulnerability

    If you're a Movable Type user, you probably already heard of the spam vulnerability of the "Email this to a friend" script in Movable Type. Six Apart has posted a fix, of course, with a disclaimer that the fix only discourages spammers, not prevent spamming outright. What is the vulnerability anyway, you ask? You may want to read this thread for the skinny.

    What can you do? Well, you should remove mt-send-entry.cgi completely if you don't use it. I doubt too many end users actually use any of that "Email this to a friend" functionality anyway so you probably have nothing to lose. Why do I say so? One word: usability.

    1. Your users have to be able to find the link first to use it.
    2. Your users probably are accustomed to using email or IM to send links (think ICQ's Send URL functionality).
    3. The average surfer is unlikely to be so enthusiastic as to send links to his/her friends. Of course, this assumes that your average surfer has friends.

    The point? Scrap that functionality, delete that file.

  • More accessing Hotmail in Thunderbird

    An interesting coincidence it is that Mozillazine has posted a link to a write-up on how to access Hotmail in Mozilla Mail in its article entitled "Accessing Your Hotmail Account Using Mozilla Mail" - see my previous entry.

    Read the comments (or rather, "talkbacks"). I wasn't aware of MrPostman. Judging from the comments so far, though, Hotmail Popper seems to be the best-received thus far. Keeping an eye on the talkbacks for an even better solution!

  • Accessing Hotmail with Thunderbird

    Not too long ago I got an email from a reader of this weblog that he needed help setting up Thunderbird to read his emails. In the end, it turned out that he was given the wrong email account information by his ISP, so I didn't provide any real assistance. As it is, he was pretty impressed ("enamored" was the word he used) with Thunderbird - always nice to see a convert. Except for one problem: it can't access Hotmail accounts.

    For that matter, Outlook Express and Outlook seem to be the only email clients that can access Microsoft's Hotmail via its web service. And as it turns out, I have a Hotmail account too and used to access it in Outlook Express. We both needed a solution, excepting logging on to the Hotmail website to check for emails. What is so troublesome in logging on to the website? For one thing, the Hotmail website doesn't really work in non-IE browsers. There is IE-only scripting involved in several portions, including the rich text editing and the adding of email recipients from a drop-down. And it bugs me terribly that the MSN Messenger application starts running whenever I log into the Hotmail website (it's true - when you close the IE window for Hotmail, MSN Messenger exits).

    I did the smartest (or should I say "only") thing possible and Googled for a solution - it turned up this FAQ entry. Great news, then, since Hotmail Popper is a free application that really works just fine. One word of caution though: make absolutely sure you change your email account settings for the Hotmail account to "Leave messages on server" if you really want to do so before downloading any emails. If you're using Thunderbird, go to Tools -> Account Settings and select your Hotmail account. Under Server Settings, check the box that says "Leave messages on server" and the box that says "Until I delete or move them from Inbox", as shown in the screenshot below.

    Thunderbird settings screenshot
  • How to really kill Windows Messenger

    One of the biggest annoyances in Windows is Windows Messenger, which springs to life of its own accord whenever you boot up. Or when you open Outlook Express. Or for no apparent reason at all. And you can't close it because "some other application is using it" or some rubbish like that. It's there like a virus you can quarantine, a worm you can't eradicate, a friend you really are not interested in entertaining because you're so damn sick of him/her. It allows spammers to alert you to ways to improve your sex life (penis enlargement, Viagra, yada yada), sell you University diplomas, and even "meta-spam" that tries to sell you ways to prevent spam.

    ... can't find no Windows Messenger?... sneaky Microsoft has hidden it!

    I've tried switching off the Windows Messenger service and unchecking all the boxes I can find that says "Start Windows Messenger when this program runs". Some people says it works. I know it doesn't, because my firewall (Sygate Personal Pro) alerts me that Windows Messenger is trying to send an outbound packet. Sneaky bastard.

    I lived with this parasite in my system for a few months. Now, I've finally found a fool-proof way to kill it - uninstall Windows Me