Intercepting JavaScript methods, Ruby-style

Ruby’s open classes and modules, along with alias_method, make it really easy to add functionality to existing methods. Take this example from my plugin AttrLocked:

class ActionView::Helpers::InstanceTag
  def tag_with_attribute_locking(name, options = nil)
    options = (options || {}).update("disabled" => attribute_locked?)
    tag_without_attribute_locking(name, options)
  end
  alias_method(:tag_without_attribute_locking, :tag)
  alias_method(:tag, :tag_with_attribute_locking)
end

All that does is intercept any calls to InstanceTag#tag and add on an option to disable the form field if required. The original tag method is copied to tag_without_attribute_locking, then tag is re-aliased to call your interception.

(If you are using Rails 1.2 or later, you can use alias_method_chain(:tag, :attribute_locking) in place of the two alias_method calls. I use alias_method in my plugins for backwards compatibility.)

JavaScript has open objects too, and it allows you to reference methods using strings, and it lets you pass functions around like regular variables. So, you could write this:

function intercept(object, method, chain, interception) {
  object[method + '_without_' + chain] = object[method];
  object[method] = interception;
}

which then lets you intercept any method on an object. Let’s say I want to log every call to Event.observe:

intercept(Event, 'observe', 'logging', function(element, ... ) {
  console.log('Event.observe: ' + element);
  Event.observe_without_logging(element, ... );
});

This works great for extending third-party libraries you’re using if you can’t/don’t want to modify their source code.

Update for Grape: ‘view source’ mode

My little Rails project source code search plugin, Grape, now includes the ability to display search results in context with their surrounding source code, rather than just listing matching file paths. Par example:

>rake grape q=Initializer

  Found 3 files matching your search.

    - /config/boot.rb : 14, 16, 30, 40, 44
    - /config/environment.rb : 13
    - /public/javascripts/prototype.js : 1639, 1657, 1670, 1684, 1698

