読者です 読者をやめる 読者になる 読者になる

15 min/d

ぼうずやのにっき

bouzuya/cyclejs-history-driver 0.2.1 をつくった

bouzuya/cyclejs-history-driver 0.2.0 0.2.1 をつくった。 2017-03-05 のことだ。

経緯について書く。

先週は bouzuya/bath 2.0.1 や bouzuya/spa-town 0.1.2 をつくった。

bath は path template engine だ。 template と parameters から path をつくったり、template と path から parameters を取り出したりできる。

spa-town は router だ。各 route pattern に bath を使っている。 bath の活用例でもある。

それらをせっかくつくったので、何かに適用したくなった。ちょうどいい題材として bbn: blog.bouzuya.net の 4.x である bouzuya/mr-jums に適用しようとした。

mr-jums は 2016-12 から commit していないので、いろいろ依存関係が古くなっていた。 Cycle.js や webpack などだ。 Cycle.js のバージョンを上げたところ、 driver の仕様も変わってしまい、動かなくなった。

動かなくなったのが cyclejs-history-driver だ。 2016-08-27 につくったものだ。Cycle.js v7.0.0 (Cycle Diversity) への対応を試している。

Cycle Diversity は複数種類の stream library への対応をうたっている。だから diversity なのだろう。で、それらの違いを吸収するために library ごとに adapter をつくるようになっていた。

それが今回の Cycle Unified で動かなくなった。 Unified は ES Observable を使って変換しているらしい。ふーん。詳細は知らないが、 Driver の API が変わっている。う、うーん。何回 API 変えるつもりだよ……。

で、bouzuya/cyclejs-history-driver 0.2.1 ができた。経緯ここまで。

mr-jums に spa-town を組み込みたかっただけなのに……。

追記。書きそびれていた。 2017-02-21 から NPM にお金を払っているし、せっかくなので NPM に scoped package として公開した。@bouzuya/cyclejs-history-driver

2017-W09 ふりかえり

2017-W09 をふりかえる。

2017-03 の目標

  • [ ]: 『型システム入門』を読む
  • [x]: 斑鳩を続ける
  • [x]: bath 2.0.0 をつくる
  • [ ]: PureScript でなにかつくる

2017-W09 の目標

いろいろ忘れていた。忘れないように確認しよう。

今週のふりかえり

つくったもの

2017-02-27/2017-03-05

PureScript で何かをつくる代わりに bath の 2.0.1 をつくった。手をかけず、なかなか良いものができた。それをもとに spa-town 0.1.2 をつくった。良い調子だ。

せっかくつくったので、それらを mr-jums に組み込もうとしたら cyclejs-history-driver が……。このあたりはまた別で書く。

なかなかいい流れだ。なにかをつくったことが次のなにかにつながる感じ。 blog.bouzuya.net はこういう blog でありたい。

その他

斑鳩。最高記録を 9 MISS に更新。欠かさず続けている。それもあって 10 MISS を切ってきた。 4 MISS 以下で目標達成だけど、ここからが厳しいだろう。

2017-W10 の目標

  • [ ]: 毎日、週の目標を確認する
  • [ ]: 毎日、斑鳩をする
  • [ ]: cyclejs-history-driver のことを書く
  • [ ]: ザ・ゲームのことを書く
  • [ ]: 『イノベーションのジレンマ』のことを書く
  • [ ]: mr-jums に bath 2.x を適用する
  • [ ]: 斑鳩の動画を投稿する

bath をつかった router の bouzuya/spa-town 0.1.2 をつくった

bouzuya/spa-town 0.1.0 0.1.1 0.1.2 をつくった。

spa-town は bouzuya/bath を基にしたシンプルなルーターだ。paramsFn ごとに name をつけた route pattern をつくっておき、複数の route pattern を並べて router にする。 router は path に対応する nameparams を返すようになっている。どれにも当てはまらないときは事前に設定された既定値を返す。

コード例は次のとおりだ。

import * as assert from 'assert';
import { result, route, router } from 'spa-town';

const router1 = router([
  route('root#index', '/'),
  route('users#index', '/users'),
  route('users#index', '/users/'),
  route('users#show', '/users/{id}', { id: /^\w+$/ }),
  route('users#show', '/users/{id}/', { id: /^\w+$/ })
], result('root#notfound', {}));

assert.deepEqual(router1('/'), result('root#index', {}));
assert.deepEqual(router1('/users'), result('users#index', {}));
assert.deepEqual(router1('/users/'), result('users#index', {}));
assert.deepEqual(router1('/users/123'), result('users#show', { id: '123' }));
assert.deepEqual(router1('/users/123/'), result('users#show', { id: '123' }));
assert.deepEqual(router1('/no-match'), result('root#notfound', {}));

bath の活用例になっている。一度は bath の examples/ に入れたのだけど、何かに使ってみたくなり、リポジトリを分けて npm package を公開した。

bath よりも実際のアプリケーションにありそうな形になっているはずだ。 path を解釈して、対応する name と params を返す。対応するものを選ぶあたりが routing ……と言っていいのかな。 router の範囲はさまざまだが、わりと小さい範囲に留めたつもりだ。

末尾 / のために別の route を用意しないといけないあたりは、最終的な利便性よりも学習性を採っている。ほかにも大文字小文字の区別や、 directory ごとの入れ子構造などを取りたい可能性もありそうだが、捨てている。 server-side を考えると method も判定に含めたいかもしれない。

入れ子構造はわりと欲しい気がするのだけど、優先順位が複雑になりそうなので避けている。現状は単純な Array なので上から順に見て、なければ default という単純な挙動だ。

