見出し画像

バグ解決のプロセス例 -Rails nested_form_fields gem-

どうも、promitsuです。

この記事は、僕の以前のnoteアカウントからのお引越し記事(加筆修正あり)です。

さて、突然ですが、僕は以前、オンラインのプログラミング学習サポートサービスやメンターマッチングサービスなどで、初学者の方へのプログラミングに関するメンタリングを行なっていたことがあります。

今日は、そのメンタリングの過程で、とあるバグを(暫定)解決した時のお話を書こうと思います。

ある日、僕とメンティーのAさんのメンタリング専用のSlackチャンネルに、Aさんから以下のようなコメントが届きました。

Aさん wrote:
promitsuさん、お疲れさまです。
現在、nested_form_fieldsというgemを利用して、一つのページにクイズを複数表示できる機能を実装しているのですが、クイズのremoveボタンがうまく動作してくれません。
removeボタンを押すと、下の動画のように、特定のクイズを削除するのではなく、ページを一度リフレッシュした時と同じように、作成途中のクイズまで全て消えてしまいます。
下の動画で説明すると、テスト2,テスト3を作成後、テスト3のデリートボタンを押すと、テスト2まで消えてしまいます。
どうすれば解決できるか、お知恵を貸していただきたいです。
動画の画面のViewは、quizzes/edit.html.erbです。
データはもうpushしてあります。
すでに何時間か使っていますが、解決できません・・・
なにとぞよろしくお願いいたします

画像1

(アニメーションGIFのアップロードに失敗するのでスクショで代替)

promitsu wrote:
了解です。ちょっとこの後見てみますね〜。
ということで、調査開始です。

まずは、なによりバグが再現できる環境を作らなくてはいけません。
ということで、Aさんの開発しているアプリのGitリポジトリからコードをCloneしてきて、各種設定を行い、サーバを起動してみます。サーバの起動は問題なく出来たので、実際にアプリにログインして、バグが発生する画面に移動してみます。

画像2

(アニメーションGIFのアップロードに失敗するのでスクショで代替)

なるほど、確かにAさんが言っている挙動になっている。
僕はこの挙動を見て、

「本来、Deleteボタンが押されるとJavascriptによるDOM操作が行われてテスト入力のフォームコンポーネントが削除されるはずが、何かの原因でJavascriptが上手く機能せず、画面全体を再読込してしまっている」

のかなと思いました。

そこで、Chromeのデベロッパーツールを使い、関連性のあるJavascriptのエラーが発生していないかを確認してみました。するとDeleteボタンを押したタイミングで下記のようなエラーが発生しているのをみつけました。

画像3

該当するJavascriptのソースを確認してみると、以下の部分でエラーが発生しているようです。

画像4

さらに、DeleteボタンのHTMLを確認すると、ボタンの実体としては、remove_nested_fields_linkのクラスが設定されたaタグになっていました。

画像5

さっき見たJavascirptのエラー箇所はremove_nested_field_linkクラスが設定されているaタグをクリックした時に発火する処理でした。総合すると、今回のバグな挙動は、Javascriptの下記のコードで $.rails.allowAction() がエラーのため falseになってしまい、if文の中のreturn false が実行されていることが原因だろうと言うことがわかってきました。

if (!$.rails.allowAction($link)) {
 return false;
}

この場合、return falseされるとDOM操作のための後続処理が実行されず、本来のaタグクリック時の動作である「hrefで設定されたリンク先への遷移処理」が実行されますが、該当するHTMLを見るとわかるように、今回はhref要素はなにも設定されていないので、deleteボタンを押下すると表示中のページのURLに対して再読込をかけてしまうことになります。
したがって、Add newボタンクリックによるDOM操作で追加されたクイズ入力用のフォームコンポーネントは破棄され、初期状態の当該ページが再表示されることになります。

さて、挙動の原因はわかったので、次はなぜ $.rails.allowAction で
「Uncaught TypeError: $.rails.allowAction is not a function」が発生しているのかについて調査します。ブレークポイントをはり、Deleteボタンをクリックした時のJavascriptの処理をステップ実行して、$.railsオブジェクトは存在しているが、そのオブジェクトがallowActionというメソッドを持っていないということを確認しました。

画像6

この時点で僕は、

「多分、他のライブラリですでに$.railsというオブジェクトが定義されていて、そのオブジェクトにallowAction()というメソッドが無いのだ」

と思いました。
と同時に、このバグは既知のバグである可能性も考えました。そこでnested_form_fields gemのGithubリポジトリのIssueを確認してみました。
すると、下記のようなIssueを見つけました。

まさに、どんぴしゃの内容ですね(笑)このIssueのスレッドに下記のようなコメントがされていました。

画像7

なるほど、つまりjquery-ujsが読み込まれていれば$.rails.allowActionでエラーは発生しないはずであると...。application.jsを確認してみるとたしかにrequireされていない。
調べてみると、それまでRails標準のJavascriptライブラリだったjquery-ujsが、Rail5.1以降jQueryへの依存から脱却するためにrails-ujsへと変更されているようでした。(Rails使ってるのに把握してなかった(汗))
そこで、再度 nested_form_fields gemのIssueを見てみると、やっぱりこの事に関するIssueがあったのですが...

画像8

ということらしいので、現状nested_form_fields gemはrails-ujsに対応していないみたいでした。

とりあえず、nested_form_fields gemを動かすためにrails-ujsの代わりにjquery-ujsをrequireしてみます。

//= require jquery
//= require jquery.turbolinks
-//= require rails-ujs

//= require turbolinks
//= require bootstrap
//= require_tree .
+//= require jquery_ujs
//= require nested_form_fields


この状態で当該画面の挙動を確認すると

画像9

(アニメーションGIFのアップロードに失敗するのでスクショで代替)

期待通りの挙動になりました。

とはいえ、今回のrails-ujsからjquery-ujsへの変更で、他の機能や挙動に不具合が起きないかを一通り確認する必要があります。
また、今後このgemを使い続けていくということであれば、rails-ujsを使った実装を開発元に提案(PullReq)することも考えなければいけません。

とりあえず一通り動作を確認して、特に不具合もなさそうだったので、今回はこの対応で完了ということにしました。(おしまい)

----

この記事は、プログラミングのメンタリングの一貫として、Aさんにバグ調査の際のプロセスを説明した時に、
「それブログの記事にできますよ!」
と言われたことをきっかけに書いてみました。

なるほど、こういうのもブログ記事になるんですね。
一つ勉強になりました。

今はメンターのお仕事はしていませんが、ココナラやTIMETICKETなどでサービス販売をしたりしています。

ではまた〜。

この記事が気に入ったらサポートをしてみませんか?