Take care when writing to files using puts


For writing text files; especially line by line, it’s perfectly acceptable to use puts. But, be warned that this will append a line break after the data being written. This behaviour is not desirable when working with binary files, when IO::write is a better choice to avoid unwanted line break characters.

For example:

file = File.new('temp.file', 'w+')
file.sync = true

while data = get_data
  file.write data
end

file.rewind

Long-holding shortcut keys in OS X (and other cool combos)


I switched to Mac around 10 years ago and have picked up a variety of shortcuts over the years to streamline my workflow. But, something I only found out about this weekend was the “long-hold”.

What’s a long-hold?
Hit your Dashboard key. Your dashboard appears. Hit the key again and it disappears in an equally pleasing manner. Now try it this way: press your Dashboard key (and keep it held), your Dashboard will appear. To make it disappear simply let go of the key!

Where else does this awesome trick work?
Well, since I’ve only just found this out, I’ve not had a huge chance to uncover many goodies, but here’s what I’ve found so far. Leave a comment if you know of any others.

  • Dashboard
  • Exposé
  • Dictionary lookup (Ctrl+Cmd+D)

What other shortcut modifiers don’t I know about?
Clicking menu items, or shortcut keys whilst holding shift or option can do slightly different things, here’s a list of things I’ve discovered, some of which are useful, some not so much:

  • Hold Shift while changing volume to suppress the system click
  • Hold Option while clicking on the volume or wireless menu items changes the menu items
  • Hold Shift while opening Spaces, Dashboard or Exposé – I’ll let you try that out out (very cool)
  • Holding Command allows you to reorder icons on the menu bar by dragging them about
  • When using Command+Tab to switch windows, holding shift as well allows you to move right to left
  • For fine-grain control when dragging scrollbars, hold Option to scroll one pixel at a time
  • Want to drag a window in the background without activating it? Hold Cmd

Those were cool, are there any other shortcuts that need a place in my life?
Yep. Some of these are amazingly handy, some not so much!

  • I find windows users complain a lot about the lack of Home and End keys which on a PC keyboard are tucked away somewhere 3 feet to the right of the keyboard.  Life with a Mac is easier as these keys are under your fingers already: Ctrl+A and Ctrl+E (should be familiar to you unix peeps) and Cmd+Left and Cmd+Right for beginning and end of line.
  • Here’s a couple for Safari that I use a lot: Ctrl+L puts focus in the Location bar and Ctrl+Shift+L will open a new window having Googled for the highlighted word :)
  • I covered it earlier, but to bring up the dictionary definition of a word in a Coco app: highlight the word and press Ctrl+Cmd+D
  • Ever wanted to blank the screen? Ctrl+Shift+Eject will turn brightness down to nothing.  Moving the mouse or typing will bring it back.  This works with long-holding too.
  • I’ve also noticed Windows users are very familiar with pressing Enter or Delete when highlighting icons in Finder.  On a Mac, the keys you want are Cmd+O to Open the item and Cmd+Delete to move it to the trash.

Please leave a comment if there are any other hidden gems you want to share with the world.

onbeforeunload event and differences between browsers


The [recently in webkit] supported onbeforeunload window event can be very useful to inform the user if they are about to loose some data which they haven’t saved yet etc. So, in your method that the event calls you will probably have some logic to decide if the method should return or not (returning a string from the method causes the message to be displayed to the user).

This is all very well and good, but we came across an instance where outside of this method we wanted to set a variable to stop the message, request a new window.location (to download an file) and then turn the variable on again. The code looked a little like this:

window.shouldWarnTheUser = false;
window.location = '/download/my.file';
window.shouldWarnTheUser = true;

And the onbeforeunload callback looked a bit like this:

window.onbeforeunload = function() {
  if (window.shouldWarnTheUser) {
    return "Some warning!";
  }
}

This gave us different results between different browsers and took some time for us to figure out why.

Consider the following example:

window.onbeforeunload = function() {
  console.log("3");
}

console.log("1");
window.location = '/download/my.file';
console.log("2");

Question:

What order do the numbers get logged out?

