pluggaloid 1.3.1 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd795f27416c6cff9155f4adbfa97cb04192a48e891d387a1a5c37a04b5b681c
4
- data.tar.gz: 82a3a43fa8f01c34b879c2d85d19f0193d54e8fa3e9989040559c5b40b70e9f4
3
+ metadata.gz: d47426a6d55a1f8bb30898b9056766dceff9346b1fdc33561f6c21af9c2c9f98
4
+ data.tar.gz: c9953550293c3f3be07e5ef429d29f90651717f74b443613cd274b702578c938
5
5
  SHA512:
6
- metadata.gz: b943be558a442022cd1d3ca2fec636ec6f6b8a9498cd2a8cc2fb8a3227df8675fc06f50d90d19af0c3db648703952c37123b0d41e2fc3ff4f686d0a87726d6c6
7
- data.tar.gz: 3f1b7c5fcfa142ad280c3a1c67405ae42536bdcca32b9e2d1ed05a34d394602509bada0001657bfc1eaa48b1aa6465f941513119508179d61feeca52fdd3441e
6
+ metadata.gz: 9ac80a75430f7721d6752698deb760b883fe3f9e0a2cf4cdd5b2866b164c01815064749dd9d0acfcdbe799aad88e91346a03272ad171fdab1df20658d14c0688
7
+ data.tar.gz: ca65d05402e64b7daa429596275d25d2af72806c66009f7e10321f2a6a1d9c4ec9f1d7c7e6dd29494394262ad26dd13cbb8fe58374bf8a2de28072d380de5960
@@ -0,0 +1,43 @@
1
+ version: '2.1'
2
+
3
+ executors:
4
+ ruby:
5
+ parameters:
6
+ tag:
7
+ type: string
8
+ docker:
9
+ - image: circleci/ruby:<< parameters.tag >>
10
+
11
+ jobs:
12
+ build:
13
+ parameters:
14
+ ruby-version:
15
+ type: string
16
+ executor:
17
+ name: ruby
18
+ tag: << parameters.ruby-version >>
19
+ steps:
20
+ - checkout
21
+ - run:
22
+ name: Which bundler?
23
+ command: bundle -v
24
+ - run:
25
+ command: bundle install --path vendor/bundle
26
+ - run:
27
+ name: test
28
+ command: bundle exec rake test
29
+ workflows:
30
+ build:
31
+ jobs:
32
+ - build:
33
+ name: 'ruby-2.5'
34
+ ruby-version: '2.5.9'
35
+ - build:
36
+ name: 'ruby-2.6'
37
+ ruby-version: '2.6.7'
38
+ - build:
39
+ name: 'ruby-2.7'
40
+ ruby-version: '2.7.3'
41
+ - build:
42
+ name: 'ruby-3.0'
43
+ ruby-version: '3.0.1'
data/README.md CHANGED
@@ -3,6 +3,8 @@
3
3
  mikutterのプラグイン機構です。
4
4
  登録したプラグイン同士がイベントを使って通信できるようになります。
5
5
 