>rake grape q=Initializer v=

  Found 11 matches for your search.

   1. /config/boot.rb
        12.  end
        13.
        14.  unless defined?(Rails::Initializer)
        15.    if File.directory?("#{RAILS_ROOT}/vendor/rails")
        16.      require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"

   2. /config/boot.rb
        14.  unless defined?(Rails::Initializer)
        15.    if File.directory?("#{RAILS_ROOT}/vendor/rails")
        16.      require "#{RAILS_ROOT}/vendor/rails/railties/lib/initializer"
        17.    else
        18.      require 'rubygems'

   3. /config/boot.rb
        28.        if rails_gem
        29.          gem "rails", "=#{rails_gem.version.version}"
        30.          require rails_gem.full_gem_path + '/lib/initializer'
        31.        else
        32.          STDERR.puts %(Cannot find gem for Rails ~>#{version}.0:

...

The v= is short for ‘verbose’, doncherknow. If you give v an actual value, say v=4, then Grape will print 4 lines of code above and below each match it finds. Install away:

ruby script/plugin install

http://svn.jcoglan.com/grape/trunk/grape

Finding files with tiny fruit

I had to rename a bunch of variables in my current project and I wanted to know which files needed adjusting. Problem is, I’m on Windows, and searching for anything in Windows blows really hard. Even Google Desktop with the any-text-file-indexer wasn’t as helpful as I’d like. So, I wrote this tiny tiny plugin that gives you a command line search for your Rails project.

ruby script/plugin install

http://svn.jcoglan.com/grape/trunk/grape

Because rake + grep = grape. I know, I’m brilliant, I know. Now I can regexp-search my whole Rails project from the command line:

rake grape q="chunky bacon"

It’s case-insensitive by default, but you can change that by tacking cs= at the end. Grape will then tell you how many files match, and list them with line numbers of matching lines. It searches Ruby files, (R)HTML, (R)XML, RJS and JavaScript files, CSS, ERB, rakefiles, YAML, SQL, plain text, (f)cgi and htaccess. If I’ve left anything off the list don’t hesitate to tell me.

If you’re this much of an idiot, you have no place writing business articles.

I wouldn’t normally comment on this in writing, but I cannot believe that there exists even one person who seriously entertains the notion that the iPhone (or anything else for that matter) should fail because it is impossible to use while driving. As pointed out by John Gruber, Rob Enderle holds such an opinion, as does Ken Dulaney (also via DF). It is nothing short of a miracle that these people are still breathing, never mind holding down paid positions as business analysts/writers/whatever. If you’re driving a car, you should be driving a car, not fiddling with your phone’s tiny tiny little keys.

I assume the people that the above articles are addressing are likely to be business users, people with Blackberrys and the like, in which case your phone is definately too fiddly to be used while doing anything else at all. I was on a bus last week that nearly hit someone who was sat in the middle of a junction talking on their phone, seemingly oblivious to the world around them. Hopefully the iPhone will force people to focus on doing one thing at a time, rather than driving around on auto pilot while pretending they’re sat in their office.

Sylvester 0.1.2

Time for the once-a-month update to Sylvester, my JavaScript vector maths library. This release fixes some bugs to do with variables being passed by reference instead of value, and allows you to pass plain arrays to various Matrix methods as long as they are properly nested into rows and columns. If they take some other form, Matrix will attempt to convert them into matrix format as before; the difference is that it won’t bother converting to proper Matrix objects if it doesn’t need to, and this can speed things up significantly.

Progress on this has been a little slow given that I started this project nearly three months ago. The first release took a couple of weeks but progress after that slowed up as I realised I needed to optimise heavily if this library was going to do any computational geometry or graphics things fast enough. Also, a bunch of Rails projects and JavaScript UI stuff has cropped up since then to occupy my time. Life’s been a little hectic recently but hopefully I’ll be able to focus on this again soon.

And before anyone asks: yes, I know doing serious graphics in JavaScript is asking for trouble and poor performance. I’m doing this to teach myself a few things and find out what this language can do.

Stop repeating your labels

I don’t know about you, but I do like a bit of form_for. I’m not so fond of having to repeat myself when writing labels though:

  <% form_for(:post, ...) do |f| %>
    <label for="post_title">Title</label>
    <%= f.text_field(:title) %>
    <label for="post_body">Body</label>
    <%= f.text_area(:body) %>
  <% end %>

You have to write the object name out every time, and the attribute name twice for every label. I’d rather do this:

  <% form_for(:post, ...) do |f| %>
    <%= f.label(:title) %>
    <%= f.text_field(:title) %>
    <%= f.label(:body) %>
    <%= f.text_area(:body) %>
  <% end %>

And now you can too:

script/plugin install

http://svn.jcoglan.com/labelhelper/trunk/label_helper

LabelHelper takes the pain out of writing accessible, usable web forms. You can overwrite the default label text if you like, and add other tag options just like any other Rails tag method. The important thing is it deals with that pesky for attribute so you don’t have to. Enjoy.

I’ve asked for this to be added to Rails, as I figured it’s in keeping with its don’t-repeat-yourself philosophy. No idea how likely it is to be included though.

Table locking in AttrLocked

As promised, AttrLocked has been updated to include table locking. If you have any tables in your app that you want to be read-only, just put this in your model:

class SomeReadOnlyTable < ActiveRecord::Base
  table_locked
end

That will stop and save/update/delete operations on the model’s table, and you won’t be able to modify the attributes of any instances of it, even new unsaved instances. You can still associate other models with your locked one, as long as the foreign key field itself is not locked in some way.

AttrLocked updated

I’ve just made an update to AttrLocked. The initial release allowed you to carry on setting locked attributes using the record[:attr] = value style. Now, both that and record.attr = value are disabled on locked attributes. I may add a feature that lets you lock down a whole table so it’s read-only, and you cannot create, delete or modify records from it. More on that when it happens.

New Rails plugin: AttrLocked

I just released a new plugin for Rails, called AttrLocked. It lets you specify that certain model attributes should not be changeable once a record has been created – perfect for making sure usernames are fixed or your financial data doesn’t get tampered with. It will not let you change a record’s locked attributes in any way, and will disable form fields as appropriate to stop people bothering to change the data. Example:

class Payment < ActiveRecord::Base
  attr_locked :time, :amount, :transaction_id
end

Now, when you grab a record from the database, using time=, amount= and transaction_id= won’t work. attributes=, save and update_attributes will silently ignore the locked attributes, and update_attribute will return false if you try to modify a locked attribute. Naturally, you are allowed to set values on new records.

I’ve only tested it on my current project, which is in Rails 1.2.2, but I’d really appreciate testing with other applications. Install like so:

script/plugin install
    git://github.com/jcoglan/attr_locked