著者アーカイブ 杉浦

著者:杉浦

【GitHub】目的に合ったスニペットをフレームワークの観点から探す

 GitHubの検索機能には様々なオプションが付いています。その中の一つにCode optionsのIn this pathがあります。これはその名の通りコードファイルが置いてあるファイルパスで絞込検索をするオプションです。
 フレームワークには大なり小なり型にはまった作法があります。よくある作法の種類に、特定の種類のコードを特定のフォルダに配置する、というものがあります。Laravelのコンソールコマンドならばapp/Console/Commands、Composerを使うなら外部ライブラリはvendor、といった具合です。
 Artisanコンソール 6.x Laravel
 この二つを合わせると特定の種類のコードと果たしたい目的の語で検索をすることができます。例えば、次のようなことができます。FuelPHPにはコンソールコマンドoilが用意されています。oilのサブコマンドとして様々なことをコンソール上で行えます。
はじめに – Oil パッケージ – FuelPHP ドキュメント
 oilは便利なのですがルーティングを確認する機能がありません。Laravelのartisan route:listの様にコンソールで一覧を見たいものです。GitHubで次のようにググるとそれを実現したコードが見つかります。

route path:fuel/app/tasks


 このコードらをたどっていくとリポジトリkenjis/fuelphp-tools: Tools for FuelPHP 1.xにたどり着きます。このリポジトリのtaskコードの一つにルーティングをコマンドライン上で表示する機能が入っています(残念ながらmodule以下のルーティング解析は未実装です)。
 このおかげで次の様にroutingの大部分をいい感じに見れるようになりました。

著者:杉浦

PlantUMLの機能が思ったより多彩

 PlantUMLはUML図を表現できる言語です。言語故にテキストファイルであり、変更やGit管理が楽で便利です。
 PlantUMLを書いていると、あまりに図が複雑になるため複数ファイルに記述を分けたい時があります。そういった時、何度も同じ意味で同じ名前を宣言する記述が重複しがちです。そういった問題はinclude機能で定義ファイルの読み込みのみを記述することで解決できます。
 Use the preprocessorのIncluding files or URL

// define.puml
@startuml
participant "ブラウザ" as br
participant "サーバ" as svr
@enduml

// child.puml
@startuml
!include define.puml
br-->svr: ページ要求
@enduml

// child2.puml
@startuml
!include define.puml
br<--svr: ページレスポンス
@enduml


 こうすると、あるファイルだけ違う変数名で同じものを指している、という混乱やコピペコードを防げます。
 デフォルトのPlantUMLでER図を書いている時、定義の記述を一行にきれいなデザインでまとめることにくたびれます。デフォルトで等幅フォントを採用しておらず、空白を使って縦を合わせるのがめちゃくちゃしんどいです。等幅フォントを使うように宣言しましょう。
 Which fonts are available ?

@startuml
skinparam {
  defaultFontName Monospaced
}

entity users {
  # id                  int(11) unsigned auto_increment
    name                varchar(255)         not null,
    login_id            varchar(50)          not null,
    password            varchar(255)         not null,
    password_changed_at datetime             not null,
    email               varchar(255)         not null,
    last_login          varchar(25)          not null,
    login_failed        tinyint              not null,
    locked              tinyint              not null,
    locked_at           int                  not null,
    status              tinyint    default 0 not null,
    login_hash          varchar(255)         not null,
    created_at          int        default 0 not null,
    updated_at          int        default 0 not null,
    deleted             tinyint(1) default 0 not null,
}

@enduml


 ここでは特に使うと確信した二例を紹介しましたが、公式の前処理、共通事項をはじめとしたUMLから離れた欄を隅まで見ていくと思わず教えて欲しかったとなる便利機能がわいてきます。
 シンプルなテキストファイルで UML が書ける、オープンソースのツール

著者:杉浦

