ActionScript 3.0 Microphoneを利用するためのクラス

Flash PlayerでMicrophoneからの入力を視覚化するためのクラスを作成してみました.

使い方は, コンストラクタにstageを渡すだけでOKです.

import classes.MediaStream;

var stream:MediaStream = new MediaStream(stage);

Microphoneの設定を変更する場合は, コンストラクタの残りの引数を指定します.
また, アニメーション (視覚化) の設定を変更するには, setDrawSettingsメソッドを呼び出します.

ActionScript 3.0 Display Object (SWF) のローダークラス

Display Object (SWF) のローダークラスを作成してみた.

基本的には, テキストやXMLのロードと変わりありませんが, 大きな違いとしては,

  • URLLoaderクラスではなく, Loaderクラスを利用する
  • LoaderインスタンスのcontentLoaderInfoプロパティにイベントリスナーを設定する

以上

ActionScript 3.0 POST

ActionScript (3.0) でサーバーにPOSTする実装.

import flash.events.Event;
import flash.events.IOErrorEvent;
import flash.net.URLRequestMethod;
import flash.net.URLRequest;
import flash.net.URLLoader;
import flash.net.URLVariables;

var url:String = 'sample.php';

var requester:URLRequest = new URLRequest(url);

// POSTデータを作成
var posts:URLVariables = new URLVariables();
posts.data1 = 'foo';
posts.data2 = 'Bar';

requester.data   = posts;  //データをPOSTする
requester.method = URLRequestMethod.POST;  //POSTメソッド

var loader:URLLoader = new URLLoader();

loader.addEventListener(Event.COMPLETE, onComplete, false, 0, true);
loader.addEventListener(IOErrorEvent.IO_ERROR, onIOError, false, 0, true);

loader.load(requester);

function onComplete(event:Event):void {
    trace(event.target.data);
}

function onIOError(event:IOErrorEvent):void {
    trace(event.text);
}

重要なのは,

  • URLVariablesインスタンスを生成してPOSTデータをキー・バリューの形式で生成する
  • リクエストメソッドをPOSTに設定する

以上.

ActionScript 3.0 汎用的なテキストローダークラス

久々のActionScript 3.0…

汎用的なテキストローダークラスを作成してみました.

使い方はこんな感じです.

import flash.events.Event;
import flash.net.URLLoaderDataFormat;
import flash.net.URLVariables;
import classes.TextLoader;

var textLoader:TextLoader = new TextLoader('sample.txt', URLLoaderDataFormat.VARIABLES, true);

textLoader.addEventListener(TextLoader.EVENT_ON_LOAD, onComplete, false, 0, true);

function onComplete(event:Event):void {
    var loadedData:URLVariables = textLoader.getLoadedData;

    for (var key:String in loadedData) {
	    trace(key + ' : ' + loadedData[key]);    
    }
}

ロードするテキストはクエリパラメータのような形式で変数名と値を記述したテキストファイルです.

result=true&message=Success

Karma + Jasmineによるユニットテスト

自作しているWeb Audio APIのライブラリ XSound.jsでKarma + Jasmineによるユニットテストを始めたので, その備忘録として記載しておきます.

Karma + Jasmineによるユニットテストの環境構築は前日の記事を参考にしてください.

1. Karma設定ファイルの生成

まず. テスト対象となるディレクトリで以下のコマンドを実行します.

$ karma init

コマンドを実行すると, 初期化ウィザードが出力されるので, 順に入力していきます.

1 – 1. テスティングフレームワークの選択

Which testing framework do you want to use ?
Press tab to list possible options. Enter to move to the next question.
> jasmine

1 – 2. RequireJSを利用するかどうか. 利用するのであれば, yesを入力

Do you want to use Require.js ?
This will add Require.js plugin.
Press tab to list possible options. Enter to move to the next question.
> no

1 – 3. テストを実行するブラウザを指定.

