その手の平は尻もつかめるさ

ギジュツ的な事をメーンで書く予定です

行の末尾の空白文字と決別する

ソースコードの各行の末尾に空白文字があると精神衛生上良くないので消す派です.常に消しておくようにすると,VCSのdiffにノイズが乗らなくて良いと思います.

そこで僕は以下のようにVimを設定して対応しています.


以下のようにすると,保存時にフックして末尾の空白を削除出来るようになります.
また,:ToggleRemoveTrailingWhiteSpaceというコマンドを実行すると,
フックして削除するか否か,という状態をトグルさせることが出来ます.
色々な理由で *1 末尾空白を消したくない時にはトグルして機能を一時的に殺すと良いでしょう.

Markdownは末尾に半角スペースを2発入れる事によってブロック内での改行を行う仕様となっているため,
自動的に半角スペースを削除されると具合が悪いことになります.
従って,以下の設定ではMarkdownの編集時には常にdisabledになるようにしています.

" 保存時にフックして常に末尾の空白を削除する
let g:does_remove_trailing_white_space = 1
au MyAutoCmd BufWritePre * call s:removeTrailingWhiteSpace()
func! s:removeTrailingWhiteSpace()
  if &ft != 'markdown' && g:does_remove_trailing_white_space == 1
    :%s/\s\+$//ge
  endif
endf

" `:ToggleRemoveTrailingWhiteSpace` というコマンドを実行すると
" 保存時にフックして空白を削除するか否かをトグル
command! -nargs=0 ToggleRemoveTrailingWhiteSpace
  \ call s:toggleRemovingTrailingWhiteSpaceStatus()
func! s:toggleRemovingTrailingWhiteSpaceStatus()
  let g:does_remove_trailing_white_space =
    \ !g:does_remove_trailing_white_space
endfunc

また,以下のようにすると末尾の空白文字を真っ赤にハイライトすることが出来るため,可視化が捗って便利.
おまけでハードタブにもささやかに色を付けたりなどしています.

highlight TrailingSpaces ctermbg=red guibg=#FF0000
highlight Tabs ctermbg=black guibg=#000000
au BufNewFile,BufRead * call matchadd('TrailingSpaces', ' \{-1,}$')
au BufNewFile,BufRead * call matchadd('Tabs', '\t')

*1:既に末尾空白がたくさんあるプロジェクトのソースコードに変更を加える時に,自分の流儀をプロジェクトに押し付けるのもなんだか気が引けるなァ……みたいな時とか

MySQL-Reference-Manual-Version-CheckerというChrome拡張を書いた

さてMySQLを使った開発をしていると,必ずと言っていいほどMySQL Reference Manualを参照することになるわけですが,
頑張って読み解いていたマニュアル (しかも英語) が実はMySQL 4.1のマニュアルで,「僕の使ってる5.6のマニュアルとちゃうやんけキエエエエエ」となり,モチベーションが根こそぎ持っていかれるということがままあると思います.

そうした悲しい事故を防ぐためにこの度,MySQL-Reference-Manual_Version-CheckerというChrome拡張をこさえました.長い名前だ.
https://chrome.google.com/webstore/detail/mysql-reference-manual-ve/nbbkeofjffbleojiodgmdipcamgmmmee
https://github.com/moznion/MySQL-Reference-Manual-Version-Checker


何をする拡張かというと,設定したReference Manualのバージョン範囲外だった時に警告を出すという拡張です.
(デフォルトだと5.1 <= version <= 5.7というバージョンレンジになっています.つまりは4系以下だと警告が出るという感じです)


実際に動かしてみると以下のような感じです.

参照しているマニュアルのバージョンが外れている場合 (デフォルトの状態で4系を参照している場合);
f:id:moznion:20140606232532p:plain
代替ページ (バージョン範囲にマッチする中で最新のマニュアルのページ) へのリンクも提供しています.

マニュアルのバージョンが適切な場合は何も出ません;
f:id:moznion:20140606232644p:plain


というような感じです.地味に便利な気がしています!
ぜひご利用下さいませ.


ところでこのChrome拡張はTypeScriptを使って書いてみたんですが,なかなか良い感じでした.
その件についてはまた別の記事で紹介できればと思います.

chrome.storage.*.set()でInfinityやundefinedやNaNを格納出来ない問題

Chrome extensionで,extension localで横断的にlocalStorageを利用するためのAPIであるchrome.storageですが,chrome.storage.local.set()chrome.storage.sync.set()ではInfinityundefinedNaN*1 を格納することが出来ません.

以下検証コードです


localStorageではこれらの値は正しく取り扱うことができるので,chromeの仕様あるいはバグ感があったのでIssueを投げました.
Issue 380509 - chromium - chrome.storage.*.set() cannot set Infinity, undefined, and NaN - An open-source project to help move the web forward. - Google Project Hosting


使えるようになって欲しい感じです.

*1:とはいえNaNを格納したいことはあまり無いでしょうが

Released Log::Minimal::Object

