@ledsun blog

無味の味は佳境に入らざればすなわち知れず

Form で遊ぶために対抗サーバーを作って公開した

背景

HTML fromを新人に説明しようとしました。 うまく説明できませんでした。 つまり、よく分かっていません。

ドキュメントを読んで言葉で理解し、 パラメーターを変えながら実際に動かして、心で理解したいです。 しかし、formは対抗するサーバーがないと試しづらいです。*1

form-exercise

formで送信したHTTPリクエストの内容(メソッド、クエリ、ボディ)を表示するサーバーを作りました。

配置先サイト

github

使い方

こういうformから

<form action="https://form-exercise.herokuapp.com/">
    <input type="submit" value="submit">
    <input type="text" name="name" value="1">
</form>

submitすると https://form-exercise.herokuapp.com/?name=1 を表示します。

postメソッドも受け付けます。

実装

connectを使って作りました。 Node.jsです。Herokuで動いています。

実装時にぶつかった課題

connectの標準middlewareは、どれを使うと今風なのかよく分からない

いつもbody-parserをそのまま使っていいのかで迷う。 いいかげんREADMEのサンプルを動かすと、警告出るの何とかして欲しいものです。

let app = connect()
    .use(bodyParser.urlencoded({
        extended: false
    }))

て、bodyParser は extended オプションを指定して使えばいいようです。

renderの共有

middlewareとして実装したい

render関数(データに表示用テンプレートを適用する)は複数のmiddlewareで共有したい。 それでいて、↓のような共通関数でなく

function render(res, values) {
    let html = `
        <div>Method: ${req.method}</div>
        <div>URL: ${req.url}</div>
        <div>Values: ${JSON.stringify(values)}</div>
    `

    res.end(html)
}

connectのmiddlewareとして実装したい。

connectのmiddleware

connectはmiddlewareを追加して機能を追加します。 middlewareはChain of Responsibilityです。

  1. requestとresponseをmiddlewareからmiddlewareと渡して行きます。
  2. 各middlewareはrequestとresponseの自分が興味がある部分を処理し、
  3. 次のmiddlewareにrequestとresponseを渡します。
結論

ExpressのAPIを真似してresponse に renderメソッドを生やすmiddlewareにしました。

デプロイ時にぶつかった課題

ES6をherokuで動かす方法

nodemon使って実装していました。 nodemonは--exec babel-nodeをつけるだけでbabelが動きます。 そこで調子に乗ってES6で実装したらHerokuでの動かし方で困りました。

gulpで変換

gulpで力技変換。こんな風に

var gulp = require('gulp');
var babel = require('gulp-babel');
var rimraf = require('rimraf');

gulp.task('clean', function(cb) {
    rimraf('./dist', cb);
});

gulp.task('lib', ['clean'], function() {
    return gulp.src([
            'src/lib/get.js',
            'src/lib/post.js',
            'src/lib/render.js'
        ])
        .pipe(babel())
        .pipe(gulp.dest('dist/lib'));
});

gulp.task('app', ['lib'], function() {
    return gulp.src([
            'src/app.js'
        ])
        .pipe(babel())
        .pipe(gulp.dest('dist'));
});

gulp.task('default', ['app']);
twitterで知見を得る

@tgfjtさんありがとうございます。

結論

package.jsonに下を書くだけで動きました

"start": "babel-node app.js"

Herokuもbabelも便利すぎる。神なのでしょうか?

※dependenciesにbabelを追加してください。

その他

全くどうでもいい蛇足情報

セキュリティ面ではノーガード実装です。 せっかくなので<script>alert()</script>投げてみると、次のエラーが出て、スクリプトは実行されませんでした。

The XSS Auditor refused to execute a script in 'https://form-exercise.herokuapp.com/?name=%3Cscript%3Ealert%3C%2Fscript%3E' because its source code was found within the request. The auditor was enabled as the server sent neither an 'X-XSS-Protection' nor 'Content-Security-Policy' header.

デフォルトでX-XSS-Protectionが有効なようです。 Google Chrome偉い。

*1:世の中にすでに、類似サーバーがあるかは確認していません。調べ方がよくわかりません。教えてください。

論理削除フラグという名の死亡フラグ

流行っているので乗っかります。

結論

「データ制約の強力さと集合としての表現力を捨てながら、Relational Databaseを使うのはなぜか?」

論理削除フラグのデメリット

大体三つあると考えています。

  1. ユーザーの言葉でない
  2. データ制約の弱さ
  3. 集合として認識できない

ユーザーの言葉でない

私の経験上は、ユーザーから「論理削除」という言葉を聞いたことがありません。 次のような要件は、聞いたことがあります

  • 社員が退職(・転属)する
  • 売掛金の回収を諦めて)売上を打ち消す
  • 「お知らせメッセージ」を公開日がくるまで非表示にする
  • 既読メッセージを表示しない
  • 保存期間が過ぎたアンケート結果をオペレーターが見れなくする

もしかして「論理削除」は実装パターンではないですか? ユーザーが「消す」と言ったのは「データベース上のレコードを削除する」ことでしょうか? ユーザーが「消す」と言った時、反射的に「消すよりは、削除フラグを立てた方が何かあった時に安心」と考えませんでしたか?

もうちょっとだけ、ユーザーがやりたいことを理解できそうです。

データ制約の弱さ

次のテーブルがあったとします。

名前 番号 生死
惣流・アスカ・ラングレー 3rd
綾波レイ 1st
綾波レイ 1st 死亡
綾波レイ 1st 死亡

データ制約上、次のような問題があります。

  • (よく使う)生きている適格者の取得時にwhere句で生死を絞りこみが必要
  • 番号にuniq制約がつけられない

これはアプリケーションカバーします。

  • レコード追加時に生きている適格者の番号が重複しないようにチェックする
  • レコード取得時はwhere句で生死を絞りこむ
  • (または)レコード取得時は「生きている適格者ビュー」を使う
  • レコード取得時に生きている適格者の番号が重複していないかチェックする

