World of Dartcraft

A survey of Google's Dart programming language, overview of platform tools, and pointers on getting up and running

Created by Naum Trifanoff / @naum

hello.dart


#!/usr/bin/env dart

void main(List<string> arg) {
  if (arg.length > 0) {
    print('hello ${arg.join(" ")}!');
  } else {
    print('hello world!');
  }
}
					
The initial reaction to doing another programming language is people say: 'We have one that’s sufficient'. Change can be hard for some people.

~Lars Bak

Why Dart?

  • Batteries included
  • Module organization
  • Runs in browser, on server, converts to Javascript
  • Eradicate "bad parts" of Javascript
  • Dart Editor IDE and/or command line plus chosen editor

What Dart Can Do For You!

  • Single Page Applications (SPA)
  • Command line scripts
  • Web server

Not Suitable For…

  • Global Javascript replacement
  • Desktop applications?
“In my quest it quickly became clear that web technologies have gained a lot of steam over the years, not just for web applications, but for desktop and mobile as well. I've found that there are hundreds of tools out there, and so far, but there doesn't seem to be the veritable Swiss Army Knife out there.” ~Nicholas Bilyk

Up and Running

Download SDK from dartlang.org/tools

and/or

brew install dart

Thinking in Dart

Classes


class Point {
  num x;
  num y;

  Point(this.x, this.y);

  // Named constructor
  Point.fromJson(Map json) {
    x = json['x'];
    y = json['y'];
  }
  
  // Operator overriding
  Point operator +(Point other) => new Point(x+other.x, y+other.y);
  
  // String interpolation
  String toString() => 'X: $x, Y: $y';
}
            

String interpolation


print('hello ${arg.join(" ")}!');
            

Library parts

index.dart

import 'horatio.dart';

main () { /* code goes in here */ }
						
horatio.dart

library horatio;

import 'dart:html';
// other imports go in here

part 'ragged_dick.dart';
part 'newsboys.dart';

// code goes in here
						
ragged_dick.dart

part of horatio;

// code goes in here
						
newsboys.dart

part of horatio;

// code goes in here
						

Pub

pubspec.yaml

name: skopon
description: a dartisan wiki
dependencies:
  crypto: any
  unittest: any

pub get

pub upgrade

Futures


import 'dart:async';
import 'dart:convert';
import 'dart:io';

var HC = new HttpClient();

grabWebContent(u, f) {
  var url = Uri.parse(u);
  HC.getUrl(url)
    .then((HttpClientRequest request) {
      return request.close();
    })
    .then((HttpClientResponse response) {
      response.transform(new Utf8Decoder()).toList().then((data) {
        var body = data.join('');
        f(body);
      });
    });
}
					

Streams


File file = new File("some_file.txt");
file.openRead()
    .transform(new StringDecoder()) 
    .listen((String data) => print(data), 
        onError: (error) => print("Error, could not open file"),
        onDone: () => print("Finished reading data"));
            

Isolates

Dart Annoyances and Gotchas

  • Only true is true
  • No regular expression literals
  • ~/ for integer division
  • Constantly shifting libraries but 1.0 is near
  • Too much Java-esque smell
  • Functions with variable number of arguments not supported
  • Immature library availability

Show me examples!

domgenesis: generate random Dominions 3 game settings


var pageactionbar = query('#pageactionbar');
var pageboard = query('#pageboard');
var properwords = '';

main() {
  HttpRequest.getString('/properwords.txt').then(prepareUI);
}

void prepareUI(respcont) {
  properwords = respcont;
  var buGenesis = new ButtonElement();
  buGenesis.text = 'genesis';
  buGenesis.onClick.listen((e) {
    rollDomWorld();
  });
  pageactionbar.children.add(buGenesis);
  window.onKeyPress.listen((e) {
    print('charCode: ${e.charCode}');
    if (e.charCode == 71 || e.charCode == 103) {
      buGenesis.click();
    }
  });
}

