Wednesday, October 31, 2012

Deprecated Dart Pub Application Layout

‹prev | My Chain | next›

Last night, I revisited Dart Pub after managing to get Hipster MVC added as an official pub package. All seems quite nice—managing package dependencies for client-side libraries is a huge step forward for web development.

There is one problem that I would like to explore tonight. When I installed hipster_mvc from Pub, I was told that my web application's public/scripts was in an improper format. Indeed, even today, we I try to update my dependencies, I am still told the same thing:
➜  scripts git:(M1) ~/local/dart/dart-sdk/bin/pub update                                       
Resolving dependencies...
Warning: Package "scripts" is using a deprecated layout.
See http://www.dartlang.org/docs/pub-package-manager/package-layout.html for details.
Dependencies updated!
This is not really a "script" package, that is just the name that I gave in my application's pubspec.yaml:
name: scripts
dependencies:
  hipster_mvc: any
I think that the deprecated layout message means that I do not have my Dart files in a lib sub-directory. Since this is my public/scripts directory on the web server, I have everything directly in here:
➜  scripts git:(M1) ✗ tree
.
├── Collections.Comics.dart
├── dart.js
├── main.dart
├── ModalDialog.dart
├── Models.ComicBook.dart
├── packages
│   └── hipster_mvc -> /home/chris/.pub-cache/hosted/pub.dartlang.org/hipster_mvc-0.1.0/lib
├── pubspec.lock
├── pubspec.yaml
├── Views.AddComic.dart
├── Views.AddComicForm.dart
└── Views.Comics.dart

2 directories, 10 files
The first thing that I try is to simply create a lib sub-directory. Perhaps the mere presence of that sub-directory will eliminate the warning. It does not:
➜  scripts git:(M1) ✗ mkdir lib
➜  scripts git:(M1) ✗ ~/local/dart/dart-sdk/bin/pub update
Resolving dependencies...
Warning: Package "scripts" is using a deprecated layout.
See http://www.dartlang.org/docs/pub-package-manager/package-layout.html for details.
Dependencies updated!
My next step is to see if my hypothesis is correct. I move all of the Dart files into the newly created lib sub-directory and try again:
➜  scripts git:(M1) ✗ mv *.dart lib
➜  scripts git:(M1) ✗ ~/local/dart/dart-sdk/bin/pub update
Resolving dependencies...
Dependencies updated!
Yup, that is definitely the cause of the deprecation warning. I can live with most of that code being inside the lib sub-directory. The Model, Collection, and View classes are all referenced from the main.dart entry point (or by each other). So the lib sub-directory might even be appropriate. But, I cannot abide having the main.dart entry point in lib. I refuse to start adding lib in the HTML that references the main.dart entry point:
<head>
  <!-- ... -->
  <script src="/scripts/main.dart" type="application/dart"></script>
</head>
It is not a library file—it is a main.dart entry point, dammit.

Perhaps Pub will let me get away with just the main.dart file being in the root public/scripts directory:
➜  scripts git:(M1) ✗ mv lib/main.dart .
➜  scripts git:(M1) ✗ ~/local/dart/dart-sdk/bin/pub update
Resolving dependencies...
Warning: Package "scripts" is using a deprecated layout.
See http://www.dartlang.org/docs/pub-package-manager/package-layout.html for details.
Dependencies updated!
Oh, darn.

I toy with the idea of moving all of the MVC code in my Dart Comics sample app into its own sub-directory—something like public/scripts/dart_comics. But that will not work.

I could add the additional dart_comics/lib/ path for each of the imports of my application's collections and views, but there is no way that I could then import the HipsterSync class that I use for syncing CRUD operations to localStorage instead of over a REST-like web service:
#import('Collections.Comics.dart', prefix: 'Collections');
#import('Views.Comics.dart', prefix: 'Views');
#import('Views.AddComic.dart', prefix: 'Views');

#import('dart:html');
#import('dart:json');

#import('package:hipster_mvc/hipster_sync.dart');

main() {
  HipsterSync.sync = localSync;
  // ...
}
The HipsterSync class is part of hipster_mvc, which would be a dependency of the fictional dart_comics library. As such, I would not have access to it from the main.dart entry point.


So, in the end, I plan to stick with the deprecation warnings. Hopefully those deprecation warnings will go away in the future—at least for applications.


Day #556

Tuesday, October 30, 2012

Using and Abusing Dart Pub

‹prev | My Chain | next›

My Dart library, Hipster MVC, was accepted into Dart Pub today as an official package. To celebrate, I am going to try to use it, in Dart Comics, the original application from which it was extracted.

After reviewing the options on the pub command, it seems that I want to pub install in my web site's public/scripts directory. For that I need a pubspec.yaml file that includes hipster_mvc as a dependency:
name: scripts
dependencies:
  hipster_mvc: any
The pub install command will not work without a name in there. Since this is just the scripts directory, I use the name "scripts". This seems like the kind of thing that could be optional in an application directory. The "any" value in the pubspec indicates that I am OK with any version (i.e. the latest version) of hipster_mvc.

Anyhow, pub install works now, though it berates me a bit:
➜  scripts git:(M1) ✗ ~/local/dart/dart-sdk/bin/pub install
Resolving dependencies...
Downloading hipster_mvc 0.1.0 from hosted...
Warning: Package "scripts" is using a deprecated layout.
See http://www.dartlang.org/docs/pub-package-manager/package-layout.html for details.
Dependencies installed!
I will worry about the warning later. For now, I note that pub install left behind a lock file containing the current version of hipster_mvc and any associated dependencies:
➜  scripts git:(M1) ✗ cat pubspec.lock 
{"packages":{"hipster_mvc":{"version":"0.1.0","source":"hosted","description":"hipster_mvc"}}}
I am used to this approach with bundler from the Ruby world. Since this is an application, I will check this lock file into Git so that anyone else working with the code will be running the same versions of hipster_mvc that I am using.

I also note that the package is not actually installed in public/scripts/packages. Rather, pub install has created a symlink to the cache directory:
➜  scripts git:(M1) ✗ tree packages 
packages
└── hipster_mvc -> /home/chris/.pub-cache/hosted/pub.dartlang.org/hipster_mvc-0.1.0/lib

1 directory, 0 files
Presumably this is so that I do not have to re-download my huge hipster_mvc library again.

With that, I fire up my Dart Comics app in Dartium and find... the following errors in the Console:
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost:3000/scripts/packages/hipster_mvc/lib/hipster_sync.dart
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost:3000/scripts/packages/hipster_mvc/lib/hipster_view.dart
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost:3000/scripts/packages/hipster_mvc/lib/hipster_collection.dart
Failed to load resource: the server responded with a status of 404 (Not Found) http://localhost:3000/scripts/packages/hipster_mvc/lib/hipster_model.dart
I added the lib in that path to conform to the pub standards. And indeed that path is in my hipster_mvc repository:
➜  hipster-mvc git:(master) tree -I docs
.
├── lib
│   ├── hipster_collection.dart
│   ├── hipster_history.dart
│   ├── hipster_model.dart
│   ├── hipster_router.dart
│   ├── hipster_sync.dart
│   └── hipster_view.dart
├── pubspec.yaml
└── README.asciidoc

1 directory, 8 files
But it is not in the package pulled from Dart Pub:
➜  scripts git:(M1) ✗ tree packages/hipster_mvc 
packages/hipster_mvc
├── hipster_collection.dart
├── hipster_history.dart
├── hipster_model.dart
├── hipster_router.dart
├── hipster_sync.dart
└── hipster_view.dart

