pluggaloid 1.3.1 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: bd795f27416c6cff9155f4adbfa97cb04192a48e891d387a1a5c37a04b5b681c
4
- data.tar.gz: 82a3a43fa8f01c34b879c2d85d19f0193d54e8fa3e9989040559c5b40b70e9f4
3
+ metadata.gz: 076d2f155418730ea29519ce8d7281343146dab271b2db4458fbf2c6db600ef9
4
+ data.tar.gz: 42e8f0dcb752dcc4535a4d7c797f99f3864c738d06cb4f55a99ec15dadb92212
5
5
  SHA512:
6
- metadata.gz: b943be558a442022cd1d3ca2fec636ec6f6b8a9498cd2a8cc2fb8a3227df8675fc06f50d90d19af0c3db648703952c37123b0d41e2fc3ff4f686d0a87726d6c6
7
- data.tar.gz: 3f1b7c5fcfa142ad280c3a1c67405ae42536bdcca32b9e2d1ed05a34d394602509bada0001657bfc1eaa48b1aa6465f941513119508179d61feeca52fdd3441e
6
+ metadata.gz: 2b6ad3377a18ea67c24c063a1c844f098401844cb46bda2df9db4db51ca3d8e225344d4703ba90ce622b9f1dbf99245ae6fb33c4dd4cf8cf77385dac6e0be222
7
+ data.tar.gz: 5e45e8b7eb9dbfd2335502ddd225cea420dfd980593ebdb1fc830840274a3d2442da05c5ca06a774a74202a0465428276b1f7a7eb8d7ad468a7af59e9765b100
@@ -0,0 +1,40 @@
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.7'
35
+ - build:
36
+ name: 'ruby-2.6'
37
+ ruby-version: '2.6.5'
38
+ - build:
39
+ name: 'ruby-2.7'
40
+ ruby-version: '2.7.0'
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'
@@ -16,7 +17,9 @@ module Pluggaloid
16
17
  VM = Struct.new(*%i<Delayer Plugin Event Listener Filter HandlerTag Subscriber>, keyword_init: true)
17
18
 
18
19
  class PrototypeStream; end
20
+ class PrototypeCollect; end
19
21
  STREAM = PrototypeStream.new.freeze
22
+ COLLECT = PrototypeCollect.new.freeze
20
23
 
21
24
  def self.new(delayer)