皆さん,Perlのアプリケーションのログを出力したい時はLog::Minimalをご利用のことと存じます.非常に便利でグレートなモジュールなので僕も毎度使っております.


さて,そのLog::Minimalですが,ログの出力をカスタムしたい時には$Log::Minimal::COLOR$Log::Minimal::TRACE_LEVELなどといったLog::Minimalの持っている変数をいじってスコープごとにゴニョゴニョ〜〜みたいな感じにやるわけですが,ある程度複雑な条件に基いてログをカスタムしたい時 (例えばローカルからのアクセスの時だけ特殊なフォーマットでログを吐きたい,だとか) なんかは記述量が増えたりそのロジックが煩雑になったりして,ログ出力の為の記述が本質的なロジックの記述を食ってしまう場合があったりします.


そうした背景からこの度リリースしたのがLog::Minimal::Objectです.
https://github.com/moznion/Log-Minimal-Object
https://metacpan.org/pod/Log::Minimal::Object

Log::Minimal::ObjectはLog::Minimalに対するOOPインタフェースを提供する簡単なラッパーです.
使い方はドキュメントを読んでもらうとして,その特徴はインスタンスごとに独立して出力のカスタマイズ出来るという点にあります.あらかじめ個別に設定したロガーインスタンスを作っておいて,条件に基いて出力するロガーを切り替えてやる,という使い方が出来ると便利なのではないかと思ったのが主な動機です.
また,副次的な感じではありますが,ログを出力するメソッドをインスタンスごとの粒度で乗っ取る,みたいな使い方もできるのでテストの時などに便利なのではないかと思っています.


まあ,ある程度の規模感のアプリケーションだと独自でロガークラスを作ってそいつに仕事をさせる事も多いと思いますが,こうしたカジュアルに使えるモジュールがあっても良いのではないか,と思って作った次第です.ご利用下さい.

Released Parse::KeyValue::Shellish

https://metacpan.org/pod/Parse::KeyValue::Shellish
https://github.com/moznion/Parse-KeyValue-Shellish

foo=barだとかhoge=(fuga piyo)みたいな,いわゆるshell的なkey-valueを解釈する必要があったので書きました.shell的key-valueの文字列を解釈してhashrefに包んで返します.例えばfoo=barを食わせると{foo => 'bar'}が返って来て,hoge=(fuga piyo)を食わせると{hoge = ['fuga', 'piyo']}という感じでarrayrefが格納されるという寸法です.
類似のモジュールはいくつかあったんですが,ファイルにしか対応していなかったり (文字列を食わせられない) ,Yappを使っていてメッチャゴツい! みたいな感じだったりだったので,フルスクラッチで書きました.1文字ずつ読み取って正規表現でバリバリ解釈するという実装なので,依存がほぼありません *1

あくまでshellishなので完全にshellをエミュレートし切れてはいませんが,ある程度なら動くと思います.詳細はREADMEを参照して下さい.
「これが動かないとはけしからん」とかありましたら,サポートするかもわかりませんのでイシュって下さい.

以上現場でした.

*1:parentに依存しているくらいだと思う

「メソッドに対してテストをするな」という話題について

―「間違っているかもしれないので,その時はこの銃で僕を撃ってくれ,良いね?」


[2014-05-19T17:48:28Z 追記]
http://a-suenami.hatenablog.com/entry/2014/05/17/131326
補足してもらったので読むと良いと思います.
わかっちゃはいたけれど上手く言語化できていなかった部分,あるいはわかっていない部分について言及されていたので参考になりました.ありがとうございます.

いやーつーかさー,「『メソッドに対するテスト』っていう言葉自体がわかりにくくね?」っていうのはその文言を見たり,この文章書いている間もずっと思ってて,つまり端的に言うとそういう事を言いたかったはずなのに,今このエントリ読み返すとそうした[趣主]旨から完全にズレててメッチャ違和感あるな! ってなりました.俺のバカ!

これを仮に言い換えるとしたら「内部構造に対するテスト」とかそういうのになるんですかね? 難しいな!
[追記ここまで]


良いテストを書くための指針として「メソッドに対してテストをするな」という言葉がありますが,これは「ユニットテストに対して分かりやすい (説明的な) 名前を付けろ」ということを言っているのであって,「メソッド単体のテストをするな,ある機能を満足するシナリオについてテストをせよ」と言っている訳ではないと思います.


極端なコード例ですが (追記: ***念の為*** 書きますけどfoo()というふざけた関数名は伏線ですので回収されるまで待つこと.だから「極端」って言ってるんですねハイ),

sub foo {
    my $num = shift;
    return ++$num;
}

という,受け取った引数をインクリメントしたものを返す関数があったとして,これに対して

subtest 'test foo()' => sub {
    is foo(1), 2;
};

というテストを書いたとしても,この“test foo()”という説明からは,このテストが何についてテストをしているのかがわかりません.これがいわゆる「メソッドに対してテストをしている」状態なのだと思います.

そこでこのテストを