0 directories, 6 files
From a library developer's perspective, I find the difference confusing. From an application developer's perspective, I appreciate the lack of the extra lib in the path. All of my import statements then become:
// ...

#import('package:hipster_mvc/hipster_sync.dart');

main() {
  HipsterSync.sync = localSync;
  // ...
}
// ...
With that, I again have my Dart Comics application running. Now with hipster_mvc installed from Dart Pub:


A consequence of the lib vs. no-lib difference is that I cannot simply swap out my pub package for my local repository:
➜  scripts git:(M1) ✗ cd packages 
➜  packages git:(M1) ✗ mv hipster_mvc hipster_mvc.pub
➜  packages git:(M1) ✗ ln -s ~/repos/hipster-mvc
If I reload the application, I again get 404s for my library—this time because they all reside under the lib path:
GET http://localhost:3000/scripts/packages/hipster_mvc/hipster_sync.dart 404 (Not Found) localhost:20
GET http://localhost:3000/scripts/packages/hipster_mvc/hipster_collection.dart 404 (Not Found) localhost:20
GET http://localhost:3000/scripts/packages/hipster_mvc/hipster_view.dart 404 (Not Found) localhost:20
GET http://localhost:3000/scripts/packages/hipster_mvc/hipster_model.dart 404 (Not Found) localhost:20
I could symlink the lib directory from the local repository of hipster_mvc:
➜  packages git:(M1) ✗ rm hipster-mvc 
➜  packages git:(M1) ✗ ln -s ~/repos/hipster-mvc/lib hipster_mvc
Now, if I reload, the application works again and I can try out changes in hipster_mvc before committing them to GitHub.

Pub does support git sources. So I can try the following instead of pulling down the official hipster_mvc package:
name: scripts
dependencies:
  hipster_mvc:
    git: git://github.com/eee-c/hipster-mvc.git
If I run pub install with that pubspec.yaml, I find that pub has made a copy of the git repository and symlinked to its lib directory:
➜  scripts git:(M1) ✗ ls -l packages                                                                        
total 4
lrwxrwxrwx 1 chris chris 83 Oct 30 23:31 hipster_mvc -> /home/chris/.pub-cache/git/hipster_mvc-e0a4aab8d6fe782f81c2c9abc83906d5fb92e190/lib
So it seems that I was not too far off base manually symlinking to my local repository's lib directory. And, if I manually symlink like that, I do not have to alter the pubspec.yaml or the pubspec.lock and potentially impact other contributors.

All things considered, Dart Pub is nothing short of amazing. It is not a revolutionary package manager as it is very familiar. But it does have the potential to revolutionize client-side development. Easy installs, a formal mechanism to ensure that everyone has the same package versions without having to check any package code into source code, dependency resolution, easy development of local copies—all make for a very exciting time to be a web developer.


Day #560

Monday, October 29, 2012

Command Line Dart Analyzer

‹prev | My Chain | next›

After a bit of effort the other day, I was able to run my Dart Comics sample app through the Dart Editor's analysis tool. I cleaned up all of the M1-related warning and problems identified in the underlying Hipster MVC library.

There are still a few problems outstanding in the sample app. Before addressing them, there is just one problem with the dart analysis tools that needs fixing—the dependency on the Dart Editor. Don't get me wrong, the Dart Editor seems pretty solid, but it's no Emacs. Happily for me, there is a command line version of the dart analyzer.

To run the analyzer from my Dart Comics sample app, I use the following command line:
➜  dart-comics git:(M1) ~/local/dart/dart-sdk/bin/dart_analyzer \
    --package-root public/scripts/packages \
    public/scripts/main.dart
I would normally symlink the dart_analyzer command into my $HOME/bin directory, but that seems to confuse the analyzer as to the location of the SDK.

The result of the the analysis too is a number of deprecation warning—many still in the Hipster MVC library:
...
file:/home/chris/repos/hipster-mvc/lib/hipster_collection.dart:130: The presence of parentheses after the name of the getter has been deprecated and will soon be disallowed. Please remove the parentheses.
   129:
   130:   String get type() =>_type;
                     ~~~~
file:/home/chris/repos/hipster-mvc/lib/hipster_collection.dart:132: The presence of parentheses after the name of the getter has been deprecated and will soon be disallowed. Please remove the parentheses.
   131:
   132:   HipsterModel get model() => _model;
                           ~~~~~
...
Darn. I really thought that I had cleaned Hipster MVC up for M1.