事前に対象ブラウザのランチャーをインストールしておきましょう.

Do you want to capture any browsers automatically ?
Press tab to list possible options. Enter empty string to move to the next question.
> Chrome
> 

1 – 4. テスト対象のディレクトリの指定

今回は, src以下にテスト対象のファイルを, test以下にテストコードを置くことにするので, 以下のように指定します.

What is the location of your source and test files ?
You can use glob patterns, eg. "js/*.js" or "test/**/*Spec.js".
Enter empty string to move to the next question.
> src/*.js
> test/*.js

> 

1 – 5. 指定したディレクトリ内でテスト対象から除外したいファイルを指定します.

Should any of the files included by the previous patterns be excluded ?
You can use glob patterns, eg. "**/*.swp".
Enter empty string to move to the next question.
> 

1 – 6. 対象ファイルが更新される度にテストを実行するかどうか.

Do you want Karma to watch all the files and run the tests on change ?
Press tab to list possible options.
> yes

以上を入力すると, 実行したディレクトリにkarma.conf.jsという設定ファイルが生成されます.

2. テスト対象のファイルの作成

今回は, toFrequencies関数をテスト対象とします. メソッド内容は以下のような感じです.

src/to-frequencies.js

/** 
 * This function calculates frequency from the index that corresponds to the 12 equal temperament.
 * @param {Array.} indexes This argument is array of index that corresponds to the 12 equal temperament.
 *     For example, This value is between 0 and 88 in the case of piano.
 * @return {Array.} This is returned as array of frequencies.
 */
var toFrequencies = function(indexes) {
    // The 12 equal temperament
    //
    // Min -> 27.5 Hz (A), Max -> 4186 Hz (C)
    //
    // A * 1.059463 -> A# (half up)

    var FREQUENCY_RATIO = Math.pow(2, (1 / 12));  // about 1.059463
    var MIN_A           = 27.5;

    if (!Array.isArray(indexes)) {
        indexes = [indexes];
    }

    var frequencies = new Array(indexes.length);

    for (var i = 0, len = indexes.length; i < len; i ++) {         var index = parseInt(indexes[i]);         frequencies[i] = (index >= 0) ? (MIN_A * Math.pow(FREQUENCY_RATIO, index)) : 0;
    }

    return frequencies;
};

テスト対象のコードなので, これをsrc以下に置きます.

3. テストコードファイルの作成

テストコードはJasmineの仕様にしたがって記述します.
describeでテストのグループ化をして, itで個々のテストを記述していきます.

また, expectの引数には実際の値を指定し, Matcherの引数 (コード例ではtoEqual) には期待する値を指定します.

Macherには,toEqualメソッド以外にもたくさんあるので, こちらを参考に必要に応じて使い分けてください.

test/test-to-frequencies.js

describe('toFrequencies', function() {

        // Negative
        it('should return 0 or empty array', function() {
            expect(toFrequencies()[0]).toEqual(0);
            expect(toFrequencies(-1)[0]).toEqual(0);
            expect(toFrequencies([-1])[0]).toEqual(0);
            expect(toFrequencies([])).toEqual([]);
        });

        // Positive
        it('should return number', function() {
            expect(toFrequencies(0)[0]).toEqual(27.5);
            expect(Math.floor(toFrequencies(48)[0])).toEqual(440);
            expect(Math.floor(toFrequencies(87)[0])).toEqual(4186);
        });

        it('should return array that contains numbers', function() {
            var frequencies = toFrequencies([0, 48, 87]);

            frequencies.forEach(function(element, index) {
                if (index !== 0) {
                    frequencies[index] = Math.floor(element);
                }
            });

            expect(frequencies).toEqual([27.5, 440, 4186]);
        });

 });

テスト対象のコードなので, これをtest以下に置きます.

4. テストの実行

あとは, テストを実行するだけです. テストを実行するには以下のコマンドを実行します.

$ karma start

