著者アーカイブ 杉浦

著者:杉浦

【Git】package.json上でコマンドとGitを連携させるためのhusky, lint-staged

 husky – npm
 lint-staged – npm
 huskyはGitのフックのほぼ全てをpackage.json上で定義できるパッケージです。フックできない部分はいずれもGitリポジトリを管理するサーバサイド用フックのみです。huskyのインストールと使い方は次の引用の通りです。

Install

npm install husky --save-dev
// package.json
{
  "husky": {
    "hooks": {
      "pre-commit": "npm test",
      "pre-push": "npm test",
      "...": "..."
    }
  }
}

typicode/husky: 🐶 Git hooks made easy#multiple-commands

 複数コマンドを実行するときはtypicode/husky: 🐶 Git hooks made easy#multiple-commandsにある様に、コマンド間を&&で区切るcmd && cmdかhusky個別の設定ファイルを用意します。すべてをpackage.jsonで完結させつつも、コマンドを&&で無理やり繋げない小技にnpm-run-all – npmを用いるやり方があります。次の様にあらかじめ小さいスクリプトを定義してnpm-run-allを介してまとめてhuskyで呼び出します。

{
    "scripts": {
        "php-cs-fix": "./vendor/bin/php-cs-fixer fix -vvv",
        "eslint": "./node_modules/.bin/eslint resources/**/*.{ts,js,vue} --fix",
        "php-unit-test": "php ./vendor/phpunit/phpunit/phpunit --configuration phpunit.xml tests/Unit --teamcity",
        "pre-commit": "npm-run-all eslint php-cs-fix",
        "pre-push": "npm-run-all php-unit-test"
    },
    "husky": {
        "hooks": {
            "pre-commit": "npm run pre-commit",
            "pre-push": "npm run pre-push"
        }
    }
}

 huskyだけでもコミット前のスクリプト起動漏れを防げて楽ですが、コミットのたびにフルでlintが走る上にlintで修正された分はコミット内容に含まれません。lint-stagedでこの問題を解決できます。
 lint-staged – npm
 lint-stagedはコミット用にステージングされたファイルの内、globで特定した対象のファイルについてのみ特定のコマンドを実行するパッケージです。対象ファイルはglob形式で指定します。大体*.{拡張子A,拡張子B}の様に指定するだけです。test絡みで通常のlintの対象にしたくない場合は!(*test).jsの様に!()を使います。!(*test).jsならばhogehoge.jsはlint, hogehoge.test.jsはnot lint。
 lint-staged実例として次のような設定をし、Hoge.jsとFoo.phpファイルをコミットしようとするとします。

{
    "lint-staged": {
        "*.{js,ts,vue}": [
            "eslint --fix",
            "git add"
        ],
        "*.php": [
            "php ./vendor/bin/php-cs-fixer fix -vvv --config .php_cs",
            "git add"
        ],
        "*.{json,css,scss}": [
            "prettier --write",
            "git add"
        ]
    }
}

 その様な場合、まず”*.{js,ts,vue}”の表現にマッチしたHoge.jsを引数として”eslint –fix Hoge.js”, “git add Hoge.js”が実行されます。Hoge.jsがESLintの自動修正によって修正され、Hoge.js全体がコミット対象になるわけです。これでコミット前に自動でlintが走り、lintの修正結果もコミット内に反映されます。Foo.phpでもphp-cs-fixerで同様にlintによる修正がかけられ、両方が終わった後に改めてコミットされます。
 husky, lint-staged, 各Linterを導入してpackage.jsonに次の様な記述を加えるとコミットのたびにLintが便利な形で走ってくれます。

{
    "husky": {
        "hooks": {
            "pre-commit": "lint-staged"
        }
    },
    "lint-staged": {
        "*.{js,ts,vue}": [
            "eslint --fix",
            "git add"
        ],
        "*.php": [
            "php ./vendor/bin/php-cs-fixer fix -vvv --config .php_cs",
            "git add"
        ],
        "*.{json,css,scss}": [
            "prettier --write",
            "git add"
        ]
    }
}
著者:杉浦

