Saturday, March 1, 2008

Catch-all getters and setters in ECMAScript 4

While reading the Tamarin and ECMAScript 4 presentation by John Resig I learned about the Catch-all getters and setters feature of ECMAScript 4.

This feature allows you to handle the request for a non declared property of an object. It is similar to Python's __getattr__/__setattr__.

An example of the syntax is the following:


dynamic class MyDynamicClass {
meta function get(name) {
return name+" requested.";
}
meta function set(name,value) {
print("setting:"+name+" to "+value);
}
}


A use of this class:


>> let w = new MyDynamicClass()
>> w.foo
foo requested.
>> w.goo = 4
setting:goo to 4
4


I wanted to write a example created for previous posts using the ECMAScript 4 reference implementation. This example consists of two classes CsvFile and CsvFileEntry the first represents a complete csv file and the second is just one entry. The catch-all mechanism will be used to access the fields of the entry.

The implementation for the CsvFileEntry class is the following:


dynamic class CsvFileEntry {
let headers : Map.<string,int>;
let data : *;
public function CsvFileEntry(data,headers : Map.<string,int>) {
this.data = data;
this.headers = headers;
}
meta function get(name) {
let columnIndex = headers.get(name);
if (columnIndex != null) {
return data[columnIndex];
} else {
return null;
}
}
}


The CsvFile implementation is the following:


use namespace intrinsic;
class CsvFile {
let headers : Map.<string,int>;
let content : Array;
public function CsvFile(filename:string) {
let fileContent = intrinsic::readFile(filename);
let rows = fileContent.split('\n');
headers = new Map.<string,int>();
content = new Array();
let i = 0;
for (let rowIndex = 0; rowIndex < rows.length;rowIndex++) {
let row = rows[rowIndex];
let data = row.split(',');
if (i == 0) {
for(let j = 0;j < data.length;j++) {
headers.put(data[j],j);
}
} else {
content.push(new CsvFileEntry(data,headers));
}
i++;
}
}
public function get Content() {
return content;
}
}


Given that we have the following file (cars.csv):


Year,Make,Model
1997,Ford,E350
2000,Mercury,Cougar



...we can use of the CsvFile class:


>> let f = new CsvFile('cars.csv')
>> f.Content[0].Model
E350
>> f.Content[0].Year
1997