JSON::TypeInference 0.04 で null かもしれない値を Maybe 型として推論するようにした

metacpan.org

github.com

Null と値型のただ2つからなる Union 型を Maybe という特別な型として推論するようにした。ちょっと便利。

Maybe だったら {"type" : ["null", "string"]} のように出力するようにすると、より厳密な JSON スキーマを生成することができるようになった。

github.com

ほか JSON::TypeInference::Type::* にドキュメントを追加するなどしました。

どうぞご利用ください。

最近の CPAN モジュールを作るときの構成

github.com

Minilla を使っている。minil new してからちょっと手を入れる。

.mailmap

<aereal@aereal.org> <aereal@users.noreply.github.com>

GitHub の Web インターフェースで Merge Pull Request ボタンを押すとコミットの AUTHOR_EMAIL がこれになる。

たぶんメールアドレスを非公開にしているとこれになるような気がする。悩ましい。

.travis.yml

sudo: false # Use container-based environment
language: perl
perl:
  - "5.12"
  - "5.14"
  - "5.16"
  - "5.18"
  - "5.20"
cache:
  directories:
    - local # Caches installed modules

before_install:
  - which carton || cpanm --notest Carton
  - carton version

install:
  - carton install

script:
  - make -f ci.mk cover

after_success:
  - make -f ci.mk coveralls

……をやっている。

ci.mk

テストで実行するコマンドを Makefile にまとめる。

CARTON_EXEC = carton exec --
COVER_IGNORE_RE = ^t/|^local/

test:
	$(CARTON_EXEC) prove t/

cover:
	$(CARTON_EXEC) cover -test -ignore_re '$(COVER_IGNORE_RE)' -make 'make -f ci.mk test'

coveralls: cover
	$(CARTON_EXEC) cover -report coveralls

感想

Dist::Maker を使ってみようかとおもったけど依存モジュールが多くてちょっとためらう。

Perl を書きはじめて4年くらいになるけど、小さな CPAN モジュールをリリースするようになってからなんとなく Perl を書くのが好きになってきた気がする。

Perl の値から JSON の型を推論するモジュールを書いた

github.com
metacpan.org

これは何か

Perl の値 (スカラ値、ハッシュリファレンス、配列リファレンスなど) が JSON のどの型であるかを推論するモジュール。

Perl1 という値は JSON の number である、Perl"a"JSON の string である、という風な。

配列やハッシュのようなコンテナ型も再帰的に推論する。[1,2,3] のような値を与えると array[number] という型である、と推論する。

候補となる型が複数ある場合は直和型 (union type) として報告する。すなわち [1, 'a'] のような値は array[number|string] と推論する。

何のために使う?

もともとこのモジュールは JSON schema を自動生成することを目指して作った。

JSON schema は JSON over HTTP API を提供するときに便利な仕様群であるものの、JSON schema それ自体を記述するのはリーズナブルとは言い難い。
これはソフトウェアが再利用することを意図しているので仕方のないことではある。とはいえ書くのはだるい。
ずっと { "type": "object", "properties": {} } と書いていると気が滅入る。

JSON schema を実際の JSON データを (複数) 与えて自動的に型や構造を出力してくれると便利そうである。

実際、そのようなことをしてくれるライブラリはある。
(参考: JSON Schema Software)

しかしながら当然 (?) Perl のライブラリは紹介されていない。CPAN でよさそうなそれらしいモジュールは見つけられなかった。なので書くことにした。
このモジュールはその一歩です。

実際に JSON schema を自動生成するデモ

github.com

JSON::TypeInference を使って実際に与えられたデータから schema を自動生成するスクリプトを書いてみた。

README にあるように eg/ 以下の JSON からこんな schema が生成される:

