Sunday, July 31, 2016

An Example of Using Dart - forcemvc, MongoDB, and objectory, part 2

/lib/model.dart

part of controllers;

class Person extends PersistentObject {
//  String get collectionName => "Name";
  String get firstName => getProperty('first_name');
  set firstName(String value) => setProperty('first_name', value);
  String get lastName => getProperty('last_name');
  set lastName(String value) => setProperty('last_name', value);
  String toString() => "$firstName $lastName";
  Future<bool> isDuplicate() {
    Completer completer = new Completer();
    bool answer;
    objectory.initDomainModel().then((success) {
      objectory[Person].find(where.eq('first_name', this.firstName)
          .and(where.eq('last_name', this.lastName))).then((matches) {
        if (matches.length > 0) {
          answer = true;
        } else {
          answer = false;
        }
        completer.complete(answer);
      });
    });
    return completer.future;
  }
}

add(Person person) {
/*  objectory =
  new ObjectoryDirectConnectionImpl('mongodb://127.0.0.1/forceobject2',
      () {    objectory.registerClass(Person, () => new Person(),
        () => new List<Person>());  }, false); */
//  init();
  objectory.initDomainModel().then((_) {
    objectory.save(person);
  });
}


Future<Person> newPerson(String first, String last) {
  Completer completer = new Completer();
/*  objectory =
  new ObjectoryDirectConnectionImpl('mongodb://127.0.0.1/forceobject2',
      () =>      objectory.registerClass(Person, () => new Person(),
          () => new List<Person>()), false);
          */  //init();
  objectory.initDomainModel().then((_) {
    Person person = new Person()
      ..firstName = first
      ..lastName = last;
    completer.complete(person);
    //   objectory.close();
  });
  return completer.future;
}

Future<List<Person>> allPeople() {
  Completer completer = new Completer();
/*  objectory =
  new ObjectoryDirectConnectionImpl('mongodb://127.0.0.1/forceobject2',
      () =>      objectory.registerClass(Person, () => new Person(),
          () => new List<Person>()), false);
          */
//  List<Person> names = new List();
//  init();
  objectory.initDomainModel().then((success) =>
      objectory[Person].find().then((List<Person> people){
        completer.complete(people);
//        objectory.close();
      })
  );
  return completer.future;
}

Future<bool> isDuplicate(Person person) {
  Completer completer = new Completer();
  bool answer;
  objectory.initDomainModel().then((success) {
    objectory[Person].find(where.eq('first_name', person.firstName)
        .and(where.eq('last_name', person.lastName))).then((matches) {
      if (matches.length > 0) {
        answer = true;
      } else {
        answer = false;
      }
      completer.complete(answer);
    });
  });
  return completer.future;
}


/lib/controllers.dart

library controllers;
import 'package:objectory/objectory_console.dart'
;import 'package:forcemvc/force_mvc.dart';
import 'dart:async';
part 'model.dart';

init() {
  objectory = new ObjectoryDirectConnectionImpl(
      'mongodb://127.0.0.1/forceobjectory2', () =>
      objectory.registerClass(
          Person, () => new Person(), () => new List<Person>()), false);
}


@Controller
class MainControllers {
  @RequestMapping(value: '/', method: RequestMethod.GET)
  String index(ForceRequest req, Model model) {
    return 'index';
  }

  @RequestMapping(value: '/verify', method: RequestMethod.POST)
  Future<String> verify(ForceRequest req, Model model) {
    req.getPostParams().then((Map params) {
      init();
      String first = params['first_name'];
      String last = params['last_name'];
      newPerson(first, last).then((Person person) {
        model.addAttribute("person", person);
        //      return isDuplicate(person).then((bool match) {
        return person.isDuplicate().then((bool match) {
          if (!match) {
            return person.save();
          }
        });
      }).then((_) {
        return allPeople().then((List people) =>
            model.addAttribute("people", people));
      }).then((_) {
        model.addAttribute("message", "It works!");
        req.async('verify');
      });
    });
    return req.asyncFuture;
  }
}


Saturday, July 30, 2016

An Example of Using Dart - forcemvc, MongoDB, and objectory, part 1

This app is a simple idea... ask the user for his name, then check it against your records. If nothing on file matches, that name gets added to the database. A list of all the saved names also appears. I decided to use Ojectory because it works with data objects more directly as objects than mongo_dart does.

Since we're using Dart, the first step is writing the pubspec.yaml file. This file configures the app's dependencies on third-party packages. For the sake of sanity, we will specify exactly which versions to use. We only need two external packages, objectory for database access and forcemvc for a web architecture. What we need is something like

/pubspec.yaml


name: ForceMVC_Objectory2
version: 0.0.1
description: A simple console application.
dependencies:
  objectory: 0.3.21
  forcemvc: 0.8.5

You can use any project name and description you like, but you'll need the rest as is. The next step is to make a /bin directory in the root of our project. All that we will write there is the main.dart file. That file contains the main function, which is always the entry point. There we create a WebApplication object to configure and start the web server. main() also tells the user what web address will be used; however, the print statement has no actual affect on it.

/bin/main.dart


library ForceMVC_Objectory2;
import 'package:forcemvc/force_mvc.dart';
import '../lib/controllers.dart';

main(List<String> args) {
  print('Opening localhost:8080');
  WebApplication application = new WebApplication();
  application.start();
}

We will be using the default settings. That means that the views will go in a /views directory. We put two files there: index.html and verify.html. index.html has the form, and verify.html shows the results.


