Rubyの型定義ファイルを中央repoにしないほうがいい理由

あるいは私がDefinitelyTyped (DT) が失敗だと思っている理由、です。

あたりが話の発端です。


DTについては以前いまいちイケてない理由を書いたことがあります。

TypeScriptのDefinitelyTypedは「ダメでもともと、うまく使えればラッキー」くらいの距離感がよい - Islands in the byte stream

この時の話を一言でまとめると「ライブラリの作者ではない第三者がメンテしていることが多く、基本的に品質が低い」のがよくないというものでした。

それ以外にもいろいろ欠点があります。

また、 あらゆるライブラリの型情報をあつめる という野心的な試みなのはいいものの、そのせいでrepositoryをcloneするのに時間がかかり、パッチを作るのも大変です。

DTの場合はバージョニングも微妙で、型定義ファイルのバージョンはmajor versionだけオリジナルと一致させてteenyなどを自動的にインクリメントさせる方式をとっています。このため、ライブラリの特定バージョンを表す型定義ファイルというものは存在しません。このへんは本質的な欠点というよりは、DTのポリシーがイケてないという話かもしれませんが。

というわけで、いま私が関わっているプロジェクトではDTをあまり使っていません。DTは、基本的には使わないほうがいいと考えています。


追記:

TSはJavaScriptそのものではないのでいろいろ難しい面はあります。JS互換の静的型AltJSだけでもTypeScriptとflowtypeがあるし。

TypeScriptでPromise.prototype.finallyを使う

github.com

Promise.prototype.finally が stage-3 になって、 ES polyfill集である core-js にも v2.5.0 で追加されたので、babel-runtime などを使っている場合はcore-jsのバージョンを上げるだけで finally を使えるようになってます。

しかし、TypeScriptの場合は型情報が必要なので、core-jsのアップデートだけでなく、次のnpm moduleを使って型情報の追加が必要になります。

www.npmjs.com

TypeScript compiler (確認したのはv2.5.2) 的には、package.jsonに追加(して当然npm installなりyarnpkg installなりする)だけで利用可能になるようです。

ただし、RubyMine (v2017.2.3) はそれだけだと解釈できないようで、エディタ上は型エラーになります。vscodeだと問題ないので、TSでちょっと変わったことをするならやはりvscodeのほうがいいですね。

RubyMineでも Invalidate Cache をしたらfinallyが見えるようになりました。よかった。

CommonMark v0.28 のイマイチなところ

作業メモとして。なお、 CommonMark ≒ GitHub Flavored Markdown くらいの感覚で書いてます(実際にはGFMはCommonMark + いくつかの独自拡張)。

膠着語分かち書きしない言語におけるスペースで区切られないトークンとインライン記法について

膠着語というのは、日本語や中国語のように単語と単語の間にスペースをいれないタイプの言語です。 膠着語は分かち書きしない言語のことではありませんでした…。素直に「分かち書きしない言語」というのがよさそうです。

CommonMarkは分かち書きしなくてもそれなりに動くように定義されていますが、エッジケースではまだ思った通りに動かないということがあるようです。

ひとつ仕様のバグっぽいのをみつけたので起票しましたが、これ系の話は中国語のコンテキストで議論されてるっぽいので見守り。

Non-spaced tokens with emphasis seems not parsed as expected · Issue #488 · commonmark/CommonMark

脚注記法

CommonMarkには(ついでにGFMにも)脚注記法は存在しません。存在しないこと自体はまあいいのですが、PandocやQiitaなど多数のmarkdownサービスで採用されている脚注記法(以下pandoc’s footnotes)が、CommonMarkのLink reference definitionという記法と一部かぶっていて意味がまったく違うため、CommonMarkとの互換性をとるとpandoc’s footnotesを実装することが不可能であるという問題があります。

これはCommonMarkの仕様を変えるかpandoc’s footnotesを諦めるかの二択なので困ったものです。

これも起票済み。

Footnote extensions vs Link Reference Definition - Extensions - CommonMark Discussion

参考スレッド:

見出し記号(#)のあとにスペースが必要な件

CommonMark以前の仕様が未定義だったGFMでは #foo で見出しになっていたのが、CommonMark化後のGFMで見出しにならないのはだいたいこのせいです。これはおそらく、GitHubで多用するissue記法(#123)と明確に区別するためにスペースを必要にしたのだと思います。GitHubはCommonMark仕様策定に噛んでますからね。

これは受け入れるしかないのですが、影響範囲がそれなりにあるのでどうしたものかなという感じです。

Androidライブラリのためのmaskaradeというorgを作った

The Maskarade project · GitHub

最近イマイチAndroidの活動ができてないんですが、Androidライブラリのメンテを諦めたわけではなくて、たとえばOrmaとかはまだやりたいことがいくつかあるのでやるつもりはあります。一方で、ちゃんと新しいメンテナがいたほうがいいなーというプロジェクトもあって、とりあえず私のメンテする気のあるなしに関わらずユーザーがいそうなAndroidライブラリを全部まとめて maskarade(マスカレード)というorganizationに移すことにしました。

何故これが必要なのかというと、新しくオーナーシップをもったメンテナを受け入れられる体制にするためにorgが必要だからです。GitHubは個人アカウントにあるリポジトリのオーナーシップをコラボレータに渡すことはできないんですよね。なので、オーナーシップを誰かに渡したいときは (1) org管理にする (2) 新しいオーナーにrepoを移譲する、という選択肢しかないのです。

ここでオーナーシップにこだわるのは、そもそも誰かをOSSのメンテナに任命するにあたってはちゃんとオーナーシップ(リポジトリのオーナーシップとリリース権)を渡さないと引き継ぎがうまくいかないという経験則があるからです。逆に言えば、オリジナル作者がメンテしなくなったときに、コントリビュータにコミット権だけ渡してオーナーシップを渡さないと、結局そのコミッターは「お客さん」以上の関わり方ができず、そのOSSは死にます。OSSにユーザーがいなくなって死ぬのであればそれは寿命がきたということですが、ユーザーがいるのにオーナーシップが不明瞭なためメンテされずに死ぬのは大変残念です。

OSS の引き継ぎ方 - builderscon tokyo 2017 でも「引き継ぎをするときはリリース権もちゃんと引き継ぐこと」みたいな話をしていましたね。

というわけで、maskaradeのリポジトリは全てメンテナを名乗り出ればオーナーシップ(リポジトリのowner権限とbintrayのリリース権限)を渡します。私の直接の知り合いであればそのまま、そうでない場合でもそのまえに2~3のテスト(pull-requestをおくってレビューするというプロセスを経験する)をしたら渡します。なお、Androidライブラリをmaskaradeにtransferしたいとうい申し出も受け入れます(私はメンテはしませんが!)。

TODO: これを英語でも書いて maskarade/README に置く

builderscon 2017 tokyo に行った

builderscon tokyo 2017 - Aug 3, 4, 5 2017

f:id:gfx:20170806134555p:plain

いろいろな分野の人がいて非常に刺激になりました。来年はなにかネタを持っていきたい。

あと名札がリバーシブルなのよかった。なお一人ひとりにユニークなQRコードを発行するのはバリアブル印刷というそうです。

印象に残ったトークについて感想など。

横山光輝三国志 全文検索システム

最高に面白いサービスなのでぜひコンテンツの権利者と仲良くしてサービス化してほしいところ!

技術的にもわりと参考になりました。

  • OCRはGoogle CVが曹操級で手書き文字もそこそこいける
  • 誤検出したテキストは “MeCabで洗う” ときれいになる
    • 解析できなかった部分を取り除けるの意
  • テキストの座標もとれるので自動翻訳して置換するのもそこそこうごく
    • 製品レベルにするのはちょっと大変そうだけどポテンシャルありそう

このへんを応用すると Kibela にアップロードした画像のテキストを検索できそうだなと。

複雑なJavaScriptアプリケーションに立ち向かうためのアーキテクチャ

アーキテクチャの話は苦手なんだよなあ…でもJSがどんどん複雑になってきてるからそろそろ真面目に考えるかーくらいの気持ちで聞きにいったんですが、めちゃくちゃ分かりやすくてよかったです。ちゃんと復習して製品開発に活かすぞ💪

repo: NekogataDrumSequencer も大変参考になりますが、 Drum Sequencer に対して無知すぎるのでそこはあまりピンときませんでした。

なおプレゼン中に紹介された FizzBuzz Enterprise Edition はめっちゃ笑いました。

OSS の引き継ぎ方

スライドは未upload?

ぼくもだいぶOSS引き継いでもらったり引き継いだりしてるので他の人はどうやっているのかな、と思ったらけっこうハードな話でした。Rubyコミッターの重責というか…。後半の事例集がわりとはしょられてしまったのが残念でした。RubyKaigi 2017でもやるみたいなので完全版の資料が楽しみです。

歴史あるOSSだとオリジナル作者がそのエコシステムを去っていたり死去(!)したりすることがままあって、しばしば「コミッターはいるけどプロダクトマネージャ(プロダクトオーナー)的な人がいなくて方向性が定まらない・年単位の開発が必要な機能がまとまらない」という感じになるそうで、これについては自分の観察範囲でも経験があり貴重な知見でした。自分が興味がある場合は「プロダクトオーナーやります!リリース権ください!」って言えばいいんですが、そのソフトウェアを使いはするけどオーナーをしたいというほど関心があるわけでもない場合は難しいんですよね、関心のあるところは直すけどプロダクトの方向性とかを舵取りしたいわけじゃないし。

このへん難しいのでうまく言葉にまとめられませんが、いい感じになってほしい。自分が干渉できる範囲ではいい感じにしたい。

Oss貢献超入門

実践的かつ具体例が豊富でよかったと思います。

OSS活動についての動機は大まかにいって「趣味」と「業務上の必要性」の2つがあって、きょうびのウェブサービス開発ではOSSに依存しないことはほとんど不可能なので、製品の品質をコントロールするためにはOSS活動は必要不可欠なんですよね。ほとんど常に業務上の必要性があるのに、実際の所OSS活動はどうやっていいのか分からんみたいなのは解決すべき課題といえるし、こういう入門セッションはいいなと思いました。

3Dプリンタで作る階差機関 / Build Difference Engine by 3D Printer

直接聞いてないけど、スライドだけでもだいぶ面白いです。「というわけで全部スクラッチで設計しました」あたりが強い。

3Dプリンタめっちゃ夢があるし、この調子でアンティキティラ島の機械にもチャレンジしてほしい。

npmjs.com で著名ソフトウェアによく似た名前のマルウェアが大量に発見された

Malicious packages in npm. Here’s what to do | Ivan Akulov’s blog

People found malicious packages in npm that work like real ones, are named similarly real ones, but collect and send your process environment to a third-party server when you install them

訳:

悪意のあるパッケージがnpmで発見された。それらは、実際のパッケージによく似た名前で同じように動くが、パッケージのインストール時にプロセスの環境変数を外部のサーバに送信する。

発見されたパッケージの一覧は元エントリをどうぞ。このようなマルウェアである偽パッケージの一例をあげると、 babelcli, d3.js, ffmepg, jquery.js, mysql.js, openssl.js などです。これらのパッケージが依存関係にある場合、tokenなどの秘匿値を再生成すべきとのこと。

package.jsonに書いた直接の依存関係だけでなく、 依存ツリーのすべて を調べる必要があります。つまり、 あなただけでなく依存ツリー中の誰かがミスって偽パッケージを使っただけであなたの環境が攻撃対象になる ということです。依存関係は npm ls などで調べられます。

ライブラリ開発者としては、 scoped packages を使うのがよさそうだと元エントリでは述べています。

所感

オンラインでコードを配布・使用する以上、どんなパッケージリポジトリでもマルウェアを配布することはできます。つまり、npmjs.com (npm) だけの問題ではありません。

一方で、ある機能を実現するための別のパッケージが非常に沢山あります。たとえば、 zstdのnode binding だけでもパッと見て4つくらいあります。これは、npmのユーザー(=JSの開発者)が他の言語にくらべて桁違いに多いため、車輪の再発明が頻繁におきるからではないかと想像しています。

また、しばしば同じライブラリに対応する複数の配布パッケージがあります。一例をあげると、lodash は単体で lodash (commonjs modules版) と lodash-es (ES modules版)という配布パッケージがあり、またlodashの提供する関数ごとに配布パッケージが提供されています (たとえば lodash.merge packageなど)。このような状況だと、たとえば react-es というパッケージを作って「ReactのES modules版です!」というdescriptionを設定すると、いかにもそれっぽく見せかけられます。つまり、著名ソフトウェアに便乗してマルウェアを配布することが簡単にできてしまいます。

この問題はなかなか難しくて対処療法ではない本質的な改善をどうしたらいいのかは分からないのですが、さしあたり自分の管理するライブラリに関しては、 scoped packages にして配布するしかないかなという気がします。

追記

npmjs.org の配布パッケージの中身を公式ウェブインターフェイスで見ることができない、というのもnpmの問題のひとつと言えるかもしれません。

…できないですよね? 例:

たとえば metacpan.org は配布パッケージの中身をウェブで確認できます。例:

rubygems.org も配布パッケージの中身をウェブでは確認できませんね。例:

追記(2)

  • 元エントリに確認用ワンライナがあり、今回発見された分に関していえば確認することはできます
    • 元エントリが更新される可能性を考慮してここには引用しません
  • 公式見解でました: npm Blog Archive: `crossenv` malware on the npm registry

ghq+pecoでターミナル作業が捗るぞ

便利なソフトウェアを定期的に掘り起こすぞ活動です。

ghq は「GitHub repoのclone先を統一することでいろいろ便利にできるコマンド」で、github repoのclone先を、カレントディレクトリに依存せず ~/.ghq/github.com/$owner/$repo/ にします。

使い方:

ghq get -p --shallow $URL

peco は、テキストのリストをgrepしてそれに対してなにかコマンドを起動するみたいなやつで、ほかのツールと組み合わせて使います。

ghq + peco

.zshrc などにエイリアスを作っておきます。

alias g='cd $(ghq list --full-path | peco)'
alias b='hub browse $(ghq list | peco | cut -d "/" -f 2,3)'
alias v='code $(ghq list --full-path | peco)'

これで、 g でghqでcloneした任意のrepoにcdできます。

bv は同様に、ターゲットのリポジトリをブラウザで開いたりvscodeを立ち上げたりできます。

See Also

ActionArgsが素晴らしい件 #Rails

github.com

Railsのcontrollerで違和感があるのって

  • actionのinputに params というインスタンスメソッド経由でアクセスすること
    • しかも params はviewからアクセスできる!
  • actionのoutputが controller のインスタンス変数への代入であること
    • しかもそのインスタンス変数はviewからアクセスできる!

というところだと思うんですよ。

なぜなら我々は「メソッドの引数でinputを受け取りメソッドの戻り値をoutputとすべし」ということを是としてコードを書いてるわけじゃないですか。リーダブルコードを読むまでもなく、変数のスコープは狭ければ狭いほどメンテナンスしやすいリーダブルなコードだというベストプラクティスを正しいものとしてコードを書いているわけじゃないですか。

そういうベストプラクティスに真っ向から反しているのが現在のRailsのcontrollerのあり方なのです。controllerとviewで params から自由自在にパラメータを引き出して、特定のcontrollerのアクションがどういうパラメータを取りうるかさっぱりわからないということになりがちです。 この params のスコープの広さはまるでグローバル変数のようです。

ActionArgsは、リクエストパラメータのうち params でアクセスするパラメータをRubyのメソッドシグネチャで表現できるようにするgemです。

ActionArgsのもとでは、controllerはたとえばこんな感じになります:

class UsersController < ApplicationController
  def index(page: '1', per: '20')
    @users = User.page(page).per(per)
  end

  def show(id:)
    @user = User.find(id)
  end
end

#index が任意パラメータとしてpageper をとり、それらのデフォルト値がそれぞれ1と20であること、show が必須パラメータとして id をとるということが、メソッドのシグネチャをみるだけで一目でわかりますね。すばらしい。もっとも、 #create#update の場合はパラメータのデータ構造がネストするのでそれほど自明ではありませんが、それでもないよりはずっとマシです。スコープの広すぎる params を使わなくて済むのですから。

個人的にはActionArgsがRails組み込みでほしいですが、まずは誰もが使う標準的なgemにならないとなと思ってこのエントリを書きました。

実際の使い方は action_args/README を読むのがよいと思います。ただし、常にキーワード引数を使うべきです。READMEでも触れられていますが、非キーワード引数の場合デフォルト引数の扱いに制約があるからです。

ActionArgs では達成できないことも多いんですが、その場合でも「メソッドの引数でinputを受け取りメソッドの戻り値をoutputとすべし」ということを念頭に置きつつcontrollerを実装するようにしています。

RailsのURL helpersをTypeScriptとしてexportするts_routesを書いた

カッとなって作りました。後悔はしてません。

github.com

/entries/:id を生成するためにRailsのviewで entry_path(42) などとしますが、それをTypeScriptからも Routes.entryPath(42) などとして使えるようにするためのgemです。

READMEにあるように rake task を定義して適当なパスに routes.ts を生成するという想定です。sprocketsには対応してません。

Rails URL heplersのJSへのエクスポートというとrailsware/js-routes があって、これをずっと使っていてしばらくは十分だったのですが、js-routesの生成するコードには型情報がありません。フロントエンドのコードベースをTypeScript化したいまとなっては、開発中にしばしばtypoして実行時エラーになるのが不満でした。

ts_routesであれば生成されるコードがTypeScriptなので型情報があり、ヘルパー名の補完も効き、TypeScriptらしい快適さでRailsのURL helpersを使えます。

RubyMineでローカル変数やブロック引数にYARD型アノテーションをつける

RubyMine 2017.1 Help :: Using Annotations にあるとおりなんですが、

local variables:

# @type [String]
my_var = magic_method

# @type my_var [String]
my_var = magic_method

# @type [String] my_var
my_var = magic_method

# @type [String] my_var Add some documentation here
my_var = magic_method

block parameters:

method_with_block do
# @type [String] param1
# @type [Range] param2
| param1, param2 |
# some code...
end

ということです。

これでローカル変数でもコード補完がうごく!!

f:id:gfx:20170614142942p:plain

とはいえプロダクションコードに残すかどうかは微妙です。開発中の助けにはなりますが、真面目に指定するとさすがに煩雑すぎるので。

メソッドの引数や戻り値はYARDの型アノテーションを指定する価値があると思いますけどね。