{
   "$schema" : "http://json-schema.org/draft-04/schema#",
   "description" : "TODO",
   "properties" : {
      "id" : {
         "example" : 1,
         "type" : "number"
      },
      "name" : {
         "example" : "yuno",
         "type" : "string"
      },
      "school" : {
         "properties" : {
            "location" : {
               "properties" : {
                  "lat" : {
                     "example" : 14,
                     "type" : "number"
                  },
                  "lon" : {
                     "example" : 15,
                     "type" : "number"
                  }
               },
               "type" : "object"
            },
            "name" : {
               "example" : "yamabuki",
               "type" : "null"
            }
         },
         "type" : "object"
      }
   },
   "title" : "TODO",
   "type" : "object"
}

union type の扱いが雑なのは将来的に直したい。

課題と展望

あるオブジェクトのプロパティが必ず出現すべきなのかどうか、も推論できるとよさそうと思って issue を立ててある。
Detect required properties · Issue #5 · aereal/JSON-TypeInference · GitHub

感想

型推論などというのもおこがましいほど素朴な実装だけど、しゅっと書けたわりには便利なものができたと思う。

どうぞご利用ください。

Config::ENV で各環境で必ず定義すべきキーを定めておく

metacpan.org

ちょうどいいデフォルト値が見当たらないので、このキーは各環境で適宜、定義せよ、という風にしたい。interface みたいなかんじ。

use strict;
use warnings;
use Test::More tests => 2;
use Test::Fatal qw(exception);

{
  package My::Config::NotImplemented;

  use strict;
  use warnings;
  use Scalar::Util qw(blessed);

  sub new {
    my ($class) = @_;
    return bless {}, $class;
  }

  sub equals {
    my ($self, $other) = @_;
    return blessed($other) && $other->isa(ref($self));
  }
};

{
  package My::Config;
  use strict;
  use warnings;
  use Config::ENV 'MY_ENV';

  use constant NOT_IMPLEMENTED => My::Config::NotImplemented->new;

  sub declare ($) {
    my ($name) = @_;
    return ($name => NOT_IMPLEMENTED);
  }

  common +{
    declare 'log.path',
  };

  config production => {
      'log.path' => '/var/log/app.log',
  };

  config test => +{};
};

{
  package Test::My::Config;
  use strict;
  use warnings;
  use Config::ENV ();

  sub import {
    my ($class) = @_;
    no strict 'refs';
    no warnings 'once';
    *My::Config::param = \&param;
  }

  sub param {
    my ($class, $name) = @_;
    my $value = Config::ENV::param($class, $name);
    return !My::Config::NOT_IMPLEMENTED->equals($value);
  }
};

Test::My::Config->import;

my $envs = do {
  my $envs = My::Config->_data->{envs};
  [ keys %$envs ];
};

for my $env (@$envs) {
  local $ENV{MY_ENV} = $env;
  my $current = My::Config->current;
  for my $key (keys %$current) {
    ok +My::Config->param($key), "Environment ($env) defines `$key'";
  }
}

declare($name) で各環境で定義すべきキーを宣言する。これは対応する値として意味のないオブジェクトを便宜上定義する (Config::ENV は設定値として hashref を期待するので)。

テスト時に Config::ENV を継承したクラスの param($name) を上書きして、値が NOT_IMPLEMENTED オブジェクトかどうかをアサートする、という風にする。

すると # Failed test 'Environment (test) defines `log.path'' という風にテストが失敗し、たしかに test で log.path が定義されていない、ということがわかる。

JSON schema とか XML とか使うともっとリッチな検証ができそうだけど、Config::ENV でも素朴にできるというコンセプト。

#yapcasia に参加した

トークしつつ、トークを聞いたりなどした。

1日目

いろいろ聞いたけど印象的だったトークについて。

yapcasia.org

kazuho さんの発表を初めて見た。声が高い。

僕は大学を中退していてアカデミアどころか学部の卒業論文がどういう雰囲気なのかよく知らないけれども、論文やベンチマークなど根拠となるデータが必ずあってそれを軸に話がなされるので、けっこう速いペースにも関わらずすんなり聞くことができた。

アセットコンパイルを行う動機はリクエスト数を減らすだけではないので必ずしも HTTP/2 によってアセットコンパイルという考え方が無用の長物になることはないだろうと思った *1。とはいえ、言及されたように考え方を変える必要はあるだろうと思う。