さらにデータメンテナンスでカバーします。

  • 重複レコードを発見したら、アプリケーションのバグなのかデータメンテナンスのミスなのか切り分けます。ソースコードやログとにらめっこする
  • 重複レコードを発見したら、SQL文を発行してレコードを修正する

「運用でカバー」は、本当に困った時だけにしたいものです。

集合として認識できない

多くの人は、生きている適格者と死んだ適格者が混じっているテーブルを集合として認識できません。 別のテーブルに分けると、集合として認識できます。

生きている適格者

名前 番号
惣流・アスカ・ラングレー 3rd
綾波レイ 1st

死んだ適格者

名前 番号 死亡日
綾波レイ 1st x月x日
綾波レイ 1st x月x日

テーブルに入っているレコードが推測しやすくなります。

デメリットにならない条件

上記のデメリットが成り立たない時、論理削除フラグは有効です。

  • ユーザーが「消した」データに興味がない
  • アプリケーションの作成工数や運用工数が、データ設計工数より低い
  • (論理削除フラグに慣れた)複雑な集合を認識できるメンバーでチームを作る

JavaScript入門用のコンソールプログラムjavascriptingを翻訳しました

javascriptingを翻訳しました。

javascripting-jpとは何か?

ledsun/javascripting

JavaScriptの文法を学ぶコンソールアプリケーションです。 文字列や数値、条件文やforループなど文法を学びます。

次のコマンドでインストールできます。

npm install --global javascripting-jp

JavaScrpitを学ぶ時にブラウザのWeb APICSSを一緒に覚えるのは大変です。 文法に集中して学ぶことができます。

プログラミング初心者には、Node.jsのインストールが壁になりそうなのが心配です。

本家javascriptingの紹介

javascriptingはNodeSchoolというワークショップ向けのアプリケーションです。

課題の回答が正しいかチェックする機能があります。 コンソールへの出力内容が一致するか確認するシンプルなものです。

厳密なチェックはできませんが、 console.logが分かれば、課題に取り組めます。 面白い割り切りだと思います。

advetureについて

javascriptingはsubstack作の adventureを使って作られています。

adventureに課題と回答を登録すると ワークショップ向けのアプリケーションができます。 実行すると問題文の表示、回答合わせ、進捗管理をしてくれます。

細かい回答合わせ用のadventure-verify というライブラリもあるようです。 使ったことはありません。

多言語化はできません。 今回は問題文を全部書き換えました。 本家の更新に追従するのは諦めています。

多言語化したいときは workshopperというライブラリを使ってワークショッププログラムを作るのが良いようです。

NodeSchoolについて

大阪では定期的に開催されているようです。 NodeSchool | Doorkeeper

(私的)npmモジュールの作りかた

npmモジュールを作るときの自分の手順をまとめました。

プロトタイプをコーディング済みで、コンセプトが固まっている前提です。

モジュール名を決める

  1. npmを検索、類似ライブラリが無いか探す。 ついでに、APIや実装にパクれるネタを探します
  2. ハイフン区切りでモジュール名を決める
  3. npmを検索して名前が被らないか確認する

READMEを書く

  1. モジュール名のディレクトリをつくる
  2. エディタ*1でREADMEを書く。 内容は、
    1. Description: モジュールの価値を伝える一文です。 githubでプロジェクトを作るときと、npm initするときにも使います
    2. Usage: 自分の「こう使うと気持ちいい」イメージを伝える、サンプルコード。 最初のテストコードになるかもしれません
    3. API: 定義する関数とその引数など*2
    4. Setup: 対象環境(npm、browserify、bowerなど)を決める。各環境向けのインストールコマンドを書きます
    5. Development: ビルドやテストの手順です。npm run buildnpm testを必要に応じて羅列します
  3. commit
git init
git add README.md
git commit -m 'First commit.'

githubにpush

  1. githubでモジュール名のプロジェクトを作る。READMEやgitignoreは作りません
  2. git remote add
  3. git push origin master github上の見た目を確認します

package.jsonをつくる

  1. npm init 内容は、
    1. name: そのまま
    2. version: 0.0.1
    3. description: READMEで決めたもの
    4. entry point: そのまま
    5. test command: そのまま
    6. git repository: そのまま
    7. keyword: descriptionの単語から適当に選んで羅列
    8. author: ledsun*3
    9. license: MIT
  2. ライセンスを修正。細かすぎて伝わらない package.json 小ネタ三選 - t-wadaのブログを参考にして、mit-license.orgを使います
  3. fixpack commit前にやった方がdiffが汚れない
  4. git commit
git add package.json
git commit -m 'Add package.json.'

テストの枠組を用意する

  1. npm install --save-dev mocha
  2. .gitignoreをつくる。 node_modules echo node_modules/ > .gitignore
  3. npm run testmochaを書く。6to5使うなら mocha --compilers js:6to5/registerにします
  4. npm test してみる
  5. test.jsを書く require('./')(ES6ならimport)だけ書く
  6. index.jsを書く。空ファイルです
  7. git commit
git add index.js test.js .gitignore package.json
git commit -m 'Add package.json.'

開発する

普通にTDDします。

  1. ソースファイルが複数になったらsrcディレクトリを作ります
  2. ストファイルが複数になったらtestディレクトリを作ります
  3. ライブラリをES6で書いて公開する所から始めよう | Web Scratchを参考にして6to5を使うなら、 npm run buildを書いたり、index.js.gitignoreに追加したりします
  4. twada/power-assert · GitHubを使うなら、npm install --save-dev espower-loader power-assertして、
require('espower-loader')({
    cwd: process.cwd(),
    pattern: 'test.js'
});

enable-power-assert.jsを作って、npm testmocha --require enable-power-assert.jsに書き換えます

最初はpower-assertを使わないで書いています。 実装が難しくて、テストの失敗を繰り返す場合には、power-assertを追加します。

公開する