オブジェクトのライフサイクルから見たモデルの分割

 プログラミングにおいてモデルを作る時は、大体何か一つのモノを表すモデルを作ります。何か一つというのが曲者でして、含意の広い一つのモノを対象にモデルを作るとコードが肥大することがあります(含意すべてを入れても小さい場合や共通処理から外れない場合があって、そういう時は問題が起きなかったり)。この分割の仕方にライフサイクルの観点から分割する方法があります。 モデルはある範囲のモノを抽象化して表現します。これを具象化してオブジェクトを作成する際、オブジェクトの操作を実行した後に保存する際にそれぞれ処理が入ります。ファイルシステムやデータベースに保存されている情報を表すモデルの場合は特にこれが顕著です。下図はEric Evans. エリック・エヴァンスのドメイン駆動設計 (Kindle の位置No.3058). 翔泳社. Kindle 版. からの引用図です。

 正直、図の完成度が高いです。モデルが肥大する兆候が見えたら図の矢印ごとに分割するだけでかなり整理できます。
 記事が短いので以下例の様なものを書きます。例ではDB(データベース)には既に値があり、保存はDBに行うと考えます。上図の再構成、格納、修正の部分を取り扱います。DBの一語だけであるモデルに関することを全てモデルの一クラスに記述すると破綻しそうなのが想像できます。少なくとも、再構成の際にDBへの問い合わせ方法(FROM table、PDOなど)が必要になり、柔軟な検索条件の取り扱いも高頻度で必要になります。格納の際にもDBで行えないバリデーション、複数の関係するデータについての同期が必要になります。これらの上でモデル自体の振る舞いについても必要になります。分割が必要です。
 DBの例では問い合わせ、格納、振る舞いの三分割ができます。フレームワークが備えていることの多いORM(オブジェクト関係マッピング)においては大体、問い合わせが隠蔽されています。データベース定義とコード内容を分離させたい際にのみ生成に関する追記が必要になるでしょう。格納もDB命令部は隠蔽されがちで関係設定が定義されいれば勝手に同期してくれます。バリデーションのみが必須です。ルールが複雑化する場合、あるルール一つを表すモデルを作るのもいいでしょう。残ったのは振る舞いです。これはアクティブ状態で直に参照できない方が問題の部分であり、インスタンス化されたモデルの本質といっていい部分です。

著者:杉浦

【PHP】全く異なる複数クラスを疑似継承する黒魔術

 PHPの継承は常に単一です。これによって知るべき親クラスの情報を単純化してコードの複雑度を上げにくくしてあります。多段継承こそあれ複数経路は作れません。

class Hoge extends Foo // Foo一つを継承することはできる
class Fuga extends Foo,Bar // FooとBarを同時に継承するのはだめ

 やりかたはDI(Dependeny Injection)とマジックメソッドの応用です。

class Foo {
    public function echoFoo() {
        echo 'foo';
    }
    public function selfIntroduction() {
        echo "I'm foo.";
    }
}

class Bar {
    public function echoBar() {
        echo 'bar';
    }
    public function selfIntroduction() {
        echo "I'm bar.";
    }
}

class FooBar {
    public $foo;
    public $bar;
    
    public function __construct($foo, $bar)
    {
        // if コンストラクタでこけるのをいとわないか引数を気にしないなら:
        //     DIせず、ここで$this->foo = new Foo()の様に直接インスタンス化することもできる
        $this->foo = $foo;
        $this->bar = $bar;
    }

   public function __call($name, $arguments)
    {
        method_exists($this->foo, $name)
         ? $this->foo->$name($arguments)
         : $this->bar->$name($arguments);
    }
}

