USO800

Web開発者oakbowのブログです

OSSのコントリビュータになりました

タイトルの通り、コントリビュータになりました。


contributorの意味 - 英和辞典 Weblio辞書

 


hadoop - contributor って何ですか? どうやったらなれますか? - Qiita

 

OSSへの貢献の仕方はいろいろあるようですが、分かりやすいのはやはり何らかのコードの修正をするやり方だと思います。

非常に小さなものですが、先日修正したコードのプルリクエストが取り込まれ、コントリビュータになることができました。

せっかくなのでその修正の経緯などを記事にしたいと思います。

 

対象のOSSプロジェクト

今回私がコントリビュータになったのは、Rails のgemである rails-footnotes です。

RailsでのWebアプリ開発の非常に強力な武器となるデバックツールで、個人的には better-errors(+binding-caller)の次くらいに手放せないツールになっています。

基本的にdevelopmentやstaging環境でしか使用しないgemですが、Webページの最下部にデバッグに役立つ各種情報を表示してくれます。

gemの利用面での詳細はQiitaに記事を書きましたので、こちらもご覧ください。


Rails4 - Rails Footnotes でWebアプリを快適開発 - Qiita

 

例外が発生

現在関わっているプロジェクトでは以前から rails-footnotes が使われていて、バージョンアップ作業を行っていました。

以前のバージョンで機能していたイニシャライザをそのまま使うと DEPRECATION WARNING が発生していたので、最新バージョンの 4.1.4 が生成してくれるイニシャライザに刷新。

コメントアウト状態で設定項目が記述されていたので、軽い気持ちでnoteと呼ばれるフッタに表示する項目の設定を有効化して動作させてみました。修正箇所はここ。

  # Only toggle some notes :
  f.notes = [:session, :cookies, :params, :filters, :routes, :env, :queries, :log, :general]

単にコメントアウトを解除しただけです。元は rails-footnotesが生成しているイニシャライザな訳ですから、設定を有効にしても基本的にちゃんと動作するはず...なのに例外発生。

Footnotes General Exception: uninitialized constant Footnotes::Notes::GeneralNote

なぜだー!ってことで調査開始です。

例外の発生源の調査

スタックトレースから原因を特定することにしました。 出力されているログはこんな感じ。

Footnotes General Exception: uninitialized constant Footnotes::Notes::GeneralNote
/activesupport-4.1.8/lib/active_support/inflector/methods.rb:240:in `const_get'
/activesupport-4.1.8/lib/active_support/inflector/methods.rb:240:in `block in constantize'
/activesupport-4.1.8/lib/active_support/inflector/methods.rb:236:in `each'
/activesupport-4.1.8/lib/active_support/inflector/methods.rb:236:in `inject'
/activesupport-4.1.8/lib/active_support/inflector/methods.rb:236:in `constantize'
/activesupport-4.1.8/lib/active_support/core_ext/string/inflections.rb:66:in `constantize'
/rails-footnotes-4.1.4/lib/rails-footnotes/filter.rb:37:in `block in start!'
/rails-footnotes-4.1.4/lib/rails-footnotes/each_with_rescue.rb:15:in `block in each_with_rescue'
/rails-footnotes-4.1.4/lib/rails-footnotes/each_with_rescue.rb:13:in `each'
/rails-footnotes-4.1.4/lib/rails-footnotes/each_with_rescue.rb:13:in `each_with_rescue'
/rails-footnotes-4.1.4/lib/rails-footnotes/filter.rb:36:in `start!'
/rails-footnotes-4.1.4/lib/rails-footnotes/extension.rb:15:in `rails_footnotes_before_filter'
   :

active_support 側に問題があるのではなく、想定外の値が rails-footnotes やそれ以前から吐き出されていることが原因と見て、filter.rbから見ていくことにします。 周辺のソースはこうなっています。

def start!(controller)
  self.each_with_rescue(Footnotes.before_hooks) {|hook| hook.call(controller, self)}

  @@klasses = []
  self.each_with_rescue(@@notes.flatten) do |note|
   klass = "Footnotes::Notes::#{note.to_s.camelize}Note".constantize # ←37行目!
   klass.start!(controller) if klass.respond_to?(:start!)
   @@klasses << klass
   end
end

見るからに怪しいですね。 見た感じ notegeneral という文字列が入っていることで、37行目で以下のような処理が実行されているようです。

klass = "Footnotes::Notes::GeneralNote".constantize

