tiny_hooks 1.0.0 → 2.0.0
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.
- 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
|
+
[](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:
|