James Slocum

Blog

A random collection of thoughts on a variety of topics


“Updated examples for LJ Dart article”

2013-03-01

When I wrote the article for Linux Journal about Dart (back in December) I was using dart version 0.2.9.9_r16323, which at the time was the newest version. Now 3 months later when the article is published, the version of dart has advanced to version 0.4.0.0_r18915. This normally would not be an issue, but changes in the standard library (released Feb. 20th 2013) have broken the 3 month old example code provided in print. If that’s not bad timing I don’t know what is! Since I cannot update the print, I will instead give the updated example code here.

The first example, writing to a file, was affected by the addition of the IOSink class to the standard library and subsequent removal of the openOutputStream() method on the file class. To fix it I used the new File.OpenWrite() and IOSink.addString()methods.

   import 'dart:io';

   void main(){
     String fileName = './test.txt';
     File file = new File(fileName);

     var out = file.openWrite();
     out.addString("This is my first file output in Dart!\n");
     out.close();
   }
   

The wunder.dart example was by far the most impacted. The changes made to the streaming API, combined with the removal of the HttpClientConnection and InputStream classes, have made this program all but useless. It had to be re-written to support the new API. The HttpClient.getUrl() method now returns a Future<HttpClientRequest>. This future object returns a request object when it is done. That request object can be used to obtain the response object when it is closed. I was able to re-use the JSON code that I wrote in the previous example but I need to change how I imported the dart:json library.

   import 'dart:io';
   import 'dart:uri';
   import 'dart:async';
   import 'dart:json' as JSON;

   void main(){
     String apiKey = "";
     String zipcode = "";
     
     //Read the user supplied data form the options object
     try {
       apiKey = new Options().arguments[0];
       zipcode = new Options().arguments[1];
     } on RangeError {
       print("Please supply an API key and a zipcode!");
       print("dart wunder.dart <apiKey> <zipCode>");
       exit(1);
     }

     //Build the URI we are going to request data from
     Uri uri = new Uri("http://api.wunderground.com/"
         "api/${apiKey}/conditions/q/${zipcode}.json");

     HttpClient client = new HttpClient();
     client.getUrl(uri).then((request) {
       var response = request.close();
       response.then((response) => handleResponse(response));
     },
     onError: (AsyncError e) {
       print(e);
       exit(3);
     });

     client.close();
   }

   void handleResponse(HttpClientResponse response){
     List jsonData = [];
     response.toList().then((list) {
       jsonData.addAll(list.first);
       
       //response and print the location and temp.
       try {
         Map jsonDocument = JSON.parse(new String.fromCharCodes(jsonData));
         if (jsonDocument["response"].containsKey("error")){
           throw jsonDocument["response"]["error"]["description"];
         }
         String temp = jsonDocument["current_observation"]
            ["temperature_string"];
         String location = jsonDocument["current_observation"]
            ["display_location"]["full"];
         
         print('The temperature for $location is $temp');
       } catch(e) {
         print("Error: $e");
         exit(2);
       }
     },
     onError: (AsyncError e){
       print(e);
       exit(4);
     });
   }
   

This version will work just like the old one, but for some reason it hangs if you use the wrong Wunderground API key. I am not sure why, but I am working on a better version of this using the HttpClientResponse.listen() method. I will post that when I have something working but for now this will do.

The fingerpaint.dart example wasn’t too badly affected. Dart has change the semantics of how it deals with events. Now it uses the Stream class to make event streams that you “listen” to. So instead of registering with an event like _canvas.on.mouseDown.add((Event e) => _onMouseDown()); you would add a listener to the event stream _canvas.onMouseDown.listen((Event e) => _onMouseDown());.

Here is the new example code that will work with the newest Dartium.

   library fingerpaint;

   import 'dart:html';

   class DrawSurface {
     String _color = "black";
     int _lineThickness = 1;
     CanvasElement _canvas;
     bool _drawing = false;
     var _context;
     
     DrawSurface(CanvasElement canvas) {
       _canvas = canvas;
       _context = _canvas.context2d;
       _canvas.onMouseDown.listen((Event e) => _onMouseDown(e));
       _canvas.onMouseUp.listen((Event e) => _onMouseUp(e));
       _canvas.onMouseMove.listen((Event e) => _onMouseMove(e));
       _canvas.onMouseOut.listen((Event e) => _onMouseUp(e));
     }

     set lineThickness(int lineThickness) {
       _lineThickness = lineThickness;
       _context.lineWidth = _lineThickness;
     }

     set color(String color) {
       _color = color;
       _context.fillStyle = _color;
       _context.strokeStyle = _color;
     }

     int get lineThickness => _lineThickness;

     int get color => _color;
     
     void incrementLineThickness(int amount){
       _lineThickness += amount;
       _context.lineWidth = _lineThickness;
     }

     String getPNGImageUrl() {
       return _canvas.toDataUrl('image/png', 1.0);
     }

     _onMouseDown(Event e){
       _context.beginPath();
       _context.moveTo(e.offsetX, e.offsetY);
       _drawing = true;
     }

     _onMouseUp(Event e){
       _context.closePath();
       _drawing = false;
     }

     _onMouseMove(Event e){
       if (_drawing == true){
         _drawOnCanvas(e.offsetX, e.offsetY);
       }
     }

     _drawOnCanvas(int x, int y){
       _context.lineTo(x, y);
       _context.stroke();
     }

   }

   void main() {
     CanvasElement canvas = query("#draw-surface");
     DrawSurface ds = new DrawSurface(canvas);
     
     List buttons = queryAll("#colors input");
     for (Element e in buttons){
       e.onClick.listen((Event eve) {
         ds.color = e.id;
       });
     }

     var sizeDisplay = query("#currentsize");
     sizeDisplay.text = ds.lineThickness.toString();

     query("#sizeup").onClick.listen((Event e) {
       ds.incrementLineThickness(1);
       sizeDisplay.text = ds.lineThickness.toString();
     });

     query("#sizedown").onClick.listen((Event e) {
       ds.incrementLineThickness(-1);
       sizeDisplay.text = ds.lineThickness.toString();
     });

     query("#save").onClick.listen((Event e) {
       String url = ds.getPNGImageUrl();
       window.open(url, "save");
     });
   }
   

The fingerpaint webapp is affected by another breaking change as well. Google will no longer host the dart.js bootstrap file. Now you must use the pubprogram that comes with Dart. Pub is the dart package manager and uses YAML files for web application configuration. You must create a file called “pubspec.yaml” in the same directory as your project that contains the following:

   name: fingerpaint
   dependencies:
      browser: any
   

Once that has been created you run the command:

   $ pub install
   Resolving dependencies...
   Dependencies installed!
   

Now you will have a new directory called packages. Remember that really long link to the Dart bootstrap file? Well, now you need to change that link from:

<script src="http://dart.googlecode.com/svn/trunk/dart/client/dart.js"></script>
   

to:

<script src="packages/browser/dart.js"></script>
   

Now you should have no problems running this code through Dartium.

While it is embarrassing to have my first Linux Journal article meet the world DOA, I have learned a great deal from this experience! First, always include what version of whatever application or language you are using in the article. Had I simply mentioned that I used Dart 0.2.9.9 this whole situation would have gone better. Second, maybe I should wait until a project is a bit more mature before giving it a full write up. I am not upset about this situation, and I am glad Dart is moving forward so rapidly with development! I am just a bit angry at the timing of it all.  Oh well, just some bad luck! The guys and girls at Linux Journal were very understanding and made this whole situation go smoother. If you are a Linux Journal reader I hope you will understand as well. This is the price we must sometimes pay for bleeding edge technology.


comments powered by Disqus