/views/index.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Force Objectory 2</title>
</head>
<body>
<form action="/verify" method="post">
    <fieldset>
        <label for="first_name">First name: </label>
        <input id="first_name" name="first_name" type="text" required>
    </fieldset>
    <fieldset>
        <label for="last_name">Last name: </label>
        <input id="last_name" name="last_name" type="text" required>
    </fieldset>
    <input id="btn" type="submit" value="Continue">
</form>

</body>
</html>
 

/views/verify.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Name List</title>
</head>
<body>
<p>{{message}}</p>
<p>Tried to add {{person}}</p>
<ul>
    {{# people}}
    <li>{{firstName}} {{lastName}}</li>
    {{/ people}}
</ul>

{{^ people}}
<h3>Empty List</h3>
{{/ people}}
</body>
</html>


{{person}} echoes the name the user entered. {{people}} is an updated list of all the names on file. "#" generates a list, and "^" means "if the list is empty". In the interest of space, the controllers and model code will be in the next post.

Tuesday, March 29, 2016

Using Dart (dartlang) as an HTML client and Go (golang) as a server

Hi all! I'm back again. This blog might be short on explanation, but basically it gives an example of mixing Google technologies. What this example does is serve MP3 files from the server to the client, which get played on the client's speakers.

First, the server. For that, you'll need Go (https://golang.org/) and a folder to put the Go code and the MP3 files. The only Go file you need is main.go, as follows:

package main      

import (
    "log"
    "net/http"
    "strings"
//    "io/ioutil"
)

func main() {
    port := "localhost:8000"
    log.Println("Opening port", port)
    http.HandleFunc("/music/", getMusic)
    http.ListenAndServe(port, nil)
    }
    


func getMusic(w http.ResponseWriter, r *http.Request)  {
    w.Header().Set("Access-Control-Allow-Origin", r.Header.Get("Origin"))
    w.Header().Set("Access-Control-Allow-Methods", "GET, POST, OPTIONS, PUT, DELETE")
    url := r.URL.Path
    urlParts := strings.Split(url, "/")
    log.Println(urlParts[2])
    fileName := urlParts[2] + ".mp3"
    log.Println("Sending file", fileName)
    http.ServeFile(w, r, fileName)
      
}

What this code does is listen for a REST "get" request for "localhost:8000/music/". The ending "/" is needed so that the sender can hitch the file name to the URL. The code attaches CORS headers to the response, as this is a cross-origin situation. The URL gets parsed, then the resulting file name gets ".mp3" attached. That file then gets send back to the client with the headers.

The client is a bit more complex. For the client, you need a pubspec.yaml file, a /web subfolder, and Dart (https://www.dartlang.org/).

Your pubspec.yaml should look like:

name: DartMusicPlayer2
author: Ross Albertson <magnusthegood@yahoo.com>
description: A Dart music player using classes

dependencies:
  browser: any
  dart_to_js_script_rewriter: any
  
transformers:
- dart_to_js_script_rewriter   

Use any project name and description you wish, and replace the author with your name and e-mail address.  You'll need the dependencies and transformers to translate your Dart and HTML correctly.

The HTML (/web/index.html) is pretty trivial:

<!DOCTYPE html>
<html>
    <head>
        <title>Dart/Go Music Player 2</title>
        <script type="application/dart" src="main.dart"></script>
        <script src="packages/browser/dart.js"></script>
    </head>
    <body>
        <p>"July" by Weekend <button id="july">Play/Stop</button></p>
        <p>"Drive Blind" by Ride <button id="drive-blind">Play/Stop</button></p>
    </body>
</html> 

The <button> id's and the plain text can be substituted by any mp3 song you have. Lastly, there is the /web/main.dart, as follows:

import 'dart:html';
import 'dart:web_audio';

AudioContext audioContext;

void main() {
  audioContext = new AudioContext();
  new Song(querySelector("#july") as ButtonElement, "July");
  new Song(querySelector("#drive-blind") as ButtonElement, "Drive_Blind");
}

class Song {
  ButtonElement button;
  bool _playing = false;
  // AudioContext _audioContext;
  AudioBufferSourceNode _source;
  String title;

  Song(this.button, this.title) {
    // _audioContext = new AudioContext();

    button..onClick.listen((e) => _toggle());
  }

  _toggle() {
    _playing = !_playing;
    _playing ? _start() : _stop();
  }

  _start() {
    return HttpRequest
        .request("http://localhost:8000/music/$title",
            responseType: "arraybuffer")
        .then((HttpRequest httpRequest) {
      return audioContext
          .decodeAudioData(httpRequest.response)
          .then((AudioBuffer buffer) {
        _source = audioContext.createBufferSource();
        _source.buffer = buffer;
        _source.connectNode(audioContext.destination);
        _source.start(0);
      });
    });
  }

  _stop() {
    _source.stop(0);
  }
}

A global AudioContext is needed in any event. The Song class just requires a <button> element and a file name, minus the file extension. If you want to use multiple file formats, just change the Go so that is uses the URL variable as is, and send the whole file name to the Song constructor. I combined several online Dart examples to come up with this code. The "decodeAudioData" is needed to make the client work with more browsers. This object-oriented approach allows you to put your songs in separate threads. It is also needed to be able to play music after stopping a song; I discovered that quirk in an earlier attempt. You might need to compile the Dart in "debug" mode (pub build --mode debug) and use those /build/web files in production. I found this necessary when using IIS. One last thing... make sure your Dart HTTP request matches with your Go URL and port number.