6
+ [![toshia](https://circleci.com/gh/toshia/pluggaloid.svg?style=svg)](https://circleci.com/gh/toshia/pluggaloid)
7
+
6
8
  ## Installation
7
9
 
8
10
  Add this line to your application's Gemfile:
data/lib/pluggaloid.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  require "pluggaloid/version"
2
+ require 'pluggaloid/collection'
2
3
  require "pluggaloid/plugin"
3
4
  require 'pluggaloid/stream'
4
5
  require 'pluggaloid/event'
@@ -7,16 +8,20 @@ require "pluggaloid/handler"
7
8
  require 'pluggaloid/listener'
8
9
  require 'pluggaloid/subscriber'
9
10
  require 'pluggaloid/filter'
11
+ require 'pluggaloid/stream_generator'
10
12
  require "pluggaloid/handler_tag"
13
+ require 'pluggaloid/mirage'
11
14
  require 'pluggaloid/error'
12
15
 
13
16
  require 'delayer'
14
17
 
15
18
  module Pluggaloid
16
- VM = Struct.new(*%i<Delayer Plugin Event Listener Filter HandlerTag Subscriber>, keyword_init: true)
19
+ VM = Struct.new(*%i<Delayer Plugin Event Listener Filter HandlerTag Subscriber StreamGenerator>, keyword_init: true)
17
20
 
18
21
  class PrototypeStream; end
22
+ class PrototypeCollect; end
19
23
  STREAM = PrototypeStream.new.freeze
24
+ COLLECT = PrototypeCollect.new.freeze
20
25
 
21
26
  def self.new(delayer)
22
27
  vm = VM.new(Delayer: delayer,
@@ -25,7 +30,8 @@ module Pluggaloid
25
30
  Listener: Class.new(Listener),
26
31
  Filter: Class.new(Filter),
27
32
  HandlerTag: Class.new(HandlerTag),
28
- Subscriber: Class.new(Subscriber))
33
+ Subscriber: Class.new(Subscriber),
34
+ StreamGenerator: Class.new(StreamGenerator))
29
35
  vm.Plugin.vm = vm.Event.vm = vm
30
36
  end
31
37
  end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pluggaloid
4
+ class Collection
5
+ attr_reader :values
6
+
7
+ def initialize(event, *args)
8
+ @event = event
9
+ args[event.collect_index] = nil
10
+ @args = args.freeze
11
+ @spec = argument_hash(args)
12
+ @values = [].freeze
13
+ end
14
+
15
+ def add(*v)
16
+ rewind do |primitive|
17
+ primitive + v
18
+ end
19
+ end
20
+ alias_method :<<, :add
21
+
22
+ def delete(*v)
23
+ rewind do |primitive|
24
+ primitive - v
25
+ end
26
+ end
27
+
28
+ def rewind(&block)
29
+ new_values = block.(@values.dup)
30
+ added, deleted = new_values - @values, @values - new_values
31
+ @values = new_values.freeze
32
+ unless added.empty?
33
+ args = @args.dup
34
+ args[@event.collect_index] = added
35
+ @event.collection_add_event.call(*args)
36
+ end
37
+ unless deleted.empty?
38
+ args = @args.dup
39
+ args[@event.collect_index] = deleted
40
+ @event.collection_delete_event.call(*args)
41
+ end
42
+ self
43
+ end
44
+
45
+ def argument_hash_same?(specs)
46
+ @spec == argument_hash(specs)
47
+ end
48
+
49
+ private
50
+
51
+ def argument_hash(specs)
52
+ @event.argument_hash(specs, @event.collect_index)
53
+ end
54
+ end
55
+ end
@@ -11,4 +11,10 @@ module Pluggaloid
11
11
  class NoDefaultDelayerError < Error; end
12
12
 
13
13
  class DuplicateListenerSlugError < Error; end
14
+
15
+ class UndefinedStreamIndexError < Error; end
16
+
17
+ class UndefinedCollectionIndexError < Error; end
18
+
19
+ class NoReceiverError < Error; end
14
20
  end
@@ -18,7 +18,26 @@ class Pluggaloid::Event
18
18
  @options = {}
19
19
  @listeners = [].freeze
20
20
  @filters = [].freeze
21
- @subscribers = {}
21
+ @subscribers = Hash.new { |h, k| h[k] = [] }
22
+ @stream_generators = Hash.new { |h, k| h[k] = Set.new }
23
+ end
24
+
25
+ def prototype
26
+ @options[:prototype]
27
+ end
28
+
29
+ # イベント _event_name_ を宣言する
30
+ # ==== Args
31
+ # [new_options] イベントの定義
32
+ def defevent(new_options)
33
+ @options.merge!(new_options)
34
+ if collect_index
35
+ new_proto = self.prototype.dup
36
+ new_proto[self.collect_index] = Pluggaloid::STREAM
37
+ collection_add_event.defevent(prototype: new_proto)
38
+ collection_delete_event.defevent(prototype: new_proto)
39
+ end
40
+ self
22
41
  end
23
42
 
24
43
  def vm
@@ -71,19 +90,29 @@ class Pluggaloid::Event
71
90
  end
72
91
  @listeners = [*@listeners, listener].freeze
73
92
  end
93
+ @stream_generators.values.each do |generators|
94
+ generators.each(&:on_subscribed)
95
+ end
74
96
  when Pluggaloid::Subscriber
97
+ accepted_hash = listener.accepted_hash
75
98
  Lock.synchronize do
76
- @subscribers[listener.accepted_hash] ||= []
77
- @subscribers[listener.accepted_hash] << listener
99
+ @subscribers[accepted_hash] << listener
78
100
  end
101
+ @stream_generators.fetch(accepted_hash, nil)&.each(&:on_subscribed)
79
102
  else
80
103
  raise Pluggaloid::ArgumentError, "First argument must be Pluggaloid::Listener or Pluggaloid::Subscriber, but given #{listener.class}."
81
104
  end
82
105
  self
83
106
  end
84
107
 
85
- def subscribe?(*args)
86
- !@listeners.empty? || @subscribers.key?(argument_hash(args))
108
+ # subscribe(_*specs_) で、ストリームの受信をしようとしているリスナが定義されていればtrueを返す。
109
+ # on_* で通常のイベントリスナが登録されて居る場合は、 _*specs_ の内容に関わらず常にtrueを返す。
110
+ def subscribe?(*specs)
111
+ !@listeners.empty? || @subscribers.key?(argument_hash(specs, nil))
112
+ end
113
+
114
+ def subscribe_hash?(hash) # :nodoc:
115
+ !@listeners.empty? || @subscribers.key?(hash)
87
116
  end
88
117
 
89
118
  def delete_listener(listener)
@@ -103,6 +132,18 @@ class Pluggaloid::Event
103
132
  @subscribers.delete(listener.accepted_hash)
104
133
  end
105
134
  end
135
+ @stream_generators.fetch(listener.accepted_hash, nil)&.each(&:on_unsubscribed)
136
+ self
137
+ end
138
+
139
+ def delete_stream_generator(listener)
140
+ Lock.synchronize do
141
+ ss = @stream_generators[listener.accepted_hash]
142
+ ss.delete(listener)
143
+ if ss.empty?
144
+ @stream_generators.delete(listener.accepted_hash)
145
+ end
146
+ end
106
147
  self
107
148
  end
108
149
 
@@ -132,25 +173,66 @@ class Pluggaloid::Event
132
173
  self
133
174
  end
134
175
 
135
- def argument_hash(args)
176
+ def argument_hash(args, exclude_index)
136
177
  args.each_with_index.map do |item, i|
137
- if i != yield_index
178
+ if i != exclude_index
138
179
  item.hash
139
180
  end
140
181
  end.compact.freeze
141
182
  end
142
183
 
143
- def yield_index
144
- unless defined?(@yield_index)
145
- @yield_index = self.options[:prototype]&.index(Pluggaloid::STREAM)
184
+ def stream_index
185
+ unless defined?(@stream_index)
186
+ @stream_index = self.prototype&.index(Pluggaloid::STREAM)
146
187
  end
147
- @yield_index
188
+ @stream_index
189
+ end
190
+
191
+ def collect_index
192
+ unless defined?(@collect_index)
193
+ @collect_index = self.prototype&.index(Pluggaloid::COLLECT)
194
+ end
195
+ @collect_index
196
+ end
197
+
198
+ # defeventで定義されたprototype引数に _Pluggaloid::COLLECT_ を含むイベントに対して使える。
199
+ # フィルタの _Pluggaloid::COLLECT_ 引数に空の配列を渡して実行したあと、その配列を返す。
200
+ # ==== Args
201
+ # [*args] Pluggaloid::COLLECT 以外の引数のリスト
202
+ # ==== Return
203
+ # [Array] フィルタ実行結果
204
+ def collect(*args)
205
+ specified_index = args.index(Pluggaloid::COLLECT)
206
+ specified_index&.yield_self(&args.method(:delete_at))
207
+ insert_index = collect_index || specified_index
208
+ if insert_index
209
+ Enumerator.new do |yielder|
210
+ cargs = args.dup
211
+ cargs.insert(insert_index, yielder)
212
+ filtering(*cargs)
213
+ end
214
+ else
215
+ raise Pluggaloid::UndefinedCollectionIndexError, 'To call collect(), it must define prototype arguments include `Pluggaloid::COLLECT\'.'
216
+ end
217
+ end
218
+
219
+ def register_stream_generator(stream_generator)
220
+ @stream_generators[stream_generator.accepted_hash] << stream_generator
221
+ self
222
+ end
223
+
224
+ def collection_add_event
225
+ self.class['%{name}__add' % {name: name}]
226
+ end
227
+
228
+ def collection_delete_event
229
+ self.class['%{name}__delete' % {name: name}]
148
230
  end
149
231
 
150
232
  private
151
233
  def call_all_listeners(args)
152
- if yield_index
153
- @subscribers[argument_hash(args)]&.each do |subscriber|
234
+ if stream_index
235
+ @subscribers[argument_hash(args, stream_index)]&.each do |subscriber|
154
236
  subscriber.call(*args)
155
237
  end
156
238
  end
@@ -19,7 +19,8 @@ class Pluggaloid::Filter < Pluggaloid::Handler
19
19
  # [tags:] Pluggaloid::HandlerTag|Array フィルタのタグ
20
20
  # [&callback] コールバック
21
21
  def initialize(event, **kwrest, &callback)
22
- super
22
+ kwrest[:name] ||= '%s line %i' % callback.source_location
23
+ super(event, **kwrest)
23
24
  @callback = callback
24
25
  event.add_filter self end
25
26
 
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Pluggaloid
4
+ module Mirage
5
+ MIRAGE_ID_BASE_NUMBER = 36
6
+
7
+ module Extend
8
+ # `Pluggaloid::Mirage` をincludeしたClassのうち、`pluggaloid_mirage_identity`
9
+ # メソッドを呼ばれたインスタンスを記録するオブジェクトを返す。
10
+ # 戻り値は、以下のメソッドに応答すること。
11
+ # - `repository#[](String id)` idに対応するオブジェクトを返す
12
+ # - `repository#[]=(String id, self obj)` objを記録する
13
+ # Class毎に適したコンテナを返すようにoverrideすること
14
+ def pluggaloid_mirage_repository
15
+ @pluggaloid_mirage_repository ||= {}
16
+ end
17
+
18
+ # `Pluggaloid::Mirage` をincludeしたClassのnamespaceを返す。
19
+ # namespaceはStringで、 `Pluggaloid::Mirage` をincludeしたほかのClassと
20
+ # 重複しない。
21
+ # 同じClassであれば、別のPluggaloid host(Pluggaloid::VMやプロセス)でも
22
+ # 同じ値を返す。
23
+ def pluggaloid_mirage_namespace
24
+ -to_s
25
+ end
26
+
27
+ def inherited(klass)
28
+ Mirage.pluggaloid_mirage_classes[klass.pluggaloid_mirage_namespace] = klass
29
+ end
30
+ end
31
+
32
+ def self.unwrap(namespace:, id:)
33
+ klass = pluggaloid_mirage_classes[namespace]
34
+ if klass
35
+ result = klass.pluggaloid_mirage_repository[id]
36
+ unless result&.is_a?(Pluggaloid::Mirage) # nilの場合は常にraise
37
+ raise ArgumentError, "The id `#{id}' was not found."
38
+ end
39
+ result
40
+ else
41
+ raise ArgumentError, "The namespace `#{namespace}' was not found."
42
+ end
43
+ end
44
+
45
+ def self.included(klass)
46
+ klass.extend(Extend)
47
+ pluggaloid_mirage_classes[klass.pluggaloid_mirage_namespace] = klass
48
+ end
49
+
50
+ def self.pluggaloid_mirage_classes
51
+ @pluggaloid_mirage_classes ||= {}
52
+ end
53
+
54
+ # このClassのなかで、Pluggaloid::Mirageがインスタンスを同定するためのid(String)を返す。
55
+ # このメソッドではなく、 `generate_pluggaloid_mirage_id` をoverrideすること
56
+ def pluggaloid_mirage_id
57
+ generate_pluggaloid_mirage_id.freeze.tap do |id|
58
+ self.class.pluggaloid_mirage_repository[id] = self
59
+ Mirage.pluggaloid_mirage_classes[self.class.pluggaloid_mirage_namespace] ||= self.class
60
+ end
61
+ end
62
+
63
+ def pluggaloid_mirage_namespace
64
+ self.class.pluggaloid_mirage_namespace
65
+ end
66
+
67
+ private
68
+
69
+ # このClassのなかで、Pluggaloid::Mirageがインスタンスを同定するためのid(String)を返す。
70
+ # Class毎に適したコンテナを返すようにoverrideすること
71
+ def generate_pluggaloid_mirage_id
72
+ object_id.to_s(MIRAGE_ID_BASE_NUMBER)
73
+ end
74
+ end
75
+ end
@@ -24,7 +24,8 @@ module Pluggaloid
24
24
  Listener: Pluggaloid::Listener,
25
25
  Filter: Pluggaloid::Filter,
26
26
  HandlerTag: Pluggaloid::HandlerTag,
27
- Subscriber: Pluggaloid::Subscriber
27
+ Subscriber: Pluggaloid::Subscriber,
28
+ StreamGenerator: Pluggaloid::StreamGenerator
28
29
  )
