tech

Rails configuration: I'd rather do XML situps than hunt through dozens of files

Update: Shahid (see comments) mentioned that this functionality, or something very much like it, has been introduced in edge Rails. Not sure how I missed that, as I do read Ryan's blog. So it could be that my rant was misplaced.

This is something that's been bothering me for a while. In the early days of Rails, much was made of the simplicity of configuration: no big XML files like you have for Java Enterprise applications; just a database.yml file and a handful of simple environment options. But it's occurred to me that this is no longer true.

Take a look at the new video for the MOle, a plugin for monitoring your Rails application in real time; I'm not talking about the plugin itself, rather the configuration files which are shown in the video. They are full of chunks of Ruby code for setting constants for use in the application, plus some other bits for working out whether localization is available.

Or have a look at the caboose sample Rails application, a neat integration of several plugins plus some Rails best practices into a template application. It's a nice application, with lots of great ideas, but that's not my point. Have a look at the environment.rb file and you notice things like this:

ASSET_IMAGE_PROCESSOR = :image_science || :rmagick || :none
...
ExceptionNotifier.exception_recipients = %w( your_email@test.com )

i.e. Ruby code to configure the various plugins and Rails components.

Or have a look at the environment.rb file for Typo. Again, a great piece of software; but the configuration file contains a multitude of options, extra load paths, checks for capabilities, setting up of constants and environment variables, etc..

My point is: there isn't "one true way" to configure Rails applications. Yes, Rails has dispensed with XML configuration files; but as the complexity of available plugins, libraries and capabilities has increased, the infrastructure for managing them hasn't kept up. We are reduced to configuring a ton of options in environment.rb and other places (e.g. app/controllers/application.rb for the session key, plugin-specific YAML files in config), none of which is particularly well documented. If you want to set up constants for your application, there is no right way to do it: you just do it how you like. This is all well and good, but makes it very hard for beginners to get their head round, and puts you back into a position not that far from configuring a PHP application in the raw.

My suggestion: some central mechanism for storing constants without having to resort to coding them in Ruby (maybe a constants.yml file); some mechanism for declaring a list of libraries you want to load without resorting to conditional require statements (e.g. a libraries.yml file which lists dependencies and provides a neat Ruby-ish syntax for reacting to error conditions); better documentation for the configuration options you can set for the various Rails libraries and a single YAML file for setting them (e.g. rails_config.yml); a standard mechanism for configuring plugins via YAML files (e.g. <plugin>_config.yml). Maybe there's a plugin already out there I don't know about that can do this...

I know this would take us away from simplicity into J2EE land: but one thing you can say about Java web applications is that there is far more consistency in how they are packaged, making them easier to deploy and their configuration more transparent. Now that Rails is becoming a complex beast, it might be time to put some structure back in.

Securing the Windows operating system

