pluggaloid 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 2549e40abed501bd7546a76f8368daed95e8436a
4
+ data.tar.gz: e28a4c206fe73a1d6baac70902ac64c29be1f9ff
5
+ SHA512:
6
+ metadata.gz: 831b352d0c7aad2f39b44de0da81ccf984e9568834f731fbf160cf929181f998c001102bfabc1eabe86158e4aaacf3775251584a5826fdf1113590d04f99ee7a
7
+ data.tar.gz: 09ffac441afd19d38dd4e318cd95cedd3d202f8eb35eb3e57e8a3314658c593219021ed61f11adeb36541a47d732e0938e3a4b418e6f6f5c504f99782557cfe1
@@ -0,0 +1,15 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ vendor/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in pluggaloid.gemspec
4
+ gemspec
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Toshiaki Asai
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,63 @@
1
+ # Pluggaloid
2
+
3
+ mikutterのプラグイン機構です。
4
+ 登録したプラグイン同士がイベントを使って通信できるようになります。
5
+
6
+ ## Installation
7
+
8
+ Add this line to your application's Gemfile:
9
+
10
+ ```ruby
11
+ gem 'pluggaloid'
12
+ ```
13
+
14
+ And then execute:
15
+
16
+ $ bundle
17
+
18
+ Or install it yourself as:
19
+
20
+ $ gem install pluggaloid
21
+
22
+ ## Usage
23
+
24
+ ```ruby
25
+ require 'pluggaloid'
26
+
27
+ main = Pluggaloid.new(Delayer.generate_class(priority: %i<high normal low>, default: :normal))
28
+
29
+ main.Plugin.create(:write_to_stdout) do
30
+ on_logging do |message|
31
+ puts "logging: #{message}"
32
+ end
33
+ end
34
+
35
+ main.Plugin.call(:logging, "boot.")
36
+ main.Plugin.call(:logging, "event test.")
37
+ main.Plugin.call(:logging, "exit.")
38
+
39
+ main.Delayer.run while not main.Delayer.empty?
40
+ ```
41
+
42
+ ### Pluggaloid::new
43
+
44
+ Pluggaloid::newは、プラグイン機構を制御するためのDelayer, Plugin, Event, Listener, Filterのサブクラスを新しく作って返すメソッドです。
45
+ 戻り値はStructで、それぞれのクラス名がメンバの名前になっています。
46
+
47
+ | Member | Description |
48
+ |----------|-------------------------------------|
49
+ | Delayer | Pluggaloid::new に渡したDelayer |
50
+ | Plugin | Pluggaloid::Plugin のサブクラス |
51
+ | Event | Pluggaloid::Event のサブクラス |
52
+ | Listener | Pluggaloid::Listener のサブクラス |
53
+ | Filter | Pluggaloid::Filter のサブクラス |
54
+
55
+ コンストラクタの唯一の引数には `Delayer.generate_class(priority: %i<high normal low>, default: :normal)`のように、優先順位付きでデフォルト優先度が設定されたDelayerを渡します。
56
+
57
+ ## Contributing
58
+
59
+ 1. Fork it ( https://github.com/toshia/pluggaloid/fork )
60
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
61
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
62
+ 4. Push to the branch (`git push origin my-new-feature`)
63
+ 5. Create a new Pull Request
@@ -0,0 +1,17 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.test_files = FileList['test/*_test.rb']
6
+ t.warning = true
7
+ t.verbose = true
8
+ end
9
+
10
+
11
+
12
+
13
+
14
+
15
+
16
+
17
+
@@ -0,0 +1,21 @@
1
+ require "pluggaloid/version"
2
+ require "pluggaloid/plugin"
3
+ require 'pluggaloid/event'
4
+ require 'pluggaloid/listener'
5
+ require 'pluggaloid/filter'
6
+ require 'pluggaloid/error'
7
+
8
+ require 'delayer'
9
+
10
+ module Pluggaloid
11
+ VM = Struct.new(*%i<Delayer Plugin Event Listener Filter>)
12
+
13
+ def self.new(delayer)
14
+ vm = VM.new(delayer,
15
+ Class.new(Plugin),
16
+ Class.new(Event),
17
+ Class.new(Listener),
18
+ Class.new(Filter))
19
+ vm.Plugin.vm = vm.Event.vm = vm
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ # -*- coding: utf-8 -*-
2
+ module Pluggaloid
3
+ class Error < ::StandardError; end
4
+
5
+ class ArgumentError < Error; end
6
+
7
+ class TypeError < Error; end
8
+
9
+ class FilterError < Error; end
10
+
11
+ class NoDefaultDelayerError < Error; end
12
+ end
@@ -0,0 +1,108 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ class Pluggaloid::Event
4
+ include InstanceStorage
5
+
6
+ # オプション。以下のキーを持つHash
7
+ # :prototype :: 引数の数と型。Arrayで、type_strictが解釈できる条件を設定する
8
+ # :priority :: Delayerの優先順位
9
+ attr_accessor :options
10
+
11
+ # フィルタを別のスレッドで実行する。偽ならメインスレッドでフィルタを実行する
12
+ @filter_another_thread = false
13
+
14
+ def initialize(*args)
15
+ super
16
+ @options = {}
17
+ @listeners = []
18
+ @filters = [] end
19
+
20
+ def vm
21
+ self.class.vm end
22
+
23
+ # イベントの優先順位を取得する
24
+ # ==== Return
25
+ # プラグインの優先順位
26
+ def priority
27
+ if @options.has_key? :priority
28
+ @options[:priority] end end
29
+
30
+ # イベントを引数 _args_ で発生させる
31
+ # ==== Args
32
+ # [*args] イベントの引数
33
+ # ==== Return
34
+ # Delayerか、イベントを待ち受けているリスナがない場合はnil
35
+ def call(*args)
36
+ if self.class.filter_another_thread
37
+ if @filters.empty?
38
+ vm.delayer.new(*Array(priority)) do
39
+ call_all_listeners(args) end
40
+ else
41
+ Thread.new do
42
+ filtered_args = filtering(*args)
43
+ if filtered_args.is_a? Array
44
+ vm.Delayer.new(*Array(priority)) do
45
+ call_all_listeners(filtered_args) end end end end
46
+ else
47
+ vm.Delayer.new(*Array(priority)) do
48
+ args = filtering(*args) if not @filters.empty?
49
+ call_all_listeners(args) if args.is_a? Array end end end
50
+
51
+ # 引数 _args_ をフィルタリングした結果を返す
52
+ # ==== Args
53
+ # [*args] 引数
54
+ # ==== Return
55
+ # フィルタされた引数の配列
56
+ def filtering(*args)
57
+ catch(:filter_exit) {
58
+ @filters.reduce(args){ |acm, event_filter|
59
+ event_filter.filtering(*acm) } } end
60
+
61
+ def add_listener(listener)
62
+ unless listener.is_a? Pluggaloid::Listener
63
+ raise Pluggaloid::ArgumentError, "First argument must be Pluggaloid::Listener, but given #{listener.class}." end
64
+ @listeners << listener
65
+ self end
66
+
67
+ def delete_listener(event_filter)
68
+ @listeners.delete(event_filter)
69
+ self end
70
+
71
+ # イベントフィルタを追加する
72
+ # ==== Args
73
+ # [event_filter] イベントフィルタ(Filter)
74
+ # ==== Return
75
+ # self
76
+ def add_filter(event_filter)
77
+ unless event_filter.is_a? Pluggaloid::Filter
78
+ raise Pluggaloid::ArgumentError, "First argument must be Pluggaloid::Filter, but given #{event_filter.class}." end
79
+ @filters << event_filter
80
+ self end
81
+
82
+ # イベントフィルタを削除する
83
+ # ==== Args
84
+ # [event_filter] イベントフィルタ(EventFilter)
85
+ # ==== Return
86
+ # self
87
+ def delete_filter(event_filter)
88
+ @filters.delete(event_filter)
89
+ self end
90
+
91
+ private
92
+ def call_all_listeners(args)
93
+ catch(:plugin_exit) do
94
+ @listeners.each do |listener|
95
+ listener.call(*args) end end end
96
+
97
+ class << self
98
+ attr_accessor :filter_another_thread, :vm
99
+
100
+ alias __clear_aF4e__ clear!
101
+ def clear!
102
+ @filter_another_thread = false
103
+ __clear_aF4e__()
104
+ end
105
+ end
106
+
107
+ clear!
108
+ end
@@ -0,0 +1,38 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ class Pluggaloid::Filter
4
+ # フィルタ内部で使う。フィルタの実行をキャンセルする。Plugin#filtering はfalseを返し、
5
+ # イベントのフィルタの場合は、そのイベントの実行自体をキャンセルする。
6
+ # また、 _result_ が渡された場合、Event#filtering の戻り値は _result_ になる。
7
+ def self.cancel!(result=false)
8
+ throw :filter_exit, result end
9
+
10
+ # ==== Args
11
+ # [event] 監視するEventのインスタンス
12
+ # [&callback] コールバック
13
+ def initialize(event, &callback)
14
+ raise Pluggaloid::TypeError, "Argument `event' must be instance of Pluggaloid::Event, but given #{event.class}." unless event.is_a? Pluggaloid::Event
15
+ @event = event
16
+ @callback = Proc.new
17
+ event.add_filter self end
18
+
19
+ # イベントを実行する
20
+ # ==== Args
21
+ # [*args] イベントの引数
22
+ # ==== Return
23
+ # 加工後の引数の配列
24
+ def filtering(*args)
25
+ length = args.size
26
+ result = @callback.call(*args, &self.class.method(:cancel!))
27
+ if length != result.size
28
+ raise Pluggaloid::FilterError, "filter changes arguments length (#{length} to #{result.size})" end
29
+ result end
30
+
31
+ # このリスナを削除する
32
+ # ==== Return
33
+ # self
34
+ def detach
35
+ @event.delete_filter(self)
36
+ self end
37
+
38
+ end
@@ -0,0 +1,29 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ class Pluggaloid::Listener
4
+ # プラグインコールバックをこれ以上実行しない。
5
+ def self.cancel!
6
+ throw :plugin_exit, false end
7
+
8
+ # ==== Args
9
+ # [event] 監視するEventのインスタンス
10
+ # [&callback] コールバック
11
+ def initialize(event, &callback)
12
+ raise Pluggaloid::TypeError, "Argument `event' must be instance of Pluggaloid::Event, but given #{event.class}." unless event.is_a? Pluggaloid::Event
13
+ @event = event
14
+ @callback = Proc.new
15
+ event.add_listener(self) end
16
+
17
+ # イベントを実行する
18
+ # ==== Args
19
+ # [*args] イベントの引数
20
+ def call(*args)
21
+ @callback.call(*args, &self.class.method(:cancel!)) end
22
+
23
+ # このリスナを削除する
24
+ # ==== Return
25
+ # self
26
+ def detach
27
+ @event.delete_listener(self)
28
+ self end
29
+ end
@@ -0,0 +1,194 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'instance_storage'
4
+ require 'delayer'
5
+
6
+ # プラグインの本体。
7
+ # DSLを提供し、イベントやフィルタの管理をする
8
+ module Pluggaloid
9
+ class Plugin
10
+ include InstanceStorage
11
+
12
+ class << self
13
+ attr_writer :vm
14
+
15
+ def vm
16
+ @vm ||= begin
17
+ raise Pluggaloid::NoDefaultDelayerError, "Default Delayer was not set." unless Delayer.default
18
+ vm = Pluggaloid::VM.new(
19
+ Delayer.default,
20
+ self,
21
+ Pluggaloid::Event,
22
+ Pluggaloid::Listener,
23
+ Pluggaloid::Filter)
24
+ vm.Event.vm = vm end end
25
+
26
+ # プラグインのインスタンスを返す。
27
+ # ブロックが渡された場合、そのブロックをプラグインのインスタンスのスコープで実行する
28
+ # ==== Args
29
+ # [plugin_name] プラグイン名
30
+ # ==== Return
31
+ # Plugin
32
+ def create(plugin_name, &body)
33
+ self[plugin_name].instance_eval(&body) if body
34
+ self[plugin_name] end
35
+
36
+ # イベントを宣言する。
37
+ # ==== Args
38
+ # [event_name] イベント名
39
+ # [options] 以下のキーを持つHash
40
+ # :prototype :: 引数の数と型。Arrayで、type_strictが解釈できる条件を設定する
41
+ # :priority :: Delayerの優先順位
42
+ def defevent(event_name, options = {})
43
+ vm.Event[event_name].options = options end
44
+
45
+ # イベント _event_name_ を発生させる
46
+ # ==== Args
47
+ # [event_name] イベント名
48
+ # [*args] イベントの引数
49
+ # ==== Return
50
+ # Delayer
51
+ def call(event_name, *args)
52
+ vm.Event[event_name].call(*args) end
53
+
54
+ # 引数 _args_ をフィルタリングした結果を返す
55
+ # ==== Args
56
+ # [*args] 引数
57
+ # ==== Return
58
+ # フィルタされた引数の配列
59
+ def filtering(event_name, *args)
60
+ vm.Event[event_name].filtering(*args) end
61
+
62
+ # 互換性のため
63
+ def uninstall(plugin_name)
64
+ self[plugin_name].uninstall end
65
+
66
+ # 互換性のため
67
+ def filter_cancel!
68
+ vm.Filter.cancel! end
69
+
70
+ alias plugin_list instances_name
71
+
72
+ alias __clear_aF4e__ clear!
73
+ def clear!
74
+ if defined?(@vm) and @vm
75
+ @vm.Event.clear!
76
+ @vm = nil end
77
+ __clear_aF4e__() end
78
+ end
79
+
80
+ # プラグインの名前
81
+ attr_reader :name
82
+
83
+ # spec
84
+ attr_accessor :spec
85
+
86
+ # 最初にプラグインがロードされた時刻(uninstallされるとリセットする)
87
+ attr_reader :defined_time
88
+
89
+ # ==== Args
90
+ # [plugin_name] プラグイン名
91
+ def initialize(*args)
92
+ super
93
+ @defined_time = Time.new.freeze
94
+ @events = Set.new
95
+ @filters = Set.new end
96
+
97
+ # イベントリスナを新しく登録する
98
+ # ==== Args
99
+ # [event_name] イベント名
100
+ # [&callback] イベントのコールバック
101
+ # ==== Return
102
+ # Pluggaloid::Listener
103
+ def add_event(event_name, &callback)
104
+ result = vm.Listener.new(vm.Event[event_name], &callback)
105
+ @events << result
106
+ result end
107
+
108
+ # イベントフィルタを新しく登録する
109
+ # ==== Args
110
+ # [event_name] イベント名
111
+ # [&callback] イベントのコールバック
112
+ # ==== Return
113
+ # EventFilter
114
+ def add_event_filter(event_name, &callback)
115
+ result = vm.Filter.new(vm.Event[event_name], &callback)
116
+ @filters << result
117
+ result end
118
+
119
+ # イベントを削除する。
120
+ # 引数は、Pluggaloid::ListenerかPluggaloid::Filterのみ(on_*やfilter_*の戻り値)。
121
+ # 互換性のため、二つ引数がある場合は第一引数は無視され、第二引数が使われる。
122
+ # ==== Args
123
+ # [*args] 引数
124
+ # ==== Return
125
+ # self
126
+ def detach(*args)
127
+ listener = args.last
128
+ if listener.is_a? vm.Listener
129
+ @events.delete(listener)
130
+ listener.detach
131
+ elsif listener.is_a? vm.Filter
132
+ @filters.delete(listener)
133
+ listener.detach end
134
+ self end
135
+
136
+ # このプラグインを破棄する
137
+ # ==== Return
138
+ # self
139
+ def uninstall
140
+ @events.map(&:detach)
141
+ @filters.map(&:detach)
142
+ self.class.destroy name
143
+ execute_unload_hook
144
+ self end
145
+
146
+ # イベント _event_name_ を宣言する
147
+ # ==== Args
148
+ # [event_name] イベント名
149
+ # [options] イベントの定義
150
+ def defevent(event_name, options={})
151
+ vm.Event[event_name].options.merge!({plugin: self}.merge(options)) end
152
+
153
+ # DSLメソッドを新しく追加する。
154
+ # 追加されたメソッドは呼ぶと &callback が呼ばれ、その戻り値が返される。引数も順番通り全て &callbackに渡される
155
+ # ==== Args
156
+ # [dsl_name] 新しく追加するメソッド名
157
+ # [&callback] 実行されるメソッド
158
+ # ==== Return
159
+ # self
160
+ def defdsl(dsl_name, &callback)
161
+ self.class.instance_eval {
162
+ define_method(dsl_name, &callback) }
163
+ self end
164
+
165
+ # プラグインが Plugin.uninstall される時に呼ばれるブロックを登録する。
166
+ def onunload
167
+ @unload_hook ||= []
168
+ @unload_hook.push(Proc.new) end
169
+ alias :on_unload :onunload
170
+
171
+ # マジックメソッドを追加する。
172
+ # on_?name :: add_event(name)
173
+ # filter_?name :: add_event_filter(name)
174
+ def method_missing(method, *args, &proc)
175
+ case method.to_s
176
+ when /\Aon_?(.+)\Z/
177
+ add_event($1.to_sym, &proc)
178
+ when /\Afilter_?(.+)\Z/
179
+ add_event_filter($1.to_sym, &proc)
180
+ when /\Ahook_?(.+)\Z/
181
+ add_event_hook($1.to_sym, &proc)
182
+ else
183
+ super end end
184
+
185
+ private
186
+
187
+ def execute_unload_hook
188
+ @unload_hook.each{ |unload| unload.call } if(defined?(@unload_hook)) end
189
+
190
+ def vm
191
+ self.class.vm end
192
+
193
+ end
194
+ end
@@ -0,0 +1,3 @@
1
+ module Pluggaloid
2
+ VERSION = "1.0.0"
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'pluggaloid/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "pluggaloid"
8
+ spec.version = Pluggaloid::VERSION
9
+ spec.authors = ["Toshiaki Asai"]
10
+ spec.email = ["toshi.alternative@gmail.com"]
11
+ spec.summary = %q{Extensible plugin system}
12
+ spec.description = %q{Pluggaloid is extensible plugin system for mikutter.}
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency 'delayer'
22
+ spec.add_dependency 'instance_storage', ">= 1.0.0", "< 2.0.0"
23
+
24
+ spec.add_development_dependency "bundler", "~> 1.7"
25
+ spec.add_development_dependency "rake", "~> 10.0"
26
+ spec.add_development_dependency "minitest"
27
+ end
@@ -0,0 +1,56 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'bundler/setup'
4
+ require 'minitest/autorun'
5
+
6
+ require 'pluggaloid'
7
+
8
+ describe(Pluggaloid) do
9
+ before do
10
+ Delayer.default = Delayer.generate_class(priority: %i<high normal low>, default: :normal)
11
+ Pluggaloid::Plugin.clear!
12
+ end
13
+
14
+ def eval_all_events(delayer=Delayer)
15
+ native = Thread.list
16
+ yield if block_given?
17
+ delayer.run while not(delayer.empty? and (Thread.list - native).empty?)
18
+ end
19
+
20
+ it "should take new plugin classes" do
21
+ pluggaloid = Pluggaloid.new(Delayer.default)
22
+ assert_equal Delayer.default, pluggaloid.Delayer, ""
23
+ assert_includes(pluggaloid.Plugin.ancestors, Pluggaloid::Plugin, "Plugin should subbclass of Pluggaloid::Plugin")
24
+ assert_includes(pluggaloid.Event.ancestors, Pluggaloid::Event, "Event should subbclass of Pluggaloid::Event")
25
+ assert_includes(pluggaloid.Listener.ancestors, Pluggaloid::Listener, "Listener should subbclass of Pluggaloid::Listener")
26
+ assert_includes(pluggaloid.Filter.ancestors, Pluggaloid::Filter, "Filter should subbclass of Pluggaloid::Filter")
27
+ end
28
+
29
+ it "call event in new vm" do
30
+ vm_a = Pluggaloid.new(Delayer.generate_class(priority: %i<high normal low>, default: :normal))
31
+ vm_b = Pluggaloid.new(Delayer.generate_class(priority: %i<high normal low>, default: :normal))
32
+ foo = bar = false
33
+ vm_a.Plugin.create(:foo_plugin) do
34
+ on_foo do
35
+ foo = true end end
36
+ vm_b.Plugin.create(:bar_plugin) do
37
+ on_bar do
38
+ bar = true end end
39
+
40
+ assert_equal(%i<foo_plugin>, vm_a.Plugin.instances_name)
41
+ assert_equal(%i<bar_plugin>, vm_b.Plugin.instances_name)
42
+
43
+ eval_all_events(vm_a.Delayer) do
44
+ vm_a.Plugin.call(:foo)
45
+ vm_a.Plugin.call(:bar) end
46
+ assert foo, "Event foo should be called"
47
+ refute bar, "Event bar should not be called"
48
+
49
+ foo = bar = false
50
+ eval_all_events(vm_b.Delayer) do
51
+ vm_b.Plugin.call(:foo)
52
+ vm_b.Plugin.call(:bar) end
53
+ refute foo, "Event foo should not be called"
54
+ assert bar, "Event bar should be called"
55
+ end
56
+ end
@@ -0,0 +1,148 @@
1
+ # -*- coding: utf-8 -*-
2
+
3
+ require 'bundler/setup'
4
+ require 'minitest/autorun'
5
+
6
+ require 'pluggaloid'
7
+
8
+ describe(Pluggaloid::Plugin) do
9
+ before do
10
+ Delayer.default = Delayer.generate_class(priority: %i<high normal low>, default: :normal)
11
+ Pluggaloid::Plugin.clear!
12
+ end
13
+
14
+ def eval_all_events
15
+ native = Thread.list
16
+ yield if block_given?
17
+ Delayer.run while not(Delayer.empty? and (Thread.list - native).empty?)
18
+ end
19
+
20
+ it "fire and filtering event, receive a plugin" do
21
+ sum = 0
22
+ Pluggaloid::Plugin.create(:event) do
23
+ on_increase do |v|
24
+ sum += v end
25
+
26
+ filter_increase do |v|
27
+ [v * 2] end end
28
+ eval_all_events do
29
+ Pluggaloid::Event[:increase].call(1) end
30
+ assert_equal(2, sum)
31
+ end
32
+
33
+ it "filter in another thread" do
34
+ filter_thread = nil
35
+ Pluggaloid::Plugin.create(:event) do
36
+ on_thread do
37
+ end
38
+
39
+ filter_thread do
40
+ filter_thread = Thread.current
41
+ [] end end
42
+ eval_all_events do
43
+ Pluggaloid::Event[:thread].call end
44
+ assert filter_thread
45
+ assert_equal Thread.current, filter_thread
46
+
47
+ Pluggaloid::Event.filter_another_thread = true
48
+ filter_thread = nil
49
+ eval_all_events do
50
+ Pluggaloid::Event[:thread].call end
51
+ assert filter_thread, "The filter doesn't run."
52
+ refute_equal Thread.current, filter_thread, 'The filter should execute in not a main thread'
53
+ end
54
+
55
+ it "uninstall" do
56
+ sum = 0
57
+ Pluggaloid::Plugin.create(:event) do
58
+ on_increase do |v|
59
+ sum += v end
60
+ filter_increase do |v|
61
+ [v * 2] end end
62
+ eval_all_events do
63
+ Pluggaloid::Plugin.create(:event).uninstall
64
+ Pluggaloid::Event[:increase].call(1) end
65
+ assert_equal(0, sum)
66
+ end
67
+
68
+ it "detach" do
69
+ sum = 0
70
+ event = filter = nil
71
+ Pluggaloid::Plugin.create(:event) do
72
+ event = on_increase do |v|
73
+ sum += v end
74
+ filter = filter_increase do |v|
75
+ [v * 2] end end
76
+ eval_all_events do
77
+ Pluggaloid::Event[:increase].call(1) end
78
+ assert_equal(2, sum, "It should execute filter when event called")
79
+
80
+ eval_all_events do
81
+ Pluggaloid::Plugin[:event].detach filter
82
+ Pluggaloid::Event[:increase].call(1) end
83
+ assert_equal(3, sum, "It should not execute detached filter when event called")
84
+
85
+ eval_all_events do
86
+ Pluggaloid::Plugin.create(:event).detach event
87
+ Pluggaloid::Event[:increase].call(1) end
88
+ assert_equal(3, sum, "It should not executed detached event")
89
+ end
90
+
91
+ it "get plugin list" do
92
+ assert_empty(Pluggaloid::Plugin.plugin_list, "Plugin list must empty in first time")
93
+ Pluggaloid::Plugin.create(:plugin_0)
94
+ assert_equal(%i<plugin_0>, Pluggaloid::Plugin.plugin_list, "The new plugin should appear plugin list")
95
+ Pluggaloid::Plugin.create(:plugin_1)
96
+ assert_equal(%i<plugin_0 plugin_1>, Pluggaloid::Plugin.plugin_list, "The new plugin should appear plugin list")
97
+ end
98
+
99
+ it "dsl method defevent" do
100
+ Pluggaloid::Plugin.create :defevent do
101
+ defevent :increase, prototype: [Integer] end
102
+ assert_equal([Integer], Pluggaloid::Event[:increase].options[:prototype])
103
+ assert_equal(Pluggaloid::Plugin[:defevent], Pluggaloid::Event[:increase].options[:plugin])
104
+ end
105
+
106
+ it "unload hook" do
107
+ value = 0
108
+ Pluggaloid::Plugin.create(:unload) {
109
+ on_unload {
110
+ value += 2 }
111
+ on_unload {
112
+ value += 1 } }
113
+ assert_equal(value, 0)
114
+ Pluggaloid::Plugin.create(:unload).uninstall
115
+ assert_equal(value, 3)
116
+ end
117
+
118
+ describe "defdsl" do
119
+ it "simple dsl" do
120
+ Pluggaloid::Plugin.create :dsl_def do
121
+ defdsl :twice do |number|
122
+ number * 2 end end
123
+
124
+ dsl_use = Pluggaloid::Plugin[:dsl_use]
125
+ assert_equal(4, dsl_use.twice(2))
126
+ assert_equal(0, dsl_use.twice(0))
127
+ assert_equal(-26, dsl_use.twice(-13))
128
+ end
129
+
130
+ it "callback dsl" do
131
+ Pluggaloid::Plugin.create :dsl_def do
132
+ defdsl :rejector do |value, &condition|
133
+ value.reject(&condition) end end
134
+
135
+ dsl_use = Pluggaloid::Plugin.create(:dsl_use)
136
+ assert_equal([2, 4, 6], dsl_use.rejector(1..6){ |d| 0 != (d & 1) })
137
+ end
138
+ end
139
+
140
+ it 'raises NoDefaultDelayerError if Delayer do not have default delayer' do
141
+ Delayer.default = nil
142
+ Pluggaloid::Plugin.clear!
143
+ assert_raises(Pluggaloid::NoDefaultDelayerError) do
144
+ Pluggaloid::Plugin.create(:raises) do
145
+ on_no_default_delayer_error do; end end end
146
+ end
147
+
148
+ end
metadata ADDED
@@ -0,0 +1,137 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pluggaloid
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Toshiaki Asai
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-08-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: delayer
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: instance_storage
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.0
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: 2.0.0
37
+ type: :runtime
38
+ prerelease: false
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 1.0.0
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: 2.0.0
47
+ - !ruby/object:Gem::Dependency
48
+ name: bundler
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.7'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.7'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rake
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '10.0'
68
+ type: :development
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '10.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: minitest
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - ">="
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ description: Pluggaloid is extensible plugin system for mikutter.
90
+ email:
91
+ - toshi.alternative@gmail.com
92
+ executables: []
93
+ extensions: []
94
+ extra_rdoc_files: []
95
+ files:
96
+ - ".gitignore"
97
+ - Gemfile
98
+ - LICENSE.txt
99
+ - README.md
100
+ - Rakefile
101
+ - lib/pluggaloid.rb
102
+ - lib/pluggaloid/error.rb
103
+ - lib/pluggaloid/event.rb
104
+ - lib/pluggaloid/filter.rb
105
+ - lib/pluggaloid/listener.rb
106
+ - lib/pluggaloid/plugin.rb
107
+ - lib/pluggaloid/version.rb
108
+ - pluggaloid.gemspec
109
+ - test/multi_vm_test.rb
110
+ - test/plugin_test.rb
111
+ homepage: ''
112
+ licenses:
113
+ - MIT
114
+ metadata: {}
115
+ post_install_message:
116
+ rdoc_options: []
117
+ require_paths:
118
+ - lib
119
+ required_ruby_version: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - ">="
122
+ - !ruby/object:Gem::Version
123
+ version: '0'
124
+ required_rubygems_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ">="
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ requirements: []
130
+ rubyforge_project:
131
+ rubygems_version: 2.4.5
132
+ signing_key:
133
+ specification_version: 4
134
+ summary: Extensible plugin system
135
+ test_files:
136
+ - test/multi_vm_test.rb
137
+ - test/plugin_test.rb