Tuesday, December 31, 2013

Polymer, External Libraries, and Latency


One of the many things I appreciate about Polymer is how it handles getting everything ready. There are a lot of interconnected parts that can be loaded in a Polymer application or widget, but Polymer manages them with aplomb. This is principally accomplished via the WebComponentsReady event which Polymer generates and uses—mostly in the form a nice, simple animated reveal of all Polymers when they are ready to be used.

I am unsure, however, how this WebComponentsReady strategy works when a Polymer is also dependent on third party libraries. I just so happen to be working on a Polymer that depends on Underscore.js. When everything is run locally with zero latency this seems to work just fine. But how will my initial approach fair under more realistic conditions?

From last night, my Polymer is pulling in Underscore.js from the same local web server that hosts my application and my Polymer code. The Polymer does this inside the defining <polymer-element>:
<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="hello-you">
  <template><!-- ... --></template>
  <script src="../bower_components/underscore/underscore.js"></script>
  <script>
    // Polymer element code here ...
  </script>
</polymer-element>
Currently, this produces network request like this:



My hello-you.html Polymer definition <link>-imports the polymer.html definition and the Underscore library. As soon as the relatively small polymer.html is imported, it requests polymer.js, which contains all of the necessary Polymer library code. While polymer.js is loading, Underscore.js completes loading and is evaluated, making it available when my Polymer code is evaluated. And this is important because my Polymer uses underscore as soon as it is ready:
<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="hello-you">
  <template>
    <!-- ... -->
  </template>
  <script src="../bower_components/underscore/underscore.js"></script>
  <script>
    Polymer('hello-you', {
      your_name: '',
      // ...
      feelingLucky: _.debounce(
        // A function in need of debouncing...
        750,
        true
      )
    });
  </script>
</polymer-element>
So what happens if there is a ton of latency (as in a mobile connection)? Will my Polymer class definition crash because the _ Underscore.js top-level variable is not defined?

That turns out to be a nearly impossible question to answer directly. Even though Underscore.js is larger than Polymer.js, the indirection of importing polymer.html which then imports polymer.js turns out to be sufficient to make it impossible to break my Polymer. Even if I create latency of up to 1.6 seconds:
$ sudo tc qdisc add dev lo root netem delay 800ms
(always, always, always remember to sudo tc qdisc del dev lo root when done testing latency)

Even this is not enough to make the evaluation times different enough so I can determine if Underscore.js is not available for Polymer. So instead, I create a gigantic JavaScript file that is mostly comment filler, but defines a single variable that I use in my Polymer. And, when I load the script I find:



That is beautiful. Even though the filler script takes significantly longer to load than Polymer does, Polymer is aware of the need to wait and does not do its thing until the <script> tag is ready.

The other question that I would like to answer has more to do with deployment—how does latency affect third party libraries when they are hosted on CDNs. It so happens that Underscore.js is hosted on a CDN, so I switch my <script> tag to point to such a CDN:
<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="hello-you">
  <template><!-- ... --></template>
  <script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>
  <script>
    // Polymer element code here ...
  </script>
</polymer-element>
Now, I can add insane latency to my network connection, leaving the localhost connection effectively instantaneous:
$ sudo tc qdisc add dev eth1 root netem delay 10000ms
With that, I still see Polymer doing the right thing:



It takes 20 seconds to load underscore from that CDN because of my absurd traffic control (tc) setting. And yet Polymer just works. It waits until the library is loaded and then reveals the fully working Polymer element.

How cool is that?

One final note here is that it does not seem to matter where the <script> tag goes. Polymer still does right when the <script> tag goes at the top of the Polymer definition:
<link rel="import" href="../bower_components/polymer/polymer.html">
<script src="http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.5.2/underscore-min.js"></script>
<polymer-element name="hello-you">
  <template><!-- ... --></template>
  <script>
    // Polymer element code here ...
  </script>
</polymer-element>
I think that serves better to illustrate the immediate dependencies of a Polymer—something akin to declaring “normal” code's imports at the top of a file. Since there does not appear to be a difference in functionality or performance, I will likely opt for that approach in Patterns in Polymer.

I do love it when I discover that libraries and tools just work like this. Maybe someday I can write tools that behave this well, but until then I will have to content myself with being able to appreciate them. And write lots of books extolling their many virtues!



Day #982

Monday, December 30, 2013

Getting Started with Bower and Polymer


Managing and using dependencies in Dart is a joy. That is rather the point of coding in Dart. Doing the same in JavaScript is an adventure. But it is an adventure that is improving. One of the ways in which it is the Bower project, which is, I suppose, one of the reasons that the Polymer project has adopted Bower for managing its own dependencies.

Tonight, I would like to explore using Bower to manage a custom Polymer. I would also like to see if I can get Bower to install a third party library like Underscore.js for use inside my Polymer. I know full well how I would go about doing this in the Dart port of Polymer, but am a little fuzzy on the JavaScript details.

I get started with bower init:
$ bower init
[?] name: underscore_example
[?] version: 0.0.0
[?] description: Mucking around with Polymer dependencies
[?] main file: 
[?] keywords: 
[?] authors: Chris Strom 
[?] license: MIT
[?] homepage: https://github.com/eee-c/polymer-patterns
[?] set currently installed components as dependencies? Yes
[?] add commonly ignored files to ignore list? Yes
[?] would you like to mark this package as private which prevents it from being accidentally published to the registry? Yes

{
  name: 'underscore_example',
  version: '0.0.0',
  homepage: 'https://github.com/eee-c/polymer-patterns',
  authors: [
    'Chris Strom '
  ],
  description: 'Mucking around with Polymer dependencies',
  license: 'MIT',
  private: true,
  ignore: [
    '**/.*',
    'node_modules',
    'bower_components',
    'test',
    'tests'
  ]
}

[?] Looks good? Yes
Next, I install Polymer, adding it to the list of dependencies in the generated bower.json file:
bower install --save Polymer/polymer
bower polymer#*                 cached git://github.com/Polymer/polymer.git#0.1.1
bower polymer#*               validate 0.1.1 against git://github.com/Polymer/polymer.git#*
bower platform#0.1.1            cached git://github.com/Polymer/platform.git#0.1.1
bower platform#0.1.1          validate 0.1.1 against git://github.com/Polymer/platform.git#0.1.1
bower polymer#~0.1.1           install polymer#0.1.1
bower platform#0.1.1           install platform#0.1.1

polymer#0.1.1 bower_components/polymer
└── platform#0.1.1

platform#0.1.1 bower_components/platform
Finally, I add my other dependency, Underscore.js:
$ bower install --save underscore     
...
bower underscore#~1.5.2        install underscore#1.5.2

underscore#1.5.2 bower_components/underscore
At this point, I have the generated bower.json plus my new dependencies (which were saved by the --save option to bower install above):
{
  "name": "underscore_example",
  // ...
  "dependencies": {
    "polymer": "Polymer/polymer#~0.1.1",
    "underscore": "~1.5.2"
  }
}
The dependencies are installed and ready to use:
tree -d -L 1 bower_components
bower_components
├── platform
├── polymer
└── underscore

