Upgrade to Pro — share decks privately, control downloads, hide ads and more …

RuboSensei

Avatar for Kuniaki IGARASHI Kuniaki IGARASHI
November 09, 2023
270

 RuboSensei

リアルタイムアドバイスツールRuboSenseiによる学習体験の向上
RubyWorld Conference 2023

Avatar for Kuniaki IGARASHI

Kuniaki IGARASHI

November 09, 2023
Tweet

Transcript

  1. RuboSenseiで実装したアドバイス elsifはcaseで書き換えよう unless else はやめてifに書き換えよう このeachはmapで書けるかも method(&:method) で書けるかも method(&:method) の説明

    ナンバーパラメータの説明 パターンマッチcase inの説明 1行パターンマッチinの説明 1行パターンマッチ=>の説明 ぼっち演算子の説明 ||= の説明 !!variable の説明
  2. コーディング中にリアルタイムアドバイス表示する仕組みの検討 構成案: アドバイスプログラム - LSP - エディタ LSP(Language Server Protocol)

    エディタを制御するためのプロトコル LSPがエディタ間の差を吸収してくれる LSPに対応しているエディタ、たとえばVSCodeやEmacsで利用可能になる この構成でも良いが、RuboCopのエコシステムに乗ると楽ができる RuboCop: Rubyコード向け静的解析検査ツールとその検査ルール集
  3. RuboCopを利用したときの構成 構成: RuboSensei - RuboCop - LSP - エディタ RuboCopにはLSP機能が標準でついていて、リアルタイムに検査結果をエディタで表示

    アドバイス対象のコード判定と表示文章をRuboCopカスタムCop(検査ルール)で実装 実行や表示などLSPより後ろはRuboCopがやってくれる RuboCopのオートコレクト機能をつかってコード置換も可能
  4. RuboSenseiのつかい方 2 .rubocop.yml を以下の内容で作成または追記 RuboSenseiの各種アドバイスはLectureカテゴリに入っているので有効化 (LectureはRuboCop標準ではつかわれていない独自カテゴリ) require: - rubocop-sensei Lecture:

    Enabled: true もしも、RuboSenseiのアドバイスだけに限定して、RuboCopの他の表示を消すときは 以下を追加してください AllCops: DisabledByDefault: true
  5. RuboSenseiの実装解説 RuboCopではコードを静的解析したASTをつかって、対象コードを判別します 要素技術 静的解析 構文解析 Parser gem: コードを解析してASTを得ます(後述) 静的解析 型解析

    TypeProf : 一部の解析に型推論をつかっています RuboCop: カスタムCop(判定ルール)としてアドバイス表示を実装 LSP(Language Server Protocol): エディタと情報をやりとりする仕様 VSCode: LSPに対応したエディタ 今日は要点だけ説明します RuboCopカスタムCopのつくり方については以下のページに書きました Railsの練習帳: RuboCop カスタムCopのつくり方 https://zenn.dev/igaiga/books/rails-practice- note/viewer/rails_rubocop_custom_cop
  6. RuboSensei実装例: アドバイス対象コード !!foo def enable? !!foo end このRubyコードの !!foo に対してアドバイスを表示

    !!foo に対するアドバイス 真偽値を反転するnot演算を行う!を2回つかっているコード 真偽値true, falseに変換したいときにつかう fooに代入されたオブジェクトがfalseかnilのときはfalseを返す それ以外のオブジェクトではtrueを返す 末尾が?で終わるメソッド(真偽値を返す)などでよくつかわれる
  7. !!foo の意味をアドバイスするRuboSenseiのコード class ExplainBangBang < Base MSG = <<~STRING `!!`

    はオブジェクトをtrue またはfalse に変換する書き方です。 # 略 STRING def on_send(node) if node.send_type? && node.method_name == :! if node.children.any?{|n| n.respond_to?(:send_type?) && n.send_type? && n.method_name == :! } add_offense(node) return end end end end https://github.com/igaiga/rubocop- sensei/blob/main/lib/rubocop/cop/lecture/explain_bang_bang.rb
  8. RuboCopカスタムCopを実装して判定する流れ Parser Gemの支援 RuboCopは標準でParser Gemをつかうようになっている 対象コードを静的解析してASTを生成 コールバックメソッド: on_send AST中にメソッド呼び出しである :send

    が出てきたら呼び出してくれる 解析結果ASTをon_sendメソッドの引数(node)に渡してくれる ほかにもいろいろな便利なコールバックメソッドがある
  9. RuboCopカスタムCopを実装する方法 RuboCopカスタムCopをつくるときの主な作業 表示するメッセージを用意する ASTを調べてアドバイス表示したいコードかどうかを判定する 判定の結果、表示するときはadd_offenseメソッドを呼び出す class ExplainBangBang < Base #

    表示するメッセージ MSG = "`!!` はオブジェクトをtrue またはfalse に変換する書き方です。( 略)" def on_send(node) # メソッド呼び出しのときに呼ばれる if # ここでAST を判定 add_offense(node) # メッセージを表示する return end end end
  10. Parser Gem が生成するAST コード !!foo AST on_sendメソッドの引数nodeに代入されて渡ってくる s(:send, s(:send, s(:lvar,

    :foo), :!), :!) 判定コード def on_send(node) # ここでAST を見て判定する add_offense(node) # アドバイス対象のときadd_offense メソッドを呼ぶ end
  11. ASTが対象コードかを判定するコード s(:send, s(:send, s(:lvar, :foo), :!), :!) ASTを観察して判定条件を考える (:send, xxx,

    :!) の中にもう1つ (:send, xxx, :!) が存在する (:send, xxx, :!) 判定 node.send_type? && node.method_name == :! 中にあるか判定 node.children.any?{|n| # ここで(:send, xxx, :!) か判定 }
  12. 対象コードかを判定するコード できあがった判定コード def on_send(node) if node.send_type? && node.method_name == :!

    if node.children.any?{|n| n.respond_to?(:send_type?) && n.send_type? && n.method_name == :! } add_offense(node) return end end end
  13. 一般的なRuboCopカスタムCopとRuboSenseiの差異 理解のためのアドバイスか、問題点の指摘か RuboSenseiでは学習者が理解しやすいアドバイスを(問題点以外でも)出力する 一般的なRuboCop Copは問題のあるコードを指摘する 難易度 RuboSenseiでは初学者も理解しやすい難易度のアドバイスを目指す 一般的なRuboCop Copのメッセージを初学者が理解するのは難しい RubyKaigi

    2022 講演 "The Better RuboCop World to enjoy Ruby" でも問題提起 有効設定、無効設定 RuboSenseiでは学習者が学んだらアドバイスをオフにしたい Lectureカテゴリを用意 RuboSenseiのアドバイスだけON/OFF可能に 一般的なRuboCop Copはコード品質向上を目的として永続的に有効にする
  14. GitHub Copilot chat 注意が必要な回答例 ActiveRecordのLIKE検索について質問したときの答え where("title LIKE ?", "%#{search}%") 動作はするが、LIKE節に関するサニタイズがなく、SQLインジェクションの可能性あり

    通常のwhereではプレースホルダをつかえばOKだが、LIKE節ではサニタイズが必要 詳しい解説: https://zenn.dev/igaiga/books/rails-practice-note/viewer/ar_sql_injection
  15. どんな世界をつくりたいか 書籍、資料とコーディング環境の距離を縮めたい 例: 「このコードはこの本のこの部分で説明されています」と教えてくれる 書籍「Ruby超入門」「パーフェクトRails」「Railsの練習帳」など 例: 「もっと学びたいときはこの資料が役立ちます」と教えてくれる 書籍「RubyとRailsの学習ガイド」など 目指したい良い学びとは 書いたコードに対して学ぶと良いことがあることを知れる

    それを学ぶための正しさが担保された、現在の能力で理解できる資料を読める 深掘りしたいときに学ぶ資料、質問できる人やコミュニティへアクセスできる 地道な設計実装計画: RuboSensei 夢のある計画: AIに自分の資料を学習させて質問対応 LLM(大規模言語モデル)+RAG(検索強化済み文章生成)などのアプローチ (後述) AIに学習させやすい書籍資料の形を模索、ライセンス面でAI学習材料許諾