As the resident IT person among my friends and family, as well as being an IT advisor at work, and despite me hardly ever using Windows, I still get quite a few phone calls asking me about securing Windows, cheap or free software for Windows, configuring wireless on Windows, etc.. I'm sure many of you are in the same boat. Anyway, I read a really useful article in the Guardian (by Jack Schofield) last week which covers a lot of the "how do I secure my Windows installation?" style questions. Here are the salient points (plus a few of my own), which should act as a checklist for me (and hopefully you too) when answering those support-desk style calls:

  • If you're using broadband, get a proper router with NAT and a built-in firewall, rather than just a modem.
  • Make sure you use Windows Update to keep your system patched.
  • Use Firefox instead of Internet Explorer.
  • If you insist on Internet Explorer, use version 7 (Windows XP Service Pack 2 or Vista).
  • Use Thunderbird instead of Outlook Express. This will also help with junk.
  • Use a firewall.
    • The one which comes with Windows is OK.
    • Kerio Personal firewall by Sunbelt Software is Jack's recommendation. There is a free (feature-hobbled) version or a full version which costs $19.95. It works with Windows 2000 or XP.
  • Use an anti-spyware program.
    • Spybot Search and Destroy works well but has quite a complicated interface.
    • Grisoft's Anti-Malware product (which also does anti-virus) is Jack's recommendation. There is a free version which doesn't have all the features (including on-access scanning - i.e. you have to explicitly run the free version for it to detect spyware).
  • Use an anti-virus program.
    • There is a free (not open source) product made available by AOL called ActiveVirusShield (for Windows 98, 2000, ME and XP).
    • Open source ClamWin: but this is an on-demand scanner (you have to explicitly run it on files, so it won't automatically scan things you download).
    • Winpooch (mentioned in the comments) is an open source tool which integrates with ClamWin to offer anti-virus, anti-spyware and anti-malware. (Might give that a try myself.)
    • Grisoft's Anti-Malware includes an anti-virus.
    • Housecall is a free online tool which you could use periodically for extra peace of mind.
  • If you're using anti-virus and anti-spyware, make sure you keep them up to date with new virus/spyware signatures.
  • Jack mentions a tool called Cyberhawk which is a so-called HIPS (Host Intrusion Prevention System). There is another tool called Winsonar which keeps an eye on system processes and alerts you to unusual new activity.
  • Turn off non-essential services. I normally do this for people, starting with Messenger (which is responsible for those annoying desktop popups advertising porn and college diplomas). I also turn off Computer Browser, Net Logon, Remote Access Auto Connection Manager, Remote Access Connection Manager, Remote Desktop Help Session Manager, RPC Locator, Server, and Terminal Services (if possible).
  • Keep backups. Please.
    • Buy a big USB disk. Most come with backup software these days. Make sure you back up important things regularly. If you can afford it, buy two disks, and keep two backups. If you've got the patience, copy really important stuff to CD periodically.
    • Online backup is a good idea too. Chris (see comments) suggests Mozy. For the technically-inclined, Strongspace is good. I rolled my own using a cheap Dreamhost account (get a cheap Dreamhost account of your own using my referral code). It might be hard to implement on Windows, however.

You could also check out this resource which lists loads of other free tools.

Disclaimer: I take no responsibility for any issues you might experience with any of this software or which arise after you follow my suggestions.

Oh happy day

EMI releasing music without DRM. Finally, someone has woken up to the future. I was very happy when I heard about this on the news: yes, it's AAC, and yes, they're more expensive (unless you buy a whole album), but it's a brilliant start. I could even use the service, as I have the facilities to convert from AAC to Ogg or MP3. So there's potentially one new customer already (providing the iTunes store is accessible via Linux; or if I can get it running on Windows under VMware).

Hopefully this will also put pressure on companies like eMusic to offer more tracks on their subscription plans: so far they've made a virtue of offering DRM-free music, but haven't been able to offer many mainstream acts. They recently lowered the number of downloads on their plans for new users, which I thought was a terrible idea. Maybe once iTunes and the like offer DRM-free music, eMusic will have to up the downloads for subscribers if they want to stay in the game.

I agree with Dave Thomas: don't pretend HTML forms support PUT and DELETE

Dave Thomas writes about a thing he's calling RADAR. His point seems to be that bending an HTML front-end into shapes so it can "pretend" to do all the HTTP verbs (PUT, GET, POST, DELETE), when in fact it can only do two (GET, POST), is daft. I've always thought this about how REST-ful Rails contorts HTML forms using hidden fields to mock PUT and DELETE. Daft.

Dave's point is that we should be writing properly REST-ful back-end servers, which we then put a thin veneer of HTML application in front of. Funnily enough, I was writing about the same idea (a REST-ful, Atom-like back-end, with different HTML client front-ends) in October 2005; in fact, I was talking about applications structured like this in 2004 (it's just I'm not Dave Thomas, so nobody listened to me). I'm glad Dave's come up with a name for this and popularised the idea. It seems to me the way all web applications should be written. (By the way, I never got beyond planning for my ecommerce platform, but I've still planning to write an application architected this way as my next project.)

Showing validation errors for "belongs_to" drop-down boxes in Rails

Update: See the comments for an explanation of why I'm not doing validates_presence_of :project_id (instead of validates_presence_of :project). I'm prepared to be proved wrong on my assertions there, but they seem to hold up to my testing.

A common Rails pattern is to have two models with a has_many/belongs_to association, e.g.

class Task < ActiveRecord::Base
  belongs_to :project
  validates_presence_of :project
  validates_associated :project
end

class Project < ActiveRecord::Base
  has_many :tasks
  validates_presence_of :name
end

Then in the form for creating/editing a task, you might provide a drop-down box to select the associated project in the view, and display project error messages underneath it:

<p>Project: <%= collection_select :task, :project_id, Project.find(:all), :id, :name %></p>
<%= error_message_on :task, :project %>

