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.
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:
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.
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.
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.)
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?
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.
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:
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.
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.
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?
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.
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:
ln -s /usr/src/linux-headers-`uname -r` /usr/src/linuxModes "1280x768" "1024x768" "800x600" "640x480"Hurrah! It works!
Credits: Thanks to Sean Flanigan for extra tips on enabling cut and paste and guest window resizing.