npmのアカウントを作ります。

  1. package.jsonfilesindex.jsを書く
  2. npm publish

*1:atomのmarkdown previewを使っています

*2:継続的にモジュールを拡張したことがありません。再生成のコストに実感がありません。修正する事は考えずに手で地道に書いています。

*3:メールアドレス入れてません。入れたほうがいいのでしょうか?

6to5で作ったnodeライブラリ

ライブラリをES6で書いて公開する所から始めよう | Web Scratch 読んで触発され、 6to5を使ってライブラリを書きました。

作ったライブラリ

チャットワークにメッセージをポストするだけの関数です*1

こんな感じで使います。

var apiToken = 'AAA',
    roomId = '1234'

postChatworkMessage(apiToken, roomId, 'hello world')

6to5の使い方

ライブラリをES6で書いて公開する所から始めよう | Web Scratch のままです。

build

6to5 src --out-dir lib --source-maps-inline

test

結果があることだけ確認して、中身は確認していないので、 power-assert無し。 mochaのみで書きました。

mocha --compilers js:6to5/register

package.jsonのfiles

libだけにしました。

個人的にnodeモジュールの使い方がわからないときに、テストコードを参考にします。 テストコードを公開パッケージに入れた方がいい気もします。

今回はテストコードがドキュメント以上のことを言わないので、 除外しました。

使ったES6構文

最初にES5で書いて traceur-compiler 入門 - from scratch を参考にしてES6に書き換えました。

  • const
  • let
  • arrow functions
  • modules
  • template strings

今回は、Array comprehensionsやGeneratorsのような、新しい使い方の文法は使いどころがありませんでした。

ドキュメントの話

同じく触発されてJSDocで書いてみました。 が

  • 一関数モジュールにはちょっと過剰?
  • jsdoc-to-markdown が生成するドキュメントは見た目があまり好みでない

JSDocが作るテーブルを真似して、手でMarkdownを書きました。 https://github.com/ledsun/post-chatwork-message#parameters

*1:AWS Lambdaでチャットワークに通知する処理に使いたくて書きました。複数メッセージ送信をPromise.allで待ち合わせたいので、Promiseを返します。

2014の成果

フロントエンドJavaScript

TextAE

LODQA

本業です。jQueryでDOM操作バリバリです。MVなんとかフレームワークは使っていません。忍者式テストの発表のネタになっています。

JavaScriptの日付に1日足したり2時間引いたり、計算するライブラリ

JavaScriptの日付に1日足したり2時間引いたり、計算するライブラリを作りました - @ledsun blog

JavaScriptのライブラリの作り方を学習しました。 CSS込みのフロントエンド向けライブラリはこの方法だと上手く行きません。

Rob Pike先生のサイン

いえーい!

グーグルカレンダーに予定を追加するURLを作るライブラリ

グーグルカレンダーに予定を追加するURLを作るライブラリを作りました - @ledsun blog

実用的な目的で作成しました。power-assertに初チャレンジしました。

とてか03 で忍者式テストの発表

とてか03にて発表してきました #toteka - @ledsun blog

レガシーコードと戦った話をメインに据えて、情熱成分を多めにしてみました。 概ね喜ばれたようで良かったです。

「俺のJavaScript、思っていたよりイケてるなー」とちょっと自信になりました。

Raspberry piをMQTT 気温 publisherにするAnsible Playbook

Raspberry piをMQTT 気温 publisherにするAnsible Playbookを公開しました - @ledsun blog

Raspberry PiとMQTTで遊ぶシリーズの成果物。結果的に温度を測るのは楽しくなくて、今は使っていません。

今はログを出す場所に悩むと、とりあえずMQTTで投げたりしています。

XP祭りで忍者式テストの発表

XP祭りに行きました #xpjug - @ledsun blog

とてかの発表の予告編。忍者式テストの説明が多めです。本編はもっと後に作りました。

Gitlabの更新情報をChatworkに通知するアプリケーション

Gitlabの更新情報をChatworkに通知するアプリケーション、Syamoを作りました - @ledsun blog

実用的で便利です。今見ると、実装があまりイケてません。

Blog オブジェクト指向設計とは

https://twitter.com/ledsun/status/538912200224567296

あまり鋭いマサカリは飛んで来ませんでした。 まあ時代遅れですよね。

ジョークアプリケーション 大拝承

大拝承

大拝承というWebアプリケーションを公開したら楽しかった話 - @ledsun blog

なぜかsocket.ioを使っています。 おかげでGuillermo Rauchに下手くそな英語で「socket.io超便利だった、ありがとう」とか言って握手できた。 イケメン充!

2014年 読んだ漫画の面白かった66

漫画

2014年もたくさん読みました。一言コメントなどを

ワールドトリガー

ワールドトリガー

漫画上手いですね

ACMA:GAME

ACMA:GAME

次のエピソードへの引きが上手くて続けて読んじゃう

テラフォーマーズ

テラフォーマーズ

初期の刃牙の「いろんな格闘技デパート」感あって面白い

絶望の犯島―100人のブリーフ男vs1人の改造ギャル

絶望の犯島―100人のブリーフ男vs1人の改造ギャル

1巻の設定の無茶苦茶さに笑った。衝撃的だった

ワイルド7

ワイルド7

面白いんですけど・・・幾ら何でも長いです

累

生きなさいキキ感ある。あそこまで悪い子全開じゃなくて迷いもある。続きが期待できていいです

狼の口 ヴォルフスムント

狼の口 ヴォルフスムント

6巻かけて、このカタルシス。さすがはビームです。普通の雑誌だとその前に連載終わる。もしかするとここがピーク

女の子が死ぬ話

女の子が死ぬ話

悪くない。でも全然印象に残らない。もったいない

双星の陰陽師

双星の陰陽師

健気な子たちは好きです。イマイチ方向性に迷っている感ある

天智と天武 ―新説・日本書紀

天智と天武 ―新説・日本書紀―

設定もおもろいし、敵役がねちねちしてて面白い。毎度毎度帯がウザい