KISS原則のためのデザインパターン

 デザインパターンは今までに至るまで作られてきたコーディングの際のパターンです。大体これは複数ファイルにまたがるつくりになっており、とてもとても複雑な問題をなるべくシンプルにされたパターンを知るだけで解決できるようにします。図は増補改訂版Java言語で学ぶデザインパターン入門 | 結城 浩 |本 | 通販 | Amazonから引用したFacadeパターンのクラス図です。

 KISS原則はKeep is simple stupidの略で「単純な作りを保て」という意味です。短くて意味が明確だと読み解きやすいものです。原則というだけあり、デザインパターンもとてもとても複雑な問題に対してこの原則を実現するために作られていることが多いです。
 KISSの原則 – Wikipedia
 デザインパターンの実現よりも原則の実現を優先すべきです。語弊がある文なので補足ですがコーディングで最も優先すべきは要件の実現です。動くきれいなコード>動く汚いコード>動かないきれいなコード、です。
 Gitのソースコードでも業務範囲でもこの二つについてのアンチパターンに本末転倒なパターンが時折見られます。簡単な作りで済むのにデザインパターンを適用したばかりに読み解くのが面倒になります。例えば、Facadeパターンなら次のようなものです。

// 極端にやってしまったパターン
function client() {
    $fasade = new Fasade()
    $fasade->echoA();
    $fasade->echoB();
    $fasade->echoC();
    $fasade->echoD();
}

class Fasade {
    use EchoA, EchoB, EchoC, EchoD;
}

trait EchoA {
    function echoA() {
        echo 'a';
    }
}
trait EchoB {
    function echoB() {
        echo 'b';
    }
}
trait EchoC {
    function echoC() {
        echo 'c';
    }
}
trait EchoD {
    function echoD() {
        echo 'd';
    }
}
// これで十分だったパターン
function client() {
    echo 'a';
    echo 'b';
    echo 'c';
    echo 'd';
}

 無暗にクラスを分けたせいで複雑化しています。例は極端ですがアクティブレコードのみで十分なのに、コントローラ->コマンド->リポジトリ->モデル->SQL文発行、の様な段階が過度に多いものはままあります。簡単な問題を複雑にしていないか、本当にそこまで大がかりなものを用意する必要があるのか、都度考えてコーディングしたいものです。

著者:杉浦

【JavaScript】配列関数を使った読みやすいオブジェクト生成の書き方

 次の様な入出力を実現する関数dictMergeをなるべく読みやすく短い関数で実現するのが目標です。dictMergeは未定義や余分な定義を含む複数の辞書から必要なカテゴリーの語のみをまとめるための関数です。たとえなので辞書ですが今回紹介する配列、オブジェクト操作のようなやり方は色々な場面で使えます。

// 入力
const categoryKeys = ['hoge', 'fuga', 'hogefuga'];
const dictA = {
	hoge : ['hoge1', 'hoge3', 'hoge5'],
	fuga : ['fuga3', 'fuga7', 'fuga13'],
}
const dictB = {
	hoge : ['hoge7', 'hoge9', 'hoge11'],
	bar : ['bar2', 'bar4', 'bar6'],
}
const dictC = {
	hoge : [],
	fuga : ['fuga19', 'fuga29', 'fuga37'],
	hogefuga : ['hogefuga'],
}

// 出力
const mergedDict = dictMerge(categoryKeys, dictA, dictB, dictC);
console.log(mergedDict);
/*
{
	hoge : ['hoge1', 'hoge3', 'hoge5', 'hoge7', 'hoge9', 'hoge11'],
	fuga : ['fuga3', 'fuga7', 'fuga13', 'fuga19', 'fuga29', 'fuga37']
	hogefuga : ['hogefuga']
}
*/

 実装したdictMerge()が次になります。コードの細部はコメントの通りです。

/**
 * @param {Array} categoryKeys
 * @param {Array} dicts
 * @return {Object}
 */
