Generateとは

Plugin.call でイベントを呼び出す時、そのイベントが実際いくつのプラグインで受け取られるかは呼び出し側にはわかりません。これは意図的な仕様で、mikutterの拡張性を担保するための重要な要素です。

Streamの登場によって、イベントを使って断続的に終わりのないデータが流れ続ける、ストリームを実現することができるようになりました。しかし、それはリスナ(受け取り側)の話であって、結局イベントを発生させるのは Plugin.call を使う必要がありました。

generateメソッドは、Streamを提供する側の表現力を上げ、効率的な実装を実現するための機能です。

以下に例を挙げます。説明のために、 user_stream というメソッドは、投稿を受信する度にModelを1つ引数にとって呼び出されることとします。

従来のイベント発生方法
user_stream do |message|
  Plugin.call(:appear, [message])
end
generateを使った例
generate(:appear) do |yielder|
  user_stream do |message|
    yielder << message
  end
end

Generateが解決すること

イベント発生側もイベント発生回数を意識しなくて良くなる

呼び出し回数の最適化では、リスナがイベントの数を意識することがなくなると説明されています。

確かにsubscribeを使えばリスナの呼び出し回数の最適化の余地はありますが、イベントを発生させる側がn個のイベントを発生させている以上、イベント発生のオーバーヘッドはあります。

generateメソッドを使えば、generateを使った例のように、イベント発生ではなくストリームのような構文になっているため、複数のイベントをまとめることができます。

購読されるまでストリームを提供しない

generateメソッドに渡されたブロックは、他のプラグインでsubscribeや通常のイベントリスナ(on_???)でリスナが登録されるまで実行されません。

generateを使った例user_stream メソッドを呼び出すとSNSのサーバにHTTPコネクションを確立するとしましょう。appearイベントを受け取るプラグインが存在しなければ、わざわざインターネットから投稿を取得して居るにも関わらず、それをすべて捨てていることになります。

そこでgenerateを使った例のような実装をすれば、appearイベントが購読されて初めて接続するようになります。実際にこの仕組みはMastodonプラグインに利用されています。あらゆるデータがあらゆるストリームでいつでも受け取れるように見えるにも関わらず、実際には必要とされている情報しかMastodonサーバから取ってこないようになっているのです。

普通、そのような最適化をするなら、イベントが購読されているか常に確認し続け、購読状況に応じてサーバに接続するような実装をする必要があるでしょう。generateメソッドを使えば、そのような判断をPluggaloidに一任できます。

いつGenerateを使うか

まず、generateメソッドの対象のイベントがストリームイベントである必要があります。ストリームイベントではない、または不明な場合は従来のイベント発生方法を使います。

generateメソッド

generate(event_slug, *rest) { |yielder| …​ } → nil

引数名 意味

event_slug

Symbol

subscribeするイベントの識別子

*rest

-

ストリーム引数以外の引数

yielder

Pluggaloid::Yielder

ストリーム引数に含める値を受け取るオブジェクト

subscribeで、 event-slugrest が完全に一致するリスナが一つでも登録されたら、ブロックを実行します。

on_ event_slug でリスナが定義されると、 rest の内容に関わらず、即座にブロックが実行されます( rest が一致したと見做して処理をします)。

条件を満たすsubscribeが2つ以上になってもブロックは最初の一度しか実行されませんが、subscribeが解除されて0になると、ブロック内で定義された全てのイベントリスナ等が解除されます。

解除されるものは以下の通りです。

  • on_??? で登録したイベントリスナ

  • filter_??? で登録したフィルタのリスナ

  • subscribe

Pluggaloid::Yielder

generateメソッドのブロックの引数に渡されるオブジェクトです。 このオブジェクトに、ストリームに流したい値を渡すことで、イベントが発行されます。

self << object → self

引数名 意味

object

-

ストリームに流すオブジェクト

戻り値

Pluggaloid::Yielder

self

ストリームに一つ値を追加します。generateメソッドに指定したストリーム引数以外の引数を伴ってイベントが発生します。

bulk_add(objects) → self

引数名 意味

objects

Enumerable

ストリームに流すオブジェクトの配列等

戻り値

Pluggaloid::Yielder

self

ストリームに2つ以上の値を同時に追加します。generateメソッドに指定したストリーム引数以外の引数を伴ってイベントが発生します。一度にたくさんのオブジェクトを追加したい場合、self << object → selfよりこちらのほうが効率的です。

互換性

generateメソッドは、イベントの発生をストリームのような構文で実現するためのものであり、あくまでイベントを発生させているだけです。したがって、generateメソッドを使ったからといって on_??? のような形式で受け取ることができなくなるわけではありません。

Table 1. メソッドと互換性
受け取り\呼び出し Plugin.call generateメソッド

on_???

subscribe