Answer:

  • Webkit: 1, 2, 3. (Safari 4.0 and Chrome 4.0 on OS X 10.6.2)
  • Firefox: 1, 3, 2. (FF 3.5 on OS X 10.6.2)
  • Both understandable depending on how you think the events are fired

  • Internet Exploder: 3, 1, 2. – What?! (IE 8 on Vista)

Keeping custom Rails model validations DRY


There are a few blog posts on writing model validators – this is another one which follows the DRY (don’t repeat yourself) philosophy.

Suppose you want to check that an attribute value is present in a table of tags in our database. Now, this isn’t a particularly good use case as you could use validates_inclusion_of, but lets assume that we want to DRY that up a bit.

Let’s write the validation to make the following work:

validates_inclusion_in_tags :primary_tag, :secondary_tag

The validation method would look a little bit like this:

def validates_inclusion_in_tags(*attr_names)
  validates_each attr_names do |record, attr_name, value|
    acceptable_values = Tag.find.all.map{ |obj| obj.name }
    record.errors.add(attr_name, "does not match any available values") unless acceptable_values.include?(value)
  end
end

What’s going on here? Firstly we attach our custom validation to the model using validates_each. This runs the block for each of the attribute names (symbols) in the attr_names array (note that we have used the splat operator in our argument list to flatten the arguments into an array so we can pass a list of attribute to validate). The validation block is passed three arguments: the record being validated, the attribute name (as a symbol) and the current value of the attribute that is being validated. We then execute a find on our Tag model to get an array of AR records which we then map to extract the name of each object into an array. Finally we associate an error to the attribute that we are validating if its value is not in the list of acceptable values we have created.

To make the validation method a little more configurable and a little more generic we can allow the validation method to be called with a hash of options. Maybe we want to define a different error message, supply a different model to validate against, or supply a different attribute to be selected from our model. This can be easily achieved like so:

def validates_inclusion_in_model(*attr_names)
  # Set up any default configuration options and merge on any passed to the validation
  configuration = {
    :message => "does not match any available values",
    :model => Tag,
    :model_attribute => :name,
  }.merge(attr_names.extract_options!)

  validates_each attr_names do |record, attr_name, value|
    acceptable_values = configuration[:model].find.all.map{ |obj| obj.send configuration[:model_attribute] }
    record.errors.add(attr_name, configuration[:message]) unless acceptable_values.include?(value)
  end
end

You’ll note that the default configuration options are merged with the configuration options that were passed into the validation method. This (very common) technique makes use of the extract_options! method which returns either the last item of an array if it is a Hash or an empty Hash.

OK, that’s all good – what’s the best way to get this method available to our models. The best ways are as follows: Either create a validators.rb file in config/initializers and open up the ActiveRecord::Base class like so:

ActiveRecord::Base.class_eval do
  # define your validation methods here
end

Or, the way I prefer is to to create a validations.rb file in your lib directory and use the following code:

module Validations
  # Extend the caller with the ClassMethods module
  def self.included(base) # :nodoc:
    base.extend ClassMethods
  end

  module ClassMethods
    # define your validation methods here
  end
end

Now you can pick and chose which validations are available to your models rather than extend every model that inherits from AR Base by using:

class Person < ActiveRecord::Base
  include Validations
end

Of course, if all of your AR models inherit from a shared base class which in turn inherits from AR Base, then you could also include the validations model at your shared base model level to give all your models access to your new validations.

Note on performance

The validation method here would load all the Tag records into AR objects in memory just to throw them away again. Also, this problem is exacerbated if you were to compare two attributes against the list as the objects are loaded for each call into validates_each.

Datatypes in Javascript – setting the record straight


It’s a question that often comes up and there are many places on the internet that give an inaccurate answer, so here’s the real-deal.

Javascript has three primitive datatypes:

  • number
  • string
  • boolean

There are another two special datatypes:

  • null
  • undefined

The others are variations of object:

  • object
  • function
  • array

See here for further reading.

Detecting an invalidly parsed date in Javascript


When parsing a date in Javascript, it is important to ensure that the resultant object has been parsed correctly. This is made slightly tricky as the resultant object will always be a Date object if created with new Date().

The problem is compounded by differences between IE and other browsers. IE will return a NaN whereas other browsers will report "Invalid Date".