function dictMerge(categoryKeys, ...dicts) { // 第二引数以下を変数dictに配列として格納する. 今回ならdicts = [dictA, dictB, dictC]
    return categoryKeys.reduce((mergedDict, category) => { // reduce関数で配列を一つのオブジェクトに変換する。詳しくは下記
        return {
            ...mergedDict, // スプレッド構文でreduceの前のループ結果を引き継がせる
            [category]: dicts.map(dict => dict[category]) // [category]で変数をキー名にする。map関数で扱う配列を[dictA[category], dictB[category], dictC[category]]にする
                .flat() // 配列の中身を一段flatにする。例えば[Array(3),Array(3),Array(0)]の二次元配列がArray(6)の一次元配列になる
                .filter(v => v !== undefined)// 未定義でない値のみを配列の中身に残す
        }
    }, {});
}

 上記の様にJavascriptに用意されている関数をチェーンするだけで少々複雑な問題も簡易な記述で解けます。この様なやり方を用いる際に注意する必要があるのはreduce関数です。reduce関数はJavaScriptの配列操作においてとても汎用的であり、書こうとさえ思えばほぼ全ての配列操作関数をreduce関数で表現できます。これを使うと次のように黒魔術的もかくやというコードを書けます。

// dictMergeと等価
function dictMergeByReduce(categoryKeys, ...dicts) {
    return categoryKeys.reduce((mergedDict, category) => {
        return {
            ...mergedDict,
            [category]: dicts.reduce((acc, value, index) => {
                acc[index] = value[category];
                return acc;
            }, [])
                .reduce((acc, value) => acc.concat(value), [])
                .reduce((acc, value) => {
                    if (value !== undefined) {
                        acc.push(value);
                    }
                    return acc;
                }, [])
        }
    }, {});
}

 なるべくreduceを使わず特化的な名前の少ない引数の配列関数を使うとわかりやすいコードになります。最初の例も正直reduceを使うまでもありません。reduce((o, k)=>{return {…o, [k]:kに関する何か}},object型引数)という構文は便利なのですが慣れない人に渡すとけっこう混乱します。注意するべきでしょう。

// 多くの人がわかりやすく読みやすいであろう版
function dictMergeSimple(categoryKeys, ...dicts) {
    const mergedDict = {};
    categoryKeys.forEach(category => {
        mergedDict[category] = dicts.map(dict => dict[category])
                .flat()
                .filter(v => v !== undefined)   
    });
    
    return mergedDict;
}
著者:杉浦

PlantUMLをwebページ的にダンプする

 具体的には各UML図をハイパーリンクによってつなげたSVGファイルにしてブラウザ上で見れるようにします。目的はアクティビティ図->シーケンス図、ER図全体->ER図部分の様な関連した別UML図を閲覧者が探しやすくすることです(WWWの元ネタそのまま)。この様にできるとPUMLを一つの場所に過度に巨大に濃密に記述する必要がなくなります。
 実際にこれを行うための手順は、PlantUMLを記述する際に出力状態のSVGリンクを相対パスで作る、階層構造を維持したままSVGにダンプする、の2ステップだけです。これによってGIFの様にできます。

 PlantUMLに次のように書くことで図のようにハイパーリンク付きの文字列を作れます。使える場所はかなり広く文字列を表示する場所ならどこでもOKな印象です。今回はpumlファイルのあるフォルダと同じ場所にsvgファイルを展開するつもりなので、記述中のpumlファイルの場所からみた特定のpumlファイルへの相対パスを記述します。

:[[greeting/HelloWorld.svg Hello world]];

 特定のディレクトリ下にsvgとしてpumlをダンプするコマンドは次になります。

