テスト容易性設計のやつOCamlでやってみた

自分の場合は現在時刻を引数で渡すようにしました。
モジュールやコンテキスを表すデータ型みたいなものを渡してもいいんですが仰々しいので。

t-wada.hatenablog.jp

コードはここに置いてあります。
Design-for-testability - OCaml version · GitHub

実装として、ファンクタに表示するメッセージのリストを渡して、挨拶用のモジュールを定義できるようにしてあります。
モジュールは下記の4つのモジュールです。

  • Time - 時刻を扱うデータ型モジュール
  • Time_range - 時刻の範囲を扱うデータ型モジュール
  • Greeter_message - 挨拶のデータ型モジュール
  • Greeter - 時刻によって挨拶を返すモジュール
open Greeter

module Greeter = Greeter.Make(struct
  (* 05:00:00以上 12:00:00未満 *)
  let morning = Time_range.create
    ~stime:(Time.create ~hour:5 ~min:0 ~sec:0)
    ~etime:(Time.create ~hour:12 ~min:0 ~sec:0)

  (* 12:00:00以上 18:00:00未満 *)
  let noon = Time_range.create
    ~stime:(Time.create ~hour:12 ~min:0 ~sec:0)
    ~etime:(Time.create ~hour:18 ~min:0 ~sec:0)

  (* 18:00:00以上 05:00:00未満 *)
  let night1 = Time_range.create
    ~stime:(Time.create ~hour:18 ~min:0 ~sec:0)
    ~etime:(Time.create ~hour:23 ~min:59 ~sec:60)
  let night2 = Time_range.create
    ~stime:(Time.create ~hour:0 ~min:0 ~sec:0)
    ~etime:(Time.create ~hour:5 ~min:0 ~sec:0)

  let messages = [
    (Greeter_message.create ~range:morning ~msg: "おはようございます");
    (Greeter_message.create ~range:noon ~msg: "こんにちは");
    (Greeter_message.create ~range:night1 ~msg: "こんばんは");
    (Greeter_message.create ~range:night2 ~msg: "こんばんは")
  ]
end)

またテスト用のパッケージをインストールするのが面倒だったので、テストの実行部分のコードを書きました。
通常はounitやppx_inline_testとかを使用した方がいいと思います。

ビルドもduneではなくocamlfind、ocamloptを使用しています。
適当なコード書くときに*.opam、duneファイル書くの結構面倒なんですよね。

感想

OCamlで時刻を扱うコードを書くの今ままでなかったので、標準モジュールを調べたりして、またOCamlに詳しくなりました。

今回はUnixモジュールのlocaltime、time関数やtm型を使用しました。
Windowsでは動かないと思います。

Levi’sのGOOGLE JACQUARD トラッカージャケット買ってみた

Levi’sのGOOGLE JACQUARD トラッカージャケット買ってみた

f:id:holyshared:20191014234502j:plain
Levi’sのトラッカージャケット

10月5日に発売ということだったので、面白そうだし買おうかなと思いWebから購入しました。
Webだと1日早くて、4日から買えたみたいです。

これは米国だと、前に発売されていたみたいで、自分の場合はrebuild.fmで知りました。

rebuild.fm

セットアップ

本当は10月12日に届く予定だったのですが、台風だったので13日に受け取り、その日のうちにセットアップし、実際に着てみました。

セットアップは専用のアプリケーションをインストールして、チュートリアルにそって操作していくだけでできました。
自分の場合は音楽再生が一番わかりやすいかなと思い、ジェスチャーの割り当ては下記のようにしました。

ジェスチャー 操作
下にスワイプ 次の曲
上にスワイプ 前の曲
ダブルタップ 曲の再生・停止

今の所の使用感

f:id:holyshared:20191014235127j:plain
ジャケットの袖口

使用感としてはなかなか良いです。
音楽再生の場合、今まで画面みながら曲を飛ばしながらしてたのですが、スマホみなくていいのは思いの外体験がよかったです。