マージナル・オペレーション

マージナル・オペレーション

現代戦術物。面白いですわ

高嶺の花

高嶺の花

おっさん恋愛漫画としてめっちゃ面白い

エリア51

エリア51

絵作りがかっこいい。ゼウスのおっさんがかっこいいのもいい

ホークウッド

ホークウッド

悪ぶっているいい人有能主人公で軍靴のバルツアー的。明るい中世ヨーロッパ戦争物

曹操孟徳正伝

曹操孟徳正伝

変態三国志漫画家による、曹操善人説漫画。蒼天航路以降だし、よく頑張ったと思う。

ホライズン

ホライズン

チンギスハンもの。子供時代から始めたのがまずかったんかな?ろくに話が始まる前に終了。蒼天航路感ある

キングダム

キングダム

戦争あってなんぼの書くのが辛い漫画に

野望の王国

野望の王国

序盤面白かったけど柿崎が強すぎて・・・もっと野望に向かって話が進むのだと思ってた。あとがきの自慢話がすごくうざい

redEyes

redEyes

アマードトルーパー戦争物。「そんな薄い素材で固過ぎ」とか思うけど、途中でフォロー入る。アクションシーンがいいよね。アーマー着てモノクロだと、キャラの見分けがつかないのが難点

ベイビーステップ

ベイビーステップ

天才スポーツ少年もの。作者がテニスオタクすぎて面白い。ここまでオタクじゃないとスポーツ漫画書けないのかという昨今の漫画界のハードルの高さ

オールラウンダー廻

オールラウンダー廻

天才スポーツ少年もの。作者が総合格闘オタクすぎて面白い。ここまでオタクじゃないとスポーツ漫画書けないのかという昨今の漫画界のハードルの高さ

のりりん

のりりん

おっさんスポーツもの。作者がロードバイクオタクすぎて面白い。ここまでオタクじゃないとスポーツ漫画書けないのかという昨今の漫画界のハードルの高さ

ちぃちゃんのおしながき

ちぃちゃんのおしながき

大井昌和 & 幼女 の鉄板さ加減

タケヲちゃん物怪録

タケヲちゃん物怪録

とよ田みのる の謎ハッピネス描写が安定

BIRDMEN

BIRDMEN

真面目の少年たちとちょっと悪い大人、いつもの 田辺イエロウ。どこに行く気なんだろう?

進撃の巨人

進撃の巨人

こんだけ進んでもネタが切れない設定がすごい

ブラッドラッド

ブラッドラッド

シリアスともギャグとの見分けにくい。不思議なバランス

地獄先生ぬ~べ~

地獄先生ぬ~べ~

今読んでも結構面白い。あの時代のジャンプすごかったんだな

EAT-MAN THE MAIN DISH

EAT-MAN THE MAIN DISH

吉富 昭仁から久しぶりのロリコン要素少なめ漫画だー!面白いかはまだわかんねー

強殖装甲ガイバー

強殖装甲ガイバー

もう殿堂入り、思う存分書いてください

イムリ

イムリ

話が進むようになって、面白くなってきました

高杉さん家のおべんとう

高杉さん家のおべんとう

ついに完結ですよ。お父さん気分で読んでました。

ヴィンランド・サガ

ヴィンランド・サガ

ヴィンランドが北米だってようやく気づきました。

孔雀王~戦国転生~

孔雀王~戦国転生~

最近の荻野 真、脱力パートがあって面白いんです

ナポレオン ~覇道進撃~

ナポレオン ~覇道進撃~

完走まで、がんばれーって感じです

深夜食堂

深夜食堂

地味にほっこり系

アド・アストラ ―スキピオハンニバル

アド・アストラ ―スキピオとハンニバル―

ローマ物。スキピオがんばれー

十 ~忍法魔界転生~

十 ~忍法魔界転生~

がんばれー

ゲート ―自衛隊彼の地にて、斯く戦えり

ゲート ―自衛隊彼の地にて、斯く戦えり

圧倒的な戦力差なのに相手に気を使ったりの心理描写が好き

機動戦士クロスボーン・ガンダム ゴースト

機動戦士クロスボーン・ガンダム ゴースト

平常運転

荒野に獣 慟哭す

荒野に獣 慟哭す

やっと完結すんの。クリーチャーの造形とアクションが素晴らしい

孔雀王ライジング

孔雀王ライジング

また孔雀王かよって思ったけど、結構あり。子供に見せられるの目指しているのは、なるほどって感じ

ビン 〜孫子異伝〜

ビン 〜孫子異伝〜

どうなるんじゃろね。がんばって完走してほしい

超人ロック 刻の子供達

超人ロック 刻の子供達

ずっと続けてんのに、よく新しい設定思いつくな

重機甲乙女 豆だけど

重機甲乙女 豆だけど

設定もいいしキャラもいい。いいぞ、もっとやれ

ヨルムンガンド

ヨルムンガンド

おもろかったよ。結末はこうするしかないですよね。贅沢を言えば、ここから体制が崩れていく話が読みたいです

墨攻(ぼっこう)

墨攻(ぼっこう)

戦術の泥臭ささ好き。中国歴史ものはハッピーエンドにはなりにくい

クライシス

クライシス

特命係長みたいなの

僕らはみんな生きている

僕らはみんな生きている

山本直樹 はエロい

ビリオンドッグズ

ビリオンドッグズ

現代版野望の王国みたいなの。続きが気になる

バベルハイムの商人

バベルハイムの商人

ブラックジャックとかアウターゾーンとかみたいなの

バーサスアース

バーサスアース

打ち切りなんだって・・・

レッツ☆ラグーン

レッツ☆ラグーン

岡崎武士復活。それ以上いらない

銃夢 Last Order

銃夢 Last Order

ついに完結っていうべきか?

White Tiger 3 〜白虎隊西部開拓譚〜

White Tiger 3 〜白虎隊西部開拓譚〜

暑苦しすぎんのかな?夏目 義徳さんてイマイチ受けないね

