tiny_hooks 1.0.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +5 -0
- data/README.md +42 -0
- data/lib/tiny_hooks.rb +67 -24
- data/lib/tiny_hooks/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '094f25167ea774a8c2019e04a891a1056a4fcfaa6036ebf04712deb38a4de83f'
|
4
|
+
data.tar.gz: 624dc86d41d19ef506b1867e5ccacf197107baeb09f43fb2376814c33f42af50
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e745e65d785c7a7a52e68be47b9a5f0d8b43103adddf39dd152a9d23b4b175f3c7edf7540f2da3ff2d383c3b817d2b34ebf6a7b9671c29abee799add67e1122e
|
7
|
+
data.tar.gz: 3268e19a06ee31caab8971785d72a112e8adfb68acd898e8845e887ce1fb84769d668e8b7b235508c0a25af965ecd89405d3089f26470e63250901aba12028e8
|
data/CHANGELOG.md
CHANGED
data/README.md
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
[![Ruby](https://github.com/okuramasafumi/tiny_hooks/actions/workflows/main.yml/badge.svg)](https://github.com/okuramasafumi/tiny_hooks/actions/workflows/main.yml)
|
2
|
+
|
1
3
|
# TinyHooks
|
2
4
|
|
3
5
|
A tiny gem to define hooks.
|
@@ -39,6 +41,46 @@ MyClass.new.my_method
|
|
39
41
|
# => "my before hook\nmy method\n"
|
40
42
|
```
|
41
43
|
|
44
|
+
You can also call `define_hook` with method name as a third argument.
|
45
|
+
|
46
|
+
```ruby
|
47
|
+
class MyClass
|
48
|
+
include TinyHooks
|
49
|
+
|
50
|
+
def my_method
|
51
|
+
puts 'my method'
|
52
|
+
end
|
53
|
+
|
54
|
+
def my_before_hook
|
55
|
+
puts 'my before hook'
|
56
|
+
end
|
57
|
+
|
58
|
+
define_hook :before, :my_method, :my_before_hook
|
59
|
+
end
|
60
|
+
|
61
|
+
MyClass.new.my_method
|
62
|
+
# => "my before hook\nmy method\n"
|
63
|
+
```
|
64
|
+
|
65
|
+
You can define hooks for class methods as well.
|
66
|
+
|
67
|
+
```ruby
|
68
|
+
class MyClass
|
69
|
+
include TinyHooks
|
70
|
+
|
71
|
+
def self.my_method
|
72
|
+
puts 'my method'
|
73
|
+
end
|
74
|
+
|
75
|
+
define_hook :before, :my_method, class_method: true do
|
76
|
+
puts 'my before hook'
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
MyClass.my_method
|
81
|
+
# => "my before hook\nmy method\n"
|
82
|
+
```
|
83
|
+
|
42
84
|
TinyHooks shines when the class/module is the base class/module of your library and your users will inherit/include it. In these cases, end users can define hooks to the methods you provide. The only thing you have to do is to provide the list of methods.
|
43
85
|
|
44
86
|
### Halting
|
data/lib/tiny_hooks.rb
CHANGED
@@ -21,6 +21,7 @@ module TinyHooks
|
|
21
21
|
def self.included(base)
|
22
22
|
base.class_eval do
|
23
23
|
@_originals = {}
|
24
|
+
@_class_originals = {}
|
24
25
|
@_targets = UNDEFINED_TARGETS
|
25
26
|
@_public_only = false
|
26
27
|
end
|
@@ -46,37 +47,70 @@ module TinyHooks
|
|
46
47
|
#
|
47
48
|
# @param [Symbol, String] kind the kind of the hook, possible values are: :before, :after and :around
|
48
49
|
# @param [Symbol, String] target the name of the targeted method
|
50
|
+
# @param hook_method_name [Symbol, String] the name of a method which should be called as a hook
|
49
51
|
# @param [Symbol] terminator choice for terminating execution, default is throwing abort symbol
|
50
52
|
# @param [Symbol] if condition to determine if it should define callback. Block is evaluated in context of self
|
51
|
-
|
52
|
-
|
53
|
+
# @param class_method [Boolean] treat target as class method
|
54
|
+
def define_hook(kind, target, hook_method_name = nil, terminator: :abort, if: nil, class_method: false, &block) # rubocop:disable Naming/MethodParameterName
|
55
|
+
raise ArgumentError, 'You must provide a block or hook_method_name' unless block || hook_method_name
|
53
56
|
raise ArgumentError, 'terminator must be one of the following: :abort or :return_false' unless %i[abort return_false].include? terminator.to_sym
|
54
57
|
raise TinyHooks::TargetError, "Hook for #{target} is not allowed" if @_targets != UNDEFINED_TARGETS && !@_targets.include?(target)
|
55
58
|
|
56
|
-
|
59
|
+
if class_method
|
60
|
+
is_private = private_methods.include?(target.to_sym)
|
57
61
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
+
begin
|
63
|
+
original_method = @_public_only ? public_method(target) : method(target)
|
64
|
+
rescue NameError => e
|
65
|
+
raise unless e.message.include?('private')
|
62
66
|
|
63
|
-
|
64
|
-
|
65
|
-
|
67
|
+
raise TinyHooks::PrivateError, "Public only mode is on and hooks for private methods (#{target} for this time) are not available."
|
68
|
+
end
|
69
|
+
@_class_originals[target.to_sym] = original_method unless @_class_originals[target.to_sym]
|
70
|
+
|
71
|
+
block ||= -> { __send__(hook_method_name) }
|
72
|
+
body = method_body(kind, original_method, terminator, binding.local_variable_get(:if), &block)
|
73
|
+
singleton_class.class_eval do
|
74
|
+
undef_method(target)
|
75
|
+
define_method(target, &body)
|
76
|
+
private target if is_private
|
77
|
+
end
|
78
|
+
else # instance method
|
79
|
+
is_private = private_instance_methods.include?(target.to_sym)
|
80
|
+
|
81
|
+
begin
|
82
|
+
original_method = @_public_only ? public_instance_method(target) : instance_method(target)
|
83
|
+
rescue NameError => e
|
84
|
+
raise unless e.message.include?('private')
|
85
|
+
|
86
|
+
raise TinyHooks::PrivateError, "Public only mode is on and hooks for private methods (#{target} for this time) are not available."
|
87
|
+
end
|
88
|
+
@_originals[target.to_sym] = original_method unless @_originals[target.to_sym]
|
89
|
+
|
90
|
+
block ||= -> { __send__(hook_method_name) }
|
66
91
|
|
67
|
-
|
68
|
-
|
69
|
-
|
92
|
+
undef_method(target)
|
93
|
+
define_method(target, &method_body(kind, original_method, terminator, binding.local_variable_get(:if), &block))
|
94
|
+
private target if is_private
|
95
|
+
end
|
70
96
|
end
|
71
97
|
|
72
98
|
# Restore original method
|
73
99
|
#
|
74
100
|
# @param [Symbol, String] target
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
101
|
+
# @param class_method [Boolean] treat target as class method
|
102
|
+
def restore_original(target, class_method: false)
|
103
|
+
if class_method
|
104
|
+
original_method = @_class_originals[target.to_sym] || method(target)
|
105
|
+
singleton_class.class_eval do
|
106
|
+
undef_method(target)
|
107
|
+
define_method(target, original_method)
|
108
|
+
end
|
109
|
+
else
|
110
|
+
original_method = @_originals[target.to_sym] || instance_method(target)
|
111
|
+
undef_method(target)
|
112
|
+
define_method(target, original_method)
|
113
|
+
end
|
80
114
|
end
|
81
115
|
|
82
116
|
# Defines target for hooks
|
@@ -86,6 +120,7 @@ module TinyHooks
|
|
86
120
|
raise ArgumentError if include_pattern.nil? && exclude_pattern.nil?
|
87
121
|
|
88
122
|
candidates = @_public_only ? instance_methods : instance_methods + private_instance_methods
|
123
|
+
candidates += @public_only ? methods : methods + private_methods
|
89
124
|
@_targets = if include_pattern && exclude_pattern
|
90
125
|
targets = candidates.grep(include_pattern)
|
91
126
|
targets.grep_v(exclude_pattern)
|
@@ -131,7 +166,7 @@ module TinyHooks
|
|
131
166
|
return if hook_result == false && terminator == :return_false
|
132
167
|
end
|
133
168
|
|
134
|
-
original_method.bind_call(self, *args, **kwargs, &blk)
|
169
|
+
original_method.is_a?(UnboundMethod) ? original_method.bind_call(self, *args, **kwargs, &blk) : original_method.call(*args, **kwargs, &blk)
|
135
170
|
end
|
136
171
|
else
|
137
172
|
proc do |*args, &blk|
|
@@ -145,7 +180,8 @@ module TinyHooks
|
|
145
180
|
return if hook_result == false && terminator == :return_false
|
146
181
|
end
|
147
182
|
|
148
|
-
original_method.bind(self).
|
183
|
+
original_method = original_method.bind(self) if original_method.is_a?(UnboundMethod)
|
184
|
+
original_method.call(*args, &blk)
|
149
185
|
end
|
150
186
|
end
|
151
187
|
end
|
@@ -153,12 +189,13 @@ module TinyHooks
|
|
153
189
|
def _after(original_method, if_proc:, &block)
|
154
190
|
if RUBY_VERSION >= '2.7'
|
155
191
|
proc do |*args, **kwargs, &blk|
|
156
|
-
original_method.bind_call(self, *args, **kwargs, &blk)
|
192
|
+
original_method.is_a?(UnboundMethod) ? original_method.bind_call(self, *args, **kwargs, &blk) : original_method.call(*args, **kwargs, &blk)
|
157
193
|
instance_exec(*args, **kwargs, &block) if if_proc.nil? || instance_exec(&if_proc) != false
|
158
194
|
end
|
159
195
|
else
|
160
196
|
proc do |*args, &blk|
|
161
|
-
original_method.bind(self).
|
197
|
+
original_method = original_method.bind(self) if original_method.is_a?(UnboundMethod)
|
198
|
+
original_method.call(*args, &blk)
|
162
199
|
instance_exec(*args, &block) if if_proc.nil? || instance_exec(&if_proc) != false
|
163
200
|
end
|
164
201
|
end
|
@@ -167,12 +204,17 @@ module TinyHooks
|
|
167
204
|
def _around(original_method, if_proc:, &block)
|
168
205
|
if RUBY_VERSION >= '2.7'
|
169
206
|
proc do |*args, **kwargs, &blk|
|
170
|
-
wrapper =
|
207
|
+
wrapper = lambda do
|
208
|
+
original_method.is_a?(UnboundMethod) ? original_method.bind_call(self, *args, **kwargs, &blk) : original_method.call(*args, **kwargs, &blk)
|
209
|
+
end
|
171
210
|
instance_exec(wrapper, *args, **kwargs, &block) if if_proc.nil? || instance_exec(&if_proc) != false
|
172
211
|
end
|
173
212
|
else
|
174
213
|
proc do |*args, &blk|
|
175
|
-
wrapper =
|
214
|
+
wrapper = lambda do
|
215
|
+
original_method = original_method.bind(self) if original_method.is_a?(UnboundMethod)
|
216
|
+
original_method.call(*args, &blk)
|
217
|
+
end
|
176
218
|
instance_exec(wrapper, *args, &block) if if_proc.nil? || instance_exec(&if_proc) != false
|
177
219
|
end
|
178
220
|
end
|
@@ -181,6 +223,7 @@ module TinyHooks
|
|
181
223
|
def inherited(subclass)
|
182
224
|
super
|
183
225
|
subclass.instance_variable_set(:@_originals, instance_variable_get(:@_originals).clone)
|
226
|
+
subclass.instance_variable_set(:@_class_originals, instance_variable_get(:@_class_originals).clone)
|
184
227
|
subclass.instance_variable_set(:@_targets, instance_variable_get(:@_targets).clone)
|
185
228
|
subclass.instance_variable_set(:@_public_only, instance_variable_get(:@_public_only).clone)
|
186
229
|
end
|
data/lib/tiny_hooks/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tiny_hooks
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- OKURA Masafumi
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-07-12 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Simple, tiny and general hooks control.
|
14
14
|
email:
|