ただし、袖をサッとやる操作はちゃんと袖口を触ってないと反応してくれない場合があるのか反応が鈍く感じる時がありました。
また、Bluetoothの接続が切れやすい感じがします。
なかなか反応してくれなくて、スマホの画面で接続確認とかする場合が1日のうち2、3回あった気がします。

後カメラ撮影の設定は便利そうでした、撮影タイミングをコントロールきるので複数人で撮りたい場合はいいと思います。

ジャケットとしてはよくできてると思います。
でも自分の場合はLeeの101Jが好きなので、できればそっちで出して欲しかった。

今の所の不満

  1. 開発用のSDKがないぽい。
  2. ジャケットの色のバリエーション増やして欲しい。

特にSDKは欲しいですね。

履いた靴の記録を取っている

昨年の6月くらいから履いた靴の記録を残すようにしました。   365shoes.style

残す理由は以下の通りです。

  • 手持ちの靴が革のものが多く、経年変化の記録を残したい。
    • いつの間にか変わってることが多い。
  • 靴が気付いたら増えているので、増減を記録したい。
    • 学習しないんや。。。
  • 靴のレビューを残したい。
    • 今まで履いてきたものの記録がない為、反省できない場合が多い。
  • 履く頻度の低い靴の処分判断がしたい。
    • あまり履いてないな、という感覚があるがどの程度が不明なことが多い。

開発のスタイル

基本的に土日にコードを書くスタイルです。
軽度のものは、勤務時間の休憩時間で対応しています。

技術スタックは下記のものを使用しています。
そのうちTypeScript、Next.jsに置きかえると思います。

  • Flow
  • Nodejs
  • React
  • Express
  • GraphQL
  • MongoDB
  • Heroku
  • AWS(S3)
  • Cloudflare
  • Cloudinary

2018年の集計結果

ちなみに昨年2018年の集計結果は下記の通りになりました。

Tokyo Sandalsのサンダルは利用頻度が差も高くて、その次にHunterのレインブーツ、後はローテンションで大体履いているので、ほぼ横並びです。

靴、サンダルの数は最終的に23足でした。(こんなにいらないと思う)

ブランド 名前 回数
Nicks Boots 6inch HotShot 11
Hunter Chelsea Boots 15
Hunter Original Festival Chelsea Boots 13
Brother Bridge JAMES 9
Brother Bridge McCLOUD 12
Rolling Dub Trio ROOTS 4
Tokyo Sandals DOBULE MONK SANDAL 31
Chippewa 6inch Service Boots Burgundy 11
Chippewa 6inch Service Boots Crazy house 11
Chippewa Bridgeman Burgundy 9
Chippewa Bridgeman Black Odessa 9
Chippewa Suburban Navy 3
Slow Wear Lion オイルドレザープレーンMIDブーツ Red 10
Slow Wear Lion 栃木レザープレーンミッドブーツ 11
Slow Wear Lion オイルドレザーサイドゴアブーツ 11
Redwing Irish Setter Black 11
Redwing Irish Setter Gold russet 10
Berwick Chukka boots 10
Timeberland 6inch Premium Boots 6
Timeberland 3eye Classic Lug Burgundy 7
Timeberland 3eye Classic Lug Brown 5
Timeberland Earthkeepers 6inch Boots 7
PISTOLERO Norwogian Split 7

Wescoのブーツが届きました

f:id:holyshared:20190819120329j:plain
Wesco Custom 38LTT

3月7日にオーダーしていたWescoのブーツが最近届きました。
当初の8月末くらいという話だったのですが、8月17日にオーダーしたお店から午前中に連絡をいただいて、その日に受け取り行きました。(オーダーから約5ヶ月)

当初はオーダーするつもりはなかったのですが、Wesco100周年記念の3rd Anniversary Modelだったので、オーダーしました。

オーダー時は店舗でサイズを測ってもらい、レギュラートゥの38RTか、レーストゥトゥの38LTTを選ぶ感じでした。