乱と灰色の世界

乱と灰色の世界

乱が好き勝手遊んでたあたりが俺的ピーク

ヘビ女はじめました

ヘビ女はじめました

ど安定

開花のススメ

開花のススメ

対象読者的に無理がある設定だし頑張ったんだと思います。

ハカイジュウ

ハカイジュウ

ひたすらパニックを描き続ける姿勢が素晴らしい

江戸の検屍官

江戸の検屍官

よくこんなマニアックな設定をみつけた。人相書きがキーなのと、顔を書き分けられる画風がマッチしていて素晴らしい

裸者と裸者~邪悪な許しがたい異端の~

裸者と裸者~邪悪な許しがたい異端の~

やっと終わったぜ。原作未完だけに続かないのが辛い。

エイス

エイス

綺麗だけど触ると壊れそうな話

サランドラの壺 光原 伸短編集

サランドラの壺 光原 伸短編集

見どころは「あの時代に長期連載してた俺すげーだろ」って作者が書いているところ

軍靴のバルツァー

軍靴のバルツァー

安定

宇宙戦艦ヤマト2199

宇宙戦艦ヤマト2199

もうちょっと女の子の表情が色々パターンがあるといいな

砂ぼうず

砂ぼうず

復活!・・・したのかなぁ

おまけ

この記事を書くために作ったスクリプトafi-blog

JavaScriptの日付に1日足したり2時間引いたり、計算するライブラリを作りました

IE9以降のブラウザとNede.jsで動きます。 AMDでも動くはずです。 試していません。動かなかったら教えてください。

どんなライブラリか?

こんな感じ

dateAdder(new Date(2014,10,27), {days: 1})
//Thu Nov 28 2014 00:00:00 GMT+0900 (JST)

dateAdder(new Date(2014,10,27), {hours: 1})
//Thu Nov 27 2014 01:00:00 GMT+0900 (JST)

dateAdder(new Date(2014,10,27), {days: 1, hours: 1})
//Thu Nov 28 2014 01:00:00 GMT+0900 (JST)

JavaScriptのDateオブジェクトに足し算したり引き算したりする関数です。

なぜ作ったか?

ledsun/generate-google-calendar-url · GitHubを使ってGoogleカレンダー用のURLを作る時に使おうと思いました。 予定に開始時刻だけあって終了時刻が無かったら、終了時刻を開始時刻の1時間後に設定します。

どうすればJavaScriptで一時間後の時刻が得られるでしょうか?

標準的なJavaScript

一般にJavaScriptで時間を足す処理を書くとこうです。

var newEnd = new Date(date.getTime())
newEnd.setHours(date.getHours() + value)

二行必要なのが、ちょっと嫌いです。

StackOverflowで検索しても似た感じです。

var today = new Date();
var tomorrow = new Date(today);
tomorrow.setDate(today.getDate()+1);

npmパッケージ

111247パッケージを誇るnpmを検索してみます。

parseメソッドやformatメソッドはちょっと余計です。 addメソッドの実装 は参考になりそうです。

1つのメソッドでget/setを両方やるjQueryぽいインターフェースは頂けません。 他のインターフェースと混ぜると辛いです。 実装やテストもコストが高いです。

Math Functionsに加算があります。新しいオブジェクトを返す、非破壊的なメソッドなのも良いです。 ただ、

  • 加算と減産を分ける価値が分かりません
  • 比較メソッドの価値も分かりません。getTimeすれば良くないです?
  • min/maxはreduceで十分な気もします
  • サポートしている単位が多いのも気になります。decadeって使います?10年足せば良くないですか?

名前がかっこいいです。 でも欲しいのはDateオブジェクトの代替えではありません。

そんな訳で、手頃なものが無いので作りました。

作っていた時の四方山話

Dateの標準API

主にゲッターセッター。 妙に統一感がありません。

特に年月日

  • getFullYear
  • getDate
  • getMonth

時以下は単位複数形で統一されて居るのに

英語的には正しいのかもしれませんね・・・。

日付のテストコードの話

https://twitter.com/ledsun/status/538131617382817792

変更した日付の値が期待通りかassertで比べたいです。 ところがassert.equalもstrictEqualも時刻の比較はしてくれない。 オブジェクトが同一かどうかを比較します。

結論からいうと頭に+を付けて数値に変換して比較しました。

オレオレassertはダメ

こんなの

var assertDate = function(expct, actual, message){
    assert.equal(expect.getTime(), actual.getTime(), message)
}

原則的にオレオレassertは悪手です。

  • オレオレassertの動作を理解しなくてはいけません
  • 関連のあるテストコード以外のコードも読まなくてはいけません

テストコードは定石がすくないので、 他人が書いたテストコードはメンテナンスしづらいです。 テストコードを書くコストが減っても、読むコストが増えると状況は悪化します。

getTime

最少単位で比較すれば良いのでミリ秒に変換して比較します。

assert.equal(expect.getTime(), actual.getTime())

毎回getTimeを書くのは面倒です。

Date系ライブラリのテストコードを見てみましょう。

valueOf派

https://github.com/JerrySievert/date-utils/blob/master/test/date-new-test.js#L11

assert.equal(expect.valueOf(), actual.valueOf())

プリミティブ値の比較は仕様上好ましそうです。 getTimeとメソッド名の長さも得られる値も同じです。

数値にキャスト派

https://github.com/jquense/date-math/blob/master/test.js#L27 https://github.com/timruffles/immutable-date/blob/master/test.js#L11

assert.equal(+expect, +actual)

JavaScriptの標準から外れずに、最も短く書けるのでこの記法にしました。

開発環境を自慢

saucelabsでクロスブラウザテスト

ソースコードはES5対応です。 実際に各ブラウザで動くかはsaucelabsで確認しました。 mochaで書いたテストコードをzuulでブラウザで動かしました。

最初はもっとたくさんのブラウザでテストしました。 時間掛かかりました。 各ブラウザの動作可能な最も古いバージョンだけテストすることにしました。 1セット約2分掛かかります。