3 directories
But my guess is that I do not want to check anything under the bower_components directory into source control. The entire contents can be recreated on another developer's machine or in a deployment environment with bower install. So my next step is to add bower_components to my dot-gitignore file:
$ echo bower_components >> .gitignore 
$ cat .gitignore 
node_modules
bower_components
Now that I have the JavaScript that I want to use, let's use it. I start with the application code that goes in the main web page. The Polymer documentation starts with the Polymer itself, but I find it easiest to have the page page ready. My index.html:
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Working with Underscore.js</title>
    <!-- 1. Load Polymer before any code that touches the DOM. -->
    <script src="bower_components/platform/platform.js"></script>
    <!-- 2. Load component(s) -->
    <link rel="import" href="scripts/hello-you.html">
  </head>
  <body>
    <div class="container">
      <hello-you></hello-you>
    </div>
  </body>
</html>
And now, my <hello-you> Polymer. I have been using a simple Polymer for illustration that binds an <input> value to a variable that is also bound in the Polymer title. I also have a feelingLucky() method that randomly changes the title's color:
<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="hello-you">
  <template>
    <h2>{{hello}} {{your_name}}</h2>
    <p>
      <input value="{{your_name}}">
      <input type=submit value="{{done}}!" on-click="{{feelingLucky}}">
    </p>
    <!-- ... -->
  </template>
  <script>
    Polymer('hello-you', {
      your_name: '',
      // ...
      feelingLucky: function() {
         // Change the H2's color here...
      }
    });
  </script>
</polymer-element>
I am not doing anything new in the code tonight. What is different is the <link> import on the first line—I am pulling the Polymer baseclass definition from the Bower installation. With my application already pulling this Polymer in from scripts/hello-you.html, I have a working Polymer built from bower components:



But how do I best go about using my Underscore.js dependency? Say, for instance, I want to debounce the “Done!” button that triggers the feelingLucky() method. I am unsure where best to pull in the Underscore.js library. No matter where I do so, I am pulling the library into global scope—this is JavaScript, after all. I will worry about where best to place the <script> tag that imports Underscore another day. For now, I get the library added via a <script> tag with an Underscore.js src attribute just above the <script> tag that defines the Polymer:
<link rel="import" href="../bower_components/polymer/polymer.html">
<polymer-element name="hello-you">
  <template>
    <!-- ... -->
  </template>
  <script src="../bower_components/underscore/underscore.js"></script>
  <script>
    Polymer('hello-you', {
      your_name: '',
      // ...
      feelingLucky: _.debounce(
        function() {
          // Change the H2's color here...
        },
        750,
        true
      )
    });
  </script>
</polymer-element>
And that works like a charm. If I click the “Done!” button, my feelingLucky() method does indeed change the Polymer's title color right away (the true 3rd parameter to _.debounce() calls the function immediately rather than waiting for the bounce to expire). If I try to double-click, then the color only changes once. If I wait a little less than a second before clicking a second time, I can see the title change colors multiple times.

I note here that, thanks to Underscore's respect for the sanctity of this, I can continue to refer to this inside the anonymous function as if this represents the current object. That is, I can still treat it like a method, which is nice.

That is a good stopping point for tonight. Bower may not be Dart Pub, but it is pretty darn nice. I still need to take a closer look at where best to place the Underscore.js <script> tag. I should also look into the best application structure—in particular, should my Polymer expect to find the bower_component directory one directory above it or can I find a more general solution? I also need to decide how best to deploy this code—especially how best to use a library like Underscore which is available on lovely CDNs.

Good questions all. For tomorrow.


Day #981

Sunday, December 29, 2013

Dart Transformers for Polymer Cleanup


All right, I really like this approach to loading configuration data in Polymer applications:
<link rel="import" href="hello-you-en.json">
<link rel="import" href="hello-you-fr.json">
<link rel="src" href="hello-you-es.json">
<polymer-element name="hello-you">
  <!-- Polymer definition here -->
</polymer-element>
The Polymer polyfill code makes sure that this JSON data is imported just like Polymer code that might otherwise go there. In JavaScript, I can then work through the Polymer's element property to access the definition and these data files:
        var list = this.
          element.
          parentElement.
          querySelectorAll('link[rel=import]').
          array().
          filter(function(el) {
            return el.href.match(/hello-you-\w+.json/)
          });
The nice thing about this approach is that the imports are guaranteed to be loaded before the Polymer code executes. There is no need for futures, promises or callbacks. It is ready when the Polymer is ready. Nice!

Only it does not quite work in Polymer.dart, the Dart version of the library. For one thing there is no element property. There is a declaration property which does nearly the same thing, but does not include access to elements outside of the <polymer-element> tag (I am pretty sure that is a bug). But, what does work is moving the <link> tags inside the <polymer-element> definition:
<polymer-element name="hello-you">
  <link rel="import" href="hello-you-en.json">
  <link rel="import" href="hello-you-fr.json">
  <link rel="import" href="hello-you-es.json">
  <!-- Polymer definition here... -->
</polymer-element>
I can then query the import data * through the declaration property that refused me access to these <link> imports when they resided outside the <polymer-element>:
@CustomTag('hello-you')
class HelloYou extends PolymerElement {
  // ...
  ready() {
    super.ready();

    var re = new RegExp(r'hello-you-(\w+).json');
    var list = declaration.
      parent.
      querySelectorAll('link[rel=import]').
      where((el)=> el.href.contains(re));
    // ...
  }
}
This is not ideal, but I can live with this. At least until I can convince the fine Polymer.dart folks that the declaration property (if they keep it) should include extra-<polymer-element> access.

What I cannot live with is what the Polymer “transformer” does to these <link> imports of JSON data. The transformer, which transforms the page into something closely resembling a thing that can be deployed (but that can also include debug code for local development), winds up including the JSON data directly in the page:



There are any number of things that I could do to get around this, but since, this residue is created by Polymer's transformer, writing my own transformer seems like a fine way of dealing with this. But how the heck do you write a transformer in Dart?

To answer that question, I dig through the Polymer code a bit. The transformer in there is named transformer.dart. I am unsure if the name is significant, but, assuming that it is, I create a lib/transformer.dart code file in my application. I eventually trace the Polymer code back to the Pub barback package, which defines the Transformer baseclass. So my lib/transformer.dart code starts as:
library i18n_example.transformer;

import 'package:barback/barback.dart';

class ScrubJsonImports extends Transformer {
  ScrubJsonImports.asPlugin(BarbackSettings settings);

  Future apply(Transform transform) {
    print('here');
  }
}
The asPlugin() named constructor turns out to be necessary to get pub serve to start. To try this skeleton transformer out, I add it to my application's pubspec.yaml:
name: i18n_example
dependencies:
  polymer: any
  polymer_elements: any
  intl: any
dev_dependencies:
  unittest: any
transformers:
- polymer:
    entry_points: web/index.html
- i18n_example:
    entry_points: web/index.html
I add it after the Polymer transformer because I want to fix Polymer's transformer output (it is also possible to run transformers in parallel).

It is when running my transformer with pub serve that I find that I need the asPlugin() named constructor. Without it, I get:
$ pub serve
No transformers that accept configuration were defined in package:i18n_example/transformer.dart or package:i18n_example/i18n_example.dart,
required by i18n_example.
Also, it does seem that the name transformer.dart is significant.

I copy the configuration for the Polymer transformer in pubspec.yaml, so I also copy some of the code to parse it:
class ScrubJsonImports extends Transformer {
  List entryPoints;

  ScrubJsonImports(this.entryPoints);

  ScrubJsonImports.asPlugin(BarbackSettings settings)
    : this(_parseSettings(settings));