The getter declaration syntax has been updated to reflect the manner in which it is called (i.e. without the empty parentheses). So the fix is a simple matter of removing the empty parentheses from those declarations:
String get type =>_type;
I also run afoul of the recent switch from the Dynamic class to the dynamic keyword:
file:/home/chris/repos/hipster-mvc/lib/hipster_sync.dart:24: no such type "Dynamic"
    23:   // forward the call to the appropriate behavior (injected or default)
    24:   static Future<Dynamic> call(method, model) {
                        ~~~~~~~
The fix:
  // static method for HipsterModel and HipsterCollection to invoke -- will
  // forward the call to the appropriate behavior (injected or default)
  static Future<dynamic> call(method, model) { /* ... */ }
The last change that I need in my Hipster MVC class is to remove the abstract modifier for some of my methods:
file:/home/chris/repos/hipster-mvc/lib/hipster_collection.dart:19: Modifier 'abstract' is deprecated for methods without body. Remove it.
    18:
    19:   abstract HipsterModel modelMaker(attrs);
          ~~~~~~~~
As the warning indicates, the compiler now recognizes that method declarations without bodies are abstract—there is no longer a need to be explicit about it.

OK now I have fixed all of the warnings and problems in the Hipster MVC library. These deprecation messages may have been available as "info" messages rather than problems and warnings, which is why I skipped them the first time.

I rather like the new getter syntax. If the getter is invoked like a property, then it might as well be declared the same way. I do not understand the switch from Dynamic to dynamic, but it is a simple enough change. I also like the removal of the abstract keyword. Coming from a dynamic language background that seems unnecessary ceremony.

And, of course, I most appreciate being able to run the analysis from the command line so that I can continue to use Emacs, the one true editor.


Day #554

Sunday, October 28, 2012

Ready for Dart Pub

‹prev | My Chain | next›

After running through the analysis tool in Dart Editor I am fairly confident that my code is ready for M1 usage. In other words, I think Hipster MVC is ready for Dart Pub, the dart package system.

In the grand tradition of minimum viable products, creating a Dart Pub project is a manual request to the Dart Team. But it requires a few things first:
  1. Conform your packages to the pub package layout.
  2. Ensure you have a valid pubspec.yaml file.
  3. Pick an OSS license and correctly add it to your package. If you don't have one, you can always use Dart's BSD-style license.
  4. Host your source code in a public location so we can retrieve it.
  5. Add dartdocs so people can easily use your libraries.
  6. Update your README to contain a getting started guide.
Currently, the Hipster MVC library has all of the code in the top-level directory:
➜  hipster-mvc git:(master) tree -I docs
.
├── HipsterCollection.dart
├── HipsterHistory.dart
├── HipsterModel.dart
├── HipsterRouter.dart
├── HipsterSync.dart
├── HipsterView.dart
├── main.dart
└── README.asciidoc
To conform to the Dart Pub layout (item #1 in the checklist), I need to move all of that into a lib sub-directory:
➜  hipster-mvc git:(master) mkdir lib
➜  hipster-mvc git:(master) git mv *.dart lib
➜  hipster-mvc git:(master) ✗ git ci -m "Move all code into lib directory" -a
[master 5aa2bac] Move all code into lib directory
 7 files changed, 0 insertions(+), 0 deletions(-)
 rename HipsterCollection.dart => lib/HipsterCollection.dart (100%)
 rename HipsterHistory.dart => lib/HipsterHistory.dart (100%)
 rename HipsterModel.dart => lib/HipsterModel.dart (100%)
 rename HipsterRouter.dart => lib/HipsterRouter.dart (100%)
 rename HipsterSync.dart => lib/HipsterSync.dart (100%)
 rename HipsterView.dart => lib/HipsterView.dart (100%)
 rename main.dart => lib/main.dart (100%)
➜  hipster-mvc git:(master) gp origin master
Counting objects: 4, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (3/3), done.
Writing objects: 100% (3/3), 563 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To git@github.com:eee-c/hipster-mvc.git
   425979e..5aa2bac  master -> master
For #2, I need a pubspec.yaml. There is not too much to these files, this should suffice:
name: hipster-mvc
version: 0.1.0
description: Dart-based MVC framework
author: Chris Strom 
homepage: https://github.com/eee-c/hipster-mvc
I already have the license information in the README (#3). Since the code has always been on GitHub, it is available (#4). I have dartdocs in the code and make it available in the respository: http://eee-c.github.com/hipster-mvc/ (#5). I add brief getting started instructions to the README (#6), which should take care of everything.

With that, I submit a ticket to get the package manually added to pub.dartlang.org. Fingers crossed!



Day #553

Saturday, October 27, 2012

M1 Dart Editor First Use

‹prev | My Chain | next›

A number of folks have suggested that I try the Dart Editor for the kind of refactoring that I have been doing. Indeed, it seems that are a number of automations in the editor for instant fixes for what has taken me a couple of days so far.

In all honesty, I prefer finding the bugs the hard way as I find it easier to internalize the changes and problems. Checking a box and pressing a button does not usually aid in a deep undestanding. But now that I believe that I have my Dart Comics sample app, and the underlying Hipster MVC on which it is built, upgraded for the M1 release of Dart, well it could not hurt to see if I missed anything.

After downloading and installing the editor, I fire it up. There does not seem be to an "import project" option, so I chose "Open Folder" instead, choosing the location of my current dart-comics local repository:


That seems to work as the project appears and the tooling gets right to work analyzing the project:


There sure are a lot of red circle-Xs in there. In fact, there are 168 problems and 262 warnings:


I eliminate a bunch of the errors by marking the test directory to not be analyzed and marking the public/packages directory as a package directory in the editor's preferences.

Only I still have hundreds of problems and warnings. Looking closer, some of the problems are things that I just fixed for M1:


Curly braces are the correct syntax for optional, named parameters in M1. I am able to run it in Dartium just fine. But the editor does not recognize it. It's almost as if the editor was old.

The "About Dart" options would seem to indicate that my Dart Editor is, in fact, old:


Build 9822? Just under the link that I used to download the editor, it says that the current version is 13841:


Elsewhere on the Dart Editor page is a link to the bleeding edge editor. I try that.

Only when I start, I get an unholy stacktrace, the very beginning of which is:
➜  ~  ~/local/dart/DartEditor 
!SESSION 2012-10-27 17:53:46.001 -----------------------------------------------
eclipse.buildId=unknown
java.version=1.6.0_24
java.vendor=Sun Microsystems Inc.
BootLoader constants: OS=linux, ARCH=x86_64, WS=gtk, NL=en_US
Command-line arguments:  -os linux -ws gtk -arch x86_64 -consoleLog -data @user.home/.dartEditor

!ENTRY org.eclipse.ui.workbench 4 0 2012-10-27 17:53:48.563
!MESSAGE Unable to create view ID com.google.dart.tools.ui.FileExplorer: Failed to find /home/chris/local/dart-sdk/lib/_internal/libraries.dart.  Is dart-sdk path correct?
!STACK 0
com.google.dart.compiler.InternalCompilerException: Failed to find /home/chris/local/dart-sdk/lib/_internal/libraries.dart.  Is dart-sdk path correct?
 at com.google.dart.compiler.SystemLibrariesReader.getLibrariesFile(SystemLibrariesReader.java:256)
 at com.google.dart.compiler.SystemLibrariesReader.(SystemLibrariesReader.java:191)
 at com.google.dart.compiler.FileBasedSystemLibraryProvider.createReader(FileBasedSystemLibraryProvider.java:39)
...
Since I am an Emacs guy, I normally use the dart-sdk when doing local development work. It seems that the Dart Editor recognizes that I have the dart-sdk installed and it tries to use it. But, my dart-sdk appears to be missing a file that the Dart Editor is trying to find. The result is a big, huge crash.

Fortunately, the solution is simple enough:
➜  ~  mv local/dart-sdk local/dart-sdk.bak
With that, I have a Dart Editor that sports an M1 splash screen.

I again add open my Dart Comics folder. I again mark a few older files as not requiring analysis. I again add the pub folder from Dart Comics as the primary location for pub packages:


That would not be a good idea if I were working on multiple projects, but it will suffice as a temporary setting as I get started.

With all of that, Dart Editor's automatic analysis now identifies 4 problems and 7 warnings:


Much better.

All but one of those errors are in files that are not directly used. I will get around to fixing them eventually, but today, I am only concerned with the warning from Collections.Comics.dart:
Concrete class Comics has unimplemented member(s) 
# From Collection:
  bool isEmpty 
  Collection<dynamic> map((dynamic) -> dynamic)
  Collection<dynamic> filter((dynamic) -> bool) 
  bool contains(dynamic) 
  dynamic reduce(dynamic, (dynamic, dynamic) -> dynamic) 
  bool every((dynamic) -> bool) 
  bool some((dynamic) -> bool)
# From Iterable: 
  Iterator<dynamic> iterator()
It is fairly easy to eliminate this warning—I need to delegate a bunch of the Collection methods in the HipsterCollection class to the underlying list of models:
class HipsterCollection implements Collection {
  // ...
  iterator() => models.iterator();

  bool isEmpty => models.isEmpty;
  map(fn) => models.map(fn);
  filter(fn) => models.filter(fn);
  contains(element) => models.contains(element);
  reduce(intialValue, fn) => models.reduce(initialValue, fn);
  every(fn) => models.every(fn);
  some(fn) => models.some(fn);
  // ..
}
There is not too much typing involved, thanks to the hash-rocket function syntax. Still, I do appreciate Ruby's Enumerable which only requires a each method to be defined.

Aside from the problems with the very-old-editor and the wrong dart-sdk, it was pretty easy to get my old project imported into Dart Editor. It did not find too many problems, which buys a bit of peace of mind. Tomorrow, I will keep working through some of the non-happy path problems and warnings that exist.


Day #552

Friday, October 26, 2012

Typed Dart Exceptions

‹prev | My Chain | next›

It seems that I was tad premature in thinking that Hipster MVC was ready for Dart Pub. While clicking through the various pieces of my Dart Comics sample app, which is built on Hipster MVC, I came across yet another issue.

Deleting records works just fine, but adding does not quite work. I can open the form, but saving results in an exception in my exception handling:
Internal error: 'http://localhost:3000/scripts/Views.AddComicForm.dart': Error: line 86 pos 22: ')' expected
    catch (Exception e) {
                     ^ 
I am not doing anything fancy with that exception handling code, just logging the exception from the try block:
    try {
      collection.create({
        'title':title.value,
        'author':author.value
      });
    }
    catch (Exception e) {
      print("Exception handled: ${e.type}");
    }
So I could simply remove it, but then I would not learn anything. Checking the M1 release notes, the catch statement was updated so that the exception class is no longer inside the catch. In practice, this means that I can simply remove the exception type from my catch:
    try {
      collection.create({ /* ... */ });
    }
    catch (e) {
      print("Exception handled: ${e.type}");
    }
As-is, I am catching any kind of exception. There is no sense in trying to reproduce the original "limitation" of catching Exception since it is the base class of all exceptions. In other words, that is what I am already doing.

If I wanted to handle a specific kind of exception (like NotImplementedExceptions) differently than the catch-all case, I can use the on keyword:
    // This won't work...
    try {
      throw new NotImplementedException("not done coding");
      // ...
    }
    on NotImplementedException catch (e) {
      print("[not implemented] ${e.type}");
    }
    catch (e) {
      print("Exception handled: ${e.type}");
    }
Except that does not work because there is no type getter on exception classes. I am unsure if the type getter used to exist or if I never actually tried this bit of code. If I wanted to print the type of the class, I can instead use runtimeType:
    try {
      throw new NotImplementedException("not done coding");
      // ...
    }
    on NotImplementedException catch (e) {
      print("[not implemented] ${e.runtimeType}");
    }
    catch (e) {
      print("Exception handled: ${e.runtimeType}");
    }
But really, I may as well just call toString() to get the exception and message:
    try {
      throw new NotImplementedException("not done coding");

      collection.create({
        'title':title.value,
        'author':author.value
      });
    }
    on NotImplementedException catch (e) {
      print("[not implemented] ${e.toString()}");
    }
    catch (e) {
      print("Exception handled: ${e.toString()}");
    }
Which gives me:
[not implemented] NotImplementedException: not done coding
If I change the throw to a different class:
      // ...
      throw new FormatException("not pretty enough");
      // ...
Then the exception falls down to the catch-all:
Exception handled: FormatException: not pretty enough
I think that pretty much covers exceptions. In the end, I revert to the M1 compatible catch-all format since that replicates my original functionality. It is hard to get too excited over try-catch exception handling—I still prefer Ruby's rescue formatting—but this seems to have a reasonable balance of typed exception handling along with familiar syntax.


Day #551

Thursday, October 25, 2012

Dartium isEmpty

‹prev | My Chain | next›

I continue to work through issues in my Hipster MVC based Dart Comics application. The issues stem from recent changes in Dart, most notably those from the recent M1 release.

I believe that I have all of the optional constructor arguments fixed after last night (have I mentioned how awesome those are?). But I still do not have a working application.

I am receiving a nice old stack trace with a less than obvious error message:
Exception: Object is not closure
Stack Trace: #0      HipsterCollection._buildModel (http://localhost:3000/scripts/packages/hipster-mvc/HipsterCollection.dart:75:37)
#1      HipsterCollection.fetch.<anonymous closure>.<anonymous closure> (http://localhost:3000/scripts/packages/hipster-mvc/HipsterCollection.dart:43:33)
#2      List.forEach (dart:coreimpl-patch:355:8)
#3      HipsterCollection.fetch.<anonymous closure> (http://localhost:3000/scripts/packages/hipster-mvc/HipsterCollection.dart:42:21)
#4      _FutureImpl._complete (bootstrap:824:19)
#5      _FutureImpl._complete (bootstrap:832:5)
#6      _FutureImpl._setValue (bootstrap:846:14)
#7      _CompleterImpl.complete (bootstrap:929:26)
#8      HipsterSync._defaultSync.<anonymous closure> (http://localhost:3000/scripts/packages/hipster-mvc/HipsterSync.dart:61:29)
It is reassuring that the Future seems to be completing properly after fetching data from the backend. That exercises a decent amount of code and would appear to be working. As for the error itself, it comes from a seemingly innocuous line inside of Hipster MVC's collection class:
  _buildModel(attrs) {
    var new_model = modelMaker(attrs);
    // Give the factory a chance to define attributes on the model, if it does
    // not, explicitly set them.
    if (new_model.attributes.isEmpty()) new_model.attributes = attrs;
    new_model.collection = this;
    return new_model;
  }
I am unsure if the problem is the condition or the assignment. I try splitting the if statement into a block with the assignment on the following line, which seems to suggest that the error is in the conditional. It is hard to see what I might be doing wrong in there. I try adding a debugger statement:
  _buildModel(attrs) {
    var new_model = modelMaker(attrs);
    // Give the factory a chance to define attributes on the model, if it does
    // not, explicitly set them.
    debugger;
    if (new_model.attributes.isEmpty()) {
      new_model.attributes = attrs;
    }
    new_model.collection = this;
    return new_model;
  }
But is seems that Dartium still does not honor that keyword. It does support explicitly set break points by clicking on the line number in the Dart console:


What I find in there is that isEmpty() is no longer a "regular" method on the attributes HashMap. Now it is a getter. This seems to contradict the "bleeding edge" documentation which still includes the method parenthesis as if this were a normal method:


Has Dart changed so much that getter is somehow implied?

Eventually, I track down a recent mailing list post that explains the situation:
Starting with r14022 isEmpty is a getter now. Affected classes are: Collection, Map, SequenceCollection, String, and StringBuffer.
And indeed, the version of Dartium that I have is after r14022:
➜  hipster-mvc git:(master) ✗ ls -1d ~/local/dartium*
/home/chris/local/dartium-lucid64-inc-11176.11176
/home/chris/local/dartium-lucid64-inc-14060.0
So this is a simple matter of documentation being out of sync with the code. I can't say that I have missed life on the bleeding edge like this. Ah, who am I kidding? I absolutely love this stuff.

After I fix the issue by simply removing the parenthesis:
  _buildModel(attrs) {
    var new_model = modelMaker(attrs);
    // Give the factory a chance to define attributes on the model, if it does
    // not, explicitly set them.
    if (new_model.attributes.isEmpty) new_model.attributes = attrs;
    new_model.collection = this;
    return new_model;
  }
I now have a working Dart application (again):


It has been a couple of months since I last updated my code. In that time, it seems that only two minor things have changed that impact me and, in my estimation, both are for the better. The optional constructor parameters in Dart have always been a big win for the language. To my surprise and delight they are even better in M1. Making isEmpty a getter has a better feel to it. There are never going to be arguments, so why explicitly require the parentheses? Without the parentheses, it is not obvious if this is a property or a method, but it hardly matters. The intent is clear, which trumps anything.

I still need to check editing and deleting records in the Dart Comics app. Assuming those are OK, I will get started publishing the most recent Hipster MVC changes to Dart Pub. Tomorrow.


Day #550

Wednesday, October 24, 2012

Back to Dart: New Optional Constructor Syntax

‹prev | My Chain | next›

I am more or less done researching for Gaming JavaScript. To be sure, there will be more that I have to dig into at various points as I touch up chapters, but at this point it is mostly brain dump. So I am shifting to catch up on other projects.

I am hard pressed to decide what is in more desperate need of an update at this point, SPDY Book or Dart for Hipsters. I do have a better handle on the latest changes to SPDY, having done much research and development when version 3 of the protocol came out. In the meantime, it seems that Dart has been changing rapidly, so I will focus there for the foreseeable future.

There are a ton of new changes in the m1 release of the language. Rather than working through those changes one-by-one, I will instead start by trying to get my dart comics sample app, along with the Hipster MVC library on which it is built, to work with recent dart builds. This should be fun.

I grab the latest Dartium, fire up the node.js backend and access the site. Not surprisingly, it does not work. In the Dart console, I find:
Exception: No such method: 'Comics', url 'http://localhost:3000/scripts/main.dart' line 14 pos 37
    , comics_view = new Views.Comics(

Stack Trace: #0      NoSuchMethodErrorImplementation._throwNew (dart:core-patch:401:3)
#1      main (http://localhost:3000/scripts/main.dart:14:37)
Interestingly, line 14 is a constructor for the Comics.View:
#import('Collections.Comics.dart', prefix: 'Collections');
#import('Views.Comics.dart', prefix: 'Views');
// ...

main() {
  var my_comics_collection = new Collections.Comics()
    , comics_view = new Views.Comics(
        el:'#comics-list',
        collection: my_comics_collection
      );

  my_comics_collection.fetch();
  // ...
}
What is interesting about this is that the Collections.Comics class seems to be defined, while Views.Comics is not. Both are imported in the same manner—prefix and all—so the problem is presumably in the library code.

And indeed, I run afoul of my first M1 breaking change: optional parameters are specified differently. Optional parameters are a fantastic little feature of Dart that formalizes what many other languages force programmers to do manually—handle default and optional parameters.

Currently, the Comics class uses the old square brackets to indicate optional parameters:
class Comics extends HipsterView {
  Comics([collection, el]):
    super(collection:collection, el:el);
  // ...
}
In M1, square brackets are used to capture optional, positional parameters. In the above, I am saying that the first and second parameters are optional and should not be named. Optional named parameters, which I want here, are defined inside a more hash-like curly braces:
class Comics extends HipsterView {
  Comics({collection, el}):
    super(collection:collection, el:el);
  //...
}
It makes all kinds of sense to use curly braces to indicate named parameters like: new Views.Comics(el: '#comics-list', collection: my_comics_collection) because this is much more akin to the options-object that we are forced to use in languages like JavaScript.

What is really nice about Dart constructor parameters is the ability to assign instance variables directly from the constructor definition. The Comics view constructor redirects to the superclass (HipsterView) constructor with two optional, named parameters. In the superclass, those named parameters correspond to instance variables of the same name. In most languages, I would need to then jump through the assign-instance-variable-if-supplied-in-the-constructor hoops.

In Dart, I can simply declare them in the constructor with the this keyword:
class HipsterView {
  HipsterCollection collection;
  HipsterModel model;
  Element el;

  HipsterView({el, this.model, this.collection}) {
    // ...
  }
}
If the model or collection named parameters are supplied, then the instance variable of the same name is assigned. That's just freaking awesome.

I am not even close to fixing all of the problems in dart-comics and HipsterMVC, but I still call it night here. I may be done with research on Gaming JavaScript, but I still have a ton of writing to do.


Day #549

Tuesday, October 23, 2012

Faye in the Code Editor

‹prev | My Chain | next›

I think I have a fairly rocking fork of Mr Doob's code editor. Thanks mostly to the work and advice of others, it seems pretty stable. I may actually switch back to othe projects for a bit, but first I need to investigate something for Gaming JavaScript that I promised my son I would: Faye.

OK, so he didn't specifically ask for Faye, but he is very keen on enabling chat within one of the games. This is something that Faye, with its pub-sub websockets, can do quite easily. I am not at all sure that an open Faye server will be a wise idea, but I would like to find out if it is even possible from within the code-editor.

I start by installing Faye into a new application directory:
➜  repos  mkdir gamingjs.faye
➜  repos  cd !$
➜  repos  cd gamingjs.faye
➜  gamingjs.faye  npm install faye
npm http GET https://registry.npmjs.org/faye
...
faye@0.8.6 node_modules/faye
├── cookiejar@1.3.0
└── faye-websocket@0.4.3
Then I copy the basic app server from the Faye site, saving it as app.js:
var http = require('http'),
    faye = require('faye');

var bayeux = new faye.NodeAdapter({mount: '/faye', timeout: 45});
bayeux.listen(8000);
I spin it up and I have a Faye server:
➜  gamingjs.faye  node app
I will likely want the Faye client-side JavaScript to reside on the same server as the code-editor. So I copy the browser client code into the local copy of the Gaming JavaScript site:
➜  gamingjs git:(gh-pages) ✗ cp ../gamingjs.faye/node_modules/faye/browser/faye-browser.js Faye.js
With that, I am ready to start my test. In the code editor, I add the following code:
<body></body>
<script src="/Faye.js"></script>
<script src="/ChromeFixes.js"></script>
<script>
  // Your code goes here...
  var client = new Faye.Client('http://localhost:8000/');

  client.subscribe('/messages', function(message) {
    alert('Got a message: ' + message.text);
  });
</script>
In a separate, incognito window, I also access my local copy of the code editor, adding the following to publish a message on the same /messages channel to which my first client is listening:
<body></body>
<script src="/Faye.js"></script>
<script src="/ChromeFixes.js"></script>
<script>
  // Your code goes here...
  var client = new Faye.Client('http://localhost:8000/');

  client.publish('/messages', {
    text: "Hello!"
  });
</script>
Only nothing happens.

It takes me a while to realize that I have copied from two different sections of the Faye tutorial. I had set my server to listen to messages at /faye, but am creating clients on the root URL: new Faye.Client('http://localhost:8000/'). The solution is easy enough. I need only tell the clients to do their pub-subbing on the proper URL (/faye):
  var client = new Faye.Client('http://localhost:8000/faye');

  client.publish('/messages', {
    text: "Hello!"
  });
Once I fix that in both locations, I have my Faye messages between code editors:


I still need to decide if I think running an open Faye server on a tiny linode somewhere is worth it for this kind of thing in the book. But it is good to know that it is possible.


Day #548

Monday, October 22, 2012

AppCache Stuff from Elsewhere on the Site

‹prev | My Chain | next›

I need to round out the application cache in my fork of Mr Doob's code editor. I have been doing all my coding in the gamingjs branch since this will be the source of the code-editor used for Gaming JavaScript. Even though the branch is named "gamingjs" and Mr Doob is not ready to accept contributions, I have done my best to keep changes that are specific to Gaming JavaScript out of this branch.

That needs to change tonight. The code-editor is an appcache application so that it can be used offline. It only make sense for a code editor to be available offline. But what about the libraries used in the book?

I have been going back and forth between referencing libraries directly on the root URL:
<body></body>
<script src="http://gamingJS.com/Three.js"></script>
<script src="http://gamingJS.com/Detector.js"></script>
<script src="http://gamingJS.com/physi.js"></script>
<script src="http://gamingJS.com/ChromeFixes.js"></script>
<script>
// Code goes here...
</script>
Or referencing them in a versioned URL-space:
<body></body>
<script src="http://gamingJS.com/52/Three.js"></script>
<script src="http://gamingJS.com/52/Detector.js"></script>
<script src="http://gamingJS.com/52/physi.js"></script>
<script src="http://gamingJS.com/52/ChromeFixes.js"></script>
<script>
// Code goes here...
</script>
Obviously the latter is the more future proof of the two. If I were making this for a "real" site, I would probably opt for it. But since this is for a book (and a kids book at that), I think that conceptual simplicity has to win.

Along those same lines, I do not think that I can bundle the 3D libraries as part of the code editor. On the Gaming JavaScript site, the code editor resides under the gamingJS.com/ice URL space. If I were to bundle the 3D libraries with the code-editor, they would end up with URLs like: http://gamingJS.com/ice/Three.js. That would allow me to write <script> tags like:
<script src="Three.js"></script>
That has some appeal—especially from a simplicity standpoint. Although it is short, I think it is actually less conceptually simple. At some point in the book, I need to explain what these tags do. It's far easier to explain a URL than a relative URL.

So, after all of that, I am left with the web site proper holding the 3D libraries (http://gamingJS.com/Three.js) and the embedded code-editor referencing them in the appcache manifest, editor.appchache:
CACHE MANIFEST
# 2012-10-22

CACHE:
/favicon.ico
/Three.js
/Tween.js
/Detector.js
/physi.js
/ChromeFixes.js
/ammo.js
/physijs_worker.js
editor.css
files/inconsolata.woff
js/editor.js
js/appcache.js
js/rawdeflate.js
js/rawinflate.js
js/ace/mode-css.js
js/ace/ace.js
js/ace/theme-chrome.js
js/ace/mode-html.js
js/ace/worker-javascript.js
js/ace/keybinding-emacs.js
js/ace/mode-javascript.js


NETWORK:
*
It is a pretty severe drawback to this approach that I will need to update the manifest in the code editor repository every time I want to update the JavaScript libraries in the host site. Given all the considerations, I see no better option.

Once I push that out to gamingJS.com, I see the manifest pull in everything—even the libraries from the URL-space above the ICE editor (gamingJS.com/ice):


And, more importantly, I can use the editor offline:



So this seems to work fairly well. Even for the stuff that would otherwise be outside of the appcache application.


Day #547

Sunday, October 21, 2012

Chrome and the Vanishing postMessage Prefix

‹prev | My Chain | next›

After managing to fix a display issue in the code editor used in Gaming JavaScript, I was not pleased to discover that my web worker issue had gotten worse. That is, the dreaded "INVALID_STATE_ERR: DOM Exception 11" has returned only now I cannot get rid of it.

The problem is that the Physijs web worker is generating the DOM exception:
<body></body>
<script src="http://gamingJS.com/52/Three.js"></script>
<script src="http://gamingJS.com/52/Detector.js"></script>
<script src="http://gamingJS.com/52/physi.js"></script>
<script src="http://gamingJS.com/52/ChromeFixes.js"></script>
<script>
// Physics
Physijs.scripts.ammo = 'http://gamingJS.com/52/ammo.js';
Physijs.scripts.worker = 'http://gamingJS.com/52/physijs_worker.js';
The other night, I could make the problem go away by moving the worker code to the root URL (http://gamingJS.com/physijs_worker.js), but this seems to have been the luck of caching. Now it generates an error regardless.

I dig into the Physijs worker code a bit and find that a recent commit tried to make the code more worky for non-Chrome browsers. In the web worker code, it did so by checking for transferable message support before formatting the messages:
 if ( self.webkitPostMessage ) {
    // format message here.
 }
Only webkit supports transferable messages, hence this method of support detection.

Transferable messages are a more efficient mechanism for sending messages from web workers. Instead of copying the entire contents of the message, a pointer to the message is sent. For this to work, the message needs to be a Float32Array instead of a regular array:
 if ( self.webkitPostMessage ) {
  // Transferable messages are supported, take advantage of them with TypedArrays
  worldreport = new Float32Array( /* ... */ );
  collisionreport = new Float32Array( /* ... */ );
 } else {
  // Transferable messages are not supported, send data as normal arrays
  worldreport = [];
  collisionreport = [];
 }
All of that works—except for the manner in which transferable message support is detected.

It turns out that canary Chrome no longer prefixes the postMessage() method, but it still requires the specially constructed message. So checking for webkitPostMessage will not work for future versions of Chrome.

I borrow an HTML5 Rocks transferable message detector to detect transferable support:
var ab = new ArrayBuffer(1);
transferableMessage(ab, [ab]);
var SUPPORT_TRANSFERABLE = (ab.byteLength === 0);
As in the HTML5 Rocks version, I send an array buffer of length 1 via postMessage, to which transferableMessage is assigned. If the message was copied (i.e. it was not transferred), then the buffer will be unchanged. If it was transferred, then the buffer length will be zero.

With that, I can check a boolean for transferable support instead of looking for a method that no longer exists:
if ( SUPPORT_TRANSFERABLE ) {
    // format message here.
 }
That seems to do the trick. It also works in the version of Firefox that I have (16), so hopefully this will work for all cases. I submit a pull request and call it a night.

Day #546

Saturday, October 20, 2012

Minimal ACE Resizing

‹prev | My Chain | next›

I got an interesting suggestion on how to better resolve my issue with code in the ACE code editor going invisible when the window is resized (or the JavaScript console is opened). I have noticed over the last few days that, under certain circumstances, the code was still going invisible, so this seems like an opportune time to revisit.

The crux of the fix is setting the container element to be absolutely positioned with the top, bottom, left, and right positions all set to zero:
#editor, #preview {
  margin: 0;
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  right: 0;
}
I suppose this is not such much a fix as it is the proper, suggested settings. Anyhow...

With the proper suggested settings in place, I have to remove a bunch of code that attempted to resize and display the editor. Much of it was my vain attempts to force the code to be visible after resizing it, but some of it was in the original code editor implementation for the CodeMirror editor.

So I remove the on-zoom hack and the resize hack. I also remove various dynamically set styles. Once all that is done...

Nothing changes.

It still mostly works, but I can force the code to go invisible with a little bit of effort. And then I realize... I have not disabled the appcache on the editor. D'oh!

I remove the manifest attribute from the main entry into the application:
<html manifest="editor.appcache">
The I remove the appcache entry itself from chrome://appcache-internals/. With that, a reload shows me that everything is working. The editor is rock-solid on resize, without so much as a flicker.

Problem solved, only now that I have really refreshed the application, I see the web worker error from last night that I had thought was not a problem:


So it looks to be back to drawing board.

At least I was able to get one problem solved. Not surprisingly, continuing to add cruft on top of existing styling cruft only made the situation work. Much thanks to \*/ for pointing out a minimal and much more robust solution.


Day #

Friday, October 19, 2012

Web Workers Freak Out Over the Silliest Things

‹prev | My Chain | next›

I was pleasantly surprised to find that the r52 release of Three.js was a drop-in replacement for the r49 version that I have been using in Gaming JavaScript. Tonight I intend to see if the same can be said for Physijs, the physics engine that I used in several games.

So I load up one of the games in http://gamingjs.com/ice/ and convert it to the r52 release. For now, Three.js and all related JavaScript libraries remain in the http://gamingJS.com/52 URL-space. So the <script> tags look like:
<body></body>
<script src="http://gamingJS.com/52/Three.js"></script>
<script src="http://gamingJS.com/52/Detector.js"></script>
<script src="http://gamingJS.com/52/physi.js"></script>
<script src="http://gamingJS.com/52/ChromeFixes.js"></script>
<script>
// Physics
Physijs.scripts.ammo = 'http://gamingJS.com/52/ammo.js';
Physijs.scripts.worker = 'http://gamingJS.com/52/physijs_worker.js';
The main selling point of Mr Doob's code editor is that you can see a preview of the code after each change. As I am adding the /52 URL to each of those lines, I can see that everything continues to work just fine… until I change that very last line.

Ugh. Web workers. Why did it have to be web workers? (Very dangerous)

In the JavaScript console, I see the following error:
Uncaught Error: INVALID_STATE_ERR: DOM Exception 11
I suspect that I am not allowed because the editor (and preview) are located in a different URL path (http://gamingJS.com/ice) than the web worker (http://gamingJS.com/52). If I use the old worker that is in the path above the code editor (http://gamingJS.com/), then it works.

The other possibility is that the new Physijs does something funky that is not being allowed.

To test this out, I drop down to my local copy of the Gaming JavaScript site with the code-editor. I reproduce the same "DOM Exception 11" error by pointing at a worker in the /52 path. Now, I copy the latest physijs_worker script over the older version in the top-level directory of the site. I reload the page and… it works just fine.

Sigh.

So it does not appear to be the new Physijs worker. Which means that my earlier hypothesis is looking likely. Except that it should not be correct. Per the specification, errors should be thrown if the origin of the site and worker are different. The origin for both is http://gamingJS.com (or http://localhost:4000).

At something of a loss, I try it out in Firefox, which works:


In that capture, the ball in the preview has fallen onto the board and rolled nearly to the front. In other words physics is working. The rigid platform stopped the ball and the ball fell in gravity. There are no console errors about security. And I am using the /52/physijs_worker.js path.

So this is looking like a Chrome bug. Or a weird interaction between web workers and the preview frame created by the code-editor. Regardless of the cause, the result is that I cannot use version URL spaces for the scripts in Gaming JavaScript. I was not sure that I wanted to include that concept in the book anyway, but I dislike being prevented from having the option.

I will ruminate on this and see if I can't come up with a solution. If you are interested, the code in question (without the /52 URL space) is editable at gamingJS.com/ice.


Day #544

Thursday, October 18, 2012

Upgrading Gaming JavaScript to the Latest Three.js

‹prev | My Chain | next›

I still need to settle on how, or even if, I will version JavaScript libraries used in Gaming JavaScript. Currently, I am having kids use <script> tags like:
<body></body>
<script src="http://gamingJS.com/Three.js"></script>
<script src="http://gamingJS.com/ChromeFixes.js"></script>
<script>
// Code goes here...
</script>
I rather like short URLs like http://gamingJS.com/Three.js. They ought to look like "normal" URLs to kids. They also do not introduce the additional concept of versioning libraries. Of course, that begs the question of how I will version libraries in the future.

I am not going to decide about versioning libraries tonight, but I am going to explore one possibility—versioning by Three.js version. The hope is to not only explore possible solutions, but also to see if my current code works with newer Three.js and Physijs.

I start by making a directory named "52" in my Gaming JavaScript home. Then, I download the latest version of Three.js from the website (r52 at the time of this writing). Then I extract and copy the necessary Three.js files into r52:
➜  src  unzip ~/Downloads/mrdoob-three.js-r52-0-gdaf110f.zip
➜  src  cd mrdoob-three.js-daf110f
➜  mrdoob-three.js-daf110f  cp src/Three.js ~/repos/gamingjs/52 
➜  mrdoob-three.js-daf110f  cp ./examples/js/Detector.js ~/repos/gamingjs/52
I do the same for Physijs:
➜  src  tar xzf ~/Downloads/chandlerprall-Physijs-40a0489.tar.gz
➜  src  cd chandlerprall-Physijs-40a0489/
➜  chandlerprall-Physijs-40a0489  cp physi*.js ~/repos/gamingjs/52
➜  chandlerprall-Physijs-40a0489  cp examples/js/ammo.js ~/repos/gamingjs/52
Even though I do not believe that it will change between releases, I will add the ChromeFixes.js source file into 52 as well
➜  52 git:(gh-pages) ✗ cp ../ChromeFixes.js .
It is more of a code-editor fix than related to 3D programming, but it might look weird to the uninitiated to have some URLs include the 52 and some not.

With that, I have all of the JavaScript libraries that I need:
➜  52 git:(gh-pages) ✗ ls
ammo.js  ChromeFixes.js  Detector.js  physi.js  physijs_worker.js  Three.js  Tween.js
I start editing this on my local machine. Hopefully I can get the the multi-sided, spinning thingy to work with the newer libraries:


But, after simply changing URLs to point to the new "52" libraries:
<body></body>
<script src="http://localhost:4000/52/Three.js"></script>
<script src="http://localhost:4000/52/ChromeFixes.js"></script>
<script>
// Code goes here...
</script>
It does not work.

In the JavaScript console, I find:
Uncaught TypeError: undefined is not a function
That correspond to the line that defines THREE.Scene. Has Three.js change that much?

It turns out that, no, it has not changed that much. I had merely copied the Three.js src from the wrong place. I need to grab the assembled Three.js from the build sub-directory instead of pulling in just the top-level Three.js that clearly does not even have scenes:
➜  mrdoob-three.js-daf110f  cp build/three.js ~/repos/gamingjs/52/Three.js
With the correct Three.js in place, I again have a spiny, multi-sided thingy:


Better yet, the JavaScript console confirms that I am now running r52. That will do for a stopping point tonight. I will check out some of the Physijs stuff tomorrow.

Day #543

Wednesday, October 17, 2012

Overboard with JavaScript Array Iterators

‹prev | My Chain | next›

Mr Doob's code editor has a nifty share feature. Clicking on the link will embed a gzip'd version of the current contents of the code editor in the hash portion of the URL: http://gamingjs.com/ice/#B/rVdbc9o4.... The programmer can then copy the URL and share their awesome code with friends. In the future, I need add a gallery to the Gaming JavaScript web site of awesome games that kids write but first, I need to fix a fairly alarming bug.

If you already have a project in the Gaming JavaScript fork of the code-editor, opening a shared link will overwrite that project. That would certainly have a chilling effect on sharing code. Today I fix that.

I think that the workflow should be something like: detect a shared link, create a new, untitled project, load the project, and optionally let the programmer rename the project.

The current implementation for handling location hashed documents looks something like:
if ( window.location.hash ) {
  var hash = window.location.hash.substr( 1 );
  // ...
  documents[ 0 ].code = decode( hash.substr( 2 ) );
}
This is why opening a shared link replaces the existing project. It overwrites the code in the first (which is the current) project. In fairness to the original Mr. Doob implementation of the code-editor, this behaved slightly different than it does in my fork. But I clearly need to change my fork.

So I change the documents[0] assignment to a new create function:
if ( window.location.hash ) {
  var hash = window.location.hash.substr( 1 );
  // ...
  create(decode(hash.substr(2)));
}
That create() function is mostly borrowed from similar functions in the code-editor:
var create = function(code, title) {
  if (!title) title = nextUntitled();
  if ( documents[ 0 ].filename != title) {
    documents.unshift({
      filetype: 'text/plain',
      autoupdate: documents[ 0 ].autoupdate
    });
  }

  documents[ 0 ].code = code;
  documents[ 0 ].filename = title;

  localStorage.codeeditor = JSON.stringify( documents );
};
Mostly borrowed except for the call to nextUntitled(). In that function, I got a bit iterator-crazy:
var nextUntitled = function() {
  var nums = documents.
    filter(function(doc) {
      return doc.filename.match(/Untitled/);
    }).
    map(function(doc) {
      return parseInt(doc.filename.replace(/Untitled\s*/, ''), 10);
    }).
    filter(function (num) {
      return !isNaN(num);
    }).
    sort();

  return 'Untitled ' + (nums.length == 0 ? 1 : nums[nums.length] + 1);
};
In there, I filter my code documents list so that I am only dealing with Untitled projects. Then I map the projects to remove "Untitled " from the names (i.e. I leave only "3" when mapping "Untitled 3"). I also convert string numbers to real numbers, recalling that it is always best to be explicit with the JavaScript radix. That will not work for all Untitled projects, so I filter out any not-a-numbers from the list. Finally, I sort the list. After all that, I might be left with a list like: [1, 2, 3, 4]. I take the last number on that list, add one, and I have the number of my next Untitled projected (e.g. 'Untitled 5').

I probably enjoy iterators a bit too much.

Anyhow, with that, when I load a shared link, I now have a new Untitled project in the projects list:


I take a little time to replace similar code with a call to the new create() function and call it a night.

Day #542

Tuesday, October 16, 2012

Edit-Only and Revisiting Appcache

‹prev | My Chain | next›

During the recent Gaming JavaScript hackathon, I mentioned to the kids that they should limit the number of faces on shapes so that the computer does not have to work too hard, especially during animations. So what does my son do? He tries 5 million faces in one of his games.

Actually, I'm quite proud. He tried something stupid, which is the only real way to learn. In the process, he uncovered a bug that others will undoubtedly run into as well—if a game overwhelms the computer, then there is no way to correct the problem. Since the editor and the preview are in the same browser window / tab, the programmer has effectively denied him/herself access to correct the situation.

This turns out to be much easier than I had expected. If the programmer appends ?edit-only (or even ?e) to the code-editor, then I want edit-only mode. It take a moment to find (because I never remember) that JavaScript calls the query string as the "search" property of the window's location:
var EDIT_ONLY = window.location.search.indexOf('?e') > -1;
With that, all that I need do is add a guard clause to the update() function of the code-editor:
var update = function () {
  if (EDIT_ONLY) return;
  // Update the preview frame...
}
In edit-only mode, I increase the number of sides in one of the games to 2 million. This is auto-saved by the code-editor, so I then remove the ?edit-only query string from the URL and am greeted by a laptop that quickly grows very hot, but does very little else. After killing the offending tab, I reenter ?edit-only mode, fix the problem, and am again able to view the preview frame back in normal mode.

With that, I am ready to deploy. I had a spot of trouble the last time that I deployed my appcache editor—changes did not seem to get applied. I am using the swapcache scheme from HTML5 Rocks to load in new versions of the code-editor. The alert dialog saying that "A new version of this site is available. Load it?" would display, but then the older version of the code-editor remained in place.

I start by updating the editor.appcache manifest with today's date:
CACHE MANIFEST
# 2012-10-16

CACHE:
/favicon.ico
/code-editor/
index.html
editor.css
...
That change, even though it is a only a comment change, is enough for the browser to see a different manifest, and reload the page. And, after deploying the changed page, I see the HTML5 Rocks confirmation alert:


And all of my changes seem to be in place. Specifically, I can enter into the edit-only mode with a ?edit-only query string. So whatever the problem was in the previous deployment, it seems fixed now. I hate that.

I try various ways to break the loading again: I remove the /code-editor/ from the manifest (it is no longer needed), I update the master page (the one that links the manifest), I update the various JavaScript libraries. All to no avail—I cannot get the deployed manifest to fail. Newly updated files are always updated in the appcache.

Bah! I will file this away for later investigation. I do not believe that I have solved this. I know that I saw this behavior on both my machine and my son's. In both cases, the only was to resolve was to manually remove the appcache. But, for now, I honestly cannot think of another way to attempt to break this.


Day #541

Monday, October 15, 2012

On-Zoom and the ACE Editor

‹prev | My Chain | next›

Last night, I fixed a relatively minor ACE code editor bug in my fork of Mr Doob's code editor. When the window or viewport resized (e.g. when the JavaScript console was shown), the contents of the ACE editor would seemingly disappear.

The fix was small so I contented myself that ACE was still likely a more robust solution than CodeMirrror and its odd Chrome paint issue in the code editor. Today, however, I found another display issue.

When the browser's text is zoomed / unzoomed, ACE's display gets all out of whack. The most visually obvious problem is when ACE no longer fills up the viewport:


Worse is when ACE will not scroll the bottom of the editor contents into view.

There are two ways that I can try to handle this. The first is to intercept Ctrl++ or Ctrl+- key events (as well as Ctrl + Mouse Scroll events) to prevent the normal means of increasing and decreasing font size. The problem with this approach is that, if a user zooms the font through other means (e.g. from a menu), then the problem still exists.

I know how to resolve the problem. The same solution from last night will work again tonight. I simply need to invoke the not-a-callback onResize() method. I might do so every 2 seconds:
setInterval(function() {
  ace.renderer.onResize(true);
}, 2000);
The problem with this approach, besides needing to compensate endlessly for a bug, is that there is a noticeable flicker in the ACE contents from time-to-time.

What I would really like is an on-zoom event listener. I even try adding a "zoom" listener to both the document and the window in the vain hopes that it might exist. It does not—at least not with that name. There are demonstration hacks that describe how to determine new zoom levels. I do not need that amount of information—I only need to know if the text has re-zoomed, not the new zoom level.

So I borrow from that hack to absolutely position an element to the left by a percentage. When the zoom changes, that percentage will change. I then only need to check the offsetLeft occasionally to see if it has changed. Only if is had changed will I invoke the onResize() method:
var zoomEl = document.createElement('div');
zoomEl.style.left = '-50%';
zoomEl.style.width = '1px';
zoomEl.style.height = '1px';
zoomEl.style.position = 'absolute';
document.body.appendChild(zoomEl);

var lastZoom = zoomEl.offsetLeft;
setInterval(function() {
  if (zoomEl.offsetLeft != lastZoom) {
    lastZoom = zoomEl.offsetLeft;
    ace.renderer.onResize(true);
  }
}, 2000);
It is not pretty and it can take upwards of 2 seconds for a bad display to correct. But in the end it works.

This solution is very reminiscent of the CodeMirror repaint solution. Since that repaint issue was one of the deciding factors in switching to ACE, I may have to revisit that decision. That said, I can (and will) add the key and mouse zoom handlers to prevent the usual zooms. If the programmer goes out of her way to zoom after all of that, she can likely abide a 2 second wait for ACE to resize.


Day #540

Sunday, October 14, 2012

ACE and the Disappearing Text

‹prev | My Chain | next›

I had the privilege of running my first Gaming JavaScript hackathon today. I made the risky decision to switch to the ACE Editor for the hackathon, but in the end it paid off. The girls did not have any difficulty working with it and it held up well. There was, however, a glitch.

When resizing the viewport, which often occurs when the JavaScript console is used, the code has the unfortunate tendency to disappear:


This is not quite as bad as the way that CodeMirror disappeared chunks of code when highlighting text, but it is definitely annoying.

It is also easier to restore the missing ACE code, a simple scroll and the code returns:


Mr Doob's code editor, from whence my fork came, already handles page resize. I add a console.log() to it to make sure that it is actually being called when I raise and lower the JavaScript console:
window.addEventListener( 'resize', function ( event ) {
  console.log("resize!");

  editor.style.width = window.innerWidth + 'px';
  editor.style.height = window.innerHeight + 'px';

  preview.style.width = window.innerWidth + 'px';
  preview.style.height = window.innerHeight + 'px';
} );
And indeed, it is:


So this seems like a good place to add a hook to account for the disappearing code. At first glance, I do not find anything in ACE that suggests a means to force a redraw, but I do find centerSelection(). It turns out that invoking this method on resize does the trick:
window.addEventListener( 'resize', function ( event ) {
  ace.centerSelection();

  editor.style.width = window.innerWidth + 'px';
  editor.style.height = window.innerHeight + 'px';

  preview.style.width = window.innerWidth + 'px';
  preview.style.height = window.innerHeight + 'px';
} );
Since that could end up centering text that was not even in the viewport prior to the resize, I will file that away as plan B.

And, after digging through a few ACE issues, I come across the onResize() method (which is a method, not an event callback). It seems that this is used in various places of the code when the window is resized. I am unsure why this does not seem to have the desired effect under normal circumstances, but I believe that I can eliminate the disappearing code with a force onResize() call in my resize listener:
window.addEventListener( 'resize', function ( event ) {
  ace.renderer.onResize(true);

  editor.style.width = window.innerWidth + 'px';
  editor.style.height = window.innerHeight + 'px';

  preview.style.width = window.innerWidth + 'px';
  preview.style.height = window.innerHeight + 'px';
} );
That was a worrisome little bug—especially to run across it in the midst of a hackathon. It is possible that this is special to the manner in which ACE is being employed in this code-editor. Regardless, the fix was small and not too hard to find.


Day #539