例外の内容を見るに、やはりここが直接的な引き金になっているようです。

そもそも note って何?ってのを知っていると解決が早いんですが、これは rails-footenots が提供するデバッグ情報の分類です。 私は良く理解できていなかったので、 note に何が入っているのか知るために、古典的なデバッグライトを使いました。 要するに pp note って記述を36行目に追加したんですね。 gemの中身のソースを修正したのでサーバを再起動して、再び例外を発生させます。 するとこんな文字列がログに出力されるようになりました。先ほどのコードの37行目に渡される note の変数の中身の一覧です。

controller
view
layout
partials
stylesheets
javascripts
assigns
session
cookies
params
filters
routes
env
queries
log
general

これは結構大きなヒント。 gemのファイルをあちこち漁っていて気づいたんですが、これらの文字列に対応するファイル群が /lib/rails-footnotes/notes/ に存在します。 controller_note.rb とか log_note.rb とか諸々。でも general_note.rb はありません。 whyなぜ?

公式に戻る

generalgrep をかけても芳しい情報は得られず、どうやら general_note.rb がないことが影響しているらしいことしか分かりません。 こういう時は公式です。こちらで general で検索してみると...、あった、ありました!

== Footnotes v4.0.0
  :
  * Remove 'general' note

rails-footnotes/CHANGELOG at 5479be18602ec4796a87fde4953dedc1970b2061 · josevalim/rails-footnotes · GitHub

GeneralNote 消されとるやんけ!そら無いもの指定したら例外吐くわ!! 思わずえせ関西弁で突っ込みたくなる結果です。 ってことは、無いもの指定していた設定ファイルである、 /config/initializers/rails-footnotes.rb が悪いということに。 この設定ファイルは公式の推奨通りのコマンドを打つと生成されるようになっていて、テンプレートファイルが lib/generators/templates/rails_footnotes.rb にあります。

  # Only toggle some notes :
  # f.notes = [:session, :cookies, :params, :filters, :routes, :env, :queries, :log, :general]

gem を generalgrep しまくっていたので存在には気づいていたんですが、この配列末尾の :general があるのがいけなかったんですね。 実際に使っているイニシャライザ側でこの記述を削除したところ、例外が発生すること無く正常に動作することを確認できました。 めでたしめでたし。

プルリクエストを送る

問題は解決しているんですが、同様の問題で苦しむ方がもしかしたらいるかもしれません。 本来であれば GeneralNote を機能的に削除した際に、このテンプレートから :general の記述を削除するべきだった訳ですから、gem のバグといっても良いでしょう。 バグは修正されるべきなので、プルリクエスト(以下PR)を送ってみることにしました。

PRを簡潔に説明したサイトをうまく見つけられませんが、要約すると「俺様がやったこの素敵改修を見てくれ。気に入ったら取り込んでね?」って機能です。 git ではなく github の機能で、bitbucket などでも使えます。 PRが送られると送り主のコメントの他、修正箇所のコードの差分が視覚的に分かりやすく表示され、素敵改修の中身をチェックすることが出来ます。 コードの行単位でコメントをし合えるので、「このコードどういう意味?」「ここってこうした方がいいんじゃない?」という風にコードレビューを物理的に離れた人同士で行える、とても強力な機能です。

PRは github や bitbucket をチーム開発で採用していれば普通に使う機能なので、これ自体には慣れていました。 とはいえ、OSS でやるのははじめて。ドッキドキです。 git の commit コメントを書く段階から、これ通じるかなー大丈夫かなーと汗かきながらがんばりました。 その結果がこれ。

fix Footnotes General Exception, because general note was removed. by oakbow · Pull Request #126 · josevalim/rails-footnotes · GitHub

f:id:oakbow:20150222175346p:plain

やったー!! 無事マージしてもらえました。 次のバージョンで反映されるそうです。

修正内容的にコミッターにはすぐそれと分かる内容だったでしょうから、特に議論などもなくマージされています。 そうなるだろうとは思ってましたが、心配で1時間に1回くらいリロードしてたのは秘密です。

gem のソースを追うやり方やデバッグのやり方は、正直もう少し効率の良いやり方がある気がします。 ソースを見るのは普段使っている RubyMine が便利なのでそれはいいとして、デバッグライトはなあ。。 ブレークポイントうまく使えればいいんですが、そのうち調べてみようと思います。

英訳に当たっては、@kon_yuさん に助けていただきました。ありがとうございます(それでも英語がアレなのはもちろん私の問題です)。