  Future<bool> isPrimary(Asset input) {
    if (entryPoints.contains(input.id.path)) return new Future.value(true);
    return new Future.value(false);
  }
}

List<String> _parseSettings(BarbackSettings settings) {
  var args = settings.configuration;
  return _readEntrypoints(args['entry_points']);
}

List<String> _readEntrypoints(value) {
  if (value == null) return null;
  return (value is List) ? value : [value];
}
Most of that pulls the settings from pubspec.yaml and creates a list of entryPoints—places in the application that need to be transformed—to be used as an instance variable. The isPrimary() method uses this list to decide if any of the assets that it sees (and will see all assets in my application) need to be transformed.

At this point, I only need define a way for my transformer to apply its fix to Polymer's work. Given a transform with a primary input (the web/index.html entry point), I needs to read the input as a string, fix the string, and add the fixed string to the transform's output:
class ScrubJsonImports extends Transformer {
  // ...
  Future apply(Transform transform) {
    var input = transform.primaryInput;

    return transform.
      readInputAsString(input.id).
      then((html){
        var fixed = html.replaceAllMapped(
          new RegExp(r'>\s*(\{[\s\S]+\})\s*<polymer-element', multiLine: true),
          (m) => '><div style="display:none">${m[1]}</div><polymer-element'
        );

        transform.addOutput(new Asset.fromString(input.id, fixed));
      });
  }
}
To keep from double-processing assets, Pub transformers assign each a unique id attribute. This lets a transformer look up the current state of an asset and create a new state for the asset.

The above regular expression looks through a Polymer transformer output such as:
...
  <body>{
  "hello": "Hello",
  "done": "Done",
  "how_many": "How many?",
  "instructions": "Introduce yourself for an amazing personalized experience!"
}
{
  "hello": "Bonjour",
  "done": "Fin",
  "how_many": "Combien?",
  "instructions": "Présentez-vous une expérience personnalisée incroyable!"
}
{
  "hello": "¡Hola!",
  "done": "Hecho",
  "how_many": "¿Cuántos?",
  "instructions": "Preséntese para una experiencia personalizada increíble!"
}<polymer-element name="hello-you">
...
And will wrap the nake JSON from my <link> imports inside a hidden <div>:
...
  <body><div style="display:none">{
  "hello": "Hello",
  "done": "Done",
  "how_many": "How many?",
  "instructions": "Introduce yourself for an amazing personalized experience!"
}
{
  "hello": "Bonjour",
  "done": "Fin",
  "how_many": "Combien?",
  "instructions": "Présentez-vous une expérience personnalisée incroyable!"
}
{
  "hello": "¡Hola!",
  "done": "Hecho",
  "how_many": "¿Cuántos?",
  "instructions": "Preséntese para una experiencia personalizada increíble!"
}</div><polymer-element name="hello-you">
...
And, with that, I have my Polymer working as desired. I have imported localization JSON files into the Polymer and scrubbed Polymer's transformer resulting in a working, localized Polymer:



The long term solution here is to file some bug reports and work with the developers to get all of this working for everybody. Admittedly, much of tonight's effort was simply because I was curious how Dart transformers worked. Mission accomplished on that account, but it is still gratifying to have my approach to internationalization working in both JavaScript and Dart. Even if I had to muck with a simple transformer to make that happen.

* This does not work in the dart2js compiled code—the import property of the <link> elements is null. To me the inconsistency is a bug and either the import property here should be the same as in the Dart VM or, better, the <link> elements should be accessible outside the <polymer-element> tag. Either way, I am not too concerned about this. I will file bugs and help get this sorted out.


Day #980

Saturday, December 28, 2013

Less Fun with Transformed Dart Polymers


Bleeding edge. Pre-alpha. Under rapid development. I love it all.

Sure, it makes writing books hard, but seeing people smarter than me solve old problems in new ways or just solving new problems is fascinating. And, if something does not work, I usually just have to wait a couple of days before it gets fixed. For example, my @observable problem seems to have cleared up in recent Polymer.dart / observerable builds. Even more useful to my recent internationalization struggles, it seems that the Polymer transformer for Dart Pub has become slightly less hostile to <link> imports.

I have been toying with using <link> imports to pull in Polymer configuration data. This works fairly well in both the JavaScript and Dart version of Polymer. But the Dart transformer that prepares an application for deployment has been silently deleting references to these imports:
    <link rel="import" href="packages/i18n_example/hello-you.html">
    <link rel="import" href="packages/i18n_example/hello-you-en.json">
    <link rel="import" href="packages/i18n_example/hello-you-fr.json">
    <link rel="import" href="packages/i18n_example/hello-you-es.json">
Any solution that I come up with is useless unless it can be deployed, so the silent deletion left me flummoxed.

But after a recent pub upgrade to grab the latest package dependencies in my application, I noticed something odd with the transformed Polymers:



That seems workable. Sure, I might have style that data to display: none and I will have to conditionally check the DOM for that data directly embedded by the transform, but that seems a minor inconvenience. If it works. Which it does not.

This will not work because, no matter where I place those <link> imports (in the document or in the Polymer template), the result is that the imported data is added directly to the document without structure. Even if I wrap the <link> tags in <div> tags, the result is the same—the transform adds the imported locale JSON directly to the document without structure:
...
  <body>{
  "hello": "Hello",
  "done": "Done",
  "how_many": "How many?",
  "instructions": "Introduce yourself for an amazing personalized experience!"
}
{
  "hello": "Bonjour",
  "done": "Fin",
  "how_many": "Combien?",
  "instructions": "Présentez-vous une expérience personnalisée incroyable!"
}
{
  "hello": "¡Hola!",
  "done": "Hecho",
  "how_many": "¿Cuántos?",
  "instructions": "Preséntese para una experiencia personalizada increíble!"
}
<polymer-element name="hello-you">
...
Bummer. I suppose from a transformer point of view, that makes sense. The transformer is converting the multi-filed application into something deployable. For Polymer, that means inserting the custom element declarations directly into the page. The transformer is not smart enough to know not to do so with JSON files and, even if it were “smart” enough, I am unsure how it ought to handle this case. Bundle them into the page in a way that somewhat mimics the <link> tag semantics? Ignore them and force these resources to continue to go through the “normal” <link> tag? The latter has its appeal, but it is being polyfilled by the Polymer platform to begin with, so ugh.

It is a shame, because defining an element in the JavaScript version of Polymer would work:
<link rel="import" href="hello-you-en.json">
<link rel="import" href="hello-you-fr.json">
<link rel="src" href="hello-you-es.json">
<polymer-element name="hello-you">
It is possible to access the <link> elements via the Polymer's element property:
this.element.previousElementSibling.import.textContent
Actually, now that I think of it, this is not even possible in non-transformed Dart Polymers. The declaration property of a Polymer is the closest analogy to Polymer's element property, but it does not include the <link> elements in the previousSiblingElement property.

Bother.

So after all that, life on the bleeding edge remains a little rough. I still think the <link> import solution is a nice one for loading configuration data in regular Polymer. But in the Dart-flavored Polymer, it is looking more and more as if I will need to rely on <polymer-ajax> to load this kind of data.


Day #979

Friday, December 27, 2013

Can't link import in Polymer.dart


I have what I think is a fairly nice localization implementation for Polymer. Only it doesn't work in Dart. At least I don't think that it does.