java -jar plantuml.jar ./**.puml -tsvg -charset utf-8

 現在のディレクトリ以下の.pumlファイル全てを文字コードutf-8のSVGにして.pumlのあるフォルダと同じ場所にダンプします。これだけでできます。特定フォルダ以下に出力するには-oオプションを用います

java -jar plantuml.jar ./**.puml -tsvg -charset utf-8 -o svg

ならば./svg以下にSVGファイルが出力されます。

著者:杉浦

GitHubを使って一般的な変数名を選ぶ

 学生時代、分かりやすい論文を書くために気にするべきことの点として「一般的な語を選ぶ」という点がありました。ここでいう一般的とは、対象の界隈(論文なら学術分野)で広く使われていて共通認識が存在する、ということです。「一般的な語を選ぶ」とは、角速度センサとジャイロセンサの様な同じものを指す語の内より多く使われている方を選ぶ、といった感じの行為です。このどれが最も多く使われているかを簡易に調べる方法としてググった際のヒット件数の比較があります。界隈を限定するべき語(プログラミング言語のRubyと宝石のRubyを分けて検索したいとか)の場合は検索する語を追加したりGoogle Scholarまで行ったりします。先ほどの例ならばジャイロセンサの方が多く使われているので、角速度を計測するセンサを表す時は”ジャイロセンサ”を使うと判断できます。

 コーディングの際に気にするべきことに可読性、検索容易性があります。中長期の開発や機能改善の際にはいくらか忘却したコードを改めて読む必要があります。この時に変数名、クラス名などの様々な値の名前から値の意味が理解できない場合、コードを読み解くのに時間がかかります。また、ある機能について調べる時に値の名前から探そうとするもあり、一般的でない名前が使われているとまともに検索できません。次のツイートは極端な例ですが似たようなことは簡単に起きます。


 この様な名前の問題を避けるためにリーダブルコードでは次の様に指南されています。

名前をつけるときには、それが変数であっても、関数であっても、クラスであっても、同じ原則を当てはめることができる。名前は短いコメントだと思えばいい。短くてもいい名前をつければ、それだけ多くの情報を伝えることができる。プログラムに使われる名前というのはハッキリしないものが多い。例えば、tmpなんかがそうだ。でも、sizeやgetみたいに一見すると問題がなさそうな名前であっても、情報が含まれていないことがある。これから情報を詰め込んだ名前のつけ方を紹介する。本章は、以下の6つのテーマで構成されている。
– 明確な単語を選ぶ
– 汎用的な名前を避ける(あるいは、使う状況を選ぶ)
– 抽象的な名前よりも具体的な名前を使う
– 接尾辞や接頭辞を使って情報を追加する
– 名前の長さを決める
– 名前のフォーマットで情報を伝える

Dustin Boswell; Trevor Foucher. リーダブルコード (Kindle の位置No.228-232). 株式会社オライリー・ジャパン. Kindle 版.

 共通認識の成り立っていると仮定できるくらい一般的な名前ならば、少ない文字数で「明確な単語を選ぶ」「汎用的な名前を避ける(あるいは、使う状況を選ぶ)」「抽象的な名前よりも具体的な名前を使う」の三つが満たせます。また「接尾辞や接頭辞を使って情報を追加する」をした際に過度に長い名前ができあがらなくなります。
 論文における一般的な語の選択の様に、コーディングにおける一般的な名前を選択しようとするならば、コーディング界隈から使用されている数を調べる必要があります。この時、Googleは程々にしかあてになりません。Googleで検索できるのはweb上のコンテンツ全てでありソースコードから離れています。代わりにGitHubの様なソースコードリポジトリを用いることができます。特にGitHubならば言語、フレームワークでソースコードを絞り込むこともでき、より適した範囲から語の検索ができます。
 例えばJavaScript界隈で緯度経度を変数にするならばlat, lngにしておくのが適切だと分かります。

著者:杉浦

【Vue.js】コンポーネント外で定義した値をテンプレート内で使う

 Vue.jsの単一ファイルコンポーネントはコンポーネントを構成するHTMLテンプレート、JavaScript、CSSを一ファイルにまとめる仕組みです。コンポーネントの構成要素全体を把握しやすい点で優秀なのですが、テンプレート部で扱うことのできる変数が若干不透明で時として直感的でない振る舞いをします。
 次の例ではコンポーネント内全体がスコープっぽいところで変数hogeStrを宣言してテンプレート内で使用しようとしますが、これはhogeStrが未定義扱いされます。

<template>
  <div>{{ hogeStr }}</div>
</template>

<script lang="ts">
const hogeStr = 'hoge';

import Vue from 'vue';
export default {};
</script>

 実際に扱うときにはVueインスタンスの内部に含まれなければいけません。変化のない定数でも必須です。そのため実際は次のようにします。

<template>
  <div>{{ hogeStr }}</div>
</template>

<script lang="ts">
const hogeStr = 'hoge';
import fuga from 'fuga';

import Vue from 'vue';
export default {
  data() {
    return {
      hogeStr // 変化する値はdata
    };
  },
  computed: {
    nearMsgEnum, // 変化せず、getterのみでいいならcomputed
  },
};
</script>

 単一ファイルコンポーネントの作りではテンプレート、JavaScript、CSSがタグごとに分かれますが、結局のところtemplateタグもVueインスタンスの一部でありVueインスタンス外部を見るスコープにないようです。
 余談ですがdata, computedで同じ名前の値を読み込んでもVue.jsは動き続けます。外部から値を読み込むように作り替えた時に想定外の挙動が起きたならば、これが起きているのではないかと疑えます。

著者:杉浦

【Vue.js】スプレッド構文と外部定義によるコンポーネントファイルの縮小化

 Vue.jsの単一ファイルコンポーネントはコンポーネントを構成するHTMLテンプレート、JavaScript、CSSを一ファイルにまとめる仕組みです。コンポーネントの構成要素全体を把握しやすい点で優秀なのですが、コンポーネントの構成要素全体を含むが故にファイルの肥大化を招きがちです。よくある肥大化を防ぐ方法の一つはコンポーネントに分割する粒度を細かくしていく方法です。理解しやすい方法なのでこれができるならこちらの方がいいでしょう。しかし分割困難かつ複雑なロジックを持つコンポーネントが現れる時があります。この状況に対応するため、ロジックを外部ファイルに定義、コンポーネントはロジックを呼び出すのみ、呼び出し方はスプレッド構文で安全に簡単に記述、という方法を考えます。
 スプレッド構文はJavaScriptの記法の一つです。正直滅多に使いません。
スプレッド構文 – JavaScript | MDN
 ここではObjectに関するスプレッド構文を扱います。動作は次の通り。public property限定の継承のといってもそう間違っていない印象があります。

var obj1 = { foo: 'bar', x: 42 };
var obj2 = { foo: 'baz', y: 13 };

var clonedObj = { ...obj1 };
// Object { foo: "bar", x: 42 }

var mergedObj = { ...obj1, ...obj2 };
// Object { foo: "baz", x: 42, y: 13 }

// https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Operators/Spread_syntax#Spread_in_object_literals
// スプレッド構文 - JavaScript | MDN#Object リテラルで使う

 注意点として浅いコピーなためコピーしたObjectとコピー元Objectの両方がネスト先の参照を共有する点があります。まあ不変な対象をコピーするならばshallow copyもdeep copyも変わらないのでそういう時は何も気にせず記述して大丈夫です。
 スプレッド構文を利用してVue.jsのコンポーネント中で定義を呼び出します。例えば、VuexのmapHoge系です。

computed: {
  // オブジェクトスプレット演算子で、外のオブジェクトとこのオブジェクトを混ぜる
  ...mapState({
    // ...
  })
  // コンポーネント個別のcomputedメソッド
  localComputed () { /* ... */ },
}
// https://vuex.vuejs.org/ja/guide/state.html#%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%B9%E3%83%97%E3%83%AC%E3%83%83%E3%83%89%E6%BC%94%E7%AE%97%E5%AD%90
// ステート | Vuex

 Vuexのステート参照用メソッドを返すmapState関数の結果をスプレッド構文でコピーして、コンポーネント独自のcomputedメソッドと混ぜ合わせます。これでcomputedの中は次のように書いたのと同じになり楽できます。

