classx-pluggable 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/ChangeLog +221 -0
  2. data/README +88 -0
  3. data/Rakefile +52 -0
  4. data/doc/output/coverage/-Library-Ruby-Gems-gems-classx-0_0_5-lib-classx-attribute_rb.html +925 -0
  5. data/doc/output/coverage/-Library-Ruby-Gems-gems-classx-0_0_5-lib-classx-attributes_rb.html +772 -0
  6. data/doc/output/coverage/-Library-Ruby-Gems-gems-classx-0_0_5-lib-classx-bracketable_rb.html +671 -0
  7. data/doc/output/coverage/-Library-Ruby-Gems-gems-classx-0_0_5-lib-classx-role-logger_rb.html +716 -0
  8. data/doc/output/coverage/-Library-Ruby-Gems-gems-classx-0_0_5-lib-classx-validate_rb.html +663 -0
  9. data/doc/output/coverage/-Library-Ruby-Gems-gems-classx-0_0_5-lib-classx_rb.html +820 -0
  10. data/doc/output/coverage/-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs-block_rb.html +661 -0
  11. data/doc/output/coverage/-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs-callbacks_rb.html +932 -0
  12. data/doc/output/coverage/-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs-change_rb.html +779 -0
  13. data/doc/output/coverage/-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs-hunk_rb.html +867 -0
  14. data/doc/output/coverage/-Library-Ruby-Gems-gems-diff-lcs-1_1_2-lib-diff-lcs_rb.html +1715 -0
  15. data/doc/output/coverage/-Library-Ruby-Gems-gems-rcov-0_8_1_2_0-lib-rcov_rb.html +1598 -0
  16. data/doc/output/coverage/examples-test_runner-bin-test_runner_rb.html +628 -0
  17. data/doc/output/coverage/examples-test_runner-lib-test_runner-plugin-setup_fixture_rb.html +641 -0
  18. data/doc/output/coverage/examples-test_runner-lib-test_runner-plugin-test_info_rb.html +638 -0
  19. data/doc/output/coverage/examples-test_runner-lib-test_runner-plugin-test_timer_rb.html +666 -0
  20. data/doc/output/coverage/examples-test_runner-lib-test_runner_rb.html +643 -0
  21. data/doc/output/coverage/index.html +711 -0
  22. data/doc/output/coverage/lib-classx-pluggable-plugin_rb.html +676 -0
  23. data/doc/output/coverage/lib-classx-pluggable_rb.html +841 -0
  24. data/examples/test_runner/bin/test_runner.rb +18 -0
  25. data/examples/test_runner/conf/config.yaml +19 -0
  26. data/examples/test_runner/lib/test_runner.rb +33 -0
  27. data/examples/test_runner/lib/test_runner/plugin/setup_fixture.rb +31 -0
  28. data/examples/test_runner/lib/test_runner/plugin/test_info.rb +28 -0
  29. data/examples/test_runner/lib/test_runner/plugin/test_timer.rb +56 -0
  30. data/lib/classx/pluggable.rb +231 -0
  31. data/lib/classx/pluggable/plugin.rb +66 -0
  32. data/spec/classx-pluggable-util/module2path_spec.rb +30 -0
  33. data/spec/classx-pluggable-util/nested_autoload_spec.rb +48 -0
  34. data/spec/classx-pluggable-util/nested_const_get_spec.rb +48 -0
  35. data/spec/classx-pluggable/component_class_get.rb +18 -0
  36. data/spec/classx-pluggable_spec.rb +5 -0
  37. data/spec/example_spec.rb +20 -0
  38. data/spec/spec.opts +1 -0
  39. data/spec/spec_helper.rb +6 -0
  40. data/tasks/basic_config.rake +22 -0
  41. data/tasks/basic_tasks.rake +139 -0
  42. metadata +127 -0
