rhook 0.1.3

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.
@@ -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