jashkenas/coffee-script — pull request #2329 by viatropos (2012-05-14 19:10:20+00:00)
Title: Provide hooks for setting static and prototype properties.

This PR provides hooks for setting prototype properties, static properties, and the extends hook.

Given a class like this:

``` coffeescript
class Tower.Model extends Tower.Class
@field: (name, options) ->

errors: null
```

... the generated javascript has 2 hooks defined: `__defineStaticProperty` and `__defineProperty`:

``` javascript
var __defineStaticProperty = function(clazz, key, value) {
if(typeof clazz.__defineStaticProperty == 'function') return clazz.__defineStaticProperty(key, value);
return clazz[key] = value;
},
__defineProperty = function(clazz, key, value) {
if(typeof clazz.__defineProperty == 'function') return clazz.__defineProperty(key, value);
return clazz.prototype[key] = value;
},
__hasProp = {}.hasOwnProperty,
__extends = function(child, parent) {
if(typeof parent.__extend == 'function') return parent.__extend(child);

for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; }
function ctor() { this.constructor = child; }
ctor.prototype = parent.prototype;
child.prototype = new ctor;
child.__super__ = parent.prototype;
if(typeof parent.extended == 'function') parent.extended(child);
return child;
};

Tower.Model = (function(_super) {
var Model;

function Model() {
return Model.__super__.constructor.apply(this, arguments);
}

Model = __extends(Model, _super);

__defineStaticProperty(Model, 'field', function(name, options) {

});

__defineProperty(Model, "errors", null);
// ...
```

This PR also includes a simple `__extends` hook. All 3 of these "hooks" just check for a property defined on the class being created, otherwise it defaults to the original CoffeeScript implementation.

That allows you to do this:

https://github.com/viatropos/tower/blob/7fc992c730818ca40893f3601ff4391df4cdeaac/src/tower/support/class.coffee#L1

``` coffeescript
coffeescriptMixin =
__extend: (child) ->
object = Ember.Object.extend.apply(@)
object.__name__ = child.name
@extended.call(object) if @extended
object

__defineStaticProperty: (key, value) ->
object = {}
object[key] = value
@[key] = value
@reopenClass(object)

__defineProperty: (key, value) ->
object = {}
object[key] = value
@reopen(object)

Ember.Object.reopenClass(coffeescriptMixin)
Ember.Namespace.reopenClass(coffeescriptMixin)
Ember.Application.reopenClass(coffeescriptMixin)
Ember.ArrayProxy.reopenClass(coffeescriptMixin)
Ember.State.reopenClass(coffeescriptMixin)
Ember.StateManager.reopenClass(coffeescriptMixin)
```

There is no decrease in performance, and all of the tests pass. What are your thoughts?

By being able to do this, I can now use CoffeeScript in a much more powerful way. All it takes is this very minor change.