フィット感がいい方が好きなので、レーストゥトゥの38LTTを選びました。

f:id:holyshared:20190819120343j:plain
入っていた物

パッケージには、メンテナンス用のクロスや、オイル、ウールのキャップ、フォルスタン、ブーツ用の袋が入っていました。 しかも、ケース付きです。

また、ギャランティカードがあるので、下記の場合は修理費用がお安くなります。

  1. 1回目のヒール交換が無料
  2. 1回目のオールソールは¥5,000円程度 (通常は¥16,000円程度)

ワークブーツは大体頑丈なので、よっぽど酷使しなければ修理はそんなにする必要はないので、使うとしても大分先になりそうです。

f:id:holyshared:20190819120443j:plain
箱の正面

履き方はフォルスタンなしで、シューレスはレザーのものを使用しています。 履くときにレザーのシューレスがフックにかけにくいのですが、Rootsで慣れているので、特に気にならなかったです。

一回試し履きした感じは、NicksのHot Shotより軽くていい感じです、足馴染みも早そうです。

これからどんどん履いて行きたいです。

HackでAttributeを表現するためのインターフェース

HHVM3.29より、Attributeを表現するインターフェースが追加されました。
今までも、Attribute自体は使用できていましたが、以下の問題がありました。

  • タイプチェックがかからない。
    • Attributeに依存する処理でバグがあると原因を特定しにくい。
  • ユーザー独自のAttributeの仕様をコードで表現できない。
    • 引数は2つなのか、型はstringなのか表現できないので、ドキュメントなどにまとめる必要があった。

この問題をHHVM3.29で組み込まれた、インターフェースで解決できるようになりました。
.hhconfiguser_attributes= を設定として追加した後に、下記のインターフェースを実装するだけで、Attributeが利用できる部分の指定、インターフェースを実装したクラスでのタイプチェックがかかります。

  • ClassAttribute - クラス
  • EnumAttribute - 列挙型
  • TypeAliasAttribute - 型の別名
  • FunctionAttribute - 関数
  • MethodAttribute - メソッド
  • InstancePropertyAttribute - インスタンスプロパティ
  • StaticPropertyAttribute - 静的プロパティ
  • ParameterAttribute - 引数

Attribute仕様の表現

例えば、クラスとメソッドに指定できるAttributeを表現したい場合、下記のようにインターフェースを指定します。

\HH\ClassAttribute, \HH\MethodAttribute をクラスに実装することで、クラスとメソッドに指定できる部分を制限できます。

<?hh //strict

namespace TypedUserAttributes;

final class Version implements \HH\EnumAttribute, \HH\ClassAttribute, \HH\MethodAttribute, \HH\FunctionAttribute {
  public function __construct(
    private float $version
  ) {
  }

  public function __toString() : string {
    return (string) $this->version;
  }
}

また、指定する場所を間違えたり、パラメータの型を間違えた場合、タイプチェックに引っかかることがわかります。

f:id:holyshared:20181223224854p:plain

Attributeの取得

Attributeの情報を取得する場合は、リフレクションを使用します。
メソッドとして、getAttributeClass が提供されているので、Attributeのクラス名を引数に渡して、Attributeのインスタンスを取得できます。

<?hh //strict

namespace TypedUserAttributes;

require_once 'Post.hh';
require_once 'Version.hh';

use \ReflectionClass;

<<__EntryPoint>>
function main(): noreturn {
  $class = new ReflectionClass(\TypedUserAttributes\Post::class);

  $classVersion = $class->getAttributeClass(\TypedUserAttributes\Version::class);
  \printf("class version: %s\n", $classVersion);

  $methodVersion = $class->getMethod('getTitle')->getAttributeClass(\TypedUserAttributes\Version::class);
  \printf("method version: %s\n", $methodVersion);

  exit();
}

最後に

インターフェースが追加されたことにより、パッケージを設計する開発者にとっては非常にありがたい恩恵を受けることができるようになりました。