$f = new Foo();
$b = new Bar();
$fb = new FooBar($f, $b);
$fb->echoFoo();// foo
$fb->echoBar();// bar
$fb->selfIntroduction();// I'm foo.

 実行結果はコメントの通りになります。環境によってはセキュリティ問題でWARNINGが出ます。
 このようにするとFooBarインスタンスで未定義のメソッドが呼ばれたとき、DIしたfooのメソッドが存在するか確認し、fooにあればfooから実行、fooにもなければbarのメソッドを探し実行します。同様のやり方でstaticメソッド、プロパティも継承もどきができます。
 この方法を使えば簡単に全てのクラスの役割を持った神クラスを作れますが、もちろんそんな状態は作るべきでありません。複雑さに際限がなくなりコードを追うのも動作を理解するのも大変になります。

著者:杉浦

【Vue】Vuexことはじめ

Vuex は Vue.js アプリケーションのための 状態管理パターン + ライブラリです。
Vuex とは何か? | Vuex

 Vuexは上記の様にVue.jsで作成したプログラム中の状態管理を担うライブラリです。乱暴な言い方をするとVuexは管理しやすいグローバル変数を提供してくれます。Vuexのやることはコードと合わせてを見るとわかりやすいです。

// Vuexのストア(状態管理定義)の記述
export const HogeStore:Module<any, any> = {
    // 管理する状態。グローバル変数の本体的なモノ。
    // これを好き勝手弄らせないが、どこからでも参照できるようにする。
    state: { 
        foo: [],
        bar: false,
    },
    // 管理する状態を直接弄ることのできる関数群。ここにある記述以外ではstateを変更できない。
    // store等を介して、commit()関数で呼び出してのみ実行される。
    // 下記の様に思いっきり抽象化すると扱いやすい。
    mutations: {
        setFoo(state, newFoo){// state.fooを変更する
            state.foo = newFoo;
        }
    },
    // 具体的なcommitの内容を記述する。
    // コンポーネント中でstore.dispatch(アクション名)とやると直接commitするより安全安心。
    actions: {
        setFoo(context, newFoo){// state.fooを変更する.mutationsと名前がかぶってもOK
            context.commit('setFoo', newFoo);
        },
        pushFoo(context, newFoo){// state.fooに追記的な
            context.commit('setFoo', newFoo);
        },
        async resetFoo(context){// state.fooをAPI等のリポジトリから取得して改めてセット
            const newFoo = await FooRepository.get();
            context.commit('setFoo', newFoo);
        }
    }
}
// コンポーネント側の記述
// 参照の際には算術プロパティを利用すると便利
computed: {
    foo: {
      get() {
        return store.state.HogeStore.foo;
      },
      set(newFoo) {
        store.dispatch('setFoo', newFoo);
      },
    },
},
methods: {
    async reset() {
        await store.dispatch('resetFoo');// dispatch('アクション名')でactions中のメソッドを呼び出す
    }
}

 公式を読むともっと多彩なことができますが、とりあえず上記のだけでも十分に機能を発揮します。setterが厳格でgetterが自由なグローバル変数な印象を受けました。getterは追記できるため同じリソースを多角的に読みたい際も複雑さがそれほど増しません。
 Vuexを用いるタイミングですが、コンポーネント間のemitが長大になる時でしょう。例えば、あるコンポーネント群(メニューとか検索ボックス)はリソースを取得、あるコンポーネント群(表とかグラフとか)はリソースを表示、あるコンポーネント群(編集モーダルとか)はリソースを加工、とかやりだすと値の変更を4つ5つ越しのコンポーネントに伝播させる必要が出たり、その伝播のルートが3つ股4つ股になったりと煩雑になります。Vuex抜きにこれを整理しようとすると各コンポーネント群の重心であるメインコンポーネント(コンポーネントを呼び出すコンポーネント。App.vueとかPages/Fuga.vueとか)の中に処理が集中しだします。こうなるとメインコンポーネントのやっていることがまさしくVuexのやるべきことになります。
 Vuexはコンポーネント間の依存が直列や一方向の様な整然とした場合は不要ですが、そうでない時は値のやりとり最低限になりすっきりします。また純粋なTypeScriptで記述ができるため動作を明確にしやすいです。先述の例の様にVueアプリが複雑な場合、どこかしらでVuexの役割を既に担おうとしている部分があります。ソースコードが複雑化し、もうどうにもならない時はいっそ後付けでVuexを追加する良いです。