@@ -0,0 +1,18 @@
1
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib')))
2
+
3
+ require 'test_runner'
4
+
5
+ require 'yaml'
6
+ conf = ARGV[0] || File.join(File.dirname(__FILE__), '..', 'conf/config.yaml')
7
+
8
+ config = nil
9
+ File.open(conf) do |f|
10
+ config = YAML.load(f.read)
11
+ end
12
+ app = TestRunner.new(config["global"].merge({
13
+ :test_cases => [:foo, :bar, :baz],
14
+ :logger => $logger ? $logger : Logger.new($stderr) # FIXME: It's for spec/example_spec.rb
15
+ }))
16
+ app.load_plugins(config["plugins"])
17
+
18
+ app.run
@@ -0,0 +1,19 @@
1
+ ---
2
+ global:
3
+ log_level: debug
4
+ check_events: true
5
+ events:
6
+ - BEFORE_EACH
7
+ - AROUND_ALL
8
+ - AROUND_EACH
9
+
10
+ plugins:
11
+ - module: TestRunner::Plugin::SetupFixture #=> autoloading TestRunner::Plugin::SetupFixture, "test_runner/plugin/setup_fixture"
12
+ - module: +TestTimer # same means of TestRunner::Plugin::TestTimer #=> autoloading TestRunner::Plugin::TestTimer, "test_runner/plugin/test_timer"
13
+ - module: +TestInfo
14
+ config:
15
+ template: start test %s
16
+ - module: +TestInfo
17
+ config:
18
+ template: you can also ouput other info for %s
19
+
@@ -0,0 +1,33 @@
1
+ require 'classx'
2
+ require 'classx/validate'
3
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '../../../lib')))
4
+ require 'classx/pluggable'
5
+ require 'classx/pluggable/plugin'
6
+
7
+ class TestRunner
8
+ include ClassX
9
+ include ClassX::Role::Logger
10
+ include ClassX::Pluggable
11
+
12
+ module Plugin; end
13
+
14
+ has :test_cases
15
+
16
+ def run
17
+ call_event_around(:ALL, :logger => logger) do
18
+
19
+ test_cases.each do |tc|
20
+ _do_test(tc)
21
+ end
22
+
23
+ end
24
+ end
25
+
26
+ # it's dummy
27
+ def _do_test tc
28
+ call_event_around(:EACH, :logger => logger, :test => tc) do
29
+ sleep(0.1)
30
+ end
31
+ end
32
+ end
33
+
@@ -0,0 +1,31 @@
1
+ require 'classx'
2
+ require 'classx/validate'
3
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '../../../../lib')))
4
+ require 'classx/pluggable/plugin'
5
+
6
+ class TestRunner
7
+ module Plugin
8
+ class SetupFixture
9
+ include ClassX
10
+ include ClassX::Pluggable::Plugin
11
+ include ClassX::Pluggable::Plugin::AutoRegister
12
+
13
+ # without C::P::P::AutoRegister
14
+ # you can do followings:
15
+ #
16
+ # define_events({
17
+ # :AROUND_ALL => :on_around_all,
18
+ # })
19
+
20
+ def on_around_all param
21
+ param = ClassX::Validate.validate param do
22
+ has :logger
23
+ end
24
+
25
+ param.logger.info "#{self.class}: Setup Fixture"
26
+ yield
27
+ param.logger.info "#{self.class}: Clear Fixture"
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,28 @@
1
+ require 'classx'
2
+ require 'classx/validate'
3
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '../../../../lib')))
4
+ require 'classx/pluggable/plugin'
5
+
6
+ class TestRunner
7
+ module Plugin
8
+ class TestInfo
9
+ include ClassX
10
+ include ClassX::Pluggable::Plugin
11
+ include ClassX::Pluggable::Plugin::AutoRegister
12
+
13
+ has :template
14
+
15
+ def on_before_each param
16
+ param = ClassX::Validate.validate param do
17
+ has :logger
18
+ has :test
19
+ end
20
+
21
+ info = self.template % [ param.test ]
22
+ param.logger.info "#{self.class}: #{info}"
23
+
24
+ info
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,56 @@
1
+ require 'classx'
2
+ require 'classx/validate'
3
+ $LOAD_PATH.unshift(File.expand_path(File.join(File.dirname(__FILE__), '../../../../lib')))
4
+ require 'classx/pluggable/plugin'
5
+
6
+
7
+ class TestRunner
8
+ module Plugin
9
+ class TestTimer
10
+ include ClassX
11
+ include ClassX::Pluggable::Plugin
12
+ include ClassX::Pluggable::Plugin::AutoRegister
13
+
14
+ # without C::P::P::AutoRegister
15
+ # you can do followings:
16
+ #
17
+ # define_events({
18
+ # :AROUND_ALL => :on_around_all,
19
+ # :AROUND_EACH => :on_arond_each,
20
+ # })
21
+
22
+ def on_around_all param
23
+ param = ClassX::Validate.validate param do
24
+ has :logger
25
+ end
26
+
27
+ param.logger.info "#{self.class}: total: start timer"
28
+ test_suite_timer = Time.now
29
+
30
+ yield
31
+
32
+ diff = Time.now - test_suite_timer
33
+ param.logger.info "#{self.class}: total: #{diff.to_f} sec."
34
+
35
+ diff
36
+ end
37
+
38
+ def on_around_each param
39
+ param = ClassX::Validate.validate param do
40
+ has :logger
41
+ has :test
42
+ end
43
+
44
+ param.logger.info "#{self.class}: test #{param.test}: start timer"
45
+ test_timer = Time.now
46
+ yield
47
+
48
+ diff = Time.now - test_timer
49
+
50
+ param.logger.info "#{self.class}: test #{param.test}: #{diff.to_f} sec."
51
+
52
+ diff
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,231 @@
1
+ require 'classx'
2
+ require 'ostruct'
3
+
4
+ module ClassX
5
+ # in your context class.
6
+ #
7
+ # require 'classx'
8
+ # require 'classx/pluggable'
9
+ # class YourApp
10
+ # include ClassX
11
+ # include ClassX::Pluggable
12
+ #
13
+ # def run
14
+ # call_event("SETUP", {})
15
+ # # you app
16
+ # call_event("TEARDOWN", {})
17
+ # end
18
+ #
19
+ # end
20
+ #
21
+ # in your plugin class
22
+ #
23
+ # require 'classx'
24
+ # require 'classx/pluggable'
25
+ # class YourApp
26
+ # class Plugin
27
+ # include ClassX
28
+ # include ClassX::Pluggable::Plugin
29
+ #
30
+ # class SomePlugin < Plugin
31
+ # def register
32
+ # add_event("SETUP", :on_setup)
33
+ # end
34
+ #
35
+ # def on_setup param
36
+ # # param is Hash
37
+ # # hooked setup
38
+ # end
39
+ # end
40
+ # end
41
+ # end
42
+ #
43
+ module Pluggable
44
+ extend ClassX::Attributes
45
+ extend ClassX::Role::Logger
46
+
47
+ class PluginLoadError < ::Exception; end
48
+
49
+ has :__classx_pluggable_events_of,
50
+ :lazy => true,
51
+ :no_cmd_option => true,
52
+ :default => proc {|mine| mine.events.inject({}) {|h,event| h[event] = []; h } }
53
+
54
+ has :plugin_dir,
55
+ :optional => true,
56
+ :kind_of => Array
57
+
58
+ has :check_events,
59
+ :default => false,
60
+ :optional => true,
61
+ :desc => "only add events that define before add_event."
62
+
63
+ has :events,
64
+ :lazy => true,
65
+ :optional => true,
66
+ :kind_of => Array,
67
+ :desc => "hook point for #{self}'s instance",
68
+ :default => proc { [] }
69
+
70
+ # register plugin and method to hook point.
71
+ #
72
+ def add_event name, plugin, meth
73
+ name = name.to_s
74
+ if self.check_events && !self.events.include?(name)
75
+ raise "#{name.inspect} should be declared before call add_event. not in #{self.events.inspect}"
76
+ else
77
+ self.__classx_pluggable_events_of[name] ||= []
78
+ end
79
+ self.__classx_pluggable_events_of[name] << { :plugin => plugin, :method => meth }
80
+ end
81
+
82
+ # load plugins.
83
+ #
84
+ # app.load_plugins([
85
+ # { :module => "YourApp::Plugin::Foo", :confiig => { :some_config => "foo"} },
86
+ # { :module => "+Bar", :confiig => { } }, # It's same meaning of YourApp::Plugin::Bar
87
+ # ])
88
+ #
89
+ def load_plugins plugins
90
+ load_components("plugin", plugins)
91
+ end
92
+
93
+ # if you customize Plugin name space. you can use this instead of +load_plugins+.
94
+ #
95
+ # app.load_components('engine', [
96
+ # { :module => "YourApp::Engine::Foo", :confiig => { :some_config => "foo"} },
97
+ # { :module => "+Bar", :confiig => { } }, # It's same meaning of YourApp::Engine::Bar
98
+ # ])
99
+ #
100
+ def load_components type, components
101
+ components.each do |component|
102
+ load_component type, component
103
+ end
104
+ end
105
+
106
+ def load_component type, hash
107
+ component = OpenStruct.new(hash.dup)
108
+ mod = self.class.component_class_get(type, component.module, { :plugin_dir => self.plugin_dir })
109
+ component.config ||= {}
110
+ component.config[:context] = self
111
+ instance = mod.new(component.config)
112
+ instance.register
113
+ logger.debug("ClassX::Pluggable: loaded #{type} #{component.module}, config=#{instance.inspect}")
114
+ end
115
+
116
+ # invoke registered event of +name+ with +args+. and return array of result each callback.
117
+ def call_event name, *args
118
+ name = name.to_s
119
+ if events = self.__classx_pluggable_events_of[name]
120
+ events.map do |event|
121
+ event[:plugin].__send__(event[:method], *args)
122
+ end
123
+ else
124
+ []
125
+ end
126
+ end
127
+
128
+ # invoke registered event of BEFORE_xxxx and yield block and invoke hook AFTER_xxxx.
129
+ def call_event_around name, *args, &block
130
+ name = name.to_s
131
+ around_name = "AROUND_#{name}"
132
+
133
+ call_event("BEFORE_#{name}", *args)
134
+ if events = self.__classx_pluggable_events_of[around_name]
135
+ procs = []
136
+ procs << block
137
+ index = 0
138
+ nested_proc = events.inject(block) {|bl, event| proc { event[:plugin].__send__(event[:method], *args, &bl ) } }
139
+ nested_proc.call
140
+ end
141
+ call_event("AFTER_#{name}", *args)
142
+ end
143
+
144
+ private
145
+
146
+ module Util
147
+ def module2path mod
148
+ mod.split(/::/).map { |s|
149
+ s.gsub(/([A-Z][a-z]+)(?=[A-Z][a-z]*?)/, '\1_').gsub(/([A-Z])(?=[A-Z][a-z]+)/, '\1_').downcase
150
+ }.join(File::SEPARATOR)
151
+ end
152
+
153
+ def nested_const_get mod
154
+ name_spaces = mod.split(/::/)
155
+ result = ::Object
156
+ name_spaces.each do |const|
157
+ result = result.const_get(const)
158
+ end
159
+ return result
160
+ end
161
+
162
+ def nested_autoload mod, path
163
+ name_spaces = mod.split(/::/)
164
+ target = name_spaces.pop
165
+ tmp = ::Object
166
+ name_spaces.each do |const|
167
+ tmp = tmp.const_get(const)
168
+ end
169
+ tmp.autoload(target, path)
170
+ end
171
+
172
+ module_function :module2path, :nested_const_get, :nested_autoload
173
+ end
174
+
175
+ module ClassMethods
176
+ include ClassX::Pluggable::Util
177
+
178
+ def component_class_get type, name, options={}
179
+ case name
180
+ when ::Class
181
+ return name
182
+ else
183
+ mod_name = nil
184
+ target_name = nil
185
+ if name =~ /\A\+([\w:]+)\z/
186
+ target_name = $1
187
+ mod_name = [ self, type.capitalize, target_name ].join("::")
188
+ else
189
+ mod_name = name
190
+ end
191
+ begin
192
+ return nested_const_get(mod_name)
193
+ rescue NameError => e
194
+ begin
195
+ if options[:plugin_dir]
196
+ options[:plugin_dir].each do |path|
197
+ begin
198
+ begin
199
+ self.const_get(type.capitalize).autoload(name, File.expand_path(File.join(path, module2path(target_name))))
200
+ rescue LoadError => e
201
+ raise ::ClassX::Pluggable::PluginLoadError, "class: #{mod_name} is not found"
202
+ end
203
+ return nested_const_get(mod_name)
204
+ rescue NameError => e
205
+ next
206
+ end
207
+ raise NameError, "must not happened unless your code is something wrong!!"
208
+ end
209
+ else
210
+ nested_autoload(mod_name, module2path(mod_name))
211
+ nested_const_get mod_name
212
+ end
213
+ rescue LoadError => e
214
+ raise ::ClassX::Pluggable::PluginLoadError, "class: #{mod_name} is not found."
215
+ end
216
+ end
217
+ end
218
+ end
219
+ end
220
+
221
+ def self.included klass
222
+ klass.extend ClassMethods
223
+ end
224
+
225
+ # It's useful for testing.
226
+ class MockContext
227
+ include ClassX
228
+ include ClassX::Pluggable
229
+ end
230
+ end
231
+ end
@@ -0,0 +1,66 @@
1
+ require 'classx'
2
+
3
+ module ClassX
4
+ module Pluggable
5
+ module Plugin
6
+ extend ClassX::Attributes
7
+
8
+ has :context, :kind_of => ClassX::Pluggable
9
+
10
+ module ClassMethods
11
+ def define_events hash
12
+ define_method :register do
13
+ add_events hash
14
+ end
15
+ end
16
+ end
17
+
18
+ # Abstract method for calling from context instance automatically that you should implement like followings:
19
+ #
20
+ # def register
21
+ # add_event('SOME_EVENT', 'on_some_event')
22
+ # end
23
+ #
24
+ # def on_some_event
25
+ # # do something.
26
+ # end
27
+ #
28
+ def register
29
+ raise NotImprementedError
30
+ end
31
+
32
+ def inspect
33
+ hash = self.to_hash
34
+ hash.delete("context")
35
+ "#{self.class}: #{hash.inspect}"
36
+ end
37
+
38
+ private
39
+
40
+ def self.included klass
41
+ klass.extend(ClassMethods)
42
+ end
43
+
44
+ def add_event name, meth
45
+ self.context.add_event(name, self, meth)
46
+ end
47
+
48
+ def add_events hash
49
+ hash.each do |event, meth|
50
+ add_event(event, meth)
51
+ end
52
+ end
53
+
54
+ module AutoRegister
55
+ EVENT_REGEX = /\Aon_(.+)\z/
56
+
57
+ def register
58
+ methods.map {|meth| meth.to_s }.grep(EVENT_REGEX).each do |meth|
59
+ meth =~ EVENT_REGEX
60
+ add_event $1.upcase, meth
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end