29
30
  vm.Event.vm = vm end end
30
31
 
@@ -64,6 +65,17 @@ module Pluggaloid
64
65
  def filtering(event_name, *args)
65
66
  vm.Event[event_name].filtering(*args) end
66
67
 
68
+ # フィルタ _event_name_ を実行し、defeventでPluggaloid::COLLECTと
69
+ # 宣言されている引数の結果を列挙するEnumeratorを返す
70
+ # ==== Args
71
+ # [event_name] イベント名(String | Symbol)
72
+ # [*specs] Pluggaloid::COLLECT以外の引数
73
+ # ==== Return
74
+ # [Enumerator]
75
+ def collect(event_name, *specs)
76
+ vm.Event[event_name].collect(*specs)
77
+ end
78
+
67
79
  # 互換性のため
68
80
  def uninstall(plugin_name)
69
81
  self[plugin_name].uninstall end
@@ -116,7 +128,7 @@ module Pluggaloid
116
128
 
117
129
  # イベントフィルタを新しく登録する
118
130
  # ==== Args
119
- # [event] 監視するEventのインスタンス
131
+ # [event_name] イベント名(String | Symbol)
120
132
  # [name:] 名前(String | nil)
121
133
  # [slug:] フィルタスラッグ(Symbol | nil)
122
134
  # [tags:] Pluggaloid::HandlerTag|Array フィルタのタグ