著者:杉浦

【Windows10】起動時に高頻度でブルースクリーンになる問題の対処法の一つ

 結論として、対処法の一つは高速スタートアップを切ることでした。切り方は以下の通りです。

アドレス:コントロール パネル\ハードウェアとサウンド\電源オプション\システム設定
UIを使った移動経路:コントロールパネル->ハードウェアとサウンド->電源オプション->電源ボタンの動作を選択する
移動した場所にある「高速スタートアップを有効にする(推奨)」のチェックを外す。
※チェックを外す前に「現在利用可能ではない設定を変更します」のメッセージをクリックする必要がある場合があります。


 これをするとPCが健康になる場合があります。勝手な想像ですが、高速スタートアップをするべきでないPCスペックであるにも関わらず高速スタートアップをしようとしていたのでしょう。
 以下、解決までの道筋です。
 今回起きていた問題は題にある通り、起動時に高頻度でブルースクリーンになる、というものでした。今回の問題の厄介な点はブルースクリーン時に表示されるエラーコードが多様だった点にありました。例えば、SYSTEM_THREAD_EXCEPTION_NOT_HANDLED、MEMORY_MANAGEMENT、BAD_POOL_CALLERです。このためエラーコードをググってピンポイントな解決を見つけることができませんでした。起きているエラーについて詳しく調べることでGoogle先生の提案する解決策を絞り込めます。エラーについて詳しく調べるにはWindowsのログを追うことが一つのやり方です。
 Windowsのログはコンピュータの管理のイベントビューアーから見ることができます。ここからエラー、警告、重大といった問題の起きそうなイベントのログを見ます。今回ならばとりあえず終了時の問題か、起動時の問題かの二択が想像でき、どちらの問題か調べようとしました。このログを見る限り、起動時に問題が発火、大炎上しています。

 これの中を読んでググってとしていくと起きていることがエラーコードのみよりもずっと深くわかります。今回は起点が常に高速スタートアップの失敗で、まさにそれが原因でした。(下図の様に高速スタートアップの失敗がいつも起きていた。そこから派生していくイベントのどこかしらで決定的な破綻が発生。)
 

 後は”高速スタートアップ”でググって予測で出てきた”高速スタートアップ 無効”で提案されたやり方を適用して処置完了です。
 処置後の経過をざっくり見るには信頼性モニター(コントロール パネル\システムとセキュリティ\セキュリティとメンテナンス\信頼性モニター)が便利です。以下の様に起きたイベントからPCの状態を評価、表示してくれます。処置後は回復に向かっており、一度も起動時のブルースクリーンは起きていません。

著者:杉浦

【Git】git-submoduleことはじめ

 Git自体のパッケージマネージャの様な機能としてgit-submoduleがあります。git-submoduleはgitコマンドの一つでgitを導入すれば勝手にくっついてきます。
 Git – git-submodule Documentation
 git submoduleはGitリポジトリの中にGitリポジトリをクローンする機能です。これを使えばバージョン固定や定期的なコピペから解放されます。ある一つのDocker設定を用いたり、開発プロジェクトで用いない言語のライブラリと連携したりする時に特に便利です。

 基本のコマンドは二つ覚えておけば大丈夫です。git submodule add [cloneする時とかのURL] [保存先ディレクトリ]でリポジトリを追加、git submodule updateでリポジトリをまとめて更新、この二つだけです。
 Gitリポジトリの中にGitリポジトリがあると言ってもいいくらいなのでGitコマンドの対応が大体用意されています。ブランチの切り替えで安定版、開発版の切り替えも楽々です。

著者:杉浦