Now if you try to save a task, you'll get the appropriate error message if you fail to select a project; also, if an invalid project gets assigned (e.g. invalid URL or incomplete Project instance), you'll get a validation error. I think this in uncontroversial.

BUT

If an error occurs on the project associated with the task, although you get an error message in the view, the drop-down box isn't highlighted (as it is with other fields). This is because the highlighting is done by looking at the errors object associated with the task, finding errors for any fields in the form with the same name, then altering the HTML for that field. So if there is an error for description and there's a field called task[description], that field gets an extra HTML <div> wrapped round it, e.g.

<div class="fieldWithErrors">
<input id="task_description" name="task[description]" size="30" 
type="text" value="" />
</div>

The issue with my task and project is that the validation errors are added for the projectattribute on the Task instance; but there isn't an HTML form field called task[project]: the drop-down is actually called task[project_id]. I tried to create a select element called task[project] instead, but this doesn't work (ActiveRecord complains that it is expecting a Project and you supplied a String).

My solution is to copy any project error for the Task to an error for project_id. I did this in the model, in the after_validation callback, like this:

class Task < ActiveRecord::Base
  belongs_to :project
  validates_presence_of :project
  validates_associate :project

  def after_validation
    project_error = errors.on(:project)
    errors.add(:project_id, project_error) if project_error
  end
end

This works fine: now any errors for the project are reflected onto project_id, so the task[project_id] drop-down box gets the error <div> (with class attribute set to "fieldWithErrors") wrapped around it. However, this doesn't feel quite right, and it makes me think I'm missing something. Is there some Rails voodoo I've overlooked which can be used to do this more cleanly?

Presentation on dynamic languages and Ruby on Rails

I was recently asked by the Coventry branch of the British Computer Society to give a presentation on dynamic languages, with special reference to Ruby on Rails. I'm not really a computer scientist (despite my Ph.D. being in that subject, I'm really academically a cognitive scientist), so I had to learn quite a bit about the topic first (dynamic vs. static, manifest vs. implicit, strong vs. weak typing). I think I did a reasonable job, though some may disagree with my definitions. I also did some live programming, and built a simple application during the presentation, showing some Ruby meta-programming features that Rails makes use of. Again, I did my best to work out what the hell was going on in Rails when you use dynamic finders etc., but in a couple of cases I made some inferences based on minimal evidence! It's tricky stuff, and involved a fair bit of forensics and consultation of Ruby for Rails.

The presentation went well (the chairman said it was one of the most detailed and technical sessions they'd had for a while, which reassured me that I did kind of know what I was talking about). The audience had quite a few old skool FORTRAN programmers who seemed initially skeptical of dynamic languages: I think I placated some of this skepticism by comparing ActiveRecord and Hibernate, and demonstrating that they have different purposes/environments/sweet spots. I also did my best to explain why/when/how you might use a static language vs. a dynamic language.

I've attached the presentation and my script for building the application live for anyone interested. Any comments or corrections are welcomed.

Drupal Last.fm module - now works with Drupal 5!

Another update: I noticed today that this module had disabled my site. I'm not sure why, but I think it may be because Last.fm wasn't responding. I'm still using it, but if you have it on your site and everything goes bad, it might be because Last.fm isn't available. I manually disabled it in the system table for my Drupal install and my site came back to life, so I know it was this module that caused the problem. I need to do some more debugging before it's suitable for production, so use at your own risk, or exclude the block from the admin page so at least you can disable the module if it causes problems.

Update: I've just updated this slightly, so that the cached track listing is used if your recent tracks from Last.fm is blank. This avoids you getting an empty listing if you haven't been listening to music for a while. I also updated it a second time to prevent errors occurring if you get broken XML back from Last.fm (which happened to me once, and caused my whole site to crash).

I've had a couple of requests for my Last.fm Drupal module, in response to a previous blog post. This was also a good opportunity to learn how to write modules for Drupal 5 and learn the new forms API, which I've now done.

The new version is attached to the bottom of this post. It requires Drupal 5 and PHP 5 with the DOM extension enabled (this is used for parsing the XML feed from Last.fm). It won't check whether the DOM extension is enabled (it's not that clever yet), so it could cause issues (like your site breaking) if you try to install it on a system without PHP 5 and/or the DOM extension.