@@ -128,6 +140,10 @@ module Pluggaloid
128
140
  @filters << result
129
141
  result end
130
142
 
143
+ def generate(event_name, *specs, **kwrest, &block)
144
+ vm.StreamGenerator.new(vm.Event[event_name], *specs, plugin: self, **kwrest, &block)
145
+ end
146
+
131
147
  def subscribe(event_name, *specs, **kwrest, &block)
132
148
  if block
133
149
  result = vm.Subscriber.new(vm.Event[event_name], *specs, **kwrest, &block)
@@ -148,6 +164,32 @@ module Pluggaloid
148
164
  vm.Event[event_name].subscribe?(*specs)
149
165
  end
150
166
 
167
+ def collect(event_name, *specs)
168
+ self.class.collect(event_name, *specs)
169
+ end
170
+
171
+ # 追加・削除がフィルタに反映されるコレクションオブジェクトを作成する。
172
+ # 同時に _event_name_ にフィルタが定義され、フィルタが呼ばれると
173
+ # その時点のコレクションオブジェクトの内容を全て列挙する。
174
+ # フィルタと対になるコレクションオブジェクトは、 _&block_ の引数として渡される。
175
+ # ==== Args
176
+ # [event_name] イベント名(String | Symbol)
177
+ # [*specs] Pluggaloid::COLLECT以外の引数
178
+ # [&block] コレクションオブジェクトを受け取って一度だけ実行されるblock
179
+ # ==== Return
180
+ # _&block_ の戻り値
181
+ def collection(event_name, *specs, &block)
182
+ event = vm.Event[event_name]
183
+ mutation = Pluggaloid::Collection.new(event, *specs)
184
+ add_event_filter(event_name, name: 'collection(%s line %i)' % block.source_location) do |*args|
185
+ if mutation.argument_hash_same?(args)
186
+ mutation.values.each(&args[event.collect_index].method(:<<))
187
+ end
188
+ args
189
+ end
190
+ block.call(mutation)
191
+ end
192
+
151
193
  # このプラグインのHandlerTagを作る。
152
194
  # ブロックが渡された場合は、ブロックの中を実行し、ブロックの中で定義された
153
195
  # Handler全てにTagを付与する。
@@ -198,7 +240,6 @@ module Pluggaloid
198
240
  end
199
241
  end
200
242
 
201
-
202
243
  # イベントを削除する。
203
244
  # 引数は、Pluggaloid::ListenerかPluggaloid::Filterのみ(on_*やfilter_*の戻り値)。
204
245
  # 互換性のため、二つ引数がある場合は第一引数は無視され、第二引数が使われる。
@@ -239,7 +280,8 @@ module Pluggaloid
239
280
  # [event_name] イベント名
240
281
  # [options] イベントの定義
241
282
  def defevent(event_name, options={})
242
- vm.Event[event_name].options.merge!({plugin: self}.merge(options)) end
283
+ vm.Event[event_name].defevent({ plugin: self, **options })
284
+ end
243
285
 
244
286
  # DSLメソッドを新しく追加する。
245
287
  # 追加されたメソッドは呼ぶと &callback が呼ばれ、その戻り値が返される。引数も順番通り全て &callbackに渡される