【PHP】PHP言語開発者らのメールを覗く

 PHPはオープンソースで開発されている言語で開発者間の相談、提案などのやり方の一つとしてメーリングリストが使われています。
 PHP: メーリングリスト – Manual
 このメーリングリストの内のいくつかはニュースの様に扱われており、言語開発用メーリングリストであるinternals listもそこに連なっています。
 PHP Mailing Lists (PHP News)
 php.internals mailing list
 ここから直読みするのもありなのですが、時系列順固定でろくな絞り込みもないので特定のモノを探すのはなかなか辛いです。これに対応するためか外部からinternalの中身を読むためのページである#externalsが公開されています。
 #externals – Opening PHP’s #internals to the outside
 #externalsからならばメールの検索も容易で、スレッド形式のためやり取りを追うのも簡単です。GitHubと連携すれば読んだ、読んでないの履歴もついてきます。
 
 PHPのマニュアルにないような実装に関わることでも、ここから追っていけばそれなりに詳しい内実がわかります。

著者:杉浦

【GitHub】サンプルコードの付属していないライブラリのサンプルコードの探し方

 npmやらcomposerやらpipやら最近のプログラミングではパッケージマネージャを用いて簡単に外部ライブラリを扱うことができます。簡単に扱えるようにできるのですが、目的に合わせた使い方をするためには可能ならば中身。少なくとも使い方を把握する必要があります。大体は説明ドキュメントぐらいあるのですが、実際に動くサンプルコードとなるとないこともままあります。そういった時はGitHubの中を探して、すぐに動かせるサンプルコードを見つけて、動かしながら確かめます。
 GitHubの検索機能ではいくつかの単位でGitHubに存在するモノを探せます。
 見つけたいのはあるライブラリを実際に用いているプロジェクトのコードです。このためコード単位で直接ライブラリ名を打ち込むだけで近い対象がヒットします。例えば、leaflet(JavaScriptの地図用ライブラリ)なら次のようになります。

 AltJSなどの拡張言語の場合における使い方を探すならば言語指定を行います。URLのGETパラメータにl=vueなどとl={言語の代名詞(拡張子とか)}を入れると更に絞り込まれ具体的になります。

 この方法を用いるとcommit数1の様なメモ帳代わりのポジトリも見つけられ中々助かります。JavaScriptの場合、package.jsonの中を探すつもりで組み合わせたいライブラリ名とjson形式コード指定で検索するというやり方もあります。他言語でも設定ファイルで同様のことができます。

著者:杉浦

【Vue.js】Vue.extend()でVue.jsらしい記述のままTypeScriptを適用する

 Vue.jsは素のwebページ用コード(HTML, JavaScript, CSS)の雰囲気を保ったままいい感じに動いてくれるJavaScriptフレームワークです。特に単一ファイルコンポーネントはHTMLをHTMLのまま、VueオブジェクトをVueオブジェクトのまま、CSSをCSSのまま表現してまとまったデザインのコンポーネントを作り上げます。

<template>
  <p>{{ greeting }} World!</p>
</template>

<script>
module.exports = {
  data: function () {
    return {
      greeting: 'Hello'
    }
  }
}
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>