A localized Polymer in my scheme looks something like:
    <link rel="import" href="packages/i18n_example/hello-you.html">
    <link rel="import" href="packages/i18n_example/hello-you-en.json">
    <link rel="import" href="packages/i18n_example/hello-you-fr.json">
    <link rel="import" href="packages/i18n_example/hello-you-es.json">
How a localized Polymer is coded by a web developer is almost inconsequential to me at this point. The JSON files can be supplied by the developer as above or can be bundled in the Polymer's <template>. What interests me more right now is getting the non-Polmer <link> imports working under Dart. And I think that means getting to know Dart Pub transformers better.

Transformers are used to build Dart applications for deployment. They also run locally with the pub serve local development web server. This local feature is nice in that it runs an application just like it would for deployment, but allows for rapid code updates. Unfortunately, I think the Polymer transformer is stripping out my <link> imports. To verify, I start by commenting out the transformer from my pubspec.yaml:
name: i18n_example
dependencies:
  polymer: any
dev_dependencies:
  unittest: any
# transformers:
# - polymer:
#     entry_points: web/index.html
And, yup, that fixes my problem. Instead of the large gap between the components and the transformed Polymer application that I had been seeing:
    <!-- Load component(s) -->
    
    
    
    
    <!-- Load Polymer -->
    <script type="application/dart" src="index.html_bootstrap.dart"></script>
I now see my Polymer along with the JSON configuration files that I wanted to see:
    <!-- Load component(s) -->
    <link rel="import" href="packages/i18n_example/hello-you.html">
    <link rel="import" href="packages/i18n_example/hello-you-en.json">
    <link rel="import" href="packages/i18n_example/hello-you-fr.json">
    <link rel="import" href="packages/i18n_example/hello-you-es.json">
    <!-- Load Polymer -->
    <script type="application/dart">
      export 'package:polymer/init.dart';
    </script>
Now that I have access to my JSON localizations, I re-implement last night's JavaScript load-from-import in Dart:
@CustomTag('hello-you')
class HelloYou extends PolymerElement {
  // ...
  @published String locale = 'en';
  Map locales = {};

  HelloYou.created(): super.created();

  ready() {
    super.ready();

    var re = new RegExp(r'hello-you-(\w+).json');
    var list = ownerDocument.
      querySelectorAll('link[rel=import]').
      where((el)=> el.href.contains(re));

    list.forEach((link) {
      var json = link.import.documentElement.text;
      var results = re.firstMatch(link.href);
      locales[results[1]] = JSON.decode(json);
    });

    locales[locale].forEach((k, v)=> this[k] = v);
  }
  // ...
}
See last night's post for the gruesome details. The brief summary is: get a list of JSON <link> elements, parse their contents as JSON, populate the corresponding locale, then set various values based on the currently selected locale. To make the the this[k] assignment work in Dart, I need to define an operator method, but that is pretty simple. That aside, the implementation is nearly identical to last night's JavaScript implementation. And it works the same. Well, almost:



Interestingly, the utf-8 encoded characters are being interpreted as some kind of single-byte character set (probably iso-8859-1). Bleh. Well, that's a bit odd, but I have no idea what the proper way to access <link> imported JSON is. My link.import.documentElement.text may be a bit off the beaten path. But that does not mean that there is no way to get it back onto the utf-8 path:
    // ...
    list.forEach((link) {
      var results = re.firstMatch(link.href);
      var json = UTF8.decode(link.import.documentElement.text.codeUnits);
      locales[results[1]] = JSON.decode(json);
    });
    // ...
With that, I am in business:



Not surprisingly, if I reinstate the Polymer transformer, this no longer works. With the JSON <link> imports stripped from the document, my code no longer works. Bother. It seems I need to dig into the Polymer transformer to figure this out. That seems a topic for another day, so instead, I try an alternate approach to <link> importing.

Instead of adding the <link> tags to the document, I add them to the Polymer's template:
<polymer-element name="hello-you">
  <template>
    <div><!-- ... --></div>
    <link rel="import" href="hello-you-en.json">
    <link rel="import" href="hello-you-fr.json">
    <link rel="import" href="hello-you-es.json">
  </template>
  <script type="application/dart" src="hello_you.dart"></script>
</polymer-element>
Unfortunately, that does not work either. It seems polyfilling <link> imports is limited to the document itself because the import property of these links is always null.

Ah, well. I suppose I can convert these to <polymer-ajax> tags or something along those lines. Tomorrow.

Day #978

Thursday, December 26, 2013

A Possible Approach to Polymer i18n


I think someone's wrong and it may be a Dart library. But it's probably me.

I continue to explore options for localization solutions in Polymer. Yesterday's bright idea includes having web developers provide JSON localizations with the same basename as the corresponding Polymer along with a locale:
    <link rel="import" href="packages/i18n_example/hello-you.html">
    <link rel="import" href="packages/i18n_example/hello-you-en.json">
    <link rel="import" href="packages/i18n_example/hello-you-fr.json">
    <link rel="import" href="packages/i18n_example/hello-you-es.json">
My assumption was that Polymer would polyfill the <link> import tag, making the referenced data available programatically. Only it did not work in the Dart version of Polymer (at least with a pub transformer present). Rather than banging my head against the proverbial wall, I quickly shifted to a JavaScript implementation, which worked. Well, the import seemed to work. Today, I try to actually use the local data.

This takes a bit of digging, but my Polymer ends up looking like this:
    Polymer('hello-you', {
      hello: 'Hello',
      done: 'Done',
      your_name: '',
      locale: 'en',
      count: null,
      count_message: '',

      locales: {},

      ready: function() {
        var that = this;
        var list = this.ownerDocument.
              querySelectorAll('link[rel=import]').
              array().
              filter(function(el) {
                return el.href.match(/hello-you-\w+.json/)
              });

        var re = /hello-you-(\w+).json/;
        list.forEach(function(link) {
          var result = re.exec(link.href);
          var json = link.import.textContent.replace(/import/, '');
          that.locales[result[1]] = JSON.parse(json);
        });

        for (var prop in this.locales[this.locale]) {
          this[prop] = this.locales[this.locale][prop];
        }
      },
      // ...
    });
The ready() is where the action takes place. The first thing that it does is find all of the import <link> elements in the containing document:
        var list = this.ownerDocument.
              querySelectorAll('link[rel=import]').
              array().
              filter(function(el) {
                return el.href.match(/hello-you-\w+.json/)
              });
With that in hand, it builds up the list of known locales by parsing the JSON that is imported. For some reason, the textContent includes the text “import” in the JSON. For instance the French JSON locale textContent looks like:
import{
  "hello": "Bonjour",
  "done": "Fin",
  "how_many": "Combien?",
  "instructions": "Présentez-vous une expérience personnalisée incroyable!"
}
So I have to strip the word “import” to get actual JSON, which can then be parsed. The locale is embedded in the URL in the convention that I am using here. Using that, I can populate a map of locale data:
        var re = /hello-you-(\w+).json/;
        list.forEach(function(link) {
          var result = re.exec(link.href);
          var json = link.import.textContent.replace(/import/, '');
          that.locales[result[1]] = JSON.parse(json);
        });
Finally, given a locale for the Polymer, I can work through each of the properties from the JSON data to set the Polymer property of the same name:
        for (var prop in this.locales[this.locale]) {
          this[prop] = this.locales[this.locale][prop];
        }
With that, I can specify the locale from the Polymer attribute and have a locale specific view:



Nice! This isn't a full-fledge internationalization solution by any stretch. I have no support for dates and pluralization. Still, this seems pretty reasonable basic implementation that builds on some of the same stuff of which Polymer is made. I really need to get this working in Dart, though for any part of it to make it into Patterns in Polymer. So I will switch back to that implementation tomorrow.


Day #977

Wednesday, December 25, 2013

Rel Import of Non Polymers


I still need a way to internationalize Polymer. As I found last night, the Dart intl package is likely to be of little use. Although the code generation is interesting, it seems prohibitively difficult for a web developer who just wants to use a nifty custom Polymer element. Besides, it is just a Dart solution and I need something that will work in both Polymer.dart and in the JavaScript version of Polymer. That said, I do like many of the intl features—especially pluralization and date formatting—so I do not want to totally disregard it.

My thought for tonight is that it might be nice for a localized Polymer element to automatically detect localization (either in code or JSON format). That is, an element that looks like:
<hello-you locale="fr"></hello-you>
Would default to French unless the observed locale attribute changes to another supported locale. And the list of supported locales could be specified by the web developer via <link> tags:
    <link rel="import" href="packages/i18n_example/hello-you.html">
    <link rel="import" href="packages/i18n_example/hello-you-en.json">
    <link rel="import" href="packages/i18n_example/hello-you-fr.json">
    <link rel="import" href="packages/i18n_example/hello-you-es.json">
I have absolutely no idea how feasible that is, but there is, as they say one way to find out. The question for tonight is, does Polymer allow me to import non-Polymers via the <link> tags?

The answer, at least in Dart, seems to be “no.” After creating those files and adding them as <link> tags to the document's <head> tag, I have something like:
  <head>
    <title>Polymer i18n</title>
    <link type="text/css" rel="stylesheet" href="assets/i18n_example/bootstrap.min.css">

    <!-- Load component(s) -->
    <link rel="import" href="packages/i18n_example/hello-you.html">
    <link rel="import" href="packages/i18n_example/hello-you-en.json">
    <link rel="import" href="packages/i18n_example/hello-you-fr.json">
    <link rel="import" href="packages/i18n_example/hello-you-es.json">
    <!-- Load Polymer -->
    <script type="application/dart">
      export 'package:polymer/init.dart';
    </script>
  </head>
But when I load the web page from pub serve, I am left with:
  <head>
    <!-- Polymer.dart JS helpers -->
    <title>Polymer i18n</title>
    <link type="text/css" rel="stylesheet" href="assets/i18n_example/bootstrap.min.css">

    <!-- START: load -->
    <!-- Load component(s) -->
    
    
    
    
    <!-- Load Polymer -->
    <script type="application/dart" src="index.html_bootstrap.dart"></script>
    <!-- END: load --
  </head>
All of my JSON localizations are gone. And, unlike the Polymers, they are not showing up in the network tab of my browser. This is the work of the Polymer transformer that I have specified in my pubspec.yaml:
name: i18n_example
dependencies:
  polymer: any
transformers:
- polymer:
    entry_points: web/index.html
But I rather need that.

At this point the question becomes: should Polymer.dart allow non-Polymer imports this?

To answer that, I re-implement my Polymer in JavaScript, load things and find that yes, the JSON <link> tags are loaded in the network tab (even in Internet Explorer 10, so it does seem like Polymer does this). Furthermore, the imported JSON is available programmatically:
> el = document.querySelectorAll('link')[3].import
Object {href: "http://localhost:8000/scripts/hello-you-fr.json", ownerNode: b, content: b}
> el.content.textContent
"import{
  'hello': 'Bonjour',
  'done': 'Fin',
  'how_many': 'Combien?',
  'instructions': 'Présentez-vous une expérience personnalisée incroyable!'
}
"
Unfortunately, this does me little immediate good since Polymer.dart is in seeming disagreement with its JavaScript sibling. Still, an approach that imports localizations seems to be possible—at least on the surface. I will dig in a little deeper tomorrow with the JavaScript version and then circle back to the Dart to see if there is anyway to cajole it into supporting non-Polymer <link> imports.


Day #976

Tuesday, December 24, 2013

Code Generation and Polymer i18n


So how do internationalization messages work in Dart? I was able to make good use of Intl.plural() in a silly Polymer example yesterday. I even used Intl.message(), but I am pretty sure it is just dead code at this point.

My Polymer displays a locale-dependent greeting, buttons and the number of balloons that a person has:



Thanks to Intl.plural(), my Polymer can display proper grammar versions of the number of balloons that I have be it 99 or 1:



Or even, sadly no red balloons:



I am doing this with locale-specific maps that are chosen at runtime:
  static Map get fr => {
    'hello': 'Bonjour',
    'done': 'Fin',
    'how_many': 'Combien?',
    'instructions': 'Présentez-vous une expérience personnalisée incroyable!',
    'count_message': (count) =>
      Intl.message(
        '''
        ${Intl.plural(
            count,
            zero: "Je n'ai pas de ballon rouge",
            one: "J'ai un ballon rouge",
            other: "J'ai $count ballons rouges"
          )}.''')
  };
I am using Intl.message() for the count_message propery-function. But as I mentioned, it serves no purpose. In fact it rather gets in the way. If I remove it, the code still works and is far more readable:
  static Map get fr => {
    // ...
    'count_message': (count) =>
      Intl.plural(
        count,
        zero: "Je n'ai pas de ballon rouge",
        one: "J'ai un ballon rouge",
        other: "J'ai $count ballons rouges"
      )
  };
But darn it, Intl.message() does something.

I extract the Intl.message() call out in to a function:
count_message(count) =>
  Intl.message(
    '''
    ${Intl.plural(
        count,
        zero: "Je n'ai pas de ballon rouge",
        one: "J'ai un ballon rouge",
        other: "J'ai $count ballons rouges"
    )}.''',
    name: 'count_message',
    desc: 'Reports the number of red balloons a person has.',
    args: [count],
    examples: {'count': 99}
  );
And I dig through the intl code to find that I should:
$ dart \
  --package-root=packages \
  /home/chris/.pub-cache/hosted/pub.dartlang.org/intl-0.9.1/test/message_extraction/extract_to_json.dart \
  lib/hello_you.dart
Which produces a intl_messages.json file with:
[{
  "name":"count_message",
  "desc":"Reports the number of red balloons a person has.",
  "examples":"{'count' : 99}",
  "args":[],
  "message":"\n    ${Intl.plural(count, zero: 'Je n\\'ai pas de ballon rouge', one: 'J\\'ai un ballon rouge', other: 'J\\'ai ${count} ballons rouges')}."
}]
(the actual file output is one a single line -- I split the lines here for readability)

Great. So what does this file actually do?

Well, based on the documentation, I think it mostly serves as a template for translation_* files. I think that I have likely made a mistake here starting with the French, so I am going to create a separate French translation as well as an English translation in translation_en.json:
{
  "locale": "en",
  "count_message": "${Intl.plural(count, zero: 'I have no red balloons', one: 'I have one red balloon', other: 'I have ${count} red balloons')}."
}
Then I run the opposite of the original extract_to_json.dart script. Now I need to generate_from_json.dart. This uses the same command line arguments as the previous script, but now includes the translation_* files as well:
$ dart \
  --package-root=packages \
  /home/chris/.pub-cache/hosted/pub.dartlang.org/intl-0.9.1/test/message_extraction/generate_from_json.dart \
  lib/hello_you.dart \
  translation_en.json translation_fr.json
