rhook 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Kaoru Kobo
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,130 @@
1
+ = rhook - Easily drive AOP & hacking existing library with Ruby
2
+
3
+ - You can provide hook point in your code,
4
+ - and can customize its behavior from outside.
5
+ - Also you can 'hack' (== injecting hook point from outside) any methods in existing code.
6
+
7
+ == Install
8
+
9
+ gem install rhook
10
+
11
+ == Basic Usage
12
+
13
+ === step. Example code
14
+
15
+ class TargetBasic
16
+ def greeting
17
+ my_name = "Mike"
18
+ your_name = "Dan"
19
+ hello(your_name) + "I am #{my_name}."
20
+ end
21
+
22
+ def hello(name)
23
+ "Hello, #{name}. "
24
+ end
25
+ end #/TargetBasic
26
+
27
+ ==== that behaves as:
28
+
29
+ t = TargetBasic.new
30
+ t.greeting.should == "Hello, Dan. I am Mike."
31
+
32
+ === step. Insert hook point in TargetBasic
33
+
34
+ require "rhook" # <== load library.
35
+
36
+ class TargetBasic
37
+ def greeting
38
+ my_name = _rhook.does(:my_name) { "Mike"; } # <== my_name = "Mike"
39
+ your_name = "Dan"
40
+ _rhook.to.hello(your_name) + "I am #{my_name}." # <== hello(your_name) + "I am #{my_name}."
41
+ end
42
+
43
+ def hello(name)
44
+ "Hello, #{name}. "
45
+ end
46
+ end #/TargetBasic
47
+
48
+ === step. Add hook from outside, and change behavior.
49
+
50
+ t = TargetBasic.new
51
+ t._rhook.bind(:hello) { |inv|
52
+ inv.args[0] = "Jenkins" # change first argument.
53
+ inv.call
54
+ }
55
+ t._rhook.bind(:my_name) { |inv|
56
+ "Katherine" # change return value.
57
+ }
58
+
59
+ ==== that behaves as:
60
+
61
+ t.greeting.should == "Hello, Jenkins. I am Katherine."
62
+ ~~~~~~~ ~~~~~~~~~
63
+
64
+ == More Basic TIPs
65
+
66
+ === about '_rhook'
67
+
68
+ Once you require 'rhook', any objects has '_rhook' method to access any rhook services. (that returns RHook::RHookService object.)
69
+
70
+ any_object = Object.new
71
+ any_object._rhook.RHOOK_METHOD
72
+
73
+ === What is 'inv' ?
74
+
75
+ RHook::Invocation object, that contains:
76
+
77
+ - call() method to proceed method invocation.
78
+ - receiver
79
+ - arguments passed to method
80
+ - blocks passed to method
81
+ - returned value by call()
82
+
83
+ === If you want 'bind' to not only an object, but any instances of class:
84
+
85
+ TargetBasic._rhook.bind(:hello) { |inv| ...
86
+
87
+ === If you want to HACK exisitng methods, use '_rhook.hack':
88
+
89
+ for the paticular instance:
90
+
91
+ t = Time.now
92
+ t._rhook.hack(:to_s) do |inv|
93
+ "haha! I hacked to_s."
94
+ end
95
+ t.to_s.should == "haha! I hacked."
96
+
97
+ for entire class:
98
+
99
+ Time._rhook.hack(:to_s) do |inv|
100
+ "haha! I hacked to_s."
101
+ end
102
+ (Time.now).to_s.should == "haha! I hacked to_s."
103
+
104
+ for class method:
105
+
106
+ Time._rhook.hack(:now) do |inv|
107
+ "haha! you cannot get time."
108
+ end
109
+ Time.now.should == "haha! you cannot get time."
110
+
111
+
112
+ == More Usage ...
113
+
114
+ Please see spec: http://github.com/kaorukobo/rhook/blob/master/spec/rhook_spec.rb
115
+
116
+ == ...
117
+
118
+ === Note on Patches/Pull Requests
119
+
120
+ * Fork the project.
121
+ * Make your feature addition or bug fix.
122
+ * Add tests for it. This is important so I don't break it in a
123
+ future version unintentionally.
124
+ * Commit, do not mess with rakefile, version, or history.
125
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
126
+ * Send me a pull request. Bonus points for topic branches.
127
+
128
+ === Copyright
129
+
130
+ Copyright (c) 2010 Kaoru Kobo. See LICENSE for details.
@@ -0,0 +1,45 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "rhook"
8
+ gem.summary = %Q{Easily drive AOP & hacking existing library with Ruby}
9
+ gem.description = %Q{You can provide hook point in your code, and can customize its behavior from outside. Also you can 'hack' (== injecting hook point from outside) any methods in existing code.}
10
+ gem.email = ""
11
+ gem.homepage = "http://github.com/kaorukobo/rhook"
12
+ gem.authors = ["Kaoru Kobo"]
13
+ gem.add_development_dependency "rspec", ">= 1.2.9"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'spec/rake/spectask'
22
+ Spec::Rake::SpecTask.new(:spec) do |spec|
23
+ spec.libs << 'lib' << 'spec'
24
+ spec.spec_files = FileList['spec/**/*_spec.rb']
25
+ end
26
+
27
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
28
+ spec.libs << 'lib' << 'spec'
29
+ spec.pattern = 'spec/**/*_spec.rb'
30
+ spec.rcov = true
31
+ end
32
+
33
+ task :spec => :check_dependencies
34
+
35
+ task :default => :spec
36
+
37
+ require 'rake/rdoctask'
38
+ Rake::RDocTask.new do |rdoc|
39
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
40
+
41
+ rdoc.rdoc_dir = 'rdoc'
42
+ rdoc.title = "rhook #{version}"
43
+ rdoc.rdoc_files.include('README*')
44
+ rdoc.rdoc_files.include('lib/**/*.rb')
45
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.3
@@ -0,0 +1,276 @@
1
+ module RHook
2
+ class Registry
3
+ attr_reader :class_cached_flag_map
4
+ def initialize
5
+ @class_cached_flag_map = {}
6
+ end
7
+ end
8
+
9
+ def self.registry
10
+ @registry ||= Registry.new
11
+ end
12
+
13
+ class RHookService
14
+ attr_reader :hooks_map
15
+
16
+ def initialize(obj)
17
+ @obj = obj
18
+ @hooks_map = {}
19
+ @class_cached_hooks_map = {} if Class === @obj
20
+ end
21
+
22
+ def bind(name, opt = {}, &block)
23
+ hook = Hook.new
24
+ hook.hook_proc = block
25
+ (@hooks_map[name.to_sym] ||= []).unshift( hook )
26
+ RHook.registry.class_cached_flag_map.delete(name)
27
+ opt[:disable] or hook.enable()
28
+ HookGroup.add_to_current_groups(hook)
29
+ hook
30
+ end
31
+
32
+ def hack(name, opt = {}, &block)
33
+ if Class === @obj
34
+ klass = @obj
35
+ klass._rhook.on_method(name)
36
+ end
37
+
38
+ klass = @obj.instance_eval("class << self; self; end")
39
+ klass._rhook.on_method(name)
40
+
41
+ bind(name, opt, &block)
42
+ end
43
+
44
+ def unbind_all
45
+ @hooks_map.clear
46
+ @class_cached_hooks_map.clear
47
+ end
48
+
49
+ # nodoc:
50
+ # Proxy class for 'to'
51
+ class Caller
52
+ def initialize(rhook, opt)
53
+ @rhook = rhook
54
+ @opt = opt
55
+ end
56
+
57
+ def method_missing(name, *args, &block)
58
+ @rhook.call_method(name, name, args, block, @opt)
59
+ end
60
+ end #/Caller
61
+
62
+ # ================================================================
63
+ # Target-side methods:
64
+
65
+ def to(opt = {})
66
+ Caller.new(self, opt)
67
+ end
68
+
69
+ def call_method(name, method_name, args, block, opt = {})
70
+ hooks = concat_hooks([], name)
71
+ hooks.empty? and @obj.__send__(method_name, *args, &block)
72
+
73
+ inv = Invocation.new
74
+ inv.target = @obj
75
+ inv.receiver = @obj
76
+ inv.args = args
77
+ inv.block = block
78
+ inv.hooks = hooks
79
+ inv.target_proc = @obj.method(method_name)
80
+ inv.hint = opt[:hint] || {}
81
+ inv.proceed()
82
+ end
83
+
84
+ def does(name, opt = {}, &block)
85
+ hooks = concat_hooks([], name)
86
+ hooks.empty? and return yield
87
+
88
+ inv = Invocation.new
89
+ inv.target = @obj
90
+ inv.receiver = nil
91
+ inv.args = []
92
+ inv.block = nil
93
+ inv.hooks = hooks
94
+ inv.target_proc = block
95
+ inv.hint = opt[:hint] || {}
96
+ inv.proceed()
97
+ end
98
+
99
+ def on_method(*names)
100
+ Class === @obj or raise("Cannot use on_method on non-Class.")
101
+ code = ""
102
+ for method_name in names
103
+ # Skip if method is not defined.
104
+ @obj.method_defined?(method_name) or next
105
+
106
+ real_method_name = "#{method_name}__rhook_real".to_sym
107
+ @obj.method_defined?(real_method_name) and next
108
+
109
+ code << "alias #{real_method_name} #{method_name}\n"
110
+ code << "def #{method_name}(*args, &block); _rhook.call_method(:#{method_name}, :#{real_method_name}, args, block); end\n"
111
+ end
112
+ @obj.module_eval(code)
113
+ end
114
+
115
+ # ================================================================
116
+ # Internal methods:
117
+
118
+ def concat_hooks(dest, name)
119
+ if Class === @obj
120
+ concat_class_hooks(dest, name)
121
+ else
122
+ concat_hooks_internal(dest, name)
123
+ @obj.class._rhook.concat_class_hooks(dest, name)
124
+ end
125
+ dest
126
+ end
127
+
128
+ def concat_class_hooks(dest, name)
129
+ # use cached one if available
130
+ if RHook.registry.class_cached_flag_map[name]
131
+ hooks = @class_cached_hooks_map[name]
132
+ if hooks
133
+ return dest.concat(hooks)
134
+ end
135
+ end
136
+
137
+ hooks = []
138
+
139
+ # collect hooks including ancestor classes
140
+ begin
141
+ concat_hooks_internal(hooks, name)
142
+
143
+ # skips class without RHookService
144
+ klass = @obj
145
+ while true
146
+ klass = klass.superclass
147
+ klass or break
148
+ if klass._has_rhook?
149
+ klass._rhook.concat_class_hooks(hooks, name)
150
+ break
151
+ end
152
+ end
153
+ end
154
+
155
+ # Store to cache.
156
+ @class_cached_hooks_map[name] = hooks
157
+ RHook.registry.class_cached_flag_map[name] = true
158
+ dest.concat(hooks)
159
+ end
160
+
161
+ def concat_hooks_internal(dest, name)
162
+ hooks = @hooks_map[name]
163
+ hooks and dest.concat(hooks)
164
+ end
165
+ end #/RHookService
166
+
167
+ class Invocation < Struct.new(:target, :receiver, :args, :block, :returned, :hooks, :target_proc, :hint)
168
+ def initialize
169
+ @hook_index = 0
170
+ end
171
+
172
+ def proceed
173
+ hook = hooks[@hook_index]
174
+ # -- If no more hook was found, calls target procedure and return
175
+ hook or return target_proc.call(*args, &block)
176
+ # -- Set hook pointer to next, then call next hook
177
+ @hook_index += 1
178
+ begin
179
+ hook.call(self)
180
+ ensure
181
+ @hook_index -= 1
182
+ end
183
+ end
184
+
185
+ alias call proceed
186
+ end #/Invocation
187
+
188
+ class Hook
189
+ attr_accessor :enabled
190
+ attr_accessor :hook_proc
191
+
192
+ def call(inv)
193
+ @enabled or return inv.proceed()
194
+ hook_proc.call(inv)
195
+ end
196
+
197
+ def enable(&block)
198
+ @enabled = true
199
+ if block_given?
200
+ begin
201
+ return yield
202
+ ensure
203
+ @enabled = false
204
+ end
205
+ end
206
+ self
207
+ end
208
+
209
+ def disable
210
+ @enabled = false
211
+ self
212
+ end
213
+ end #/Hook
214
+
215
+ class ::Object
216
+ def _rhook
217
+ @_rhook ||= RHook::RHookService.new(self)
218
+ end
219
+
220
+ def _has_rhook?
221
+ @_rhook ? true : false
222
+ end
223
+ end
224
+
225
+ class HookGroup
226
+ def initialize
227
+ @hooks = []
228
+ end
229
+
230
+ def add(hook)
231
+ @hooks << hook
232
+ end
233
+
234
+ def wrap(&block)
235
+ group_stack = (Thread.current["rhook_group"] ||= [])
236
+ group_stack << self
237
+ begin
238
+ yield
239
+ ensure
240
+ group_stack.pop
241
+ end
242
+ self
243
+ end
244
+
245
+ def enable(&block)
246
+ @hooks.each do |h|
247
+ h.enable
248
+ end
249
+ if block_given?
250
+ begin
251
+ return yield
252
+ ensure
253
+ disable
254
+ end
255
+ end
256
+ self
257
+ end
258
+
259
+ def disable
260
+ @hooks.each do |h|
261
+ h.disable
262
+ end
263
+ self
264
+ end
265
+
266
+ def self.add_to_current_groups(hook)
267
+ (Thread.current["rhook_group"] || []).each do |group|
268
+ group.add(hook)
269
+ end
270
+ end
271
+ end #/HookGroup
272
+
273
+ def self.group(&block)
274
+ HookGroup.new.wrap(&block)
275
+ end
276
+ end
@@ -0,0 +1,55 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{rhook}
8
+ s.version = "0.1.3"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Kaoru Kobo"]
12
+ s.date = %q{2010-11-11}
13
+ s.description = %q{You can provide hook point in your code, and can customize its behavior from outside. Also you can 'hack' (== injecting hook point from outside) any methods in existing code.}
14
+ s.email = %q{}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ "LICENSE",
23
+ "README.rdoc",
24
+ "Rakefile",
25
+ "VERSION",
26
+ "lib/rhook.rb",
27
+ "rhook.gemspec",
28
+ "spec/rhook_spec.rb",
29
+ "spec/spec.opts",
30
+ "spec/spec_helper.rb"
31
+ ]
32
+ s.homepage = %q{http://github.com/kaorukobo/rhook}
33
+ s.rdoc_options = ["--charset=UTF-8"]
34
+ s.require_paths = ["lib"]
35
+ s.rubygems_version = %q{1.3.7}
36
+ s.summary = %q{Easily drive AOP & hacking existing library with Ruby}
37
+ s.test_files = [
38
+ "spec/rhook_spec.rb",
39
+ "spec/spec_helper.rb"
40
+ ]
41
+
42
+ if s.respond_to? :specification_version then
43
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
44
+ s.specification_version = 3
45
+
46
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
47
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
48
+ else
49
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
50
+ end
51
+ else
52
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
53
+ end
54
+ end
55
+
@@ -0,0 +1,280 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ # ================================================================
4
+ describe "rhook (basic usage)" do
5
+ class TargetBasic
6
+ def greeting
7
+ my_name = _rhook.does(:my_name) { "Mike"; }
8
+ your_name = "Dan"
9
+ _rhook.to.hello(your_name) + "I am #{my_name}."
10
+ end
11
+
12
+ def hello(name)
13
+ "Hello, #{name}. "
14
+ end
15
+ end #/TargetBasic
16
+
17
+ before :each do
18
+ TargetBasic._rhook.unbind_all()
19
+ end
20
+
21
+ example "without any hook" do
22
+ t = TargetBasic.new
23
+ t.greeting.should == "Hello, Dan. I am Mike."
24
+ end
25
+
26
+ example "bind to object" do
27
+ t = TargetBasic.new
28
+ t._rhook.bind(:hello) { |inv|
29
+ inv.args[0] = "Jenkins"
30
+ inv.call
31
+ }
32
+ t.greeting.should == "Hello, Jenkins. I am Mike."
33
+ end
34
+
35
+ example "bind to all instances of class" do
36
+ t = TargetBasic.new
37
+ TargetBasic._rhook.bind(:hello) { |inv|
38
+ inv.args[0] = "Jenkins"
39
+ inv.call
40
+ }
41
+ t.greeting.should == "Hello, Jenkins. I am Mike."
42
+ end
43
+
44
+ example "does(NAME) {}" do
45
+ t = TargetBasic.new
46
+ TargetBasic._rhook.bind(:my_name) { |inv|
47
+ "Katherine"
48
+ }
49
+ t.greeting.should == "Hello, Dan. I am Katherine."
50
+ end
51
+ end
52
+
53
+ # ================================================================
54
+ describe "rhook (advanced usage)" do
55
+ class Target
56
+
57
+ end
58
+
59
+ before :each do
60
+ Target._rhook.unbind_all()
61
+ end
62
+
63
+ # ================================================================
64
+ describe "on_method" do
65
+ class Target
66
+ def test__on_method()
67
+ "foo"
68
+ end
69
+ _rhook.on_method :test__on_method
70
+ end #/Target
71
+
72
+ example "Hook on any method call: on_method" do
73
+ # not recommanded.
74
+
75
+ t = Target.new
76
+ t._rhook.bind(:test__on_method) do |inv|
77
+ inv.call + "&bar"
78
+ end
79
+ t.test__on_method.should == "foo&bar"
80
+ end
81
+ end
82
+ # ================================================================
83
+
84
+ # ================================================================
85
+ describe "Hack" do
86
+ class Target
87
+ def test__hack()
88
+ "foo"
89
+ end
90
+ end #/Target
91
+
92
+ example "Hack on object" do
93
+ t = Target.new
94
+ t._rhook.hack(:test__hack) do |inv|
95
+ "bar"
96
+ end
97
+ t.test__hack.should == "bar"
98
+ t2 = Target.new
99
+ t2.test__hack.should == "foo"
100
+ end
101
+
102
+ example "Hack on class" do
103
+ t = Target.new
104
+ Target._rhook.hack(:test__hack) do |inv|
105
+ "bar"
106
+ end
107
+ t.test__hack.should == "bar"
108
+ t2 = Target.new
109
+ t2.test__hack.should == "bar"
110
+ end
111
+
112
+ class Target
113
+ def self.hack_class_method
114
+ "foo"
115
+ end
116
+ end #/Target
117
+
118
+ example "Hack on class method" do
119
+ Target._rhook.hack(:hack_class_method) do |inv|
120
+ "bar"
121
+ end
122
+ Target.hack_class_method.should == "bar"
123
+ end
124
+ end
125
+ # ================================================================
126
+
127
+
128
+ # ================================================================
129
+ describe "Hook object (enable/disable)" do
130
+ class Target
131
+ def enable_disable()
132
+ "disabled"
133
+ end
134
+ end #/Target
135
+
136
+ example do
137
+ hook = Target._rhook.hack(:enable_disable) do |inv|
138
+ "enabled"
139
+ end
140
+
141
+ t = Target.new
142
+ t.enable_disable.should == "enabled"
143
+
144
+ hook.disable
145
+ t.enable_disable.should == "disabled"
146
+
147
+ # with block
148
+ hook.enable do
149
+ t.enable_disable.should == "enabled"
150
+ end
151
+ t.enable_disable.should == "disabled"
152
+ end
153
+ end
154
+
155
+
156
+ # ================================================================
157
+ describe "Hook group" do
158
+ class Target
159
+ def group_1
160
+ "g1"
161
+ end
162
+
163
+ def group_2
164
+ "g2"
165
+ end
166
+ end
167
+
168
+ example "group and enable/disable by group." do
169
+ group = RHook.group do
170
+ Target._rhook.hack(:group_1) do |inv|
171
+ "hack1"
172
+ end
173
+ Target._rhook.hack(:group_2) do |inv|
174
+ "hack2"
175
+ end
176
+ end
177
+
178
+ t = Target.new
179
+ group.disable
180
+ t.group_1.should == "g1"
181
+ t.group_2.should == "g2"
182
+ group.enable do
183
+ t.group_1.should == "hack1"
184
+ t.group_2.should == "hack2"
185
+ end
186
+ t.group_1.should == "g1"
187
+ t.group_2.should == "g2"
188
+ end
189
+
190
+ example "Nested group" do
191
+ @parent_group = RHook.group do
192
+ Target._rhook.hack(:group_1) do |inv|
193
+ "hack1"
194
+ end
195
+ @child_group = RHook.group do
196
+ Target._rhook.hack(:group_2) do |inv|
197
+ "hack2"
198
+ end
199
+ end
200
+ end
201
+
202
+ t = Target.new
203
+
204
+ @parent_group.disable
205
+ t.group_1.should == "g1"
206
+ t.group_2.should == "g2"
207
+
208
+ @child_group.enable
209
+ t.group_1.should == "g1"
210
+ t.group_2.should == "hack2"
211
+
212
+ @parent_group.enable
213
+ t.group_1.should == "hack1"
214
+ t.group_2.should == "hack2"
215
+ end
216
+ end
217
+ # ================================================================
218
+
219
+ end
220
+
221
+ # ================================================================
222
+ describe "rhook (behavior)" do
223
+ describe "Super class's hook" do
224
+ class Target
225
+ end
226
+
227
+ class ChildTarget < Target
228
+ def superclass_test
229
+ _rhook.does(:superclass_test) { "foo"; }
230
+ end
231
+ end
232
+
233
+ example "Super class's hook affects children class." do
234
+ child_t = ChildTarget.new
235
+ child_t.superclass_test.should == "foo"
236
+ # Add hook to superclass:
237
+ Target._rhook.bind(:superclass_test) do |inv|
238
+ "bar"
239
+ end
240
+ # then it affects to children class:
241
+ child_t.superclass_test.should == "bar"
242
+ end
243
+ end
244
+
245
+ # ================================================================
246
+ describe "multiple (chained) hooks" do
247
+ class Target
248
+ def chained
249
+ ["original"]
250
+ end
251
+ end
252
+
253
+ example "multiple hooks" do
254
+ t = Target.new
255
+ t._rhook.hack(:chained) do |inv|
256
+ inv.call + ["1st_hook"]
257
+ end
258
+ t._rhook.hack(:chained) do |inv|
259
+ inv.call + ["2nd_hook"]
260
+ end
261
+
262
+ t.chained.should == ["original", "1st_hook", "2nd_hook"]
263
+ end
264
+
265
+ example "Hook to class is executed later, than one to object" do
266
+ t = Target.new
267
+ t._rhook.hack(:chained) do |inv|
268
+ inv.call + ["object_hook"]
269
+ end
270
+ Target._rhook.hack(:chained) do |inv|
271
+ inv.call + ["class_hook"]
272
+ end
273
+
274
+ t.chained.should == ["original", "class_hook", "object_hook"]
275
+ end
276
+ end
277
+
278
+
279
+
280
+ end
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,12 @@
1
+ require 'rubygems'
2
+ gem 'rspec'
3
+
4
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ require 'rhook'
7
+ require 'spec'
8
+ require 'spec/autorun'
9
+
10
+ Spec::Runner.configure do |config|
11
+
12
+ end
metadata ADDED
@@ -0,0 +1,90 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rhook
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 3
9
+ version: 0.1.3
10
+ platform: ruby
11
+ authors:
12
+ - Kaoru Kobo
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-11-11 00:00:00 +09:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rspec
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ segments:
29
+ - 1
30
+ - 2
31
+ - 9
32
+ version: 1.2.9
33
+ type: :development
34
+ version_requirements: *id001
35
+ description: You can provide hook point in your code, and can customize its behavior from outside. Also you can 'hack' (== injecting hook point from outside) any methods in existing code.
36
+ email: ""
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - LICENSE
43
+ - README.rdoc
44
+ files:
45
+ - .document
46
+ - .gitignore
47
+ - LICENSE
48
+ - README.rdoc
49
+ - Rakefile
50
+ - VERSION
51
+ - lib/rhook.rb
52
+ - rhook.gemspec
53
+ - spec/rhook_spec.rb
54
+ - spec/spec.opts
55
+ - spec/spec_helper.rb
56
+ has_rdoc: true
57
+ homepage: http://github.com/kaorukobo/rhook
58
+ licenses: []
59
+
60
+ post_install_message:
61
+ rdoc_options:
62
+ - --charset=UTF-8
63
+ require_paths:
64
+ - lib
65
+ required_ruby_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ segments:
71
+ - 0
72
+ version: "0"
73
+ required_rubygems_version: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ segments:
79
+ - 0
80
+ version: "0"
81
+ requirements: []
82
+
83
+ rubyforge_project:
84
+ rubygems_version: 1.3.7
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: Easily drive AOP & hacking existing library with Ruby
88
+ test_files:
89
+ - spec/rhook_spec.rb
90
+ - spec/spec_helper.rb