* doc/classes.texi (Defining Classes): Remove the recommendation for using
the `public' keyword always (instead of omitting it), and the reference
stating that it may be required in the future. This is not the case.
Error subtyping (creating your own error types) in ECMAScript is notoriously
crude, and getting it to work intuitively is even harder. ease.js will now
transparently handle all necessarily boilerplate when extending Error or its
subtypes.
Take, for example, the common boilerplate for creating your own Error type:
```javascript
function ErrorSubtype( message )
{
var err = new Error();
this.name = 'ErrorSubtype';
this.message = message || 'Error';
this.stack = err.stack;
this.lineNumber = err.lineNumber;
this.columnNumber = err.columnNumber;
this.fileName = err.fileName;
}
ErrorSubtype.prototype = new Error();
ErrorSubtype.prototype.constructor = ErrorSubtype;
```
There are a number of problems with this:
- That's a lot of boilerplate for any type you wish to create;
- Some of those features may not be supported by your environment
(e.g. column numbers or stack traces);
- The stack trace will include `ErrorSubtype` _itself_ (as the top frame);
and
- Consequently, the `{line,column}Number` and `fileName` will represent
that top frame---the error constructor itself.
With ease.js, it's just like extending any other class/constructor:
```javascript
Class( 'ErrorSubtype' )
.extend( Error, {} );
```
More information can be found in the "Error Subtypes" section of the manual.
Happy Error hacking. Maybe you'll actually want to create them now.
* lib/ctor/ErrorCtor.js (createCtor): Add `after' parameter to be
invoked by `__$$ector$$__' at end of function.
* test/ctor/ErrorCtorTest.js: Add respective tests.
Apparently the SPDX license list used by NPM supports an "or later"
identifier; that's good, as this is an incredibly important distinction; I
was otherwise going to drop it and use my own custom identifier.
* package.json: License field set to GPL-3.0{=>+}
GNU ease.js is a pretty trivial case with respect to reproducibility---not
much goes on during the build aside from concatenation and minification.
Non-determinism is essentially confined to filesystem operations, which can
be rectified by sorting using a static locale's collation sequence (which is
done here).
This does not resolve any concerns with autoconf-installed scripts (those in
tools/), or the distribution tarball file metadata.
It's fun when you're about to make a release and find that ES3 fallback
tests are failing. That's also what happens when you decide not to run
the combined tests until the last minute, because they usually don't fail.
Before this change, mixin attempts would fail at the time of mixin when
easejs attempts to invoke the `__mixin` method on the object. This is both
cryptic and void of any useful information on the stack.
Traits can now override methods of their class supertypes. Previously, in
order to override a method of some class `C` by mixing in some trait `T`,
both had to implement a common interface. This had two notable downsides:
1. A trait that was only compatible with details of `C` could only work
with `C#M` if it implemented an interface `I` that declared `I#M`.
This required the author of `C` to create interfaces where they would
otherwise not be necessary.
2. As a corollary of #1---since methods of interfaces must be public, it
was not possible for `T` to override any protected method of `C`; this
meant that `C` would have to declare such methods public, which may
break encapsulation or expose unnecessary concerns to the outside
world.
Until documentation is available---hopefully in the near future---the test
cases provide detailed documentation of the behavior. Stackable traits work
as you would expect:
```javascript
var C = Class(
{
'virtual foo': function()
{
return 'C';
},
} );
var T1 = Trait.extend( C,
{
'virtual abstract override foo': function()
{
return 'T1' + this.__super();
},
} );
var T2 = Trait.extend( C,
{
'virtual abstract override foo': function()
{
return 'T2' + this.__super();
},
} );
C.use( T1 )
.use( T1 )
.use( T2 )
.use( T2 )
.foo();
// result: "T2T2T1T1C"
```
If the `override` keyword is used without `abstract`, then the super method
is statically bound to the supertype, rather than being resolved at runtime:
```javascript
var C = Class(
{
'virtual foo': function()
{
return 'C';
},
} );
var T1 = Trait.extend( C,
{
'virtual abstract override foo': function()
{
return 'T1' + this.__super();
},
} );
var T2 = Trait.extend( C,
{
// static override
'virtual override foo': function()
{
return 'T2' + this.__super();
},
} );
C.use( T1 )
.use( T1 )
.use( T2 )
.use( T2 )
.foo();
// result: "T2C"
```
This latter form should be discouraged in most circumstances (as it prevents
stackable traits), but the behavior is consistent with the rest of the
system.
Happy hacking.
I upgraded Texinfo recently and found that ease.js' documentation would no
longer compile. The errors make sense, but it's an unfortunate regression.
The previous version that I was using was 4.13, which is quite old.
This allows ease.js classes to mimic the structure of ES6 classes, which use
`constructor` to denote the constructor. This patch simply aliases it to
`__construct`, which ease.js handles as it would normally.
To that note, since the ES6 `class` keyword is purely syntatic sugar around
the prototype model, there is not much benefit to using it over ease.js if
benefits of ease.js are still desired, since the member definition syntax is
a feature of object literals:
```
// ease.js using ES6
let Person = Class(
{
_name: '',
// note that __construct still works as well
constructor( name ) {
this._name = ''+name;
},
sayHi() {
return "Hi, I'm " + this.getName();
},
// keywords still work as expected
'protected getName'() {
return this._name;
}
} );
// ES6 using `class` keyword
class Person
{
// note that ES6 will _not_ make this private
_name: '',
constructor( name ) {
this._name = ''+name;
},
sayHi() {
return "Hi, I'm " + this.getName();
}
// keywords unsupported (you'd have to use Symbols)
getName() {
return this._name;
}
}
// ES3/5 ease.js
var Person = Class(
{
_name: '',
__construct: function( name ) {
this._name = ''+name;
},
sayHi: function() {
return "Hi, I'm " + this._name;
},
'protected getName': function() {
return this._name;
}
} );
```
As you can see, the only change between writing ES6-style method definitions
is the syntax; all keywords and other features continue to work as expected.
This redefines the index as a function (rather than a vanilla object) so
that it may be invoked to yield an ease.js Class that wraps the given
prototype.
Importantly, this also removes loading from ajax.googleapis.com, which is a
problem, because the domain must be allowed using NoScript, and hosts many
other things.
Why this was added to begin with is beyond me. Perhaps it demonstrates my
novice abilities back in the day.