言ってしまえば高々60分に満たない時間で HTTP/2 について一本筋の通った理解が得られたのは、事前に断片的な知識が自分にあったことを差し引いてもすごいことだな、と思った。

また、発表の内容自体もさることながら、自分の中で「わかりやすい」「聞きやすい」プレゼンテーションの具体例がひとつできたのも尊い。

yapcasia.org

WebAudio と信号入門の話。

「今まで Web エンジニアはディスプレイという限られた光情報しか操ることしかできなかったが、WebAudio によって空気を振動させディスプレイを越えて影響を及ぼすことができる」という風なことをおっしゃっていた。

少しおどけた風だったような気がするけれども、実際、胸を打たれた。僕はわりと音楽が好きで音が出ると喜ぶので WebAudio が Web API に入るときはおもしろそうだ、と思ったけれどもそれ以上のことは考えられなかった。

音波とか音響に関する知識はわずかながらあったけれども、信号の話は不明だったのでとてもおもしろかった。とてもエンターテイナーだなあ、と感じた。

yapcasia.org

2日目

yapcasia.org

タイトルにある通り Go におけるプロファイリングおよび最適化の話で、それはそれでもちろん参考になったけれども、思いがけずライブコーディングを見ることができてとてもよかった。

ごく個人的な感想

2012年にチケットを買っていて行くつもりだったけれどもぎりぎりでやめて、別の人に譲った。2014年も参加しなかった。どちらの年もトークを応募しなかったから。

自分はエンジニアで普段からインターネットなどを通じて情報を得たり OSS コミュニティからソフトウェア資産を享受し (つつ、わずかながら還元し) ており、では YAPC のようなカンファレンスで自分が果たせる役目とはなんなのかと問うてみると、おもしろトークをすることだと思っている。

また、そうやってトークをするという形で還元することをイベントに参加する条件として課しているのは「お客様気分」を持たせないためでもある。YAPC は有料のイベントなのでなおさらである。

YAPC::Asia Tokyo にて2回スピーカーとして登壇できたことはとても褒まれ高いことだと思っている一方で、YAPC::Asia Tokyo に区切りがついたとしてもそういったコミュニティへの還元への考え方は変わることはないだろう。

おもしろトークのために技術を磨いていかねば。

みなさまお疲れさまでした。

*1: たとえば browserify などはブラウザにおける JS 実行環境においてモジュールシステムがないことに対するアプローチである

#yapcasia で『世界展開する大規模ウェブサービスのデプロイを支える技術』という発表をした

世界展開する大規模ウェブサービスのデプロイを支える技術 - YAPC::Asia Tokyo 2015

2年ぶり2度目の YAPC::Asia Tokyo でタイトルにある通り発表をしてきました。資料は近日公開予定です。

立ち見続出のようで満員御礼といった具合で非常に嬉しいしありがたいことです *1

懇親会などで「おもしろかった」と声をかけてもらえることが多くて非常に嬉しかったです。 「おもしろかった」「最高」と思ったらぜひ ベストトークに投票 してもらえると嬉しいです!!!

*1: もっとも立ち見されていた方は60分近いトークだったので大変だったでしょうが

YAPC::Asia Tokyo 2015 のトークスケジュールを集めたカレンダーを作った

https://www.google.com/calendar/ical/037qm3orvt1bkf5g2lsi1br0bg%40group.calendar.google.com/public/basic.ics

YAPC::Asia Tokyo 2015 も来週に控えて、どういうトークを見にいくか悩むこの頃なのでカレンダーをつくりました。

yapcasia.org

トークリストから素朴にスクレイピングして作ってあります。organizer にスピーカーの名前が入れられるとよかったのですが、メールアドレスが必須で手頃に入手できそうになかったので諦めてイベントのサマリに入れてあります。

Google Calendar などにインポートするなどして、どうぞご利用ください。

npm install -g gulp とかしない流儀