こんなバッジがもらえます。 Sauce Test Status

最初はtestling使う予定でした。 ローカルでテストして、travis.ci設定して、githubにpushしても反応がありません。 調べてみたら ci.testling.com Service Timeout · Issue #88 · substack/testling · GitHub でした。

UMD.js

generate-google-calendar-url もブラウザとNode.jsで動きます。 EventEmitter2を真似て手で書きました。

UMD.jsという標準を知り、 従うことにしました。 ソフトウェア開発のコツは「解決したい問題以外は他人に押し付ける」です。

手で書いたら、ソースコードが読みにくくなったのでgulp-umdを使いました。

元が1ファイルだったので楽でした。 複数ファイルに分けるとどうなるんでしょうね?

Jxck_さんの教え

npm で依存もタスクも一元化する - Qiita

"script" を "npm run" で実行する場合は、パスが自動で通る

必要なコマンドはすべてnpmから実行出来るようにしました。

  • npm run build
  • npm test
  • npm run browser
  • npm run saucelabs

npm run saucelabstravis.ci用なので人間はあまり実行しないはずです。

t-wadaさんの教え

細かすぎて伝わらない package.json 小ネタ三選 - t-wadaのブログ

  • package.json の files フィールド
  • package.json のフィールド並び順に悩むくらいなら fixpack
  • ライセンス、 MIT で良いなら mit-license.org

一通りやりました。

もちろんpower-assert使いました。 途中でテストコマンド間違えてエラー詳細で無くなったらパニックに陥りました。 知らず知らずのうちに頼り切りです。

まとめ

npmモジュールの開発は簡単ですね<3

ソースコードは40行しかないのに、こんなに書くことがある!

技術的負債の数え方

技術的負債の数え方に関する与太話。 落ちを先に書くと「シュレディンガーの猫」って言いたかっただけです。

技術的負債とは

Ward Cunningham 曰く

最初のコードを出荷することは、借金をしに行くことと同じである。
小さな負債は、代価を得て即座に書き直す機会を得るまでの開発を加速する。
危険なのは、借金が返済されなかった場合である。

品質の良くないコードを使い続けることは借金の利息としてとらえることができる。
技術部門は欠陥のある実装や、不完全なオブジェクト指向などによる借金を目の前にして、
立ち尽くす羽目になる

技術的負債 - Wikipediaから引用

要約すると「汚いソースコードは後で直すのが大変。けど、拙速も大事」という話です。

数え方

「技術的負債が借金だとすれば、いくら借りているのか数えられるのでしょうか?」が今日のお題です。

技術的負債を観測する方法

Michael Feathers の話を聞いた安井力さん曰く、 これに対してこんな方法があるようです

機能見積りトレンド (Feature Trend Cards)

ある機能の実装にどのくらい時間がかかるか見積もる。
で、実装しない。定期的に同じ機能を見積もる。
もし見積りが大きくなっていたら、それは負債が増えているということだ。

リファクタリングしてみる (Scratch Refactoring)

やりたいように大規模にリファクタリングをする。
でも保存しない(コミットしない)し、動かなくてもいい。
どんな感じかつかむためで、そこから情報を得る。

こちらはレガシーコード改善ガイドに載っているそうです。

これらの方法に対する疑問は

  • 「機能追加」や「リファクタリング」は実際に起こるのでしょうか?
  • いつまでも見積もれる「機能」は追加しなくてよいのではないでしょうか?

変更しないと技術的負債は観測できない

例えば、インデントが揃っていない、変数名が連番のソースコードがあったとします。

  • 変更を設定で吸収できた場合は、技術的負債は見つかるでしょうか?
  • ソフトウェアが変更されなかったら、技術的負債は見つかるでしょうか?

ソフトウェアを変更してみるまでは、技術的負債は観測できません。

技術的負債はシュレディンガーの猫である

シュレディンガーの猫とは

この実験で箱のなかの猫は、放射性物質のアルファ崩壊という量子力学的な振る舞いにのみ生死が
決定するため、観測者が箱を開けて中を観測しない限り、猫は 量子力学のウィグナーらが唱えた
確率的解釈により生きている猫と死んでいる猫が50:50で重ね合わせで存在している事になる。

つまり箱の中の猫はふたを開けて観測するまで、生きてもいないし死んでもいないのである

シュレーディンガーの猫とは (シュレーディンガーノネコとは) [単語記事] - ニコニコ大百科から引用

技術的負債は、この「シュレディンガーの猫」のような存在に思えます。 変更を加える前は「技術的負債」は有りも無くもしないのです。

「技術的負債」が問題になるソフトウェアの変更は予想できません。 「予想出来る変更」は設定で吸収できるようソフトウェアを設計すれば対応できます。 「技術的負債」は問題になりません。

「技術的負債」は予想できない変更に依存します。 変更を加える前は「技術的負債」は「有る」と「無い」が重なりあった状態です。 変更を開始して「技術的負債」が観測された時に状態が収束します。

この仮説が成り立つなら、量子の観測と同じ方法で定量的に評価できそうです。

量子の観測方法を借りてくる

ソースコードは技術的負債は持たず、技術的負債を発する能力「技術的負債能」を持つと考えます。 *1

具体的には、イテレーション中に「技術的負債」を発見したらカウントアップします。 あるイテレーションでは技術的負債が30発見され、別のイテレーションでは100発見されます。 発見数をグラフ化すればトレンドを見ることができます。 *2

ボウリングの観測方法を借りてくる

イテレーション毎に変更内容は変わります。 イテレーション毎に発見する「技術的負債」数も変わります。 どうすれば異なるイテレーションの「技術的負債」数を比較できるでしょうか?

ボウリングでは、レーンとの相性や体調の変化により1ゲームのスコアに変動があります。 レベルの異なるプレイヤーを集めて大会を開くにはハンディキャップの設定が必要です。 一般的な大会では、ある程度公平なハンディキャップを決めために、 30ゲームのスコアの平均を使っています。 つまり30ゲームのスコアの移動平均でプレイヤーの能力を測っています。