@@ -0,0 +1,88 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'securerandom'
3
+ require 'set'
4
+
5
+ class Pluggaloid::StreamGenerator < Pluggaloid::Handler
6
+ attr_reader :accepted_hash
7
+
8
+ def initialize(event, *specs, plugin:, **kwrest, &callback)
9
+ raise Pluggaloid::UndefinedStreamIndexError, 'To call generate(%{event}), it must define prototype arguments include `Pluggaloid::STREAM\'.' % {event: event.name} unless event.stream_index
10
+ super(event, **kwrest)
11
+ @callback = callback
12
+ @specs = specs.freeze
13
+ @accepted_hash = @event.argument_hash(specs, nil)
14
+ @last_subscribe_state = @event.subscribe?(*@specs)
15
+ @plugin = plugin
16
+ subscribe_start if @last_subscribe_state
17
+ @event.register_stream_generator(self)
18
+ end
19
+
20
+ def on_subscribed
21
+ if !@last_subscribe_state
22
+ @last_subscribe_state = true
23
+ subscribe_start
24
+ end
25
+ end
26
+
27
+ def on_unsubscribed
28
+ subscribe_state = @event.subscribe_hash?(@accepted_hash)
29
+ if @last_subscribe_state && !subscribe_state
30
+ @last_subscribe_state = false
31
+ subscribe_stop
32
+ end
33
+ end
34
+
35
+ # このリスナを削除する
36
+ # ==== Return
37
+ # self
38
+ def detach
39
+ @event.delete_stream_generator(self)
40
+ @yielder&.die
41
+ @yielder = nil
42
+ self
43
+ end
44
+
45
+ private
46
+
47
+ def subscribe_start
48
+ @tag = @plugin.handler_tag do
49
+ @yielder = Yielder.new(@event, args: @specs)
50
+ @callback.call(@yielder)
51
+ end
52
+ end
53
+
54
+ def subscribe_stop
55
+ @plugin.detach(@tag)
56
+ @yielder.die
57
+ @yielder = nil
58
+ end
59
+
60
+ class Yielder
61
+ def initialize(event, args:)
62
+ @event = event
63
+ @args = args.freeze
64
+ @alive = true
65
+ end
66
+
67
+ def bulk_add(lst)
68
+ raise Pluggaloid::NoReceiverError, "All event listener of #{self.class} already detached." if die?
69
+ args = @args.dup
70
+ args.insert(@event.stream_index, lst)
71
+ @event.call(*args)
72
+ end
73
+
74
+ def add(value)
75
+ bulk_add([value])
76
+ end
77
+ alias_method :<<, :add
78
+
79
+ def die?
80
+ !@alive
81
+ end
82
+
83
+ def die
84
+ @alive = false
85
+ freeze
86
+ end
87
+ end
88
+ end
@@ -12,9 +12,10 @@ class Pluggaloid::Subscriber < Pluggaloid::Handler
12
12
  # [tags:] Pluggaloid::HandlerTag|Array リスナのタグ
13
13
  # [&callback] コールバック
14
14
  def initialize(event, *specs, **kwrest, &callback)
15
+ raise Pluggaloid::UndefinedStreamIndexError, 'To call subscribe(%{event}), it must define prototype arguments include `Pluggaloid::STREAM\'.' % {event: event.name} unless event.stream_index
15
16
  super(event, **kwrest)
16
17
  @callback = callback
17
- @accepted_hash = @event.argument_hash(specs)
18
+ @accepted_hash = @event.argument_hash(specs, nil)
18
19
  event.add_listener(self)
19
20
  end
20
21
 
@@ -22,7 +23,7 @@ class Pluggaloid::Subscriber < Pluggaloid::Handler
22
23
  # ==== Args
23
24
  # [stream] イベントの引数
24
25
  def call(*args)
25
- @callback.call(args[@event.yield_index])
26
+ @callback.call(args[@event.stream_index])
26
27
  end
27
28
 
28
29
  # このリスナを削除する
@@ -1,3 +1,3 @@
1
1
  module Pluggaloid
2
- VERSION = '1.3.1'
2
+ VERSION = '1.7.0'
3
3
  end