単一ファイルコンポーネント — Vue.js
 これは便利なのですが、TypeScriptとはどうにも相性が悪いです。少なくとも2点問題点がありそれはJavaScriptコード部で定義された値をテンプレートに当てはめた時に型定義を持ち越すのが難しい点とJavaScript部の定義がほとんどが素朴なオブジェクトのためTypeScriptの型構文を記述する余地が少ない点です。
 上記の問題を解決する方法の一つとしてvue-property-decoratorがあります。下記の引用の様にclass構文と@以下によるデコレートでVueコンポーネントであることを表現します。

    @Component
    class Test extends Vue {
      @Prop(Number) [propertyName]!: number // @Propでpropsの一つであることを表現
    }

 kaorun343/vue-property-decorator: Vue.js and Property Decorator
 これはこれでありですし言語実装としては簡単(何がどこに現れるのか、どこからどこまでが区切りなのか厳密)なのでしょうが、Vue.jsからいくらか離れた記法を用いる点でIDE、ESlint、storybookなどと組み合わせることと機能の把握に面倒が起きます。こうなるならばReactを使って新たにJavaScriptでまとめ切る方が便利な気がします。
 vue-property-decoratorの難点を解決する方法としてVue.extend()というやり方があります。これはvue-property-decoratorからいくらか後の実装で追加されたようです(また聞きで一次ソース未確認)。
 API — Vue.js#Vue-extend
 このやり方とTypeScriptを組み合わせると次のように記述できます。素の状態では素朴なオブジェクトしか返せませんでしたがVue.extendの引数としての型であるComponentOptionsを得ました。

<template>
  <p>{{ greetingToTarget }}</p>
</template>

<script lang="ts">
import Vue, {ComponentOptions, PropType} from 'vue';

type HelloWorldDataType = {
    greeting: string,
    modalLevel: string,
}
export default Vue.extend({
  props: {
    target: {type: String as PropType<string>, required: true},
  },
  data: function (): HelloWorldDataType {
    return {
      greeting: 'Hello' // data全体の返り値の型を決めるのでなく、ここでas stringとするのも手
    }
  },
    computed: {
      greetingToTarget():string {
        return `${this.greeting} ${this.target}!`;
      }
  },
} as ComponentOptions);
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>

 上述のようにするとIDEの型に関する機能や予測補完が強く働きだし便利な上、想定外の挙動をした時には厳しく警告を飛ばしてくれます。

 webpackのプロダクト用変換などの方法で純粋なJavaScriptに圧縮変換するとなんやかんや動くJavaScriptの特徴も復活させられます。

著者:杉浦

【TypeScript】TypeScriptの型定義の仕方

 TypeScriptはJavaScriptの拡張言語です。Microsoftがメインのコントリビューターでサポートも充実しています。TypeScriptはAltJSの中でも穏やかな方でJavaScriptに独自の型定義機能と変数、プロパティと結合した型宣言機能といった型関連の機能を付けただけです。変化が少なく汎用的なためかVue.js+TypeScriptやRact+TypeScriptといったフレームワークとの結合を解決するパッケージも盛んに開発されています。型が付くことによりIDEの予測や補完、バグの潜在性アラートなどが強力に働きます。

TypeScript – JavaScript that scales.
microsoft/TypeScript: TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

 始めるならQuick Start · TypeScript試すならTypeScript playgroundがやりやすかったです。特にクイックスタートでは各パターンのテンプレがGitリポジトリに用意されており、それをひな型に手を動かすだけで慣れていけます。
 TypeScriptは基本的に変数、プロパティ、関数の宣言時に宣言部の後に:をつけて型を決めます。

const a: number = 1;

function hoge(): string {
    return 'hoge'
}

class Bar {
}

interface FooBar {
    foo: Date;
    bar: Bar;
}

class Fuga implements FooBar {
    foo!: Date;
    // !はnullになることはないとTypeScriptに伝える.
    // フレームワーク中のコンストラクタっぽいもので初期化される時とかに便利.
    bar: Bar;

    constructor(bar: Bar) {
        this.bar = bar;
    }
}
type StringOrStrings = string|string[];
const char: StringOrStrings = 'ch';
const chars: StringOrStrings = ['c', 'h'];

 この様にすると、それぞれの部分に型が定義され値の型を追うことができます。上記のみならずEnum等さらに拡張することもできます。
 厳密な型定義から外れますが利便性のために型定義を変数を介さず文中に入れたい時があります。そういった時は次のようにします。

(document.head.querySelector('meta[name="csrf-token"]') as HTMLMetaElement).content

 この様にすると括弧でくくった部分の型を定義できます。セレクタで特定の種類の要素(metaタグ、inputタグなど)を参照するときなど特に便利です。