コマンドを実行すると, 設定ファイルで指定したブラウザが起動し, 以下のような画面が表示されます.

Karma テストの実行
Karma テストの実行

また, ターミナルにもテスト結果が表示されます.

INFO [karma]: Karma v0.12.31 server started at http://localhost:9876/
INFO [launcher]: Starting browser Chrome
INFO [Chrome 40.0.2214 (Mac OS X 10.9.5)]: Connected on socket _RA_ZqrC2-TO8Bf0jMVB with id 88154411
Chrome 40.0.2214 (Mac OS X 10.9.5): Executed 4 of 4 SUCCESS (0.015 secs / 0.009 secs)

あとは, テストをパスするように, コードの修正を繰り返します.
設定ファイルで更新の度にテストするようにしておけば, 再度テスト実行のコマンドを入力する必要もありません.

Karma + Jasmineのインストール

Karma + Jasmineのインストールには, Node.js (npm) が必要になるので, まずはこちらのページからダウンロードしてインストールしてください.

1. Karmaのインストール

$ npm install karma

2. Jasmineのインストール

$ npm install karma-jasmine

3. テストを実行するブラウザランチャーのインストール (Chromeの場合)

$ npm install karma-chrome-launcher

Chromeだけでなく, karma-safari-launcher, karma-firefox-launcher, , karma-phantomjs-launcherなどもインストール可能です.

4. Karmaコマンドラインインターフェースのインストール

$ npm install karma-cli

これで, Karma + Jasmineを利用したユニットテストの環境がインストールできました.

QUnitによるユニットテスト

そろそろテストコードもきちんと書いていかないとなあ〜なんて思っていたので, まずは, 手始めに自作したjQueryプラグインのjqFlipSwitchのテストコードを書いてみることにしました.

一般的に, jQueryやjQueryプラグインではQUnitが利用されることが多いので, QUnitを利用することにしました.

もっとも, QUnit自体はjQueryに依存しているわけではないので, JavaScriptのユニットテストであれば利用可能です.

QUnit

備忘録として, 手順をまとめておきます.

1. QUnitのインストール

インストール方法はいくつかありますが, bowerでインストールしました (npmでも可能ですし, インストールしなくてもCDNから読み込む方法もあります). インストールしたい (テストしたい) ディレクトリで以下のコマンドを実行します.

$ bower install --save-dev qunit

2. qunit.cssとqunit.jsの読み込み

qunitのパッケージにはたくさんファイルがありますが, 重要なのはqunit.css と qunit.jsの2つです.

この2ファイルをテスト結果を表示するためのHTMLに読み込みます (パスはディレクトリ構成に合わせて変えてください).

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>MOUSE_EVENTS | QUnit</title>
<link rel="stylesheet" href="qunit/qunit.css" type="text/css" />
<script type="text/javascript" src="qunit/qunit.js"></script>
</head>
<body>
    <div id="qunit"></div>
    <div id="qunit-fixture"></div>
</body>
</html>

また, このHTML (テスト結果を表示するHTML) には, “qunit”のid属性をもつ要素と, “qunit-fixture”のid属性をもつ要素を定義しておきます.

3. テスト対象となるコードを読み込む

今回テストしたいのは, jquery.jqflipswitch.jsですが, 説明のために, マウスイベントをラップするプライベートな定数に関してテストをしたいと思います. プライベートな定数なので, 別途別ファイルにコピペしておきます. 内容は以下のようになります.

mouse-events.js

// This object wraps events
var MOUSE_EVENTS = {};

MOUSE_EVENTS.CLICK = 'click';

// Touch Panel ?
if (/iPhone|iPad|iPod|Android/.test(navigator.userAgent)) {
    MOUSE_EVENTS.START = 'touchstart';
    MOUSE_EVENTS.MOVE  = 'touchmove';
    MOUSE_EVENTS.END   = 'touchend';
} else {
    MOUSE_EVENTS.START = 'mousedown';
    MOUSE_EVENTS.MOVE  = 'mousemove';
    MOUSE_EVENTS.END   = 'mouseup';
}