@@ -0,0 +1,135 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'minitest/autorun'
5
+
6
+ require 'pluggaloid'
7
+ require_relative 'helper'
8
+
9
+ describe(Pluggaloid::Plugin) do
10
+ include PluggaloidTestHelper
11
+
12
+ before do
13
+ Delayer.default = Delayer.generate_class(priority: %i<high normal low>, default: :normal)
14
+ Pluggaloid::Plugin.clear!
15
+ end
16
+
17
+ it 'collect' do
18
+ Pluggaloid::Plugin.create(:event) do
19
+ defevent :list, prototype: [Integer, Pluggaloid::COLLECT]
20
+
21
+ filter_list do |i, yielder|
22
+ i.times(&yielder.method(:<<))
23
+ [i, yielder]
24
+ end
25
+ end
26
+
27
+ assert_equal([0, 1, 2], Pluggaloid::Event[:list].collect(3).to_a)
28
+ end
29
+
30
+ it 'fail when collect with undefined' do
31
+ Pluggaloid::Plugin.create(:event) do
32
+ filter_list do |i, yielder|
33
+ i.times(&yielder.method(:<<))
34
+ [i, yielder]
35
+ end
36
+ end
37
+
38
+ assert_raises(Pluggaloid::UndefinedCollectionIndexError) do
39
+ Pluggaloid::Event[:list].collect(3)
40
+ end
41
+ end
42
+
43
+ it 'success caller arguments include Pluggaloid::COLLECT when collect with undefined' do
44
+ Pluggaloid::Plugin.create(:event) do
45
+ filter_list do |i, yielder|
46
+ i.times(&yielder.method(:<<))
47
+ [i, yielder]
48
+ end
49
+ end
50
+
51
+ assert_equal([0, 1, 2], Pluggaloid::Event[:list].collect(3, Pluggaloid::COLLECT).to_a)
52
+ end
53
+
54
+ it 'success different caller argument specific and prototype definition' do
55
+ Pluggaloid::Plugin.create(:event) do
56
+ defevent :list, prototype: [Integer, Pluggaloid::COLLECT]
57
+
58
+ filter_list do |i, yielder|
59
+ i.times(&yielder.method(:<<))
60
+ [i, yielder]
61
+ end
62
+ end
63
+
64
+ assert_equal([0, 1, 2], Pluggaloid::Event[:list].collect(Pluggaloid::COLLECT, 3).to_a)
65
+ end
66
+
67
+ describe 'collection' do
68
+ it 'add' do
69
+ Pluggaloid::Plugin.create(:event) do
70
+ defevent :list, prototype: [Integer, Pluggaloid::COLLECT]
71
+ defevent :insert, prototype: [Pluggaloid::STREAM]
72
+
73
+ collection(:list, 3) do |collector|
74
+ subscribe(:insert).each do |i|
75
+ collector.add(i * 3)
76
+ end
77
+ end
78
+ end
79
+ eval_all_events do
80
+ Pluggaloid::Event[:insert].call([2, 5])
81
+ end
82
+ assert_equal([6, 15], Pluggaloid::Event[:list].collect(3).to_a)
83
+ end
84
+
85
+ it 'delete' do
86
+ Pluggaloid::Plugin.create(:event) do
87
+ defevent :list, prototype: [Integer, Pluggaloid::COLLECT]
88
+ defevent :destroy, prototype: [Pluggaloid::STREAM]
89
+
90
+ collection(:list, 3) do |collector|
91
+ collector << 1 << 2 << 3
92
+ subscribe(:destroy).each do |i|
93
+ collector.delete(i)
94
+ end
95
+ end
96
+ end
97
+ eval_all_events do
98
+ Pluggaloid::Event[:destroy].call([2, 4])
99
+ end
100
+ assert_equal([1, 3], Pluggaloid::Event[:list].collect(3).to_a)
101
+ end
102
+
103
+ it 'rewind' do
104
+ added = []
105
+ deleted = []
106
+ Pluggaloid::Plugin.create(:event) do
107
+ defevent :list, prototype: [Integer, Pluggaloid::COLLECT]
108
+
109
+ collection(:list, 3) do |collector|
110
+ collector << 1 << 2 << 3
111
+ on_rewind do |e|
112
+ collector.rewind do |lst|
113
+ e
114
+ end
115
+ end
116
+ end
117
+
118
+ subscribe(:list__add, 3) do |elm|
119
+ added += elm
120
+ end
121
+
122
+ subscribe(:list__delete, 3) do |elm|
123
+ deleted += elm
124
+ end
125
+ end
126
+ eval_all_events do
127
+ Pluggaloid::Event[:rewind].call([2, 3, 4])
128
+ end
129
+ assert_equal([2, 3, 4], Pluggaloid::Event[:list].collect(3).to_a)
130
+ assert_equal([1, 2, 3, 4], added)
131
+ assert_equal([1], deleted)
132
+ end
133
+ end
134
+
135
+ end
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'minitest/autorun'
5
+
6
+ require 'pluggaloid'
7
+ require_relative 'helper'
8
+
9
+ describe(Pluggaloid::Plugin) do
10
+ include PluggaloidTestHelper
11
+
12
+ before do
13
+ Delayer.default = Delayer.generate_class(priority: %i<high normal low>, default: :normal)
14
+ Pluggaloid::Plugin.clear!
15
+ end
16
+
17
+ describe 'event' do
18
+ before do
19
+ log = @log = []
20
+ Pluggaloid::Plugin.create(:event) do
21
+ defevent :list, prototype: [Integer, Pluggaloid::STREAM]
22
+
23
+ generate(:list, 1) do |yielder|
24
+ log << [:g1, :start]
25
+ on_tick do |digit|
26
+ log << [:g1, digit]
27
+ yielder << digit
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ it 'does not call generate block in not subscribed' do
34
+ assert_equal([], @log)
35
+ end
36
+
37
+ it 'does not call generate block in subscribed other caller arguments' do
38
+ Pluggaloid::Plugin.create(:event) do
39
+ subscribe(:list, 2) {}
40
+ end
41
+ assert_equal([], @log)
42
+ end
43
+
44
+ describe 'after subscribe' do
45
+ before do
46
+ listener = nil
47
+ Pluggaloid::Plugin.create(:event) do
48
+ listener = subscribe(:list, 1) {}
49
+ end
50
+ @listener = listener
51
+ eval_all_events
52
+ end
53
+
54
+ it 'call generate block' do
55
+ assert_equal([[:g1, :start]], @log)
56
+ end
57
+
58
+ it 'call once generate block if subscribe twice' do
59
+ Pluggaloid::Plugin.create(:event) do
60
+ subscribe(:list, 1) {}
61
+ end
62
+ eval_all_events
63
+ assert_equal([[:g1, :start]], @log)
64
+ end
65
+
66
+ describe 'detach all listeners' do
67
+ before do
68
+ Pluggaloid::Plugin.create(:event).detach(@listener)
69
+ Pluggaloid::Plugin.call(:event, 1, [69])
70
+ eval_all_events
71
+ end
72
+
73
+ it 'should detach handlers in generate block after unsubscribed' do
74
+ assert_equal([[:g1, :start]], @log)
75
+ end
76
+ end
77
+
78
+ end
79
+
80
+ describe 'after add_event_listener' do
81
+ before do
82
+ listener = nil
83
+ Pluggaloid::Plugin.create(:event) do
84
+ listener = on_list { |_num, _lst| ; }
85
+ end
86
+ @listener = listener
87
+ eval_all_events
88
+ end
89
+
90
+ it 'call generate block' do
91
+ assert_equal([[:g1, :start]], @log)
92
+ end
93
+
94
+ it 'call once generate block if subscribe twice' do
95
+ Pluggaloid::Plugin.create(:event) do
96
+ on_list { |_num, _lst| ; }
97
+ end
98
+ eval_all_events
99
+ assert_equal([[:g1, :start]], @log)
100
+ end
101
+
102
+ describe 'detach all listeners' do
103
+ before do
104
+ Pluggaloid::Plugin.create(:event).detach(@listener)
105
+ Pluggaloid::Plugin.call(:event, 1, [105])
106
+ eval_all_events
107
+ end
108
+
109
+ it 'detach handlers in generate block after unsubscribed' do
110
+ assert_equal([[:g1, :start]], @log)
111
+ end
112
+ end
113
+
114
+ end
115
+ end
116
+
117
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'minitest/autorun'
5
+
6
+ require 'pluggaloid'
7
+ require_relative 'helper'
8
+
9
+ describe(Pluggaloid::Plugin) do
10
+ include PluggaloidTestHelper
11
+
12
+ before do
13
+ Delayer.default = Delayer.generate_class(priority: %i<high normal low>, default: :normal)
14
+ Pluggaloid::Plugin.clear!
15
+ end
16
+
17
+ it 'hoge' do
18
+ refute Pluggaloid::Event[:tick].subscribe?
19
+ end
20
+
21
+ describe 'event' do
22
+ before do
23
+ log = @log = []
24
+ listener = nil
25
+ Pluggaloid::Plugin.create(:event) do
26
+ defevent :list, prototype: [Integer, Pluggaloid::STREAM]
27
+
28
+ generate(:list, 1) do |yielder|
29
+ log << [:g1, :start]
30
+ on_tick do |digit|
31
+ log << [:g1, digit]
32
+ yielder << digit
33
+ end
34
+ end
35
+
36
+ listener = subscribe(:list, 1) do |stream|
37
+ ;
38
+ end
39
+ end
40
+ @listener = listener
41
+ eval_all_events
42
+ end
43
+
44
+ it 'subscribed event which define in generate block' do
45
+ assert Pluggaloid::Event[:tick].subscribe?
46
+ end
47
+
48
+ describe 'unsubscribe generate stream' do
49
+ before do
50
+ Pluggaloid::Plugin.create(:event).detach(@listener)
51
+ eval_all_events
52
+ end
53
+
54
+ it 'unsubscribed event which define in generate block' do
55
+ refute Pluggaloid::Event[:tick].subscribe?
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,129 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'minitest/autorun'
5
+
6
+ require 'pluggaloid'
7
+ require_relative 'helper'
8
+
9
+ describe(Pluggaloid::Plugin) do
10
+ include PluggaloidTestHelper
11
+
12
+ it 'resume' do
13
+ k = Class.new do
14
+ include Pluggaloid::Mirage
15
+ end
16
+ a = k.new
17
+ assert_equal(a, Pluggaloid::Mirage.unwrap(namespace: k.to_s, id: a.pluggaloid_mirage_id))
18
+ end
19
+
20
+ it 'custom mirage id' do
21
+ k = Class.new do
22
+ include Pluggaloid::Mirage
23
+
24
+ def initialize(a)
25
+ @a = a
26
+ end
27
+
28
+ def generate_pluggaloid_mirage_id
29
+ @a.to_s
30
+ end
31
+ end
32
+
33
+ a = k.new('same')
34
+ b = k.new('differ')
35
+ assert_equal('same', a.pluggaloid_mirage_id)
36
+ assert_equal(a, Pluggaloid::Mirage.unwrap(namespace: k.to_s, id: a.pluggaloid_mirage_id))
37
+ assert_equal('differ', b.pluggaloid_mirage_id)
38
+ assert_equal(b, Pluggaloid::Mirage.unwrap(namespace: k.to_s, id: b.pluggaloid_mirage_id))
39
+ end
40
+
41
+ it 'not confuse other mirage classese' do
42
+ definition = ->(*) do
43
+ include Pluggaloid::Mirage
44
+
45
+ def initialize(a)
46
+ @a = a
47
+ end
48
+
49
+ def generate_pluggaloid_mirage_id
50
+ @a.to_s
51
+ end
52
+ end
53
+
54
+ k = Class.new(&definition)
55
+ l = Class.new(&definition)
56
+
57
+ a = k.new('same')
58
+ b = l.new('same')
59
+ c = l.new('differ')
60
+
61
+ refute_equal(k.to_s, l.to_s)
62
+ assert_equal('same', a.pluggaloid_mirage_id)
63
+ assert_equal('same', b.pluggaloid_mirage_id)
64
+ assert_equal('differ', c.pluggaloid_mirage_id)
65
+
66
+ assert_equal(b, Pluggaloid::Mirage.unwrap(namespace: l.to_s, id: 'same'))
67
+ assert_equal(c, Pluggaloid::Mirage.unwrap(namespace: l.to_s, id: 'differ'))
68
+ assert_equal(a, Pluggaloid::Mirage.unwrap(namespace: k.to_s, id: 'same'))
69
+ assert_raises(Pluggaloid::ArgumentError) { Pluggaloid::Mirage.unwrap(namespace: k.to_s, id: 'differ') }
70
+ end
71
+
72
+ it 'inherit class' do
73
+ k = Class.new do
74
+ include Pluggaloid::Mirage
75
+
76
+ def initialize(a)
77
+ @a = a
78
+ end
79
+
80
+ def generate_pluggaloid_mirage_id
81
+ @a.to_s
82
+ end
83
+ end
84
+ l = Class.new(k)
85
+
86
+ a = k.new('same')
87
+ b = l.new('same')
88
+ c = l.new('differ')
89
+
90
+ refute_equal(k.to_s, l.to_s)
91
+ assert_equal('same', a.pluggaloid_mirage_id)
92
+ assert_equal('same', b.pluggaloid_mirage_id)
93
+ assert_equal('differ', c.pluggaloid_mirage_id)
94
+
95
+ assert_equal(b, Pluggaloid::Mirage.unwrap(namespace: l.to_s, id: 'same'))
96
+ assert_equal(c, Pluggaloid::Mirage.unwrap(namespace: l.to_s, id: 'differ'))
97
+ assert_equal(a, Pluggaloid::Mirage.unwrap(namespace: k.to_s, id: 'same'))
98
+ assert_raises(Pluggaloid::ArgumentError) { Pluggaloid::Mirage.unwrap(namespace: k.to_s, id: 'differ') }
99
+ end
100
+
101
+ it 'inherit before include mirage' do
102
+ k = Class.new do
103
+ def initialize(a)
104
+ @a = a
105
+ end
106
+
107
+ def generate_pluggaloid_mirage_id
108
+ @a.to_s
109
+ end
110
+ end
111
+ l = Class.new(k)
112
+ k.include Pluggaloid::Mirage
113
+
114
+ a = k.new('same')
115
+ b = l.new('same')
116
+ c = l.new('differ')
117
+
118
+ refute_equal(k.to_s, l.to_s)
119
+ assert_equal('same', a.pluggaloid_mirage_id)
120
+ assert_equal('same', b.pluggaloid_mirage_id)
121
+ assert_equal('differ', c.pluggaloid_mirage_id)
122
+
123
+ assert_equal(b, Pluggaloid::Mirage.unwrap(namespace: l.to_s, id: 'same'))
124
+ assert_equal(c, Pluggaloid::Mirage.unwrap(namespace: l.to_s, id: 'differ'))
125
+ assert_equal(a, Pluggaloid::Mirage.unwrap(namespace: k.to_s, id: 'same'))
126
+ assert_raises(Pluggaloid::ArgumentError) { Pluggaloid::Mirage.unwrap(namespace: k.to_s, id: 'differ') }
127
+ end
128
+
129
+ end
@@ -32,9 +32,33 @@ describe(Pluggaloid::Plugin) do
32
32
  assert_equal(%i[one], sum)