gulp とかを使っているプロジェクトの場合、ビルドツール類も devDependencies に含めてバージョンを固定したいという要求があると思う。

ところが実行ファイルにパスを手軽に通したいという理由のみで npm install -g gulp などしてしまうとバージョンが固定できなくなってしまい、本末転倒である。

とはいえ ./node_modules/.bin/gulp build してくれ、というのも面倒であるので、どうするとよいのか書いておく。

node_modules/.bin に自動でパスを通す

たとえば direnv を使う。

# .envrc
export PATH="$(npm bin):$PATH"
direnv allow

.envrc は作業ディレクトリを移動した時に評価されるので、npm bin の出力が異なる環境でもそれぞれうまく動く (はず) なのでリポジトリに含めてもよい。

package.json の scripts フィールドを使う

package.jsonscripts フィールドに定義したタスクは npm run NAME で実行できる。

また、この scripts フィールドに定義したタスクの実行時には node_modules/.bin にパスを通した上で実行される。

In addition to the shell's pre-existing PATH, npm run adds node_modules/.bin to the PATH provided to scripts.

run-script | npm Documentation

統一されたインターフェースを用意するという意味でも scripts フィールドは望ましい。

結論

direnv と scripts フィールドは役割が少し異なるので、どちらか一方のみを利用するということもなく両方活用できるとよさそうですね。

#小学生のときにやってた悪夢っぽいこと選手権

スコアメーカーという楽譜入力ソフトがあってこれを買ってもらって遊びはじめたところ、MIDI というもので勝手にコンピュータに演奏させることができるとわかって大喜びしはじめた。

ひたすら入力して演奏させる。アーティキュレーションが思ったかんじではないので調整してまた聞く、の繰り返しを土日のあいだ飽きることもなくやっていた。

小学生を卒業したころだったかもしれないけど、ヤマハがミッドラジオプレイヤーというソフトを公開した。

ミッドラジオプレーヤ

ミッドラジオプレイヤーはソフトウェア音源を内蔵していて、スコアメーカーに再生させるよりずっとリッチな音色になるし、なによりそれが無料で使えるのでヤマハ最高! と唱えながら毎日使っていた。

その後に MP3 というものの存在を知ることとなる。MP3 はダウンロードに時間がかかるけどなにやらきれいな音で MIDI が聞ける、という理解のものだった。

後に MP3 は音声を録音するためのフォーマットのことであり、自分が言っていた MP3 はいいソフトウェア音源の演奏を録音したものだと知る。わからなかったことがわかってよかった、と思う一方で、自分が MP3 を作れたとしてもソフトウェア音源を買わなければいけないことを知ってがっかりした。

しかし自分にはミッドラジオプレイヤーがあった。ミッドラジオプレイヤーで再生してそれを録音すればいいということを思い付いたときは自分が天才かと思った。

作った曲はヤマハの音楽投稿サービス (もうなくなった) に投稿していた。

www.itmedia.co.jp

打ち込みしてるうちにいい曲ができたので、中学生にあがってから朝日作曲賞に応募したことがあった。

打ち込みしていたのは悪夢っぽくはないけど、できた曲のことを思い出すと悪夢っぽい。

近況

YAPC::Asia Tokyo 2015

yapcasia.org

採択された。

App-xslatert と Text-Xslate-AST-Walker

github.com

App-xslatert というツールを作り始めた。

App-PRT を便利に使っていて、Perl のコードのリファクタリングはだいぶ楽になったので、テンプレートのリファクタリング、たとえば変数名を変えるといったことを同じような使い心地で行いたいと思ったので作り始めた。

github.com

前に作った Text-Xslate-AST-Walker を使っている。AST を変更する機能が必要になったので issue の優先度を上げたりした。

github.com

mackerel-agent で便利に使える OS X 向けプラグインを Homebrew でインストール

github.com

mackerel-agent-plugins-osx というリポジトリを非公式に作って置いておくという活動を前からしている。今のところをバッテリ残量をホストのメトリクスとして投稿できるのみ。