これを真似て、30イテレーション移動平均を取れば有効な数値が取れそうです。

技術的負債にはビジネスが含まれている

この値にはビジネス価値に依る変更箇所の偏り、 ビジネス環境の変化による変更箇所の変動も含まれているはずです。

まとめ?

「技術的負債」はソースコードの属性ではありません。 チームの属性です。 また、ただの技術力の指標ではなくビジネスの予測力を含みます。

*1:放射線放射能のメタファーです。

*2:簡単に書いていますが、変更時にはたいてい技術的負債が見つかります。発見数は単にストーリー数やストーリーポイント数になりそうな気がします。

グーグルカレンダーに予定を追加するURLを作るライブラリを作りました

Node.jsとブラウザどちらでも動くように作ってあります。

作った理由

Google Chrome拡張で使いたかった。探してもURLだけを作ってくれるライブラリがありませんでした。

URLは作ってくれるだけど、ボタンを作る機能は不要でした。

仕様メモ

作るにあたって公式仕様を調べたのですが、見つけられませんでした。 ぐぐって分かった情報を残しておきます。

終日予定(allday event)を作る方法を調べるのに苦労しました。

action(必須)

action=TEMPLATE

TEMPLATE固定です。

text

text=Garden%20Waste%20Collection

予定のタイトルです。 URLエンコードします。

空文字を指定すると(日本語の場合)無題の予定になります。

dates

dates=20090621T063000Z/20090621T080000Z

予定の開始と終了時刻です。 ISO日付フォーマットでUTCを指定します。 付けないと現在時刻から設定されます。

終日予定の場合は日付のみを指定します。

dates=20131208/20131209

ユーザーのタイムゾーンを使う場合はZをつけません

dates=20131208T160000/20131208T180000

location

location=Home

予定の場所です。 URLエンコードします。

details

details=Happy

予定の詳細です。 URLエンコードします。

参考リンク

stackoverflowです。

TDDについて

このライブラリはTDDで作りました。 TDDを採用した理由

  • Google Chrome拡張での動作確認はテンポが悪い
    1. Google Chrome拡張を読み直す
    2. ブラウザのタブを切り替える
    3. 対象サイトを読み直す
    4. ロード待ち
    5. 結果を確認
  • 入力データが限定されていた。作るGoogle Chrome拡張の仕様も大体決まっていた
  • 出力仕様が明快だった。迷ったらGoogleカレンダーが期待通りに動くURLにすれば良かった
  • コードのイメージはあった。詳細設計するのは面倒くさかった。三角測量したかった

ライブラリを作る時はTDDがいいですね。

power-assertについて

テストコードを書くのにt_wadaさん作の twada/power-assert · GitHub を使いました。

実に良いです。テストに失敗した時だけ、詳細な値を表示してくれるのはとても自然で、そして便利でした。 まるで、気の利いた執事とテストをしているようです。

power-assertは良いものです。次も使います。

参考図書

テスト駆動JavaScript サーバサイドJavaScript Node.js入門 実践Node.js プログラミング

東京Node学園祭2014に参加しました #nodefest

東京Node学園祭2014に参加しました。

nodeschool in Japan

maxwell ogdenによるNode.jsのワークショップの日本出張版。 Nodeschool Tokyoに大体の内容が書いてある。

maxogden/levelmeup-jp · GitHub をやってみました。

  • levelmeup-jpコマンドでターミナル上に問題とチュートリアルを表示
  • levelmeup-jp verify my_program.jsで、作ったプログラムが正しいか評価してくれます

このバランスは新鮮です。社内研修に使えるかもしれません。

内容は非同期IOに慣れなくて、結果が取れる前に表示しようとしたり苦労しました。

基調講演

Socket.ioの作者、Guillermo Rauchの基調講演。 partyというブラウザからのファイルをアップロードするためのフレームワークの紹介です。

bittorrentっぽくファイルをチャンクに分けてアップロードする。 帯域を有効に使えるしresumeもできる。 File APIでファイル情報取ったり、XHR2を使って進捗取ったりしているらしい。

テストサイトで動いているのが見れます。 まだ、開発中でソースはAutomattic · GitHubで公開予定だそうです。

Guillermoの発表はFTPとかHTTPとか背景の説明がすごい丁寧。 最初に何の話をするのか教えて欲しいな・・・

What’s coming in Node, Express & LoopBack

StrongLoopのCEOによるLoopBackの紹介。 英語だったけど聞き取れるぐらいにゆっくりしゃべってくれたのがありがたかったです。

ツール類がすごかった

LoopBack自体はExpressベースのWeb APIフレームワークslcコマンドでyomenみたいにscaffoldできるそうです。 zoneはデバッギング用のツールで、この辺をしっかり作っているのがEnterprize向けにありがたい感じです。

APIを作りたくなったら使ってみようと思います。

すべてのノードトランスパイラーがひどい!ならば、ノードトランスパイラーをいかに改善できるか。

Martin Heideggerの発表。

ちょっと難しかった。「トランスパイラーの品質特性を定義する」話だと理解しました。

テスト用ライブラリ power-assert, その開発で学んだ npm モジュール設計の勘所

我らがt_wadaさんの発表

最近、power-assertを使ったので便利なのは知っていました。

講演を聞いて、感じてた以上に馴染んでいたっぽいとわかりました。 あまりにもしっくり動くので、便利さを軽視していた気がします。 このしっくり感はBrowserifyっぽいです。 assertion warはホントに終わるかもしれません。 *1

発表内容がsubstack pattern > UNIX哲学と突き進んで行くのがすばらしかった。

要チェックキーワード

  • concat-stream
  • esprima - estraverse -escodegen

オレオレ(C言語的な意味での)マクロが作れるってこと?ますますテストが大事になりそうです。

f:id:ledsun:20141116110216j:plain