これからはAttributeを使う際には、インターフェースを使用して欲しいです。

また、ここで紹介したコードはここに置いて起きました。 gist.github.com

HackのコードジェネレーターをHackTestに対応させた

Hack用のテストフレームワークが出たので、HackUnitから置き換えました。
github.com

そのついでに、CodegenにHackTestのテストコードを生成できるジェネレーターを追加しました。 github.com

HackUnitからの移行

名前空間をHackUnitからHackTestに置き換えるだけで移行できます。

移行前

use HHPack\Codegen\HackUnit\{TestClassGenerator};

移行後

use HHPack\Codegen\HackTest\{TestClassGenerator};

生成されるテストコード

下記のようなテストコードを生成します。
テストコード自体は、HackTestとFBExpectを使用する前提のコードなので、composerで別途パッケージをイントールする必要があります。

<?hh // strict
/**
 * This file is generated. Do not modify it manually!
 *
 * @generated SignedSource<<d89af2ac5307bacbedfa27fd6389c1ff>>
 */
namespace HHPack\Codegen\Example\Test;
use type Facebook\HackTest\HackTest;
use function Facebook\FBExpect\expect;

final class Bar extends HackTest {

  public function testExample(): void {
    expect(true)->toBeTrue();
  }
}

パッケージのインストールはHackTestのREADME.mdに書いてある通りにコマンドを実行するだけで、インストールできます。

hhvm /path/to/composer.phar require --dev hhvm/hacktest facebook/fbexpect

HackUnit、PHPUnit使わなくて済むのと、オフィシャルのテストフレームワーク(ブログで発表された)ぽいので、これ使った方がいいと思います。

jbuilderからduneに変えた

dune1.0がリリースされたので、jbuilderからduneに置き換えました。
作業内容としてはそんなに修正箇所はありませんでした。

github.com

作業内容

  1. jbuildファイルの変更
    • jbuildファイルをduneにリネーム
    • 設定ファイルの変更
  2. コマンドをjbuilderからduneに変更
  3. opamファイルの変更
    • 依存しているパッケージをjbuilderからduneに変更
  4. コンパイル時のwarningメッセージを出ないように修正
    • コンパイル時のオプションが変わったのか、warningがでるようになったのでソースを修正

1. jbuildファイルの変更

jbuildファイルをduneに名前を変えた後に、S式の()を一つ外します。
duneではいらなくなったようです。

削除し忘れていても、ビルド時にメッセージで()がいらないと言われるので、すぐ直せます。

不要な()を削除する

修正前
(library (
  (public_name typesafety.misc)
  (name misc)
))
修正後
(library
  (public_name typesafety.misc)
  (name misc)
)

パラメメータの置き換え

パラメータは${}で指定できましたが、duneからは%{}に変わったようです。  

(rule (
  (targets (github_t.ml github_t.mli))
  (deps (github.atd))
  (action (run ${bin:atdgen} -t ${<}))
))
(rule
  (targets github_t.ml github_t.mli)
  (deps github.atd)
  (action (run %{bin:atdgen} -t %{deps}))
)

2. コマンドをjbuilderからduneに変更

これは単純にjbuilderをduneに置き換えるだけです。

修正前

build:
    jbuilder build

修正跡

build:
    dune build

3. opamファイルの変更

これも単純にjbuilderをduneに置き換えるだけです。

修正前

build: [
  ["jbuilder" "build" "-p" name "-j" jobs]
]
build-test: [
  ["jbuilder" "runtest" "-p" name]
]
depends: [
  "jbuilder" {build}
]

修正後

build: [
  ["dune" "build" "-p" name "-j" jobs]
]
build-test: [
  ["dune" "runtest" "-p" name]
]
depends: [
  "dune" {build}
]

4. コンパイル時のwarningメッセージを出ないように修正

使用していない式や型の定義などは、warningがでるようになりました。
これは単純に直していけばいいので、そんなに難しくないかと思います。