Use the following code to be sure:

var d = new Date("Malformed date")
if (d != "NaN" && d != "Invalid Date") {
  // date is good
} else {
  // date is bad
}

Does anyone else have any good ways of doing this detection?

Reading Geek Night


An open community event for techy-types is getting underway in Reading.  These events are for people to get together in an informal setting, share knowledge and have a couple of beers!

The first of these monthly events is happening next Tuesday on the 10th November.  Add http://readinggeeknight.com/ to your bookmarks and check it out regularly for updates.  You can also follow @rdggeek on twitter or the #rdggeek hashtag.

Talks lined up for the first event are:

Who do you think you are? Ben NunneyThe internet is everywhere, and all of us in the tech community are connected in some way, shape or form. But how powerful is your digital identity, and what does it actu- ally matter to you? The talk will be a brief look at what your digital identity is, was, and can be – who are you, in the eyes of the internet? Does your boss really care what you had for lunch?

A practical introduction to Ruby on Rails Chris TingleyThis live code demonstration will take you from your first line of Ruby code, through some fundamental features of Rails with pit stops in MVC, CRUD, REST, automated testing and meta-programming, crossing the finish line with a functional web app! Hold on to your hats, all this WILL happen in 15 minutes.

What Is Windows Azure? Dom GreenWhat is Windows Azure? How will cloud computing change how we develop applications, manage our IT infrastructure, or even set up an online business? This whirl-wind talk will introduce Windows Azure, Microsoftʼs cloud computing platform.

Coding for kids Jim Anning – Coding is not routinely taught in UK schools until 6th form level. How can we encourage a new generation of kids to code? A quick introduction to MIT’s Scratch programming environment.

Installing modporter for Rails on Ubuntu Hardy Heron


It’s not an uncommon scenario for Apache to not play nicely with Rails when dealing with multipart forms and large uploaded files. There are a few solutions out there, but the best we have seen is modporter from the guys at ActionRails.

So, you’ll need a few things to beef up Apache; if you haven’t got them already:

sudo apt-get install libapache2-mod-apreq2
sudo apt-get install libapreq2-dev
sudo apt-get install apache2-threaded-dev

Now fetch the latest ModPorter from github from here. Once you have downloaded and unpacked this, edit the Rakefile and change the apxs variable to apxs2, then type:

cd modporter-directory
rake

And, you’ll probably want the modporter plugin for Rails to give you a nice consistent interface for uploaded files rather than differentiate String IOs and UploadedFile objects.

If you have git:

script/plugin install git://github.com/actionrails/modporter-plugin.git

Otherwise, download the tar from here, unpack and stick in your vendor/plugins directory.

Now configure Apache to use load some additional modules, edit your apache.conf file to include (your path to modules may be different):

LoadModule apreq_module modules/mod_apreq2.so
LoadModule porter_module modules/mod_porter.so

Tracking down a stolen laptop


This is a bit off-topic, but worthwhile sharing.

I recently, rather unfortunately had my laptop stolen and now I have a replacement; I remember hearing about software that could use the built in iSight to take photos of the thieves and mail them back to me.

After looking on the net and finding some rather good, but pay-ware applications, I came across the Prey Project offering an open source free alternative. I went about installing the software and after checking that it worked went about tweaking it to work with how I have my laptop set up.

I use file vault (thank God it was switched on the laptop that was stolen) and have a password on wake so my personal files are secure. However, even with crontab running Prey once a minute, I figured that if the thief couldn’t log into my laptop, it was a bit pointless. So I have used two tactics that will hopefully result in my getting some pictures if the unfortunate were to happen again:

  1. Enable logging in of the Guest account. This means if the thief reboots the laptop (to try and log in as a different user rather than get the password screen from wake), they can log into a sandboxed account with Prey happily snapshotting away.
  2. Put Prey in the crontab that runs for the login screens:

in /private/etc/crontab (this may not exist):

*/1 * * * * cd /usr/share/prey; ./prey.sh

Of course, there are no guarantees, but it’s better than nothing.

10,000 Hits!

Just a quick note to say we have just reached 10,000 hits to the Workbooks Technical Blog. Thanks for reading!