We can call this a bugfix...it's more of a neglected feature that's
otherwise completely inconsistent with the rest of the system. :)
* lib/Trait.js (createNamedTrait): Support base.
(_createStaging) [extend]: Support base.
* test/Trait/NamedTest.js: Add test.
See test cases for more information. This was a pretty unfortunate and
nasty bug that I discovered while working on a project that uses easejs; it
wasn't something that was found previously because this support was only
added relatively recently, and this problem does not exist if an interface
is used.
* lib/Trait.js (bindSuperCtx): Add function.
(tctor): Use it.
* test/Trait/ScopeTest.js: Add calling context tests.
Autoconf generates INSTALL boilerplate, so I had added it to .gitignore in
the past. Unfortunately, I do include my own INSTALL file, which is present
in the distribution, but has never been committed to the repo!
Thanks to Stefan Schweter for pointing this out to me.
* INSTALL: Added.
* .gitignore (/INSTALL): Removed.
* 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.