subtest '#foo()' => sub {
    subtest '引数がインクリメントされる' => sub {
        is foo(1), 2;
    };
};

という風に書き換えてやると,テストの対象と期待する動作が明確になってマシになった感じがします.これがいわゆる「メソッドに対するテスト」ではない状態と言えるのでは無いでしょうか.
(というか,foo()とかいうふざけたメソッド名はいったいなんだ!!! 後述!!!)


そもそも,「あるシナリオを満足しているかどうかをチェックするテスト」と「メソッドに対するユニットテスト」はそれぞれ負っている責務が異なるように感じています (前者は「振る舞いテスト」と呼ばれるかもしれません) .
振る舞いテストは「ビジネスが要求するシナリオが壊れている (満足していない) 時に教えてくれる存在」であり,これを満足しない限りはコードを成果物としてリリースが出来ないという受け入れテスト的な側面を有している一方,
ユニットテストは「どこが原因でそのシナリオが壊れているのか」あるいは「どうすればそのシナリオを満足することができるのか」といった問題を切り分ける為に役に立つ情報を提供してくれたり,コードの一部を修正した時にそのメソッドが提供する単一機能の挙動が変わってしまってはいないかをスピーディに確認出来るのでコードに対する変更を恐れずに済んだり,といったメリットを享受する為の開発者向けのテストであるように思います.ただし,ユニットテストだけでは「シナリオを満足しているかどうか」を担保することは出来ないと思います.

端的に言うと,ソフトウェアがちゃんと動いているかどうかを担保する為には振る舞いテストだけで十分という事になりますが,その開発の効率やコードの品質を向上させたい場合にはユニットテストが必要になるという事になると感じています.
たいていの場合は,開発にスピード感が無ければプロジェクトは死ぬし,コードの品質が劣っているとプロジェクトは地獄と化した末に死ぬので,結果的にユニットテストも書くことになると思います.

従って,振る舞いテストとユニットテストはどちらか一方だけを書けば良いというものではなく,つまり「メソッドに対してテストをするな」という言葉は「単一メソッドに対するユニットテストをするな」という論旨のものではない,という認識です.


ところで,メソッドの命名が適切になされていれば (つまりメソッドの名前が説明的であれば) ,いわゆる「メソッドに対するテスト」を書いたとしてもおのずと (ある程度) 説明的なテストになると思うのですが,どうか.
いわゆる「メソッドに対するテスト」を書いてそのテストの情報量が低い場合は,プロダクションコードの方にも何らかの問題があるように感じていますが,どうでしょうね.という事を上のコード例を書いている時に思いました.

「Kyoto.なんか」でgithub-commit-comment.vimの話をしました

Kyoto.なんか」というイベントで,最近書いたgithub-commit-comment.vimというVimプラグインの話をしてきました.お招きいただきありがとうございます.

github-commit-comment.vim

https://github.com/moznion/github-commit-comment.vim


Vim上からGitHubのコミットコメントの投稿と参照が出来るというプラグインです.
GitHubのコミットコメントには,

  • 行単位のコメント
  • ファイル単位のコメント
  • コミット単位のコメント

と,3種類のコミットコメントが存在しており,これらすべてに対するコメントが可能となっています.
詳しくは上記URLのREADMEを読むと良いと思います.Gifアニメを大量に貼っているので,なんとなく雰囲気がつかめるでしょう.
なお,READMEには書いていないのですが,このプラグインはcommit commentの内容をすべてquickfixに流し込んでいるので,
:cwindowコマンドを発行してやると,そのファイルに対するコメント一覧を見ることができます.地味に便利だ.


開発のモチベーションとしては,コードレビューをする時 (というかコードを読む時) に任意のエディタでやりたいという欲求があって,僕の場合はVimでそれをやっているんですけれども,
Vimでコード読みながら「コミットコメントを付けたいなーー」って時にGitHubが開いているブラウザとそのエディタとの間を行ったり来たりするのは大変に面倒なわけで,
そこら辺を上手く解決したい!!!!! という事でグワーッと書いたという感じです.


ひとまず便利は便利なのですが,色々と問題点というかわからない部分があって,それは例えば

  • コメントは,HEADのリビジョンではなく,そのファイルが最後に変更されたリビジョンに反映されるのはOK?
  • そもそもgitのoriginに決め打ちなのはどうなのか
  • 行単位のコメントをした時に,そこが変更点ではない *1 場合に微妙 (とはいえexpandすれば見れる)
  • 実際Pull-Requestとかでも使いたいよねー

などなど色々あり,ここら辺は使っていって追々機能を追加したり修正したりしていく感じかなあと思っています.


類似のプラグインvim-github-commentというものがあるのですが,
こちらは1行しかコメントを追加できなかったり,Vimからcommit commentを参照出来なかったりしてちょっと不便,
というか思想が違うのかな,と思ってフルスクラッチで書いた次第です.


フィードバック等頂けると嬉しいです.

*1:つまりGitHubのdiffとして現れない