That fails for me with the following error:
Unable to open file: /home/chris/repos/polymer-book/play/i18n/dart/packages/serialization/serialization.dart'file:///home/chris/.pub-cache/hosted/pub.dartlang.org/intl-0.9.1/test/message_extraction/generate_from_json.dart': error: line 25 pos 1: library handler failed
import 'package:serialization/serialization.dart';
^
Bleh. So I add serialization to my pubspec.yaml, pub get and try again. And still get an error:
Unhandled exception:
The null object does not have a getter 'string'.

NoSuchMethodError : method not found: 'string'
Receiver: null
Arguments: []
#0      Object.noSuchMethod (dart:core-patch/object_patch.dart:42)
#1      generateLocaleFile (file:///home/chris/.pub-cache/hosted/pub.dartlang.org/intl-0.9.1/test/message_extraction/generate_from_json.dart:95:32)
#2      main (file:///home/chris/.pub-cache/hosted/pub.dartlang.org/intl-0.9.1/test/message_extraction/generate_from_json.dart:63:23)
#3      _startIsolate.isolateStartHandler (dart:isolate-patch/isolate_patch.dart:188)
#4      _RawReceivePortImpl._handleMessage (dart:isolate-patch/isolate_patch.dart:93)
Looking through the code in the stacktrace, it seems that my JSON translation files need a _locale attribute, not a locale attribute:
{
  "_locale": "fr",
  "count_message": "${Intl.plural(count, zero: 'Je n\\'ai pas de ballon rouge', one: 'J\\'ai un ballon rouge', other: 'J\\'ai ${count} ballons rouges')}."
}
With that now working, I have three new files:messages_all.dart, messages_en.dart, and messages_fr.dart.

So what do I do with these?

I start with a simple import:
import 'messages_all.dart';
That, combined with setting the Intl.defaultLocale has no effect in the code -- the default French message is always shown. I find that I need to call the initializeMessages() function from the generated messages_all.dart file:
@CustomTag('hello-you')
class HelloYou extends PolymerElement {
  @observable String balloon_message;
  // ...
  HelloYou.created(): super.created() {
    initializeMessages('en_US');
  }
  // ...
  updateCount() {
    // count_message is the i18n function:
    balloon_message = count_message(int.parse(count));
  }
}
With that, I am setting my locale such that the count_message() function will honor it. Almost:



It seems that my translation_en.json is not generating a proper messages_en.dart. At this point, I just want to get this working, so I remove backslashes from messages_en.dart:
class MessageLookup extends MessageLookupByLibrary {
  get localeName => 'en';
  static count_message(count) => "\${Intl.plural(count, zero: \'I have no red balloons\', one: \'I have one red balloon\', other: \'I have ${count} red balloons\')}.";
  // ...
}
After removing the backslashes, I am left with:
  static count_message(count) => "${Intl.plural(count, zero: 'I have no red balloons', one: 'I have one red balloon', other: 'I have ${count} red balloons')}.";
Then I have my Dart intl-based solution working.

Having gone through all that, I can safely say that this is a huge pain. Even if the code was not mis-generated in the last step, this feels awkward to use. Much of that is probably my own biases, so I am not dismissing this approach outright. I personally prefer metaprogramming to code generation. But I recognize that code generation may very well be the right approach in this case. I also recognize that this approach may scale a little better than my poor man's map-based approach.

But in the end, I do not understand what the intializeMessages() does—even after digging through it. That alone may be enough to push me away. But I will sleep on it first.


Day #975

Monday, December 23, 2013

Dart Intl.message (and Polymer)


I have to confess that I am still a little confused by the intl package in Dart. I think that last night's solution for localizing Polymer elements contains pretty much what I need without making any use of the intl package. But I know there must be useful functionality in intl that I am overlooking so tonight I set out to see if I can understand it.

Part of my problem is, I think, a forest through the trees situation. The examples in the source code contain a lot of library lookup and base classes that I do not quite follow. Perhaps I can adapt my existing code to the Dart Way™.

I currently have maps of label values:
class Labels {
  // ...
  static Map get en => {
    'hello': 'Hello',
    'done': 'Done',
    'instructions': 'Introduce yourself for an amazing personalized experience!'
  };

  static Map get fr => {
    'hello': 'Bonjour',
    'done': 'Fin',
    'instructions': 'Présentez-vous une expérience personnalisée incroyable!'
  };
}
I can use a locale string to chose one of those maps at runtime to be interpolated into my Polymer:
<polymer-element name="hello-you">
  <template>
    <div>
      <h2>{{hello}} {{your_name}}</h2>
      <p>
        <input value="{{your_name}}">
        <input type=submit value="{{done}}!" on-click="{{feelingLucky}}">
      </p>
      <p class=instructions>
        {{instructions}}
      </p>
    </div>
  </template>
  <script type="application/dart" src="hello_you.dart"></script>
</polymer-element>
So where do those composite message classes come into play? In the spirit of forest / trees, I am going to look at two trees really close and move on from there.

The two trees in questions are Intl.message() and Intl.plural(). The latter lets me return different messages based on a number supplied. In my case, I will supply the count from the Polymer. The other tree, er… method “returns a message that can be internationalized.” Using the count from the Polymer, I return the number of red balloons in my possession, in French:

  // ...
  static Map get fr => {
    'hello': 'Bonjour',
    'done': 'Fin',
    'how_many': 'Combien?',
    'instructions': 'Présentez-vous une expérience personnalisée incroyable!',
    'count_message': (count) =>
      Intl.message(
      '''${Intl.plural(count,
             zero: "Je n'ai pas de ballons rouge",
             one: "J'ai un ballon rouge",
             other: "J'ai $count ballons rouge")}.''')
  };
  // ...
And that more or less does the trick. I can bind a button to an updateCount() method:
<polymer-element name="hello-you">
  <template>
      <!-- ... -->
      <p>
        {{how_many}}
        <input value="{{count}}">
        <input type=submit value="{{done}}!" on-click="{{updateCount}}">
      </p>
      <p>
        {{count_message}}
      </p>
      <!-- ... -->
  <script type="application/dart" src="hello_you.dart"></script>
</polymer-element>
And my backing method can update the count_message string:
@CustomTag('hello-you')
class HelloYou extends PolymerElement {
  // ...
  @observable int count;
  @observable String count_message;
  // ...
  updateCount() {
    count_message = _labels['count_message'](int.parse(count));
  }
}
The result is that I can have 99 red balloons in French:



And I can have 99 English balloons:



In order to get the latter, I need to duplicate the Intl.message() in English:
// ...
  static Map get en => {
    // ...
    'count_message': (count) =>
      Intl.message(
      '''${Intl.plural(count,
             zero: "I have no red balloons",
             one: "I have one red balloon",
             other: "I have $count red balloons")}.''')
  };
  // ...
I think that is proper use of Intl.plural, but I have not quite got Intl.message() right. Ultimately, I would like to either be able to set the default locale of my Polymer at runtime to choose the correct version of a “count message” function. My current approach kinda / sorta does that, but there is an actual Intl.defaultLocale property that I am currently not using at all.

In order to do so, I at least need to supply the name parameter to Intl.message(). I am still somewhat vague on how that fits into the grander scheme of intl and its locale lookups, but hopefully that will become clearer as I continue to explore. Tomorrow.