33
33
  end
34
34
 
35
- it "subscribe?" do
35
+ it "subscribe 2" do
36
36
  sum = []
37
37
 
38
+ Pluggaloid::Plugin.create(:event) do
39
+ defevent :increase, prototype: [Pluggaloid::STREAM, Integer]
40
+ subscribe(:increase, 1) do |v|
41
+ sum = v
42
+ end
43
+ end
44
+
45
+ eval_all_events do
46
+ Pluggaloid::Event[:increase].call([:one], 1)
47
+ Pluggaloid::Event[:increase].call([:two], 2)
48
+ end
49
+
50
+ assert_equal(%i[one], sum)
51
+ end
52
+
53
+ it "raises subscribe without definition" do
54
+ assert_raises Pluggaloid::UndefinedStreamIndexError do
55
+ Pluggaloid::Plugin.create(:event) do
56
+ subscribe(:increase, 1) { ; }
57
+ end
58
+ end
59
+ end
60
+
61
+ it "subscribe? first value" do
38
62
  Pluggaloid::Plugin.create(:event) do
39
63
  defevent :increase, prototype: [Integer, Pluggaloid::STREAM]
40
64
  subscribe(:increase, 1) do |v|
@@ -42,12 +66,19 @@ describe(Pluggaloid::Plugin) do
42
66
  end