computed: {
  // mapStateの結果が展開
  storeComputed1 () { /* ... */ },
  storeComputed2 () { /* ... */ },
  storeComputed3 () { /* ... */ },
  // コンポーネント個別のcomputedメソッド
  localComputed () { /* ... */ },
}
// https://vuex.vuejs.org/ja/guide/state.html#%E3%82%AA%E3%83%96%E3%82%B8%E3%82%A7%E3%82%AF%E3%83%88%E3%82%B9%E3%83%97%E3%83%AC%E3%83%83%E3%83%89%E6%BC%94%E7%AE%97%E5%AD%90
// ステート | Vuex
著者:杉浦

アンドロイド用SSHクライアントJuiceSSHの紹介

 GNSS関連の開発を行っていると外に出ての作業が増えてきます。衛星の信号を受信するには天頂に障害物のない外が一番なためです。一方で外に出ると開発用の道具を持ち運びにくいです。ノートPCならばぎりぎりですが、それでもそこそこ重く危険です。その点スマホならば全然問題ありません。JuiceSSHはAndroid上で開発を行うための便利なアプリの一つです。
 JuiceSSH – SSH Client – Google Play のアプリ
 使い方は正直アプリを見ながらポチポチするだけで大体わかります。サーバにつないでサーバ内部でいつも通り色々やるだけです。スマホのまま色々やれるのは結構便利です。
 一見分かりにくく重要だったのがスクリーンショットで囲った特殊キー群です。文字入力時に文字入力部の上をタップすると現れます。スマホのままでは扱いにくい部分全般(パイプとか区切りとかスクリーンやエディタ絡みとか補完入力とか)をカバーしてくれます。

