Where to hook into CoffeeScript `extends`? #599

Closed
lancejpollard opened this Issue Mar 19, 2012 · 6 comments

Comments

Projects
None yet
6 participants
@lancejpollard

Given CoffeeScript won't likely integrate the extended hook, if you did override the generated __extends method CoffeeScript provides, how would you make it so you can make it use Ember.js' object model?

I messed around with the CoffeeScript compiler to make it generate code like this:

https://gist.github.com/2127034

But even then, it doesn't look like there's any clean way to integrate Ember. Is there a way you guys have/had in mind to do this?

I am thinking about just using a fork of CoffeeScript with the extended hook so I can apply the Ember object model to the classes involved. But, from reading the Ember.js source, it looks like the only places you can do this are:

  • Ember.Object.create
  • Ember.Mixin.apply

Yet, with those I still can't get the constructor code from Ember.js, I'm stuck with what CoffeeScript gives me:

https://github.com/jashkenas/coffee-script/blob/master/src/nodes.coffee#L1952
vs
https://github.com/emberjs/ember.js/blob/master/packages/ember-runtime/lib/system/core_object.js#L23

Any ideas? If there was a one-liner way to make CoffeeScript use the Ember.js model, using a fork of CoffeeScript is a viable option.

Thanks!

@ghost ghost assigned wycats Mar 19, 2012

@wagenet

This comment has been minimized.

Show comment
Hide comment
@wagenet

wagenet Mar 19, 2012

Member

I think @wycats is the best one to answer this.

Member

wagenet commented Mar 19, 2012

I think @wycats is the best one to answer this.

@d4tocchini

This comment has been minimized.

Show comment
Hide comment

+1

@goodwink

This comment has been minimized.

Show comment
Hide comment

+1

@KasperTidemann

This comment has been minimized.

Show comment
Hide comment
@KasperTidemann

KasperTidemann May 14, 2012

Contributor

+1

Contributor

KasperTidemann commented May 14, 2012

+1

@lancejpollard

This comment has been minimized.

Show comment
Hide comment
@lancejpollard

lancejpollard May 14, 2012

Here's what I'm doing now.

Forked version of coffee-script that provides hooks for setting prototype properties, static properties, and the extends hook:

https://github.com/viatropos/coffee-script

That allows me to do this:

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

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)

And a class like this:

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

  errors: null

... is generated using that custom version of coffee-script to look like this:

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);
  // ...

The last thing I'm trying to figure out is how not to create a million Ember.Mixin instances - one for each method/variable. If only there were some way to append methods to some sort of "current" or "last" mixin...

Here's what I'm doing now.

Forked version of coffee-script that provides hooks for setting prototype properties, static properties, and the extends hook:

https://github.com/viatropos/coffee-script

That allows me to do this:

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

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)

And a class like this:

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

  errors: null

... is generated using that custom version of coffee-script to look like this:

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);
  // ...

The last thing I'm trying to figure out is how not to create a million Ember.Mixin instances - one for each method/variable. If only there were some way to append methods to some sort of "current" or "last" mixin...

@wagenet

This comment has been minimized.

Show comment
Hide comment
@wagenet

wagenet Oct 18, 2012

Member

Closing since this is generally inactive and not really on our list of priorities. We gladly accept PRs and other plugin projects.

Member

wagenet commented Oct 18, 2012

Closing since this is generally inactive and not really on our list of priorities. We gladly accept PRs and other plugin projects.

@wagenet wagenet closed this Oct 18, 2012

@thehydroimpulse thehydroimpulse referenced this issue in tower/tower Nov 7, 2012

Closed

CoffeeScript and Philosophy #216

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment