Unexpected logout, expired cookies, sessions and the CKeditor

The Workbooks Desktop, different though it is to most web applications, is typical in that it uses cookies to track sessions. So when you log in a session cookie is stored in the user’s browser and from then on each request from the Desktop has the cookie included in the request.

But, starting a couple of weeks ago we started getting strange behaviour: some users reported getting logged out at various, seemingly random, times. We ruled out all the obvious causes, went through all the changes in our most recent release, but could not see what was going on. It wasn’t specific to any particular user, browser, request, network, operating system or time of day. Or anything else we could think of. We added diagnostic code and all that told us was that after a few hundred perfectly normal Ajax requests suddenly and without any apparent cause there was no cookie included with a request and our Desktop would respond by asking the user to login again. After a few logouts this rapidly becomes unfunny.

Eventually we discovered that we had literally dozens of cookies stored against our domain name in the affected browsers with names like scayt_1__options. A quick search through our source found that our new version of CKeditor had changed so that now the ‘SCAYT’ (Spell Check As You Type) plugin is enabled by default – and that integration creates literally dozens of cookies to hold your spelling preferences!

The browsers didn’t send all these cookies to us every time – they have different Paths to most of our app – but those browsers did discard the much more important Session cookie randomly – because browsers guarantee surprisingly little with regards to the retention of cookies.

Once we’d identified the problem the solution was easy: disable the plugin.

Advertisements

Strange problems with ruby-debug (rdebug) after installation of Ruby Enterprise (REE)

Some of us had some troubles with ruby-debug after upgrading to Ruby Enterprise Edition.  One of the issues was with ‘debugger’ statements within the source code causing the debugger to break, but to report line numbers multiplied by two!

To ensure you have a clean installation of ruby-debug that works alongside your shiny REE installation, follow these steps:

Remove all of the ruby-debug gems (including all versions if there’s more than one, and all executables):

sudo gem uninstall ruby-debug

Remove all of the ruby-debug-base gems (including all versions if there’s more than one):

sudo gem uninstall ruby-debug-base

Check there are no other rdebug executables lying around:

whereis rdebug

Remove any that you find

sudo rm /usr/bin/rdebug

Reinstall ruby-debug using the Ruby Enterpise Edition gem installer:

sudo /usr/local/bin/gem install rub

Essential TextMate Plugins

[tweetmeme service=bit.ly]
Our development team has now got upwards of 20 man-years experience with TextMate so we thought it would be a good idea to share with you the most useful bundles we’ve come across so far. If you feel there are any glaring omissions, then please leave a comment!

AckMate
http://github.com/protocool/AckMate

If you have worked with TextMate for any period of time, you will come to realise that the Find in Project is woefully inadequate. Fortunately, the plugin architecture means that a much superior alternative is available. Most programmers will have come across Ack, but for those of you who haven’t, it’s a technology that harnesses the power of Perls’ regular expressions and is very good at working with large trees of heterogeneous source code.

Once installed, the selected folder in the project drawer will be the root of the search and Ctrl + Alt + Cmd + F will open up the AckMate window. Results are displayed with a line above and below the matching text giving some context to the results.

See http://betterthangrep.com for more information on Ack.

RubyAMP
http://www.ruby-lang.org

RubyAmp adds a few productivity aids to TextMate. The most useful I find being the “Complete across tabs” (Ctrl + ; ). This works in a similar way to how pressing Escape will autocomplete within a file and provides the same functionality across multiple open files showing a popup list of possible matches, with the first 10 mapped to the number keys. Another really useful feature common amongst IDEs for statically typed languages is the ability to easily jump to method definitions, classes etc by using shortcuts.

ProjectPlus
http://ciaranwal.sh/2008/08/05/textmate-plug-in-projectplus

This bundle adds a few bells and whistles to the project drawer in TextMate. Instead of the standard fly-out drawer, ProjectPlus gives you a pane which remembers the state of your project tree and open tabs between opening and closing projects. One key feature adds SCM badges to the files and folders in the project drawer giving a quick and clear indication of files and folders within your SCM. Other small features include: “Show folders at top” instead of the default alphabetical order, “Sort by filetype”, Finder colour labels and QuickLook.

Installing Bundles

To install a .tmbundle file, place it in ~/Application Support/TextMate/Bundles and then select “Bundles > Reload Bundles” from TextMate. If you use TextMate with multiple OS X user accounts then .tmbundle files can copied to /Application Support instead.

Final note

On the subject of TM additions, by far my favourite syntax colouring scheme is the one used by railscasts.com available from here. Also, if you use code within presentations, it’s often really useful to copy blocks of code whilst retaining their style when pasting into another application. This is easily achieved using “Copy with style” available from here.

Take care when writing to files using puts

[tweetmeme service=bit.ly]
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
Posted in Ruby. Tags: , , , . 1 Comment »

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

[tweetmeme service=bit.ly]
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

[tweetmeme service=bit.ly]
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

[tweetmeme]
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.