著者:杉浦

【Python】ログを読み取りやすく作る

 長期にわたって動かすプログラムを運用する際ログは重要です。ログがないと何が起こったかもさっぱりわからず想定しない動作の対応もとても困難です。そんなわけでログを作るわけですが、ただファイルに記録を書きだすだけならログ機能なんて使わず、ファイルの書き込み機能だけで十分です。Pythonのログ機能にはログを書きこむ際にあらかじめ用意された変数の様なものがあり、それを用いることで労せず情報の濃いログを作れます。
 やることはログフォーマットの指定です。一度書きだされるログの形式を定めます。フォーマットは次のようにロガーに渡します。

# グローバルなロガーで定義するならこれ
FORMAT = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s'
logging.basicConfig(format=FORMAT)
# 個別にロガーを作るならこれ
logger = logging.getLogger(__name__)
handler = logging.FileHandler(filename='%s/log/debug-%s.log' % (
    os.path.dirname(os.path.abspath(__file__)), dt.now().date()
))
handler.setFormatter(logging.Formatter(FORMAT))
logger.addHandler(handler)

 上の様に好きな文字列をフォーマットとしてロガーに渡せます。この時のasctimeやmessageの様な%()sとprintf的に定義されている部分にログ書き込み時の状況に応じた値が書き込まれます。変数はLogRecord 属性として次の部分にまとめられています。
logging — Python 用ロギング機能 — Python 3.8.0 ドキュメント #LogRecord 属性
 多くの人が凝ったロギング機能をGitHubやpypiで公開してくれていますが、どうにも必要な分が足りない時には自作する必要が出てきます。大体、スレッドやプロセスの分岐とどこからどこまでが一連の流れか、一連の流れであることをどう示すか、外部サービスとの連携ログはどうするか、とシステムに特化した部分を考え出すようになると自作した方がお得です。