ギャルでもゎかる node-webkit

upgrade_ayp氏によるnode-webkitの紹介。

序盤緊張してたけど、載ってくるとすごいエネルギーだった。 可愛いギャルなのに技術キーワードバリバリw。

mochaがCUIで動かないのでgulpで出力先変えるハックとか、ソースコードプロテクションのために読込みhookするとかアプローチが技術的すぎて、違和感との戦いが大変でした。

いま、社内システムの魔改造Chrome拡張でやっている。 ページ遷移があるとnode-webkitが良さそうなので、切り替えてみます。*2

※以前の発表資料です

その他のメモ

まとめ

Node.js勢は勢いに乗っています。

*1:assertion warは千日戦争(ワンサウザンドウォーズ)みたいな響きがあって良い

*2:いずれにしろDOMトラバースの辛みは変わらない

reduceでPromiseをつないでタイムライン実行する

Promiseとreduceを組み合わせたトリック。 適当な間を置いてイベント発行するstubを作る時に使いました。

指定時間後に処理を実行するPromiseを作ります。

var DelayPromise = function(delay, action) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      try {
        action();
        resolve();
      } catch (err) {
        reject(err);
      }
    }, delay);
  });
};

map/reduceでつなげて実行します。

[1, 2, 3].map(function(record) {
    // Promiseはnewすると実行します。前のPromise完了後に実行する関数を作ります。
    return function() {
      // 指定秒後に数字を表示します。
      return new DelayPromise(record * 1000, function() {
        console.log(record)
      });
    }
  })
  .reduce(function(prev, action) {
    // 順次実行するために各actionをthenで繋ぎます。
    return prev.then(action);
  }, Promise.resolve())
  .catch(function(err) {
    console.error(err, err.stack);
  });

1秒後に1が、それから2秒後に2が、それから3秒経って3が表示されます。

Mac OS X で Raspberry PiのOSイメージを焼く

Mac OS XMac に刺したSDカードにddコマンドを使ってOSイメージを焼く方法です。

OSイメージの入手

Downloads | Raspberry Piから好きなイメージをダウンロードします。 特にこだわりがなければ、サンプルが多いRASPBIANが良いと思います。

Download Zipをすると数時間掛かるので、面倒でもBitTorrentをインストールして、BitTorrent経由でダウンロードするのをオススメします。

SDカードの確認

SDカードのデバイス名を確認します。 SDカードをMacに刺し、ターミナルから次のコマンドを実行します。

diskutil list

次のような結果が表示されます。

/dev/disk0
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:      GUID_partition_scheme                        *251.0 GB   disk0
   1:                        EFI EFI                     209.7 MB   disk0s1
   2:                  Apple_HFS Macintosh HD            250.1 GB   disk0s2
   3:                 Apple_Boot Recovery HD             650.0 MB   disk0s3
/dev/disk1
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     Apple_partition_scheme                        *374.6 MB   disk1
   1:        Apple_partition_map                         32.3 KB    disk1s1
   2:                  Apple_HFS Adobe Photoshop CC 2014 374.6 MB   disk1s2
/dev/disk2
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     Apple_partition_scheme                        *46.1 MB    disk2
   1:        Apple_partition_map                         32.3 KB    disk2s1
   2:                  Apple_HFS Bitcasa Installer       46.1 MB    disk2s2
/dev/disk3
   #:                       TYPE NAME                    SIZE       IDENTIFIER
   0:     FDisk_partition_scheme                        *8.0 GB     disk3
   1:                 DOS_FAT_32 NO NAME                 8.0 GB     disk3s1

Appleで始まるキーワードはOSで使っているデバイスです。 それ以外のデバイスが今回刺したSDカードです。 この例では/dev/disk3/です。

OSイメージを焼く

ddコマンドを使ってOSイメージを焼きます。

アンマウント

ddコマンドはマウントされているデバイスには実行できません。 一度アンマウントします。 指定するデバイス名はさきほど確認した/dev/disk3/です。

diskutil unmountDisk /dev/disk3 

焼く

ddコマンドでOSイメージを焼きます。

sudo dd if=2014-09-09-wheezy-raspbian.img of=/dev/rdisk3 bs=1m

完了まで6分掛かります。注意点がたくさんあります。

  • デバイス名を間違えるとMacのデータを壊せるので気をつけましょう
  • デバイス名にrを付けて/dev/rdisk3/にすると10倍速く焼けます
  • bs=1mオプションを付けると10倍速く焼けます

指定を間違えると、ものすごく時間が掛かるので気をつけましょう。

進捗確認

ddコマンドは実行中にCtrl + Tを押すと現在の進捗を表示します。

load: 1.00  cmd: dd 3056 uninterruptible 0.00u 1.31s
1935+0 records in
1934+0 records out
2027945984 bytes transferred in 258.285589 secs (7851565 bytes/sec)

2014-09-09-wheezy-raspbian.imgのファイルサイズは3276800000バイトです。 おおよその残り時間が推測できます。

動作確認

Raspberry PiをHDMIケーブルでモニタと繋ぎ、SDカードを刺して電源を入れてみましょう。 起動画面が表示されば成功です。

参考リンク

参考書籍

Raspberry Piユーザーガイド 第2版 ラズパイマガジン (日経BPパソコンベストムック)

Raspberry piをMQTT 気温 publisherにするAnsible Playbookを公開しました

過去のBlogで手動でやっていたことをAnsible Playbookに書き起こしました。

変更点は?

  • 秘密情報(Sangoへの接続情報)をansible-vaultで隠蔽した
  • cron設定(15分に一回)を追加した

困ったこと

  • 電車の中や外出中はRaspberry Piの実機がなくて動作確認できなかった

次にやりたいこと

  • Raspberry Pi でMQTTで更新トピックを監視、メッセージが飛んで来たらansible-pull
  • Raspberry Pi を増やして、それぞれ別トピックにpublish。ansibleのrollを使ってトピック名を変数で指定する