type holyshared = Engineer<mixed>

技術的なことなど色々

久しぶりにスニーカー買った

ここ数年は革靴(ドレスシューズ / ワークブーツ)ばかり履いていてスニーカーを履いてなかった。
それはそれでいいとして、あまりにも履いていないのでスニーカーの履き心地どん感じか忘れてしまっていた。

なのでスニーカーを買うことにしたわけです。

BROTHER BRIDGE model: BERLIN
BERLIN

はい、名作のジャーマントレーナーです。
いくつかのブランドがサンプリングしていますが、私は魂を売っているBROTHER BRIDGEさんのBERLINを買いました。
ソール交換ができるのやっぱりいいですね、長く履きたいし。

BROTHER BRIDGEさんはずっと気に入っているブランドです。
いつも浅草のFLAGSHIP STOREで買っています。

履いた感想としては、軽くて屈曲性がメチャクチャいいですね。
グリップ力もあるので、グイグイ足が進みます。

ブーツはガツガツ歩く感じですが、また変わった履き心地です。

プリズナージャケットとパンツの経年変化

何回かWAREHOUSEから出ているプリズナージャケット & プリズナーパンツ。
これはJUNKY STYLE別注モデルで、ボーダーの染める回数を増やして黒になるようになっているやつです。

1年ちょっとの着用で、経年変化の記録ために写真を撮っていたのを忘れてた。

プリズナージャケット

パンツより先に買ったはず。
多少色が薄くなっているような気がする。

プリズナージャケット(WAREHOUSE / JUNKY STYLE別注)
プリズナージャケット

f:id:holyshared:20200402163240j:plain
色落ちの具合

プリズナーパンツ

普段30inchのパンツを着用することが多いのだけど、これは31inch。
ノンウォッシュのものを買ったので、洗ったらメチャクチャ縮んだのを覚えている。

これもちょっと色が落ちた気がする。

プリズナーパンツ (WAREHOUSE / JUNKY STYLE)
プリズナーパンツ

パンツの色落ち具合

上下セットで着ることはあまりないけど、囚人服はいいですね。
ボーダー柄じゃないやつもあるし。

Mongooseドキュメントのserialize/deserialize

Mongooseを利用している場合、読み込んだドキュメントをserialize/deserializeしたい場合があると思います。
serializeは雑にtoJSON、でいいと思うのですがdeserializeはどうするのか?

一つのやり方として、hydrateメソッドを使用するやり方があります。

const user = await User({
  name: 'name',
  password: 'password',
  createdAt: new Date(),
  updatedAt: new Date()
});
await user.save();

// JSONからUserドキュメントに戻す
const jsonOfUser = await user.toJSON();
const hydratedUser = User.hydrate(jsonOfUser);

console.log(hydratedUser); // User { name: ... }

ここで気になるのがhydrateした場合は、永続化済みかどうかのコンテキストがわかるのかという疑問があります。
通常はドキュメントのisNewプロパティ調べられますが、hydrateした場合はどうかということです。

検証してみる

試してみた結果はisNewプロパティがfalseになっていることがわかりました。

const user = await User({
  name: 'name',
  password: 'password',
  createdAt: new Date(),
  updatedAt: new Date()
});
console.log(user.isNew); // まだ永続化していないので、true
await user.save();
console.log(user.isNew); // 永続化したのでfalse

const jsonOfUser = await user.toJSON();
const hydratedUser = User.hydrate(jsonOfUser);

console.log(hydratedUser.isNew); // falseが返ってくるが根拠はなんだろうか?

しかし、isNewがfalseになる根拠はなんでしょうか?
これはMongooseのソースを読まないとわからないのでみてみます。(バージョンはv5.8.9)

// model.js
Model.hydrate = function(obj) {
  _checkContext(this, 'hydrate');

  const model = require('./queryhelpers').createModel(this, obj);
  model.init(obj);
  return model;
};

queryhelpersを読み込み、createModelを実行してモデルを初期化しているようです。
引数にisNewオプションを指定していますね。
なので、hydrateでドキュメントに戻した場合は強制的にisNewがfalseになります。

// queryhelpers.js
return new model(undefined, fields, {
  skipId: true,
  isNew: false,
  willInit: true
});

当たり前といえば当たり前ですね。
てっきりhydrateを使用する際の引数のオブジェクトにObjectIdが指定されていれば、永続化済みの状態にするのかと思っていたのですが そんなことはなかったです。

netlifyのNodejsのバージョン

netlifyを利用していて、デプロイ時のNodejsのバージョンを指定したいので調べてみました。
ドキュメントでは下記のやり方があるようです。

docs.netlify.com

  1. 環境変数のNODE_VERSIONで指定する。
  2. .node-version または .nvmrcをリポジトリのルートに置く。

環境変数はバージョンを切り変える際に変更するのが面倒なので使用しないことにしました。
なので.node-versionを置くことにしました。

というのも、自分は普段ローカルではnodenvを使用しているので、.node-verisonを置くだけでバージョンの切り替えができて都合がよかったからです。

バージョンあげるのも.node-verisonを書き換えるだけで済みますし、非常に楽ができます。