これを新しい MacBook にインストールする上で手軽にしたいと思って、Homebrew の Tap を作った。

github.com

どうぞご利用ください。

OS X のセットアップを Ansible にだいたい任せる

新しい MacBook Pro がやってきたのでセットアップの記録を書いておく。

「だいたい」とあるように、実際のところ自分の手を動かさざるを得ない手順はまだまだあるし、むしろ増えたりもしている。

手順

  • App Store.app で Xcode をダウンロード
  • System PreferencesSecurity & PrivacyFirewall からファイアウォールを有効にする
  • System PreferencesSharing を開く
    • Computer Name を変更する (今回は izanagi にした)
    • Remote Login を有効にして sshd を起動する
  • Ansible Playbook を現マシンから SSH 越しに適用する
  • System PreferencesKeyboardModifier Keys ... から caps lock を control に (後述するが OS X におけるこの修飾キーの設定の扱いは思ったよりも込み入っていたので手作業することにした)
  • Xcode.app を開いて additional components と以下をインストール:
  • System PreferencesSecurity & PrivacyPrivacyAccessibility から Karabiner を許可する
  • (再度 Ansible Playbook を適用する)
  • Alfred の Powerpack License を入力する
  • Dropbox に認証情報を入力し同期させる

Ansible の Playbook について

github.com

今回、スクラッチから書いてみた。

以前にも Playbook を書いて公開しているのだけれども、Ansible の雰囲気に慣れながら書いていったものなので「今だったこう書くな・こう分けるな」と思っていたことと、Playbook に書いていた設定やインストールすべきソフトウェアにけっこう手垢がついており本当に必要な設定・ソフトウェアだけに絞りたい、という目的があってスクラッチから書いた。

また、前に書いた Playbook では別のホストでも実行できるような汎用性を持たせようとしていたけれども結果的に破綻して実現できていなかった反省とそもそもそのような汎用性は用途上不要だと考えて、今のマシンに特化させることにした。

書く時に考えた果たすべき冪等性の基準として「変更を加えない限り何度実行しても失敗しないこと」を掲げた。

「失敗しないこと」が意味するものは、Ansible の出力が failed にならないこと。

Ansible では task を実行した結果、副作用が及ぼされると changed というステータスになる。

Ansible のコアモジュールでは設定で記述した状態に既に収束していた場合、変更は実際に行わないように書かれている。

また、「変更を加えない限り」というのは、たとえば ssh でログインしてディレクトリが既に作られている場合などを特別に考慮しない、ということ。だいたいは Ansible のコアモジュールがカバーできるし、それらでカバーできない場合はおそらく自分でひとつモジュールをひとつ作るに相当する労力をかけることになるので、それはコストに見合わないと考えた。

また、前提条件をはっきりさせておかないと考慮すべき条件が爆発するという問題もある。

OS X の修飾キーの設定について

apple.stackexchange.com

上記 stackexchange のスレッドに書いてあることがすべてだが:

  • Property list でキーボードの修飾キーを設定する際には識別子が必要
  • MacBook シリーズの本体備え付けのキーボードは内部的には USB 接続のデバイスとして識別されている

……ということらしく、けっこうめんどうそうだったので諦めて手作業することにした。

Path::Tiny を継承する

Path::Tiny - search.cpan.org

Path::Tiny には new という、いかにもオブジェクト指向的なコンストラクタのような名前のメソッドが定義されているのでいかにもオブジェクト指向的に継承できるように見えるが結論から言うと できない

sub new { shift; path(@_) }

Path-Tiny/Tiny.pm at 37c371dbd100d82968b57bafc52222463ceb53bd · dagolden/Path-Tiny · GitHub

This is just like C, but with method call overhead. (Why would you do that?)

とあるので、ドキュメントにある通りとも言える。

どうしても継承したい場合は再度 bless を呼べば上書きされる。

package My::Path;
use parent qw( Path::Tiny );

sub new {
  my ($class, $path) = @_;
  my $self = $class->SUPER::new($path);
  return bless $self, $class;
}