22
25
  vm = VM.new(Delayer: delayer,
@@ -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 |privitive|
17
+ privitive + v
18
+ end
19
+ end
20
+ alias_method :<<, :add
21
+
22
+ def delete(*v)
23
+ rewind do |privitive|
24
+ privitive - 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,6 @@ module Pluggaloid
11
11
  class NoDefaultDelayerError < Error; end
12
12
 
13
13
  class DuplicateListenerSlugError < Error; end
14
+
15
+ class UndefinedCollectionIndexError < Error; end
14
16
  end
@@ -21,6 +21,24 @@ class Pluggaloid::Event
21
21
  @subscribers = {}
22
22
  end
23
23
 
24
+ def prototype
25
+ @options[:prototype]
26
+ end
27
+
28
+ # イベント _event_name_ を宣言する
29
+ # ==== Args
30
+ # [new_options] イベントの定義
31
+ def defevent(new_options)
32
+ @options.merge!(new_options)
33
+ if collect_index
34
+ new_proto = self.prototype.dup
35
+ new_proto[self.collect_index] = Pluggaloid::STREAM
36
+ collection_add_event.defevent(prototype: new_proto)
37
+ collection_delete_event.defevent(prototype: new_proto)
38
+ end
39
+ self
40
+ end
41
+
24
42
  def vm
25
43
  self.class.vm end
26
44
 
@@ -83,7 +101,7 @@ class Pluggaloid::Event
83
101
  end
84
102
 
85
103
  def subscribe?(*args)
86
- !@listeners.empty? || @subscribers.key?(argument_hash(args))
104
+ !@listeners.empty? || @subscribers.key?(argument_hash(args, stream_index))
87
105
  end
88
106
 
89
107
  def delete_listener(listener)
@@ -132,25 +150,61 @@ class Pluggaloid::Event
132
150
  self
133
151
  end
134
152
 
135
- def argument_hash(args)
153
+ def argument_hash(args, exclude_index)
136
154
  args.each_with_index.map do |item, i|
137
- if i != yield_index
155
+ if i != exclude_index
138
156
  item.hash
139
157
  end
140
158
  end.compact.freeze
141
159
  end
142
160
 
143
- def yield_index
144
- unless defined?(@yield_index)
145
- @yield_index = self.options[:prototype]&.index(Pluggaloid::STREAM)
161
+ def stream_index
162
+ unless defined?(@stream_index)
163
+ @stream_index = self.prototype&.index(Pluggaloid::STREAM)
146
164
  end
147
- @yield_index
165
+ @stream_index
166
+ end
167
+
168
+ def collect_index
169
+ unless defined?(@collect_index)
170
+ @collect_index = self.prototype&.index(Pluggaloid::COLLECT)
171
+ end
172
+ @collect_index
173
+ end
174
+
175
+ # defeventで定義されたprototype引数に _Pluggaloid::COLLECT_ を含むイベントに対して使える。
176
+ # フィルタの _Pluggaloid::COLLECT_ 引数に空の配列を渡して実行したあと、その配列を返す。
177
+ # ==== Args
178
+ # [*args] Pluggaloid::COLLECT 以外の引数のリスト
179
+ # ==== Return
180
+ # [Array] フィルタ実行結果
181
+ def collect(*args)
182
+ specified_index = args.index(Pluggaloid::COLLECT)
183
+ specified_index&.yield_self(&args.method(:delete_at))
184
+ insert_index = collect_index || specified_index
185
+ if insert_index
186
+ Enumerator.new do |yielder|
187
+ cargs = args.dup
188
+ cargs.insert(insert_index, yielder)
189
+ filtering(*cargs)
190
+ end
191
+ else
192
+ raise Pluggaloid::UndefinedCollectionIndexError, 'To call collect(), it must define prototype arguments include `Pluggaloid::COLLECT\'.'
193
+ end
194
+ end
195
+
196
+ def collection_add_event
197
+ self.class['%{name}__add' % {name: name}]
198
+ end
199
+
200
+ def collection_delete_event
201
+ self.class['%{name}__delete' % {name: name}]
148
202
  end
149
203
 
150
204
  private
151
205
  def call_all_listeners(args)
152
- if yield_index
153
- @subscribers[argument_hash(args)]&.each do |subscriber|
206
+ if stream_index
207
+ @subscribers[argument_hash(args, stream_index)]&.each do |subscriber|
154
208
  subscriber.call(*args)
155
209
  end
156
210
  end
@@ -64,6 +64,17 @@ module Pluggaloid
64
64
  def filtering(event_name, *args)
65
65
  vm.Event[event_name].filtering(*args) end
66
66
 
67
+ # フィルタ _event_name_ を実行し、defeventでPluggaloid::COLLECTと
68
+ # 宣言されている引数の結果を列挙するEnumeratorを返す
69
+ # ==== Args
70
+ # [event_name] イベント名(String | Symbol)
71
+ # [*specs] Pluggaloid::COLLECT以外の引数
72
+ # ==== Return
73
+ # [Enumerator]
74
+ def collect(event_name, *specs)
75
+ vm.Event[event_name].collect(*specs)
76
+ end
77
+
67
78
  # 互換性のため
68
79
  def uninstall(plugin_name)
69
80
  self[plugin_name].uninstall end
@@ -116,7 +127,7 @@ module Pluggaloid
116
127
 
117
128
  # イベントフィルタを新しく登録する
118
129
  # ==== Args
119
- # [event] 監視するEventのインスタンス
130
+ # [event_name] イベント名(String | Symbol)
120
131
  # [name:] 名前(String | nil)
121
132
  # [slug:] フィルタスラッグ(Symbol | nil)
122
133
  # [tags:] Pluggaloid::HandlerTag|Array フィルタのタグ
@@ -148,6 +159,32 @@ module Pluggaloid
148
159
  vm.Event[event_name].subscribe?(*specs)
149
160
  end
150
161
 
162
+ def collect(event_name, *specs)
163
+ self.class.collect(event_name, *specs)
164
+ end
165
+
166
+ # 追加・削除がフィルタに反映されるコレクションオブジェクトを作成する。
167
+ # 同時に _event_name_ にフィルタが定義され、フィルタが呼ばれると
168
+ # その時点のコレクションオブジェクトの内容を全て列挙する。
169
+ # フィルタと対になるコレクションオブジェクトは、 _&block_ の引数として渡される。
170
+ # ==== Args
171
+ # [event_name] イベント名(String | Symbol)
172
+ # [*specs] Pluggaloid::COLLECT以外の引数
173
+ # [&block] コレクションオブジェクトを受け取って一度だけ実行されるblock
174
+ # ==== Return
175
+ # _&block_ の戻り値
176
+ def collection(event_name, *specs, &block)
177
+ event = vm.Event[event_name]
178
+ mutation = Pluggaloid::Collection.new(event, *specs)
179
+ add_event_filter(event_name) do |*args|
180
+ if mutation.argument_hash_same?(args)
181
+ mutation.values.each(&args[event.collect_index].method(:<<))
182
+ end
183
+ args
184
+ end
185
+ block.call(mutation)
186
+ end
187
+
151
188
  # このプラグインのHandlerTagを作る。
152
189
  # ブロックが渡された場合は、ブロックの中を実行し、ブロックの中で定義された
153
190
  # Handler全てにTagを付与する。
@@ -198,7 +235,6 @@ module Pluggaloid
198
235
  end
199
236
  end
200
237
 
201
-
202
238
  # イベントを削除する。
203
239
  # 引数は、Pluggaloid::ListenerかPluggaloid::Filterのみ(on_*やfilter_*の戻り値)。
204
240
  # 互換性のため、二つ引数がある場合は第一引数は無視され、第二引数が使われる。
@@ -239,7 +275,8 @@ module Pluggaloid
239
275
  # [event_name] イベント名
240
276
  # [options] イベントの定義
241
277
  def defevent(event_name, options={})
242
- vm.Event[event_name].options.merge!({plugin: self}.merge(options)) end
278
+ vm.Event[event_name].defevent({ plugin: self, **options })
279
+ end
243
280
 
244
281
  # DSLメソッドを新しく追加する。
245
282
  # 追加されたメソッドは呼ぶと &callback が呼ばれ、その戻り値が返される。引数も順番通り全て &callbackに渡される
@@ -14,7 +14,7 @@ class Pluggaloid::Subscriber < Pluggaloid::Handler
14
14
  def initialize(event, *specs, **kwrest, &callback)
15
15
  super(event, **kwrest)
16
16
  @callback = callback
17
- @accepted_hash = @event.argument_hash(specs)
17
+ @accepted_hash = @event.argument_hash(specs, @event.stream_index)
18
18
  event.add_listener(self)
19
19
  end
20
20
 
@@ -22,7 +22,7 @@ class Pluggaloid::Subscriber < Pluggaloid::Handler
22
22
  # ==== Args
23
23
  # [stream] イベントの引数
24
24
  def call(*args)
25
- @callback.call(args[@event.yield_index])
25
+ @callback.call(args[@event.stream_index])
26
26
  end
27
27
 
28
28
  # このリスナを削除する
@@ -1,3 +1,3 @@
1
1
  module Pluggaloid
2
- VERSION = '1.3.1'
2
+ VERSION = '1.4.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
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.4.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Toshiaki Asai
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-06 00:00:00.000000000 Z
11
+ date: 2020-02-01 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
@@ -117,6 +119,7 @@ files:
117
119
  - lib/pluggaloid/subscriber.rb
118
120
  - lib/pluggaloid/version.rb
119
121
  - pluggaloid.gemspec
122
+ - test/collect_test.rb
120
123
  - test/handler_tag_test.rb
121
124
  - test/helper.rb
122
125
  - test/multi_vm_test.rb
@@ -146,6 +149,7 @@ signing_key:
146
149
  specification_version: 4
147
150
  summary: Extensible plugin system
148
151
  test_files:
152
+ - test/collect_test.rb
149
153
  - test/handler_tag_test.rb
150
154
  - test/helper.rb
151
155
  - test/multi_vm_test.rb