2019年の靴の着用回数を集計した

2018年から引き続き履いているもの、買い足したもの、手放したものを集計対象にしています。
昨年の末くらいから集計できるように年間のレポートを表示できる仕組みをTop | 365shoesに追加していました。

2019年は520件を投稿していたようです。
一つの靴について、だいたい年間で15回ぐらいの着用、サンダルのみ使用頻度が高いので、倍ぐらいの着用回数になっています。

靴は1年間で10足増えて、5足手放し、合計で28足になりました。

集計のレポートはまたちょっと変える予定です。

2018年から引き続き履いているもの

No. ブランド モデル 2019年までの着用回数 2019年の着用回数
1 Tokyo Sandals DOBULE MONK SANDAL 90 59
2 Rolling dub trio ROOTS 33 29
3 Chippewa Suburban 19 16
4 Pistorelo Norwogian Split 23 16
5 Berwick Chukka boots 27 17
6 Redwing Irish Setter 29 19
7 Redwing Irish Setter 30 19
8 SLOW WEAR LION OB-8208G オイルドレザーサイドゴアブーツ 29 18
9 SLOW WEAR LION OB-8593GT 栃木レザープレーンミッドブーツ 28 17
10 SLOW WEAR LION OB-8593G オイルドレザープレーンMIDブーツ 26 16
11 Chippewa Bridgeman 25 16
12 Chippewa Bridgeman 26 17
13 Chippewa 6inch Service Boots 29 18
14 Chippewa 6inch Service Boots 28 17
15 BROTHER BRIDGE McCLOUD 29 17
16 BROTHER BRIDGE JAMES 29 20
17 Hunter Chelsea Boots 30 15
18 Nicks 6inch HotShot 29 18

買い足したもの

オーダーしたWescoが届いたり、サンダルをオーダーしたり、Redwingのブーツ買ったり色々ありました。
イギリスの靴が増えましたね。

No. ブランド モデル 2019年までの着用回数 2019年の着用回数
1 Berwick Chukka boots 2 2
2 Crockett&Jones CONISTON 5 5
3 BROTHER BRIDGE Morgan 5 5
4 Redwing 1930s Sport Boot 7 7
5 Scotch Grain Spider 9 9
6 Wesco Custom Jobmaster 8 8
7 Sanders Military Apron Derby Boots 11 11
8 Berwick Wing tip 16 16
9 Tokyo Sandals Enginier Slip on 35 35
10 RAMSEY 442 MILITARY CAP TOE OXFORD 31 31

手放したもの

昨年は住んでいる上の階で水漏れがあり、リビングで水が垂れ始めたので対応する場所を確保するため泣く泣く手放しました。
おかげで、ブーツフェスティバルにいけませんでした。

No. ブランド モデル 2019年までの着用回数 2019年の着用回数
1 Timberland Earthkeepers 6inch Boots 12 5
2 Timberland 3eye Classic Lug 7 2
3 Timberland 3eye Classic Lug 12 5
4 Hunter Original festival chelsea boots 22 9
5 Timberland 6inch Premium Boots 11 5

OCamlのパッケージインストール時にファイルをコピーする

OCamlプログラムをduneを使用してビルドしている場合、インストールする時にバイナリファイル以外のファイルも一緒にコピーしたい場合があると思います。

それをopam、duneを使用している時にどのように行うか調べました。

Duneの設定

設定はduneファイルにStanzaの設定を追加するだけです。 dune.readthedocs.io

filesにコピーしたいファイル、sectionにコピー先、packageにパッケージ名を指定します。
パッケージ管理にopamを利用している場合は、/[ホームディレクトリ]/.opam/[コンパイラバージョン]/[Sectionで指定したカテゴリ]/[パッケージ名] にファイルがコピーされます。

具体的には /Users/holyshared/.opam/4.09.0/share/ogenになります。

(install
  (files ../templates/opam.mustache ../templates/default.mk)
  (section share)
  (package my_package)
)

大抵の場合はexecutable, executables stanzaのいずれかと一緒に指定する事になると思います。

(executables
  (names main)
  (public_names ogen)
  (libraries ogen cmdliner)
)

(install
  (files ../templates/opam.mustache ../templates/default.mk)
  (section share)
  (package my_package)
)

プログラムからのファイルの参照

インストール時にコピーしたファイルを読み込んだりしたい時、opamの場合はパスを環境変数のOPAM_SWITCH_PREFIXを利用して解決します。

OPAM_SWITCH_PREFIX/[ホームディレクトリ]/.opam/[コンパイラバージョン]/ になります。
これはopamでコンパイラのバージョン切り替えたりしても正しく切り替わります。

この環境変数は /[ホームディレクトリ]/.opam/opam-init/variables.sh に定義されています。
後はプログラムで Sys.getenv_opt などを利用してパスを取得します。

let prefix = match Sys.getenv_opt "OPAM_SWITCH_PREFIX" with
  | Some v -> v
  | None -> ""

パッケージのインストール

パッケージのインストールはビルドした後に、installコマンドを実行することでローカルにインストールできます。

dune build
dune install

テスト容易性設計のやつ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では動かないと思います。