上記のような要望やあるいは他の要望も、必要に応じて変更するかもしれないし、別物としてつくるかもしれない。これくらいならアプリケーションごとに実装しても良いとは思うので、無理にこれを使う必要はない。

path template engine の bouzuya/bath 2.0.1 をつくった

bouzuya/bath の 2.0.0 および 2.0.1 をつくった。

bath は path template engine 。 URL の pathname を組み立てるためのテンプレートエンジンだ。

path template engine という分類があるのかは知らない。ただ path をつくるためだけのテンプレートエンジンがあっても良いと思い、つくった。ちなみに「テンプレートエンジン」という言葉を、テンプレートとデータを与えると結果を返すもの、くらいの認識で使っている。

特徴としては次のとおりだ。

  • 低機能
  • 双方向の変換 (template -> path -> params / template -> params -> path)
  • 必要分のみを import 可能
  • RFC 6570 level 1 に似たテンプレート構文

bath の利用イメージは次のようなもの。 README から抜粋した。

import assert from 'assert';
import bathFn from 'bath';

const { params, path } = bath('/users/{id}');

assert.deepEqual(params('/users/123'), { id: '123' });
assert.deepEqual(path({ id: '123' }), '/users/123');
import assert from 'assert';
import { params, path } from 'bath';

const template = '/users/{id}';
assert.deepEqual(params(template)('/users/123'), { id: '123' });
assert.deepEqual(path(template)({ id: '123' }), '/users/123');
import assert from 'assert';
import { params } from 'bath/params'; // import `params()` only
import { path } from 'bath/path';     // import `path()` only

const template = '/users/{id}';
assert.deepEqual(params(template)('/users/123'), { id: '123' });
assert.deepEqual(path(template)({ id: '123' }), '/users/123');

いわゆる router の実装に使える……といいな。

競合は pillarjs/path-to-regexp 。あちらは express の router に使われているし、勝負にはならない。違いとして、こちらは RegExp という実装を露出させないようにしている。また低機能なだけ使いかたを理解しやすい……と良いのだけど……。

1.x は path-to-regexp の wrapper だったが、2.x は独自の構文を持つことにした。 RFC 6570 の level 1 に似たものを選んだ。

JavaScript の Error は適切に使われているだろうか

だらだらしていたら遅くなった。

bouzuya/bathpath 側をつくっていた。だいたいできた。ここからが長いんだけど……。

不正な入力に対して Error を投げるべきか迷う。型検査もないので、かなり変な値を受け取る可能性があるのだけど、それらを毎回検査するのはあまり良い習慣でもなさそうだ。またそれらの違反を Error で表現するより、多少のことなら | null を返してごまかすほうが JavaScript 界隈の空気にあっているんじゃないかとか思ってしまう。

bouzuya/bath 2.x をつくっている

今日は bouzuya/bath の 2.x をつくっていた。

path-to-regexp の機能から不要なものを削って自前で実装する。実行イメージは次のような感じ。

import * as assert from 'assert';
import { params } from 'bath/params';

const p1 = params('/users/{id}');
assert.deepEqual(p1('/users/123'), { id: '123' });
assert.deepEqual(p2('/users/abc'), { id: 'abc' });

const p2 = params('/users/{id}', { id: /^\d+$/ });
assert.deepEqual(p2('/users/123'), { id: '123' });
assert.deepEqual(p2('/users/abc'), null);

2.x で考えている path-to-regexp との違いは次のとおりだ。

  • regexp を強調しない
  • path と template + params の相互変換への配慮
  • template + path -> params な params と template + params -> path な path を必要な側だけ import できる
  • template を RFC 6570 の level 1 に近づけている

同じで言うと、 router における path との match に使う点だろうか。ぼくのほうは逆に path を構築する用途にも使うつもりだけど。

2017-02 ふりかえり

2017-02 をふりかえる。

期間別の目標

2017 の目標

  • 自分の使うものはできるだけ自分でつくる。
  • なるべくはやく見えるもの・かたちあるものにする。
  • 納得のいくものをつくる。
  • こつこつとやる。
  • 斑鳩ノーマルをノーコンティニュークリアする。
  • 型システム入門を読む。

2017-02 の目標

週別のふりかえり

つくったもの

blog などは除外している。

その他

2017-01 は病気で出遅れて、 2017-02 は嫌なことがあってめげて甘いものをたくさんとったり……。日課や習慣をすこし変えている。朝に牛乳やオレンジジュースを飲んだり、早寝を心がけるなど。

『型システム入門』はどこかのタイミングで読むのをやめてしまった。何度目だ……。また再開する。漫画は『ジョジョの奇妙な冒険』をひさしぶりに読んだ、 4 部まで。『イノベーションのジレンマ』を読んだ。また書くけどイマイチだった。

ゲームは斑鳩を進めている。最高が 11 ミス。先月が 5 コンティニュー (15 〜 18 ミス) なので、すこしは伸びている。途中サボって伸びなかったので、きちんと毎日することにした。

アナログゲームは『ザ・ゲーム』を買った。ボードゲーム同好会 #8 で遊んだものだ。家族からはあまり受けが良くない。協力ゲーではあるものの、責任を感じるからだろう。わがままだ。

Netflix で『ファイト・クラブ』・『スタンド・バイ・ミー』・『言の葉の庭』・『攻殻機動隊 S.A.C 2nd GIG 』を観ている。もう解約しようかな。

bouzuya.net が https になった。NPM にお金を払うようになった。 PureScript で rally-cli をつくった。上達している感じはない。もっといろいろつくるようにしたい。

あっという間に過ぎてしまった。

2017-03 の目標

  • 『型システム入門』を読む
  • 斑鳩を続ける
  • bath 2.0.0 をつくる
  • PureScript でなにかつくる