Ruby on Rails | Screencasts | Download | Documentation | Weblog | Community | Source

Ticket #9611 (new enhancement)

Opened 2 years ago

Last modified 2 years ago

[PATCH] String#toFunction to mirror Symbol#to_proc

Reported by: jcoglan Assigned to: sam
Priority: normal Milestone: 2.x
Component: Prototype Version: edge
Severity: normal Keywords: unverified
Cc:

Description

For the sake of cleaning up my code, I'd really like to see the Enumerable#pluck idea extended to many other methods a-la Symbol#to_proc in Ruby. That way I can write things like...

var checked = someRadioBottons.findAll('checked');
var minimum = myInputs.min('value');
var groups = objects.partition('validated');

This patch allows you to do that, and it even works with methods, so you can do:

var elements = $$('div').findAll('visible');

It works by adding a String#toFunction method, and adding a check for that method to many functions in Enumerable. I know this would clean up some of my code considerably. Not sure if I've missed any methods where it might be useful, but I've included tests for all the methods I've patched.

Attachments

string_to_function_with_tests.diff (7.6 kB) - added by jcoglan on 09/20/07 23:51:28.
string_to_function_recursive_with_tests.diff (8.4 kB) - added by jcoglan on 09/22/07 06:27:04.
alternate_enumerable_syntax_with_tests.diff (10.6 kB) - added by jcoglan on 09/22/07 06:27:19.

Change History

09/20/07 22:46:00 changed by jcoglan

string_to_function_recursive_with_tests.diff provides functionality similar to Methodphitamine in Ruby, in that it allows you to chain method/property calls within the string. e.g. say you have a User class, which has a location instance variable. location is an object with a getCoordinates method, which returns an object containing lat and lng values. This patch lets you do...

var positions = users.collect('location.getCoordinates.lat');

I've added a test of this functionality. With each property retrieved in the sequence, if the property is a function then it is executed within the context of its containing object and its return value becomes the next object in the chain. Not sure if I explained that too well - see testCollect in enumerable.html.

09/20/07 23:51:28 changed by jcoglan

  • attachment string_to_function_with_tests.diff added.

09/21/07 01:44:44 changed by jcoglan

alternate_enumerable_syntax_with_tests.diff expands on the ideas of using strings as iterators, and allows the use of arrays and objects/hashes as well. From the included tests:

$$('div').findAll(['hasClassName', 'finder'])
$$('div').findAll({hasClassName: 'finder'})
$$('div').findAll({hasClassName: ['finder']})

Both forms allow an indefinite number of arguments. If supplying just one argument when using a hash, the enclosing array brackets are optional (as above).

Using a hash allows you to use several methods and properties at once:

$$('div').findAll({hasClassName: 'finder', tagName: 'DIV'})
$$('div').findAll({hasClassName: 'finder', visible: true})

For each key, if the key is a method name then the method is executed with the arguments given. If it's a property, the property is checked for equality with the argument given. The above are equivalent to:

$$('div').findAll(function(div) {
  return div.hasClassName('finder') && div.tagName == 'DIV';
})

$$('div').findAll(function(div) {
  return div.hasClassName('finder') && div.visible(true);
})

Both array and hash forms can be used for many other Enumerable methods. One final (contrived) example: sorting some words by their second letter:

['apples', 'oranges', 'pairs'].sortBy({replace: [/^./, '']})
// -> ['pairs', 'apples', 'oranges']

09/21/07 18:09:02 changed by jcoglan

Just realised my spelling slip-up. Let's make that...

['apples', 'oranges', 'pears'].sortBy({replace: [/^./, '']})
// -> ['pears', 'apples', 'oranges']

09/22/07 06:27:04 changed by jcoglan

  • attachment string_to_function_recursive_with_tests.diff added.

09/22/07 06:27:19 changed by jcoglan

  • attachment alternate_enumerable_syntax_with_tests.diff added.

09/22/07 06:44:00 changed by jcoglan

I'm working on features to let you use binary operators (as in, operators that sit between two variables, not just bitwise operators) within this #toFunction-based scheme. Things like...

// Find numbers divisible by 3...
$R(1,10).reject(['%', 3])
// -> [3, 6, 9]

// Other crazy stuff
someRandomJunk.findAll({'instanceof': Number, '>=': 4})

So far things seem to work okay but are very much experimental. I'm releasing this patch as a self-contained script - latest release at http://blog.jcoglan.com/reiterate/, and source code over at http://svn.jcoglan.com/reiterate/trunk. If I can make sure it's rock solid I'll add a patch here for binary operators.