As everybody is no doubt aware, there are new rules for getting a patch into Rails. With that in mind, I thought I’d share some of my patches here in the hopes that one of my army of readers (ha!) will find something I wrote interesting enough to try out for themselves. I’ve got a few Rails patches I want to talk about, but today I’m announcing a Prototype patch (Prototype is part of the Rails project) that I just submitted: ticket 9611.
Those of you who use Rails and Ruby will know all about Symbol#to_proc
, that
bit of magic that lets you write names = users.collect(&:name)
rather than the
more verbose
names = users.collect { |user| user.name }
I know, a simple example, but in some situations this improves readability like
you would not believe. I wanted a similar thing for JavaScript, and for the
example above, Prototype does just fine: users.pluck('name')
is equivalent to
users.collect(function(user) { return user.name; })
But what about all the other Enumerable
methods, and indeed collect
itself
– why can’t it handle this situation instead of us having to use pluck
? So,
taking inspiration from Ruby, I wrote a String#toFunction
method that works
with both properties and methods, just like in Ruby. (Not strictly true, since
anything you expose using attr_accessor
or some other getter method in Ruby is
still just a message to the receiving object, just like all other method calls.
My point is that I want the same semantic effect in JavaScript.) So, on with the
code:
String.prototype.toFunction = function() {
var property = this;
return function(o) {
var member = o[property];
return (typeof member == 'function') ? member.apply(o) : member;
};
};
What does this do? It returns a function that, given some object as an argument, returns the property of that object named by the string. So
radioInputs.findAll('checked')
is equivalent to
radioInputs.findAll(function(radio) { return radio.checked; })
(assuming we’ve modified the Enumerable
methods slightly to check for a
toFunction
method on their inputs). What about that member.apply
business?
If it turns out that the property required is in fact a function, then that
function is executed in the context of the object to which it belongs –
member.apply(o)
. This means to can write things like
$$('div').findAll('visible')
rather than
$$('div').findAll(function(div) { return div.visible(); })
or even ['FOO', 'BAR', 'BAZ'].map('toLowerCase')
. Pretty no? I know you can do
that last one using invoke
, and that invoke
lets you pass arguments to the
function. My point with all this is that these special ‘do something using a
string’ methods shouldn’t be necessary – we can make all Enumerable
methods
accept strings with minimal effort. Wouldn’t you like to be able to
users.sortBy('age')
? I know I do.
If you think this is a good idea, why don’t you take a look at my patch and consider running the tests and giving me a +1 vote? Thanks a bunch.