これをqunit.jsよりもあとに読み込みます

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>MOUSE_EVENTS | QUnit</title>
<link rel="stylesheet" href="qunit/qunit.css" type="text/css" />
<script type="text/javascript" src="qunit/qunit.js"></script>
<script type="text/javascript" src="src/mouse-events.js"></script>
</head>
<body>
    <div id="qunit"></div>
    <div id="qunit-fixture"></div>
</body>
</html>

4. テストコードの作成

Qunitのテストコードは以下のような感じになります.

test-mouse-events.js

test('MOUSE_EVENTS', function() {

    deepEqual(MOUSE_EVENTS.CLICK, 'click', 'MOUSE_EVENTS.CLICK');

    if (/iPhone|iPad|iPod|Android/.test(navigator.userAgent)) {
        deepEqual(MOUSE_EVENTS.START, 'touchstart', 'MOUSE_EVENTS.START');
        deepEqual(MOUSE_EVENTS.MOVE,  'touchmove',  'MOUSE_EVENTS.MOVE');
        deepEqual(MOUSE_EVENTS.END,   'touchend',   'MOUSE_EVENTS.END');
    } else {
        deepEqual(MOUSE_EVENTS.START, 'mousedown', 'MOUSE_EVENTS.START');
        deepEqual(MOUSE_EVENTS.MOVE,  'mousemove', 'MOUSE_EVENTS.MOVE');
        deepEqual(MOUSE_EVENTS.END,   'mouseup',   'MOUSE_EVENTS.END');
    }

});
  • test関数の第1引数でテストをグループかして,  第2引数の関数でテストコードを記述していきます.
  • deepEqual(‘テスト値’, ‘期待する値’,  ‘メッセージ’) の順に記述していきます. deepEqual関数以外にも様々なテスト関数があるので, そのあたりはドキュメントなどを参考にしていください

5. テストコードの読み込み

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>MOUSE_EVENTS | QUnit</title>
<link rel="stylesheet" href="qunit/qunit.css" type="text/css" />
<script type="text/javascript" src="qunit/qunit.js"></script>
<script type="text/javascript" src="src/mouse-events.js"></script>
<script type="text/javascript" src="test/test-mouse-events.js"></script>
</head>
<body>
    <div id="qunit"></div>
    <div id="qunit-fixture"></div>
</body>
</html>

以上の準備をして, テスト結果を表示するHTMLにブラウザからアクセスすると, 以下のような表示がされます.

QUnit テストページ
QUnit テストページ

以上が, QUnitを利用したユニットテストの大まかな流れになります.

ちなみに, jqFlipSwitchのユニットテストコードもGitHubにpushしましたので, 参考までにご覧ください.

Node.js express 4.x系のセッション管理

express 4.x系ではセッションのモジュールも分離されているので, 別途インストールが必要になります.

$ (sudo) npm install (-g) express-session

そして, app.jsでexpress-sessionを読み込みます.

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./routes/index');
var users = require('./routes/users');

var session = require('express-session');
var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
     secret : 'hogehoge',
     resave : true,
     saveUninitialized : true,
}));
app.use('/', routes);
app.use('/users', users);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
    app.use(function(err, req, res, next) {
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});


module.exports = app;

使い方としては, それほど難しくないですね.

また, セッションストアを利用する場合は以下のような感じになります (以下のサンプルコードでは, MongoStoreを利用しています).

MongoStoreのインストール

$ (sudo) npm install (-g) connect-mongo
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./routes/index');
var users = require('./routes/users');