Day #974

Sunday, December 22, 2013

A Slightly Better i18n Polymer


Last night, I got application-wide localization working in my Polymer application. It is not real internationalization and localization—it only allows for a single application-wide localization, but it is a start. Tonight, I hope to use some of Dart's intl package to come up with something better. Specifically, I would like to be able to dynamically change the locale at runtime.

There is an example of Polymer internationalization in the Dart repository. I am not quite following along with that, but it does prove invaluable.

Yesterday's solution relied on attributes and <content> tags to include information from other locales:
      <hello-you hello="Bonjour" done="Fin">
        <p>Présentez-vous une expérience personnalisée incroyable!</p>
      </hello-you>
Those attributes and <content> are then included in the template as:
<polymer-element name="hello-you">
  <template>
    <div>
      <h2>{{hello}} {{your_name}}</h2>
      <p>
        <input value="{{your_name}}">
        <input type=submit value="{{done}}!" on-click="{{feelingLucky}}">
      </p>
      <p class=instructions>
        <content></content>
      </p>
    </div>
  </template>
  <script type="application/dart" src="hello_you.dart"></script>
</polymer-element>
As I mentioned, the attributes like hello and done have to to hard-coded in the page for this approach to work. Instead, I want those properties to be dynamic getters.

In the end, I do not even use the intl package, which seems useful for strings requiring parameters (numbers or dates). I find that I can achieve what I want with a Labels class like:
class Labels {
  var locale;
  Labels(this.locale);

  operator [](label) {
    var lookup;
    switch (locale) {
      case 'fr' : lookup = fr; break;
      default: lookup = en;
    }

    return lookup[label];
  }

  static Map get en => {
    'hello': 'Hello',
    'done': 'Done',
    'instructions': 'Introduce yourself for an amazing personalized experience!'
  };

  static Map get fr => {
    'hello': 'Bonjour',
    'done': 'Fin',
    'instructions': 'Présentez-vous une expérience personnalisée incroyable!'
  };
}
The localized labels are included as static methods. An instance of the Labels class provides an [] lookup, switching the labels based on the current locale.

The various getters in the Polymer can then be written as lookups of Labels:
@CustomTag('hello-you')
class HelloYou extends PolymerElement {
  // ...
  @published String locale = 'en';

  HelloYou.created(): super.created();

  String get hello => _labels['hello'];
  String get done => _labels['done'];
  String get instructions => _labels['instructions'];

  var __labels;
  get _labels {
    if (__labels == null) __labels = new Labels(locale);
    return __labels;
  }
  // ...
}
That still will not quite work if I wanted to allow a human to change the locale on the fly, but it allows the Polymer to change locales with a simple attribute:
<hello-you locale="fr"></hello-you>
Which results in:



