バグ #1357
完了定義にブロックを伴う抽出条件に対して演算子「=」を使用すると、抽出条件のブロックが評価されない
説明
defextractcondition でブロックを伴う抽出条件を定義し、抽出タブの絞り込み条件で演算子に「=」を使用するとその抽出条件が評価されなくなります。
この際、評価結果は偽の扱いになりますので、常にその条件ではマッチしなかったことになってしまいます。
経緯¶
1. 以下のようなプラグインを開発していたところ、debugオプションで起動してもnoticeが出力されておらず、ブロックが全く評価されていないことに気づく。
# -*- coding: utf-8 -*-
Plugin.create(:extract_retweeter) do
defextractcondition(:retweeter, name: 'リツイートしたユーザ名', operator: true, args: 1) do |argument, message: raise, operator: raise, &compare|
notice "|||||||| eval extract_retweeter ||||||||"
message.retweeted_by.any? { |u| compare.(u.idname, argument) }
end
end
2. Slackにて情報交換を行い、演算子「=」に問題がある可能性が浮上する。
原因¶
ブロックを伴う抽出条件の評価に使用されている、extract.rb の Plugin::Extract::Calc クラスが == 演算子を期待通りに処理できていません。
- いずれかにマッチする: ON
- リツイートしたユーザ名 = miku (条件1)
- リツイートしたユーザ名 ≠ yukari (条件2)
すると、extract.rb の compile メソッド内ではMIKU関係の処理を経由して以下のコードが生成されます。
lambda{ |message|
retweeter = Plugin::Extract::Calc.new(message, extract_condition[:retweeter])
if retweeter == "miku"
true
else
true
if retweeter.__send__(:!=, "yukari")
true
else
true
nil
end
end
}
先に上手く動く方をおさらいすると、条件2は retweeter.__send__(:!=, "yukari") という式になり Plugin::Extract::Calc#method_missing を経由して抽出条件のブロックが評価されます。
(あまり読み込めていないのですが __send__ の第1引数として、演算子が指定されていることと推測しています。)
一方、条件1は retweeter "miku" とコンパイルされています。この場合 Plugin::Extract::Calc#method_missing ではなく演算子の既定動作である Object# のほうが呼ばれてしまいます。
そのため、この式で抽出条件のブロックが評価されることはなく、結果は常に偽となります。
どうしたいか¶
Plugin::Extract::Calc#== のオーバーライドが必要なのでは?と考えていますが、抽出プラグインの仕組みの理解が浅いので実装には踏み切れていません。
原因と修正先が正しいかをチェックしていただきたいです。
toshi_a 初音 さんが6年以上前に更新
- ステータス を 分類待ち から 実装待ち に変更
再現しました。
以下、やるとき用メモ
BasicObject#== がある以上、これをmethod_missingでトラップするのは不可能なので、Plugin::Extract::Calcに必要なメソッドを動的に定義する。
多分一番楽なのがPlugin::Extract::Calcが初期化されるたびに、インスタンスに動的にdefine_methodなどでメソッドを定義するやり方。Plugin::Extract::Calcは使い捨てられるので、プラグインのロードによってあとからPlugin::Extarct::ExtensibleConditionが増えても問題ない。
高速化するならPlugin::Extarct::ExtensibleConditionが増減するたびに(compileが呼ばれるタイミングで良さそう)Plugin::Extract::Calcの無名サブクラスを作って、そちらでメソッドをオーバライドする方法。これならMessage受信のたびに払うオーバーヘッドを縮小できるかもしれない。
toshi_a 初音 さんが6年以上前に更新
- ステータス を 実装待ち から レビュー待ち に変更
- 担当者 を Shibafu Midorino にセット
- ブランチ を topic/1357-catch-up-yukari-query にセット
お願いします
Shibafu Midorino さんが6年以上前に更新
- ステータス を レビュー待ち から マージ待ち に変更
RTを待つのはつらいので、以下のプラグインを利用しつつあひるを焼いて検証しました。問題なく動作しています!
Plugin.create(:extract_body_ruby) do
defextractcondition(:body_ruby, name: '本文 (Ruby)', operator: true, args: 1) do |argument, message: raise, operator: raise, &compare|
compare.(message.to_s, argument)
end
end