「textlint-rule-kmu-termcheck」というtextlintルールを作った
文章チェックツールのtextlintに、類似度検査で製品名とかサービス名の表記ミスを見つけるためのルールを作ってみたよ、という話。Node.jsベイビーなので、リファクタリングなどパッチ大歓迎。
実行例。
以下は中身についての長い話で、実装に興味のある方向け。
前職からで業務制作するときの校正チェックは私物のJust Right!でおおむね自分自身としては事足りているんだけど、そこそこお高いので、今のチームの皆で導入して使いましょうという代物ではない。
部署や全社的に広げていくことを考えるとまぁtextlintが無難じゃろ、ということで、ちまちまルールプリセットに着手している。
で、作業を進めていくと、文章面の校正は相互レビューでひっかけられるのでそこまで必要性はないし(あっても損ではないけど)、実行時間もかかりすぎる(これはトレードオフにしてはちょっと重いなと感じている)ので、表記揺れ系、特にサービス名まわりのほうが何も考えずとも自動チェックしてほしいのでは?と思い至った。
たとえば「Mackerel」というのは英語的・音声的に自然に感じられればタイプミスはしにくいんだけど、表記された文字のみで覚えているとミスしやすい。AWSのサービスなどだとスペースがあったりなかったり大文字だったり小文字だったりで、ルールを覚えようがない。
textlint的にはprhルールにひたすら可能性を正規表現で並べるというのが王道っぽいのだけれども、この場合はどうしても漏れが発生するのと、前後に誤った文字を1つタイプミスで入れたとか、1文字抜けたとかいったあたりを検出しづらいという問題がある。
ということで何か方法はないかなと考えたときに、最近のプログラミング言語のインタラクティブシェル(irbとか)ではタイプミスしたときに「もしかして○○のつもりでしたか?」と煽ってくる…じゃなかった、サジェストしてくれる機能がある。
言語と違って辞書量が再現なく増えていく恐れはあるが、まぁドメイン限定で使う程度なら問題ないだろうと判断して、この仕組みを真似してみることにした。
おそらくリーベンシュタイン距離のアルゴリズムを使ったものだろうと推測して、textlintのルールを書く言語のNode.jsでも何かあるかと探したところ、didyoumeanというライブラリが見つかった。didyoumean3とかいろいろフォークされたものがあるけれども、とりあえずdidyoumeanのざっとソース見て安全そうだなと判断したので、そのままそれを使うことにしている。
英語部分しか使わない/使えないので、textlintでよく使われているkuromoji、というかそのラッパーのkuromojinの形態素解析の結果から、英字アルファベットのみで構成されているものを対象にした。
まずは簡単なTypeScriptコードでdidyoumeanに簡単な辞書配列と誤単語を入れたときを実験し、それっぽくsuggestされることを確認。次にkuromojinでの形態素解析を実験。ふむふむ。
さくっとできて、方向性としては悪くなさそうだったので、create-textlint-ruleを使い、textlintルールとして機能するように実装していく。Promiseで最終的にどう返していくのか最初わからなくて悩んでたり、テストルールもよくわかっていなかった。
単語レベルでは一応動くところまではいったものの、複数単語からなる製品名だと、1単語ずつに分けてしまったときにあまりに一般用語と近すぎて誤検出したり、「OpenShift」が「Oepn Shift」になっているといった誤記を検出できない。
形態素解析し終えたトークンから必死に再構成するのは辛いので、英単語間にスペースが入っているときにはそのスペースを代替文字に置き換えて擬似的に1つの単語にするようにした。記号類だとkuromojiに分断されてしまうのだが、補助ラテン文字範囲ならたぶん英字扱いなのではと予想したところ、これでうまくいった。ウムラウト付き文字でもよかったんだけど、\x7fは字形は除算記号なのだけれども英字扱いになるという発見があり、これを使うことにする。
辞書はシンプルに改行区切りのテキスト打ち。JSONでもいいんだけど、入力の簡易さを優先した。この手のもので#
でコメント化できるようにするのは私の基本実装ルールの1つだったりする(なので、ユーザーが入力するものでコメント入れられないデータ構造は嫌い)。
kuromojiで数字箇所で分割するので、「EC2」と入れてもダメなことがわかった。つらい。たぶんkuromojiに固有名詞として追加すればいいのだろうなとは思うけれども、kuromojin経由でその登録ができるのかは未調査。
辞書は最初コード内に持っていたのを分割し、ユーザー辞書も読めるようにしたのだが、fs
ライブラリでエラーになって、Web向けに使えないのはわかるとしてtsc
だとビルドできるのになぜだ…と悩んでいた。結局ビルドコマンドのtextlint-scriptsのほうでfs
について制約していて、ちゃんとそっちのREADMEにも書いてあった…。環境変数渡しが必要なので、cross-envも導入し、NO_INLINE=1
を渡してビルドできるように。
ちょこちょことnpm publishしながら、細部を改善していく。
普通は先頭大文字だけれども、URLに含まれると小文字になってエラーになる、というのがわずらわしいので、その対処をすることにした。先頭大文字小文字を無視するモードを用意して、比較時に先頭のdowncaseをするようにしてみる。「GitHub」みたいなのはダメだけど、かといって全downcase比較してしまうと「OpenShift」をひっかけられなくなる。まぁこのあたりはJust Right!でも無理だったし、許容する。トークンに文の背景情報(MarkdownのURLだよとかコード類だよ)まで乗せられるならワンチャンあるかもしれない。
Node.jsベイビーレベルなので、今回はChatGPTを壁打ち相手にして、「importでこのエラーが出たときの理由は?」「配列をソートして要素をユニークにした配列はどう作る?」など聞いたりしていた。コードの細部で悩んだときにGoogleに聞くよりは良かった。
全体のロジックまで聞くのはMITライセンス公開物としてよろしくないコードが出てきちゃいそうだし、リファクタリングまではしてくれないので、そのあたりはやはり人間とペアプロしたいなと思った。試行錯誤自体はよかったのだけど、つまらないところでつまづいて時間を無駄にしてはいたので、エキスパートに都度ツッコミをしていただきながらのほうが学習効率は良いな。
ともあれ、偽陽性の問題は多めではあるけれども、まぁまぁ良いものが出せたのでは、という気はする。機能強化のPRは大歓迎なので、よろしく! Happy Hacking!
いろいろリリースした 〜Re:VIEW、sabatrapd、mackerel-statsd〜
先月末はいろいろOSSに関わってそれらをリリースできたので、記録がてら。
Re:VIEW
2月28日にRe:VIEW 5.7.0をリリースした(リリースエンジニアリングしてくださった@takahashimさんありがとうございます)。
リリース解説にも書いたけど、ちょうど転職活動〜順応必死時期と重なって、中身をいじる時間が全然取れず、最低限のエラー対処と安定化を施す程度に留まってしまった。
review-knowledge-ja.readthedocs.io
リアル会合の開発会議でこそドライブする面があるので、今年どっかでやりたいんだよなー。
Dockerイメージについては @vvakame さんから反応を早くいただいているのですぐに更新対応がなされるんだけど、技術書典でよく参照される ReVIEW-Template のほうの更新体制に心配がある(@mhidaka さんが超絶忙しそうだ)。EPUBのvalidationエラーとMEMOまわりがあるので早めにマージしたいのと、今後も同様のときにサッと更新したい。あと、紙面的チャレンジも何か盛り込めないかなぁと思ったりしている。
sabatrapd
SNMP Trapのメッセージを監視サービスのMackerelに送りたいねーという雑談からシュシュッと生まれたOSS。2月26日にリリースした。SNMP Trapを受け付けて、Mackerelのアラートとして即時投稿する。
社内で困りごとを聞く→17日にkmutoが妄想設計書→19日にysetoさんがベース実装→20日にkmutoが機能追加要望→同日にysetoさんが反映実装→24日〜26日でissueとPRを繰り返してプロダクトリリース、という超特急デリバリー。
Go実装やテストフレームワーク・CI/CDはysetoさんにお任せ。私のほうはOSSらしくライセンスやプロダクト名決めたり、ロゴを作ったり、ドキュメント書いたり、systemdサービスユニットやインストーラ書いたり、さまざまに壊れたデータを作って食わせてみてはGoでPANICする部分を捕捉するコードを入れていったり、と周辺整備。
それぞれ得意分野がうまく噛み合ったし、issue/PRだけという非同期縛りのわりには即review approvedが飛び交うライブ感で、プロダクトリリースまで大変楽しく、良い経験。
業務タスクじゃないけどチーム横断的なOSS開発 & バリュー提供ということで、先日金曜に開催された社内の「ほたて賞」で発表して、他の部署からも高評価をいただいた。わいわい。
今後もビビッと来たものには突っ走っていきたいし、OSS開発一緒にやろうず的なお誘いもお待ちしております。
mackerel-statsd
mackerel-statsdは、StatsDプロトコルで届く数値データを受け付けて蓄積し、合計としてのメトリック、あるいは平均/最大/最小としてのメトリックとしてMackerelに1分ごとに投稿するというミドルウェア。2月27日にリリース。
入社直後に拝見していた社内開発合宿で生まれたOSSで、開発自体には関わっていないんだけど、Mackerelに今足りないものがここにあるやん!(Mackerel自体の粒度は1分ごとなので、1分の間に起きた大きな変動を掴み切れない可能性がある)と期待がふくらんで、合宿には飛び込みでデモを作ったりしていた。
公認のOSS配布物(mackerelio)として名乗るにはいろいろ完成度として厳しいが、素材として良さそうなのでOSS公開してみよう(mackerelio-labs)ということで、このたびmackerel-statsdも公開となった。
自分的に期待感が超高いOSSではあるもののREADMEなしでは紹介しづらい! ということで、ドキュメントを書いてPRして取り込んだ。
荒削りなところはあるけれども可能性はきっといろいろあるはずなので、ぜひ遊んでほしい。