var session = require('express-session');
var MongoStore = require('connect-mongo')(session);
var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
     secret : 'hogehoge',
     resave : true,
     saveUninitialized : true,
     store : new MongoStore({
         db : /* データベース名 */,
         host : /* ホスト名 */,
         port : /* ポート番号 */
      }),
      cookie : {
          path : '/',
          secure : false,
          httpOnly : true
      }
}));
app.use('/', routes);
app.use('/users', users);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
    app.use(function(err, req, res, next) {
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});


module.exports = app;

注意が必要なのは, セッションストアのコンストラクタの引数です. 3.x系までは, この引数にはexpress関数を渡していましたが, 4.x系では, express-sessionを渡す必要があります.

以外と見落としやすいので, ハマってしまわないように注意してください…

Node.js express 4.x系のスケルトンコード

まずは, expressとexpress-generatorをインストールしておきます.

$ (sudo) npm install (-g) express(@4)
$ (sudo) npm install (-g) express-generator

スケルトンコード用のディレクトリも作成して移動しておきましょう. ディレクトリ名は手キトーで構いません.

$ mkdir app
$ cd app

expressコマンドを実行すればファイルが生成されます.

 

$ express

   create : .
   create : ./package.json
   create : ./app.js
   create : ./public
   create : ./public/images
   create : ./public/stylesheets
   create : ./public/stylesheets/style.css
   create : ./routes
   create : ./routes/index.js
   create : ./routes/users.js
   create : ./views
   create : ./views/index.jade
   create : ./views/layout.jade
   create : ./views/error.jade
   create : ./bin
   create : ./bin/www
   create : ./public/javascripts

   install dependencies:
     $ cd . && npm install

   run the app:
     $ DEBUG=app ./bin/www

これだけだと, 肝心のnode_modulesがインストールされていないので, 以下のコマンドを実行します.

$ (sudo) npm install

express 3.x系だと, ここまでできれば,

$ node app.js

これで, スケルトンコードが動作したのですが, 4.x系では動作しません.

4.x系でスケルトンコードを動作させるには,

$ node ./bin/www

と実行する必要があります.

デフォルトのポートは3000番なので, 例えば, http://localhost:3000/ にアクセスすると,

Express

Express

Welcome to Express

 

のように表示されます.

ちなみに, 4.x系のスケルトンコード (app.js) は以下のようになっています.

var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var routes = require('./routes/index');
var users = require('./routes/users');

var app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');

// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
app.use('/users', users);

// catch 404 and forward to error handler
app.use(function(req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
    app.use(function(err, req, res, next) {
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}

// production error handler
// no stacktraces leaked to user
app.use(function(err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});


module.exports = app;

パッと見た感じでも, 3.x系までとはずいぶん違います. 最大の違いは,

  • 4.x系からはモジュールの分離が進んでいるので, 多くのモジュールをrequireしている
  • ルーティングの定義が別ファイル (routes/index.js) に移動している

ちなみに, 4.x系のルーティングは以下のようになります (スケルトンコード routes/index.jsから抜粋)

 

var express = require('express');
var router = express.Router();

/* GET home page. */
router.get('/', function(req, res, next) {
  res.render('index', { title: 'Express' });
});

module.exports = router;

と. このようにルーティングの実装もずいぶん変わりました.

Node.js express 4.x系のインストール

express 4.x系からはモジュールの分離化がかなり進められているので, 3.xまでとはインストール方法から異なってきます.

ちなみに, 3.x系までは,

$ (sudo) npm install (-g) express

これだけで, expressコマンドがインストールされ, expressコマンドを実行すると, スケルトンコードが生成されました.

4.x系では, express自体のインストールは同じです.

$ (sudo) npm install (-g) express(@4)

ところが, expressインストールだけではexpressコマンドはインストールされないので, スケルトンコードの生成ができません.

expressコマンドを利用可能にするには, 追加でexpress-generatorをインストールする必要があります.

$ (sudo) npm install (-g) express-generator

これで, expressコマンドが利用可能になります.
確認として, バージョンを表示します.

$ express -V
4.11.1