I will pick back up tomorrow trying to allow a human to hot swap locales. I would also like to come up with a contrived example to use for intl messages. I do not think that I will end up using them in Patterns in Polymer (there's not an easy JavaScript equivalent), but I misunderstood them badly enough tonight to confuse myself into thinking that I needed them. This seems a fine chance to understand what they really can do.


Day #973

Saturday, December 21, 2013

Silly Polymer i18n


I haven't the foggiest idea what internationalization and localization should look like in Polymer. Heck, I'm not sure formal i18n / l10n is necessary in Polymer—a combination of distributed nodes and attributes might be sufficient for most cases. But to explore the topic, I start modifying a toy Polymer to support different text.

At lease some of this will be based on the example in the Dart code repository. Not tonight's work, which takes a more simplistic approach. But I will eventually get to that other sample.

I am starting exploration of this topic in Dart rather than JavaScript because Dart includes an internationalization library (which is admittedly still early stages). To start using, I add the intl package to the list of dependencies for my code:
name: i18n
dependencies:
  polymer: any
  intl: any
dev_dependencies:
  unittest: any
transformers:
- polymer:
    entry_points: web/index.html
After a pub get, I have Dart's internationalization library at my disposal:
➜  dart git:(master) ✗ pub get
Resolving dependencies...........................................
Downloading intl 0.9.1 from hosted...
Got dependencies!
My play Polymer looks something like:



The thing about localizing this particular Polymer is that I can do so without the formalization of the intl package. The help text at the bottom is actually distributed into the Polymer via <content> tag:
      <hello-you>
        <p>Introduce yourself for an amazing personalized experience!</p>
      </hello-you>
I can localize that to French with:
      <hello-you>
        <p>Présentez-vous une expérience personnalisée incroyable!</p>
      </hello-you>
And that works just fine, leaving only the initial title and button to be localized to French:



Before moving on, I feel that I must admit that I am a typical American. I took 6 years of French in school and would struggle to ask for directions to the library in French today. I am fully at the mercy of Google translate in this matter. Actually, that is not entirely true—I know that “incroyable” means “incredible” because Bomb Voyage calls Mr Incredible “Monsieur Incroyable” in The Incredibles. Anyhow...

The remainder of my play Polymer can be localized through attributes:
      <hello-you hello="Bonjour" done="Fin">
        <p>Présentez-vous une expérience personnalisée incroyable!</p>
      </hello-you>
To support that, I next need to bind the hello and done attributes in the Polymer template:
<polymer-element name="hello-you">
  <template>
    <div>
      <h2>{{hello}} {{your_name}}</h2>
      <p>
        <input value="{{your_name}}">
        <input type=submit value="{{done}}!" on-click="{{feelingLucky}}">
      </p>
      <p class=instructions>
        <content></content>
      </p>
    </div>
  </template>
  <script type="application/dart" src="hello_you.dart"></script>
</polymer-element>
And I need some dumb American defaults in the backing class:
import 'package:polymer/polymer.dart';

@CustomTag('hello-you')
class HelloYou extends PolymerElement {
  @published String your_name;
  @published String hello = 'Hello';
  @published String done = 'Done';
  HelloYou.created(): super.created();
  // ...
}
With that, I have my play Polymer localized in French:



I fully admit that this is not a complete and robust solution. If my Polymer contained dates, for instance, I will still be using the stupid American date format where the month comes first. Regardless of an application-wide intl locale setting, most of the world would still be subjected to the month first. But… for this very simple case, this simple approach works fairly well. I am still going to pursue a more robust solution that actually uses the intl package. Tomorrow.


Day #972

Friday, December 20, 2013

Building Application Polymers in Dart


I tend to doubt that I will include a deployment chapter in Patterns in Polymer. It would be difficult at best to find commonalities between the Dart and JavaScript version. I would also imagine that best practices on the matter would change and likely be better in web form than in book form. Still, I am curious enough to play with it one more night.

Last night, I found that, instead of organizing a Dart application with application-specific polymers in the usual lib structure:
lib
├── hello_you.dart
└── hello-you.html
web
└── index.html
I had to include my application specific Polymers in a subdirectory of the web application directory:
web
├── elements
│   ├── hello_you.dart
│   └── hello-you.html
└── index.html
I can live with that, especially since I found that the ToDo MVC does the same. But then James Hurford reported that he was able to get it working with the lib approach. So let's see if I can get this going.

This is not the first time that James has reported being able to do something that I could not get working. The most obvious difference between our setups is that James is coding in the DartEditor while I rely on the one true editor, Emacs. To eliminate this as a possible cause, I fire up the application in the DartEditor and right-click build a Polymer app:



When I run my app in Dartium, I am still not seeing my Polymer, just the text wrapped by the Polymer:



In addition to not seeing Polymer in action, I also see that my assets, like:
<link type="text/css" rel="stylesheet" href="/assets/ie/bootstrap.min.css">
Are not being loaded. At this point, I realize that I am specifying my URLs absolutely, including the URLs that point to my application Polymers:
<link rel="import" href="/packages/ie/hello-you.html">
If I change both to relative URLs:
<link type="text/css" rel="stylesheet" href="assets/ie/bootstrap.min.css">
<!-- ... -->
<link rel="import" href="packages/ie/hello-you.html">
Then re-build, then my application Polymer works!



That turns out to be the solution to my application specific Polymers. Switching back to Emacs and the command line, I change the Polymer URL back to an absolute URL and run pub build (part of Dart Pub). Serving the application up in pub serve, my Polymer fails again. It should not matter with pub serve because the relative and absolute URLs are the same in this case (DartEditor adds another URL path, which is why I noticed the assets not loading).

Switching that single Polymer import from an absolute to relative URL fixes the problem:
<link rel="import" href="packages/ie/hello-you.html">
After a quick pub build, my Polymer is again working just fine (in all browsers).

It seems that the pub build process really, really wants that to be a relative URL. I suppose that makes some sense since it is converting a web URL into a file system path. Still, I would think that the build process ought to be able to make that conversion on its own. For now, I am happy to have a solution, but I may file that as a bug after sleeping on it.


Day #971

Thursday, December 19, 2013

Polymer.Dart in IE


The browser wars are long since over, won by Firefox when it reached 10% market share ca. 2005. Once that inflection point hit, it was impossible to target a single browser without alienating 1 out of every ten potential customers. At this point, it hardly matters which browser is “winning.” If a particular browser or browser version has 10% or more of the market share, we developers have to support it. If a particular browser or browser version has less than 10%, then we have to make a cost-benefit judgment on whether to support it.

That said, I rather like the approach of Dart and Polymer (and others): support only the most recent versions of evergreen (self-updating) browsers. To be sure, applications that are delivered today would ignore a fair portion of potential customers (10%-25% depending on which damn statistics you believe). But the cost of maintaining libraries and codebases for antique browsers is high—often prohibitively so. And as modern, evergreen browsers like Chrome, Firefox and Internet Explorer continue to evolve, the number of ignored potential customers goes down rapidly.

Bottom line: supporting browsers like Internet Explore 10+ is important. I have every reason to expect that Polymer, Dart, and even Polymer.dart will work on IE. But I have not tried it yet. I create a “hello world” type Polymer.dart application (actually, copy it from Patterns in Polymer) and serve it up via pub serve. When run in Dartium, the bound variables update the title and the button randomly changes the color in the same title:



My next step is to add the Polymer transformer to pubspec.yaml so that pub can transform this into JavaScript for other browsers:
name: ie
dependencies:
  polymer: any
  observe: "0.9.0+1"
dev_dependencies:
  unittest: any
transformers:
- polymer:
    entry_points: web/index.html
Only that doesn't work. Even in Dartium, I now find that my Polymer is being ignored. It is almost as if it is not being loaded from the lib directory.

After checking the sample ToDo MVC code, it seems that is exactly what is going on. In there, they have all of the Polymers in sub-directories of web.

So I copy my elements from the lib directory into web/elements:
➜  dart git:(master) ✗ cd web
➜  web git:(master) ✗ mkdir elements
➜  web git:(master) ✗ cd elements
➜  elements git:(master) ✗ cp -r ../../lib/hello* 
Then I update the page to point to the new location of the Polymer:
    <!-- Load component(s) -->
    <link rel="import" href="elements/hello-you.html">
    <!-- Load Polymer -->
    <script type="application/dart">
      export 'package:polymer/init.dart';
    </script>
    <script src="packages/browser/dart.js"></script>
And I restart pub serve. With that, I have my Polymer.dart application serving up properly—even on Internet Explorer:



I will have to dig into the lib vs. web directory locations a bit more. I suspect that I need to package my Polymers for pub deployment rather than serving them from lib. For now, I am happy to have a proof of concept Polymer.dart successfully running under Internet Explorer.


Day #970

Wednesday, December 18, 2013

Vertically Flexed Polymer


I know next to nothing about CSS flex boxes. I have heard passing mention of them, but have never played with them. So when I was casting about looking for another topic to research for Patterns in Polymer, flex boxes seemed a reasonable next step. Then I ran into minor, unrelated technical difficulties in both JavaScript and Dart that sidetracked me. Tonight, I would like to make sure that I understand this before moving on.

The default, horizontal flex box seems to work fairly well. The following HTML (using Polymer.dart):
<!DOCTYPE html>
<html lang="en">
  <head>
    <title>Flex Test</title>
    <!-- Load component(s) -->
    <link rel="import" href="/packages/polymer_elements/polymer_flex_layout/polymer_flex_layout.html">
    <script type="application/dart">export 'package:polymer/init.dart';</script>
    <style>
      .main {
        background-color: palegoldenrod;
      }
    </style>
  </head>
  <body>
    <polymer-flex-layout isContainer>
      <div>One</div>
      <div class="main" flex>Main</div>
      <div>Two</div>
    </polymer-flex-layout>
  </body>
</html>
Produces a simple, horizontal layout with the middle column “flexed” by the flex attribute:



Changing the layout to vertical alignment seems less easy. I expect that the flex attribute would expand the middle row to occupy the entire viewport while the other two <polymer-flex-layout> get a single line each. But the following:
    <polymer-flex-layout isContainer vertical
      <div>One</div>
      <div class="main" flex>Main</div>
      <div>Two</div>
    </polymer-flex-layout>
Results in:



After a bit of experimentation, it seems that, to get the height that I desire, I have to do it myself with CSS:
  <head>
    <!-- ... -->
    <style>
      polymer-flex-layout[vertical] {
        min-height: 95vh;
      }
      .main {
        background-color: palegoldenrod;
      }
    </style>
  </head>
  <body>
    <polymer-flex-layout isContainer vertical>
      <div>One</div>
      <div class="main" flex>Main</div>
      <div>Two</div>
    </polymer-flex-layout>
  </body>
With that, I see that the flex attribute is working as I expected:



This does seem to have quite the power of the MDN example. For instance, I see no way to rearrange order or change from horizontal to vertical depending on the screen size (at least not without code). Still, this element, and the related <polymer-layout> element do have some power:
  <head>
    <!-- ... -->
    <style>
      .container, polymer-flex-layout[vertical] {
        min-height: 92vh;
      }
      .container {
        border: 5px dashed orange;
      }
      .main {
        background-color: palegoldenrod;
      }
    </style>
  </head>
  <body>
    <div class=container>
      <polymer-layout vertical></polymer-layout>
      <div>One</div>
      <div class=main flex>
        <polymer-flex-layout vertical=false isContainer>
          <div>11111</div>
          <div>22222</div>
          <div flex align=center>xxxxx</div>
          <div>33333</div>
          <div>44444</div>
</polymer-flex-layout>
        </div>
      </div>
      <div>Two</div>
    </div>
  </body>
Produces:



Both <polymer-layout> and <polyer-flex-layout> have the feel of something of limited use. Perhaps a quick and easy way to get started with a layout or something along those lines. But they also feel like it would be easy to run into limitations that might necessitate rolling my own. But if I had to roll my own, it is nice having these example handy.


Day #969