Install it as a standard module (drop into the modules directory, enable in Administer > Site building > Modules). I've integrated it with the admin. system (under Administer > Site configuration > Last.fm module settings), so you'll need to configure it there before it works. The settings required are:

  • Your Last.fm username.
  • The number of tracks you want to show (1-10).
  • The period for which you want to cache the list (5 minutes to 1 hour), or no caching. The listing is cached in your Drupal root directory (under the filename lastfm_cached.html) for efficiency. In cases where Last.fm doesn't respond quickly enough, you will get an error message (rather than it just leaving your site hanging).

Then turn on the block with the title Recent tracks in the block admin. pages. And away you go! As proof it actually works, see the left-hand side of this site, where it's in action.

Linux desktops - a real life example

Here's an interesting, real example of a normal (power, but not especially technical) user trying to make a switch from Windows to Linux. It's a good article, and should be required reading for all Linux on the desktop evangelists. I agree with the author (Sharon Machlis) that large parts of the problem with adopting Linux are down to lack of vendor support. I still think this is the big sea change required to make Linux acceptable on the desktop: decent hardware support, applications ported to Linux, support for consumer software (games) and devices. We have to be realistic if we expect people to use Linux in their day-to-day lives.

I use Linux on my laptop all the time, but I'm quite a techie, and can install stuff from source or obscure repositories if I need to. Also, I deliberately avoid hardware I know won't work with it (e.g. iPods) or will probably not work straight away (e.g. webcams). What's the first thing I do when I do a fresh install of Linux? Put the Windows fonts on it, install mp3 support, install Realplayer, install the Flash plugin for Firefox, install the Quicktime codecs for mplayer and the mplayer Firefox plugin, install Java (should be easier soon), and install support for DVD decryption (naughty!). So I can actually use the system in my daily life.

I'd willingly pay someone a decent amount of money if they'd provide me with a legal Ubuntu repository which does all of this for me. Even better would be if DRM were abolished and everyone used Ogg Vorbis to encode their music: but it just isn't going to happen any time soon. While I am (deep down) a free software person, lots of people who provide content or provide services aren't, and I often need to be able to use their stuff. I stop short of using anything which requires Internet Explorer, but apart from that, needs must.

New ecommerce module for Drupal