43
67
  assert(Pluggaloid::Plugin[:event].subscribe?(:increase, 1))
44
68
  refute(Pluggaloid::Plugin[:event].subscribe?(:increase, 2))
69
+ end
45
70
 
71
+ it "subscribe? last value" do
72
+ Pluggaloid::Plugin.create(:event) do
73
+ defevent :increase, prototype: [Pluggaloid::STREAM, Integer]
74
+ subscribe(:increase, 1) do |v|
75
+ end
76
+ end
77
+ assert(Pluggaloid::Plugin[:event].subscribe?(:increase, 1))
78
+ refute(Pluggaloid::Plugin[:event].subscribe?(:increase, 2))
46
79
  end
47
80
 
48
81
  it "subscribe? returns always true if plugin listener exist" do
49
- sum = []
50
-
51
82
  Pluggaloid::Plugin.create(:event) do
52
83
  defevent :increase, prototype: [Integer, Pluggaloid::STREAM]
53
84
  on_increase do |i, y|
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pluggaloid
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.3.1
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Toshiaki Asai
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-06 00:00:00.000000000 Z
11
+ date: 2021-05-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: delayer
@@ -99,12 +99,14 @@ executables: []
99
99
  extensions: []
100
100
  extra_rdoc_files: []
101
101
  files:
102
+ - ".circleci/config.yml"
102
103
  - ".gitignore"
103
104
  - Gemfile
104
105
  - LICENSE.txt
105
106
  - README.md
106
107
  - Rakefile
107
108
  - lib/pluggaloid.rb
109
+ - lib/pluggaloid/collection.rb
108
110
  - lib/pluggaloid/error.rb
109
111
  - lib/pluggaloid/event.rb
110
112
  - lib/pluggaloid/filter.rb
@@ -112,13 +114,19 @@ files:
112
114
  - lib/pluggaloid/handler_tag.rb
113
115
  - lib/pluggaloid/identity.rb
114
116
  - lib/pluggaloid/listener.rb
117
+ - lib/pluggaloid/mirage.rb
115
118
  - lib/pluggaloid/plugin.rb
116
119
  - lib/pluggaloid/stream.rb
120
+ - lib/pluggaloid/stream_generator.rb
117
121
  - lib/pluggaloid/subscriber.rb
118
122
  - lib/pluggaloid/version.rb
119
123
  - pluggaloid.gemspec
124
+ - test/collect_test.rb
125
+ - test/generate_test.rb
126
+ - test/handle_in_generate_test.rb
120
127
  - test/handler_tag_test.rb
121
128
  - test/helper.rb
129
+ - test/mirage_test.rb
122
130
  - test/multi_vm_test.rb
123
131
  - test/plugin_test.rb
124
132
  - test/reactive_test.rb
@@ -126,7 +134,7 @@ homepage: https://rubygems.org/gems/pluggaloid
126
134
  licenses:
127
135
  - MIT
128
136
  metadata: {}
129
- post_install_message:
137
+ post_install_message:
130
138
  rdoc_options: []
131
139
  require_paths:
132
140
  - lib
@@ -141,13 +149,17 @@ required_rubygems_version: !ruby/object:Gem::Requirement
141
149
  - !ruby/object:Gem::Version
142
150
  version: '0'
143
151
  requirements: []
144
- rubygems_version: 3.1.2
145
- signing_key:
152
+ rubygems_version: 3.2.15
153
+ signing_key:
146
154
  specification_version: 4
147
155
  summary: Extensible plugin system
148
156
  test_files:
157
+ - test/collect_test.rb
158
+ - test/generate_test.rb
159
+ - test/handle_in_generate_test.rb
149
160
  - test/handler_tag_test.rb
150
161
  - test/helper.rb
162
+ - test/mirage_test.rb
151
163
  - test/multi_vm_test.rb
152
164
  - test/plugin_test.rb
153
165
  - test/reactive_test.rb