Project

General

Profile

バグ #1357

定義にブロックを伴う抽出条件に対して演算子「=」を使用すると、抽出条件のブロックが評価されない

Added by Shibafu Midorino about 2 years ago. Updated about 2 years ago.

Status:
終了
Priority:
通常
Target version:
プラグイン名:
extract
ブランチ:
topic/1357-catch-up-yukari-query
クラッシュする:
No

Description

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#== のオーバーライドが必要なのでは?と考えていますが、抽出プラグインの仕組みの理解が浅いので実装には踏み切れていません。
原因と修正先が正しいかをチェックしていただきたいです。

#1

Updated by toshi_a 初音 about 2 years ago

  • Status changed from 分類待ち to 実装待ち

再現しました。

以下、やるとき用メモ

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受信のたびに払うオーバーヘッドを縮小できるかもしれない。

#2

Updated by toshi_a 初音 about 2 years ago

  • Status changed from 実装待ち to レビュー待ち
  • Assignee set to Shibafu Midorino
  • ブランチ set to topic/1357-catch-up-yukari-query

お願いします

#3

Updated by Shibafu Midorino about 2 years ago

  • Status changed from レビュー待ち to マージ待ち

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
#4

Updated by toshi_a 初音 about 2 years ago

  • Status changed from マージ待ち to 終了

merged.

Also available in: Atom PDF