void rollDomWorld() {
  var domworld = new DomWorld(properwords);
  pageboard.innerHtml = '
${domworld}
'; }

domgenesis.nauminous.net

skopon: A Dart flavored Wiki!


library nawms;

final ESCMARK = new String.fromCharCode(949);
final LINKMARK = new String.fromCharCode(955);

List codestack = [];
List escblock = [];
List extlink = [];

String emit(String htag, num depth) {
  var tout = new StringBuffer();
  if (depth > codestack.length) {
    while (codestack.length < depth) {
      codestack.add(htag);
      tout.write('<${htag}>\n');
    }
  }
  if (depth < codestack.length) {
    while (codestack.length > depth) {
      tout.write('\n');
    }
  }
  if (depth > 0 && depth == codestack.length && htag != codestack[codestack.length - 1]) {
    tout.write('\n');
    tout.write('<${htag}>\n');
    codestack[depth - 1] = htag;
  }
  return tout.toString();
}

String escapeBlock(Match m) {
  escblock.add(m.group(1));
  return '${ESCMARK}${escblock.length}${ESCMARK}';
}

String escapeHtmlSpecialChars(String s) {
  return s.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>');
}

String externalLink(Match m) {
  extlink.add(m.group(0));
  return '${LINKMARK}${extlink.length}${LINKMARK}';
}

String fillEscapeBlock(Match m) {
  var n = int.parse(m.group(1)) - 1;
  return '
${escblock[n]}
\n'; } String fillExternalLink(Match m) { var n = int.parse(m.group(1)) - 1; return '${prettifyUrl(extlink[n])}'; } String prettifyUrl(String u) { var reHostPrefix = new RegExp(r'^(http|https|ftp|mailto):(\/\/)?'); return u.replaceFirst(reHostPrefix, ''); } String wikiToHtml(String tex) { var hout = new StringBuffer(); tex = unixfyEol(tex); tex = escapeHtmlSpecialChars(tex); var reEscBlock = new RegExp(r'\=\=\=\=((.|\n)*?)\=\=\=\=', multiLine: true); var reExtLink = new RegExp(r'\b(http|https|ftp|mailto):\S*'); var reBlankLine = new RegExp(r'^\s*$'); var reBulletListItem = new RegExp(r'^(\*+)(.*)$'); var reEnumeratedListItem = new RegExp(r'^(\#+)(.*)$'); var reBlockquote = new RegExp(r'^((\>\;)+)(.*)$'); var reDefinitionList = new RegExp(r'^(:+)(.+?):( +)(.*)$'); var reHorizontalRule = new RegExp(r'^----*(.*)$'); var reHeading = new RegExp(r'^(!{1,4})(.*)$'); var reStrong = new RegExp(r'\*\*(.*?)\*\*'); var reEmphasis = new RegExp(r'\_(.*?)\_'); var reEmphasisAlt = new RegExp(r'\*(.*?)\*'); var reMonospace = new RegExp(r'\`\`(.*?)\`\`'); var reWikiLink = new RegExp(r'\~(\w+)'); tex = tex.replaceAllMapped(reEscBlock, escapeBlock); tex = tex.replaceAllMapped(reExtLink, externalLink); for (var line in tex.split('\n')) { num depth = 0; if (reBlankLine.hasMatch(line)) continue; if (reBulletListItem.hasMatch(line)) { line = line.replaceAllMapped(reBulletListItem, (m) { depth = m.group(1).length; return '
  • ${m.group(2).trim()}
  • '; }); hout.write(emit('ul', depth)); } else if (reEnumeratedListItem.hasMatch(line)) { line = line.replaceAllMapped(reEnumeratedListItem, (m) { depth = m.group(1).length; return '
  • ${m.group(2).trim()}
  • '; }); hout.write(emit('ol', depth)); } else if (reBlockquote.hasMatch(line)) { line = line.replaceAllMapped(reBlockquote, (m) { depth = m.group(1).length ~/ 4; return '

    ${m.group(3)}

    '; }); hout.write(emit('blockquote', depth)); } else if (reDefinitionList.hasMatch(line)) { line = line.replaceAllMapped(reDefinitionList, (m) { depth = int.parse(m.group(1)); return '
    ${m.group(2)}
    ${m.group(4)}
    '; }); hout.write(emit('dl', depth)); } else if (reHorizontalRule.hasMatch(line)) { line = line.replaceAllMapped(reHorizontalRule, (m) => '
    \n'); hout.write(emit('eots', 0)); } else if (reHeading.hasMatch(line)) { line = line.replaceAllMapped(reHeading, (m) { var h = 'h${m.group(1).length + 2}'; return '<$h>${m.group(2)}\n'; }); hout.write(emit('eots', 0)); } else { line = (! line.contains(ESCMARK)) ? '

    $line

    ' : '
    $line
    '; hout.write(emit('eots', 0)); } line = line.replaceAllMapped(reStrong, (m) => '${m.group(1)}'); line = line.replaceAllMapped(reEmphasis, (m) => '${m.group(1)}'); line = line.replaceAllMapped(reEmphasisAlt, (m) => '${m.group(1)}'); line = line.replaceAllMapped(reMonospace, (m) => '${m.group(1)}'); line = line.replaceAllMapped(reWikiLink, wikilink); hout.write('${line}\n'); } hout.write(emit('eots', 0)); var gh = hout.toString(); var reLinkMark = new RegExp('${LINKMARK}' + r'(\d+)' + '${LINKMARK}'); gh = gh.replaceAllMapped(reLinkMark, fillExternalLink); var reEscMark = new RegExp('${ESCMARK}' + r'(\d+)' + '${ESCMARK}'); gh = gh.replaceAllMapped(reEscMark, fillEscapeBlock); return gh; } String unixfyEol(String s) { return (s.contains('\n')) ? s.replaceAll('\r', '') : s.replaceAll('\r', '\n'); } String wikilink(Match m) { var page = m.group(1); var pagedesc = page.replaceAll('_', ' '); return '$pagedesc'; }

    github.com/naum/skopon

    genpassphrase.dart

    
    import 'dart:io';
    import 'dart:math';
    
    var PN = new RegExp(r"^[A-Z]");
    var RNG = new Random();
    
    main() {
      var df = new File("/usr/share/dict/words");
      var wstr = df.readAsStringSync();
      var wl = wstr.split("\n");
      wl = wl.where(isAcceptableWord).toList();
      //print('wl.length: ${wl.length}');
      shuffle(wl);
      var pp = wl.sublist(0, 4).join(' ');
      print(pp);
    }
    
    bool isAcceptableWord(String w) {
      return (w.length >= 2 && w.length <= 7);
    }
    
    shuffle(List l) {
      var n = l.length;
      while (n > 0) {
        var i = RNG.nextInt(n);
        n -= 1;
        var t = l[n];
        l[n] = l[i];
        l[i] = t;
      }
    }
    

    irisring: generate Tumblr tag report

    
    import 'dart:async';
    import 'dart:convert';
    import 'dart:io';
    
    var HC = new HttpClient();
    var RE_SP = new RegExp('\{SP\}');
    var MAXPOSTNUM = 300;
    var STARTPOSTNUM = 0;
    var TUMTAGTAB = {};
    var TURL = 'http://{TUMBLR_URL}/api/read/json/?num=50&start={SP}';
    
    main(arg) {
      if (arg.length > 0) {
        var tu = arg[0];
        for (var n = 0; n < MAXPOSTNUM; n += 50) {
          var du = new Duration(seconds: ((n ~/ 50) * 5));
          new Timer(du, () { 
              var tb = { 'TUMBLR_URL': tu, 'SP': n };
              var desiredUrl = mold(TURL, tb);
              print('Fetching ${desiredUrl}...');
              grabWebContent(desiredUrl, showPageContent);
          });
        }
        new Timer(
          new Duration(seconds: (MAXPOSTNUM ~/ 50) * 5),
          showTagTab
        );
      } else {
        print('Usage: irisring.dart url');
      }
    }
    
    grabWebContent(u, f) {
      var url = Uri.parse(u);
      HC.getUrl(url)
        .then((HttpClientRequest request) {
          return request.close();
        })
        .then((HttpClientResponse response) {
          response.transform(new Utf8Decoder()).toList().then((data) {
            var body = data.join('');
            f(body);
          });
        });
    }
    
    makeTumblrMap(str) {
      var bp = 22;
      var ep = str.length - 2;
      var tjs = str.substring(bp, ep);
      return JSON.decode(tjs);
    }
    
    String mold(String t, Map b) {
      var reTS = new RegExp(r'\{(\w+)\}');
      var sout = t.replaceAllMapped(reTS, (m) {
        var s = m.group(1);
        return (b.containsKey(s)) ? b[s] : '';
      });
      return sout;
    }
    
    showPageContent(bc) {
      var tpc = makeTumblrMap(bc);
      for (var p in tpc['posts']) {
        if (p.containsKey('tags')) {
          print("${p['id']} ${p['tags']}");
          tallyTags(p['tags']);
        }
      }
    }
    
    showTagTab() {
      print('----');
      print(TUMTAGTAB);
      print('----');
    }
    
    tallyTags(List tl) {
      for (var t in tl) {
        if (TUMTAGTAB.containsKey(t)) {
          TUMTAGTAB[t] += 1;
        } else {
          TUMTAGTAB[t] = 1;
        }
      }
    }
                      

    https://github.com/naum/irisring

    Would you like to know more?

    dartlang.org

    Blogs

    Books

    • Dart in Action by Chris Buckett
    • Dart for Hipsters by Chris Strom
    • Dart: Up and Running by Kathy Walrath and Seth Ladd
    • The Dart Programming Language by Gilad Bracha (May 2014)

    Community

    finally { }

    Naum Trifanoff / dartcraft.nauminous.net

    dartcraft.tumblr.com

    @naum