Yesterday a new ecommerce module for Drupal, Ubercart, was announced. This is pretty exciting. I'd looked at the existing Drupal ecommerce module before, but it's a bit of a mess, cobbled together from what seemed to be endless disparate modules that half work together. (Not to belittle the achievements of the team which produced the ecommerce module: it has masses of functionality, and integration with all sorts of stuff; but it's too complex for normal people who just want to sell books or something.)

What marks out Ubercart?

  • Nice documentation. It looks like the development company behind it has actually written a professional, clear, comprehensive set of manuals for it. This makes the world of difference to how easy a product is to use.
  • Support for anonymous checkout. Users new to the site can be gently guided through checkout without having to create an account before they can do anything. The explanation of scenarios this encompasses is thorough and well thought through.
  • The system looks modular and well designed. You can just load up the parts of the framework you need, and the dependencies and features of each are properly explained.
  • The developer's guide is clear, concise and gives you a decent number of examples of the API (hooks), variables available to templates, etc.. They've even gone to the trouble of providing a coding standards guide, and a list of useful open source applications you can employ to start doing your own development.
  • Because it's based on Drupal, and Drupal has a whole slew of modules (including CRM), Ubercart increases the potential of Drupal as a simple, extensible, general-purpose business framework. Imagine if your online shop and your CRM could easily share customer data...

The only issue I'm not clear on (probably somewhere in the documentation) is whether you could use it to build a electronic downloads site (e.g. to sell images or PDFs). I'm sure other stuff will crop up too (like currency handling).

This is a really exciting project, and is the kind of thing that could give Drupal a big boost as a general purpose web framework. It definitely looks like it could give osCommerce a kick up the arse. It's actually a joy to read the well-written, clear documentation (there are a few areas where it's incomplete, but generally it's pretty comprehensive). I've added it to my todo list of products to have a look at within the next month, and hopefully I'll end up using it myself (eventually).

Congratulations to the team, and I wish them the best of luck in getting it off the ground.

Installing Ubuntu 6.10 (Edgy) as a guest operating system on VMware

Note: This is an update of my earlier set of instructions about installing Ubuntu Dapper as a guest operating system on VMware.

This time I'm using VMware Workstation 5.5.3 build-34685 and Ubuntu Dapper as the host operating system. I am installing Ubuntu Edgy as the guest operating system. I also worked out how to get the VMware Tools to work. Here's what I did:

  1. Downloaded the Ubunutu .iso file from http://ubuntu.org/. (I tried to install off CD and failed miserably: it hung when it tried to create the Ubuntu Live CD user.)
  2. Created a new virtual machine in VMware (I accepted all the defaults). I set the hard disk to 4Gb which should be big enough.
  3. Edit the virtual machine and set its CD to use an ISO image; point it at the Ubuntu .iso file you downloaded.
  4. Boot the virtual machine. Press enter when prompted to install Ubuntu. This will boot into the Live CD version of Ubuntu.
  5. Once in, double-click the "Install" icon on the desktop to install the Live CD image onto the hard disk.
  6. Follow through the installation procedure.
  7. When you power off ready for reboot, you can point the CD for the virtual machine back at the physical drive (I used the "autodetect" setting). This prevents the virtual machine booting from the CD image in future (you can delete the image once you've completed the install, if you want).
  8. Boot into Ubuntu. You are now using the hard disk installation rather than the Live CD image.
  9. Upgrade all the packages on the system. You do this by clicking on the orange asterisk icon on the right-hand side of the top menu bar. You'll also need the networking to be up to do this. It takes quite a while, but it's worth doing.
  10. Select the VM > Install VMware Tools option from the VMware workstation menu. You should get a CD icon on your Ubuntu desktop; if not, go to Places and choose the CD there.
  11. Copy the .tar.gz file to your desktop. Right click on it and select "Extract here". This will give you a folder called vmware-tools-distrib.
  12. Get a console up and cd to the vmware-tools-distrib directory.
  13. Make yourself root (sudo su).
  14. The installation requires a recent gcc and the Linux headers. Fortunately, these both appear to be installed by default on Edgy, so you don't need to do anything (unlike the previous set of instructions for Dapper).
  15. It's worth creating a symlink to your kernel source to make the installer run more smoothly (it looks for /usr/src/linux and complains if it's not there):
    ln -s /usr/src/linux-headers-`uname -r` /usr/src/linux
  16. While you're still in the vmware-tools-distrib directory, run the install script:
    ./vmware-install.pl
    I accepted all the default settings and said yes to everything.
  17. This gets everything (pretty much) working except the mouse. You can fix this like so:
    1. sudo ln -s /usr/lib/vmware-tools/configurator/XOrg/7.0/vmmouse_drv.so /usr/lib/xorg/modules/input/
    2. Edit /etc/X11/xorg.conf and find the section headed Section "InputDevice", with the line Driver "mouse". Change the Driver "mouse" line so it reads Driver "vmmouse".
  18. To get copy and paste working, you have to run vmware-toolbox inside the virtual machine; to get the "Autofit Guest" feature requires the vmware-user program to be running in the virtual machine. You can enable both of these applications to start when your Edgy virtual machine starts like so:
    1. First, you need to symlink a couple of VMware libraries so that Ubuntu knows where to find them (for running vmware-user). At a command prompt, run this command:
      ln -st /usr/lib /usr/lib/vmware-tools/lib32/lib*/lib*
    2. Go to the main Ubuntu menu (inside the virtual machine) and select System > Preferences > Sessions. Then select the Startup Programs tab.
    3. Click on the Add button and type vmware-user in the text box. Click on OK.
    4. Click on the Add button and type vmware-toolbox --minimize in the text box. Click on OK. Note that this window must always be open for cut and paste between the guest OS and the host one to work. If you want to push it into the system tray, you could use AllTray to do that. There is an --iconify flag for this command, which should presumably turn it into an icon, but it doesn't seem to work for me.
  19. As I'm using a widescreen laptop, I also had to edit the settings for my screen resolution in the Section "Screen", e.g.:
    Modes "1280x768" "1024x768" "800x600" "640x480"
    This works fine on my IBM Z60t.
  20. Logout and kill X with Ctrl-Alt-Backspace. This should make all your new settings come alive.

Hurrah! It works!

Credits: Thanks to Sean Flanigan for extra tips on enabling cut and paste and guest window resizing.

Syndicate content