tiny_hooks 0.3.0 → 1.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/.rubocop.yml +7 -2
- data/CHANGELOG.md +8 -0
- data/Gemfile +4 -0
- data/README.md +160 -5
- data/benchmark/compare_to_as_callbacks.rb +101 -0
- data/lib/tiny_hooks.rb +158 -69
- data/lib/tiny_hooks/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c5d6e7ef3a23f548af13f1f6497d5ac5eedcf833d8090380b085a9c85bddf37a
|
4
|
+
data.tar.gz: 6992a9ce9f85531803f35f009817b0b7c0b140088e64b9f38c67f4b0225e7d0d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9dfd1ec6b94c48b2309bf4f713dccdd7d8ae572b6ea42615ebb891238306c84224b15657f9fe77d1f3f6ae8a7f45b26c6ac347183d7bde7750e0a6d6a0879a38
|
7
|
+
data.tar.gz: 109d45de44c9309ad233b8a819550306016e6dacb33a7f2a8980de4fddda7562839e6f7463c9c2b0a730544322941d21acfba1b77ea50bb6e7dbc18cdf62dffb
|
data/.rubocop.yml
CHANGED
@@ -30,13 +30,18 @@ Layout/MultilineAssignmentLayout:
|
|
30
30
|
Lint/ConstantResolution:
|
31
31
|
Enabled: false
|
32
32
|
|
33
|
-
|
34
|
-
|
33
|
+
# I couldn't come up with code clean enough
|
34
|
+
# For now I gave up following Metrics cops
|
35
|
+
Metrics:
|
36
|
+
Enabled: false
|
35
37
|
|
36
38
|
Security/Eval:
|
37
39
|
Exclude:
|
38
40
|
- 'test/**/*.rb'
|
39
41
|
|
42
|
+
Style/ClassVars:
|
43
|
+
Enabled: false
|
44
|
+
|
40
45
|
Style/ConstantVisibility:
|
41
46
|
Exclude:
|
42
47
|
- 'lib/tiny_hooks/version.rb'
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,13 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [1.0.0] - 2021-05-09
|
4
|
+
|
5
|
+
- Support halting
|
6
|
+
- [BREAKING CHANGE] Inferface changed from `extend` to `include`
|
7
|
+
- Support public only mode enabled by `public_only!`
|
8
|
+
- Support targeting with `target!`
|
9
|
+
- Support `if` option of `define_hook`
|
10
|
+
|
3
11
|
## [0.3.0] - 2021-04-10
|
4
12
|
|
5
13
|
- Implement restoration of original method
|
data/Gemfile
CHANGED
@@ -14,3 +14,7 @@ gem 'rubocop-minitest', '~> 0.11.0', require: false # For lint
|
|
14
14
|
gem 'rubocop-performance', '~> 1.10.1', require: false # For lint
|
15
15
|
gem 'rubocop-rake', '>= 0.5.1', require: false # For lint
|
16
16
|
gem 'rubocop-sensible', '~> 0.3.0', require: false # For lint
|
17
|
+
|
18
|
+
# Benchmark
|
19
|
+
gem 'activesupport'
|
20
|
+
gem 'benchmark_driver'
|
data/README.md
CHANGED
@@ -20,11 +20,11 @@ Or install it yourself as:
|
|
20
20
|
|
21
21
|
## Usage
|
22
22
|
|
23
|
-
`
|
23
|
+
`include TinyHooks` in your class/module and you're all set to use `define_hook`!
|
24
24
|
|
25
25
|
```ruby
|
26
26
|
class MyClass
|
27
|
-
|
27
|
+
include TinyHooks
|
28
28
|
|
29
29
|
def my_method
|
30
30
|
puts 'my method'
|
@@ -41,15 +41,170 @@ MyClass.new.my_method
|
|
41
41
|
|
42
42
|
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
43
|
|
44
|
-
|
44
|
+
### Halting
|
45
|
+
|
46
|
+
You can halt hook and method body execution by `throw`ing `:abort`.
|
47
|
+
|
48
|
+
```ruby
|
49
|
+
class MyClass
|
50
|
+
include TinyHooks
|
51
|
+
|
52
|
+
def my_method
|
53
|
+
puts 'my method'
|
54
|
+
end
|
55
|
+
|
56
|
+
define_hook :before, :my_method do
|
57
|
+
throw :abort
|
58
|
+
puts 'my before hook'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
MyClass.new.my_method
|
63
|
+
# => ""
|
64
|
+
```
|
65
|
+
|
66
|
+
You can change how to halt from two options: throwing `:abort` and returning `false`. This can be done via `terminator` option.
|
67
|
+
|
68
|
+
```ruby
|
69
|
+
class MyClass
|
70
|
+
include TinyHooks
|
71
|
+
|
72
|
+
def my_method
|
73
|
+
puts 'my method'
|
74
|
+
end
|
75
|
+
|
76
|
+
define_hook :before, :my_method, terminator: :return_false do
|
77
|
+
false
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
MyClass.new.my_method
|
82
|
+
# => ""
|
83
|
+
```
|
84
|
+
|
85
|
+
### Targeting for hooks
|
86
|
+
|
87
|
+
You can limit the targets for hooks in two ways. You can enable hooks for public methods only by using `public_only!` method and include/exclude targets with Regexp pattern by using `targets!` method.
|
88
|
+
|
89
|
+
```ruby
|
90
|
+
class MyClass
|
91
|
+
include TinyHooks
|
92
|
+
|
93
|
+
def my_method
|
94
|
+
puts 'my method'
|
95
|
+
end
|
96
|
+
|
97
|
+
private
|
98
|
+
|
99
|
+
def my_private_method
|
100
|
+
puts 'my private method'
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
class MyClassWithPublicOnly < MyClass
|
105
|
+
public_only!
|
106
|
+
|
107
|
+
define_hook :before, :my_private_method do
|
108
|
+
puts 'my_private_method'
|
109
|
+
end
|
110
|
+
# => This causes PrivateError
|
111
|
+
end
|
112
|
+
|
113
|
+
class MyClassWithExclude < MyClass
|
114
|
+
target! exclude_pattern: /my_method/
|
115
|
+
|
116
|
+
define_hook :before, :my_method do
|
117
|
+
puts 'my_method'
|
118
|
+
end
|
119
|
+
# => This causes TargetError
|
120
|
+
end
|
121
|
+
```
|
122
|
+
|
123
|
+
You can call `include_private!` method to disable the effect of `public_only!`.
|
124
|
+
|
125
|
+
### Conditional hooks
|
126
|
+
|
127
|
+
You can add `if` option to `define_hook` call. `if` option must be a Proc and is evaluated in context of an instance.
|
128
|
+
|
129
|
+
```ruby
|
130
|
+
class MyClass
|
131
|
+
include TinyHooks
|
132
|
+
|
133
|
+
def initialize(hook_enabled = true)
|
134
|
+
@hook_enabled = hook_enabled
|
135
|
+
end
|
136
|
+
|
137
|
+
def my_method
|
138
|
+
puts 'my method'
|
139
|
+
end
|
140
|
+
|
141
|
+
def hook_enabled?
|
142
|
+
@hook_enabled
|
143
|
+
end
|
144
|
+
|
145
|
+
define_hook :before, :my_method, if: -> { hook_enabled? } do
|
146
|
+
puts 'my before hook'
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
MyClass.new(true).my_method
|
151
|
+
# => "my before hook\nmy method\n"
|
152
|
+
|
153
|
+
MyClass.new(false).my_method
|
154
|
+
# => "my method\n"
|
155
|
+
```
|
156
|
+
|
157
|
+
## Differences between TinyHooks and ActiveSupport::Callbacks
|
45
158
|
|
46
159
|
While `TinyHooks` and `ActiveSupport::Callbacks` share the same purpose, there are a few major differences.
|
47
160
|
|
48
|
-
|
161
|
+
### Differences in usage
|
162
|
+
|
49
163
|
* While `ActiveSupport::Callbacks` has a set of methods for callbacks to work, `TinyHooks` has only one method.
|
50
164
|
* You can apply callbacks/hooks into any existing methods without any changes with `TinyHooks`, while you need to change methods to call `run_callbacks` method within them to apply callbacks with `ActiveSupport::Callbacks`.
|
51
165
|
|
52
|
-
|
166
|
+
### Differences in performance
|
167
|
+
|
168
|
+
According to the [benchmark](https://github.com/okuramasafumi/tiny_hooks/blob/main/benchmark/compare_to_as_callbacks.rb), `TinyHooks` is 1.6 times as fast as `ActiveSupport::Callbacks` when before and after callbacks are applied, and twice as fast when no callbacks are applied.
|
169
|
+
|
170
|
+
The result on my machine:
|
171
|
+
|
172
|
+
```
|
173
|
+
Warming up --------------------------------------
|
174
|
+
ActiveSupport 246.181k i/s - 256.956k times in 1.043769s (4.06μs/i)
|
175
|
+
TinyHooks 282.834k i/s - 293.502k times in 1.037719s (3.54μs/i)
|
176
|
+
Calculating -------------------------------------
|
177
|
+
ActiveSupport 230.196k i/s - 738.542k times in 3.208320s (4.34μs/i)
|
178
|
+
TinyHooks 373.057k i/s - 848.501k times in 2.274453s (2.68μs/i)
|
179
|
+
|
180
|
+
Comparison:
|
181
|
+
TinyHooks: 373057.2 i/s
|
182
|
+
ActiveSupport: 230195.9 i/s - 1.62x slower
|
183
|
+
|
184
|
+
Warming up --------------------------------------
|
185
|
+
ActiveSupport no callback set 1.992M i/s - 2.096M times in 1.052258s (501.99ns/i)
|
186
|
+
TinyHooks no callback set 3.754M i/s - 3.791M times in 1.009753s (266.39ns/i)
|
187
|
+
Plain 3.852M i/s - 3.955M times in 1.026654s (259.57ns/i)
|
188
|
+
Calculating -------------------------------------
|
189
|
+
ActiveSupport no callback set 2.005M i/s - 5.976M times in 2.980861s (498.79ns/i)
|
190
|
+
TinyHooks no callback set 4.025M i/s - 11.262M times in 2.798054s (248.46ns/i)
|
191
|
+
Plain 3.765M i/s - 11.557M times in 3.069944s (265.63ns/i)
|
192
|
+
|
193
|
+
Comparison:
|
194
|
+
TinyHooks no callback set: 4024854.4 i/s
|
195
|
+
Plain: 3764695.4 i/s - 1.07x slower
|
196
|
+
ActiveSupport no callback set: 2004848.9 i/s - 2.01x slower
|
197
|
+
```
|
198
|
+
|
199
|
+
### Differences in functionality
|
200
|
+
|
201
|
+
There are few things TinyHooks doesn't cover. For example, TinyHooks doesn't support `unless` option in `define_hook` method or Symbol as a callback body since they are just syntax sugar.
|
202
|
+
|
203
|
+
One of the features TinyHooks doesn't have is `reset_callbacks` which resets all callbacks with given condition. In order to do this, you must call `restore_original` method in iteration.
|
204
|
+
|
205
|
+
### Conclusion
|
206
|
+
|
207
|
+
In short, in most cases, TinyHooks is simpler, easier and faster solution.
|
53
208
|
|
54
209
|
## Development
|
55
210
|
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'benchmark_driver'
|
2
|
+
|
3
|
+
Benchmark.driver do |x|
|
4
|
+
x.prelude <<~RUBY
|
5
|
+
require 'active_support/callbacks'
|
6
|
+
require 'active_support/core_ext/object/blank'
|
7
|
+
class Record
|
8
|
+
include ActiveSupport::Callbacks
|
9
|
+
define_callbacks :save
|
10
|
+
|
11
|
+
def save
|
12
|
+
run_callbacks :save do
|
13
|
+
puts "- save"
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class PersonRecord < Record
|
19
|
+
set_callback :save, :before, :saving_message
|
20
|
+
def saving_message
|
21
|
+
puts "saving..."
|
22
|
+
end
|
23
|
+
|
24
|
+
set_callback :save, :after do |object|
|
25
|
+
puts "saved"
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
require 'tiny_hooks'
|
30
|
+
|
31
|
+
class TinyRecord
|
32
|
+
include TinyHooks
|
33
|
+
|
34
|
+
def save
|
35
|
+
puts '- save'
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class TinyPersonRecord < TinyRecord
|
40
|
+
def saving_message
|
41
|
+
puts 'saving...'
|
42
|
+
end
|
43
|
+
|
44
|
+
define_hook :before, :save do
|
45
|
+
saving_message
|
46
|
+
end
|
47
|
+
|
48
|
+
define_hook :after, :save do
|
49
|
+
puts 'saved'
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
person = PersonRecord.new
|
54
|
+
tiny_person = TinyPersonRecord.new
|
55
|
+
RUBY
|
56
|
+
|
57
|
+
x.report 'ActiveSupport', %( person.save )
|
58
|
+
x.report 'TinyHooks', %( tiny_person.save )
|
59
|
+
end
|
60
|
+
|
61
|
+
Benchmark.driver do |x|
|
62
|
+
x.prelude <<~RUBY
|
63
|
+
require 'active_support/callbacks'
|
64
|
+
require 'active_support/core_ext/object/blank'
|
65
|
+
|
66
|
+
class ASNoCallbackSet
|
67
|
+
include ActiveSupport::Callbacks
|
68
|
+
define_callbacks :save
|
69
|
+
|
70
|
+
def save
|
71
|
+
run_callbacks :save do
|
72
|
+
puts "- save"
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
require 'tiny_hooks'
|
78
|
+
|
79
|
+
class TinyNoCallbackSet
|
80
|
+
include TinyHooks
|
81
|
+
|
82
|
+
def save
|
83
|
+
puts '- save'
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class Plain
|
88
|
+
def save
|
89
|
+
puts '- save'
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
as_no_callback_set = ASNoCallbackSet.new
|
94
|
+
tiny_no_callback_set = TinyNoCallbackSet.new
|
95
|
+
plain = Plain.new
|
96
|
+
RUBY
|
97
|
+
|
98
|
+
x.report 'ActiveSupport no callback set', %( as_no_callback_set.save )
|
99
|
+
x.report 'TinyHooks no callback set', %( tiny_no_callback_set.save )
|
100
|
+
x.report 'Plain', %( plain.save )
|
101
|
+
end
|
data/lib/tiny_hooks.rb
CHANGED
@@ -8,92 +8,181 @@ require_relative 'tiny_hooks/version'
|
|
8
8
|
module TinyHooks
|
9
9
|
class Error < StandardError; end
|
10
10
|
|
11
|
+
class PrivateError < Error; end
|
12
|
+
|
13
|
+
class TargetError < Error; end
|
14
|
+
|
15
|
+
HALTING = Object.new.freeze
|
16
|
+
private_constant :HALTING
|
17
|
+
UNDEFINED_TARGETS = [].freeze
|
18
|
+
private_constant :UNDEFINED_TARGETS
|
19
|
+
|
11
20
|
# @api private
|
12
|
-
def self.
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
21
|
+
def self.included(base)
|
22
|
+
base.class_eval do
|
23
|
+
@_originals = {}
|
24
|
+
@_targets = UNDEFINED_TARGETS
|
25
|
+
@_public_only = false
|
26
|
+
end
|
27
|
+
base.extend ClassMethods
|
18
28
|
end
|
19
29
|
|
20
|
-
#
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
body = case kind.to_sym
|
31
|
-
when :before
|
32
|
-
_before(original_method, &block)
|
33
|
-
when :after
|
34
|
-
_after(original_method, &block)
|
35
|
-
when :around
|
36
|
-
_around(original_method, &block)
|
37
|
-
else
|
38
|
-
raise Error, "#{kind} is not supported."
|
39
|
-
end
|
40
|
-
undef_method(target)
|
41
|
-
define_method(target, &body)
|
42
|
-
end
|
30
|
+
# @api private
|
31
|
+
def self.with_halting(terminator, *args, **kwargs, &block)
|
32
|
+
hook_result = nil
|
33
|
+
abort_result = catch :abort do
|
34
|
+
hook_result = instance_exec(*args, **kwargs, &block)
|
35
|
+
true
|
36
|
+
end
|
37
|
+
return HALTING if abort_result.nil? && terminator == :abort
|
38
|
+
return HALTING if hook_result == false && terminator == :return_false
|
43
39
|
|
44
|
-
|
40
|
+
hook_result
|
41
|
+
end
|
45
42
|
|
46
|
-
#
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
43
|
+
# Class methods
|
44
|
+
module ClassMethods
|
45
|
+
# Define hook with kind and target method
|
46
|
+
#
|
47
|
+
# @param [Symbol, String] kind the kind of the hook, possible values are: :before, :after and :around
|
48
|
+
# @param [Symbol, String] target the name of the targeted method
|
49
|
+
# @param [Symbol] terminator choice for terminating execution, default is throwing abort symbol
|
50
|
+
# @param [Symbol] if condition to determine if it should define callback. Block is evaluated in context of self
|
51
|
+
def define_hook(kind, target, terminator: :abort, if: nil, &block) # rubocop:disable Naming/MethodParameterName
|
52
|
+
raise ArgumentError, 'You must provide a block' unless block
|
53
|
+
raise ArgumentError, 'terminator must be one of the following: :abort or :return_false' unless %i[abort return_false].include? terminator.to_sym
|
54
|
+
raise TinyHooks::TargetError, "Hook for #{target} is not allowed" if @_targets != UNDEFINED_TARGETS && !@_targets.include?(target)
|
51
55
|
|
52
|
-
|
53
|
-
define_method(target, original_method)
|
54
|
-
end
|
56
|
+
is_private = private_instance_methods.include?(target.to_sym)
|
55
57
|
|
56
|
-
|
58
|
+
begin
|
59
|
+
original_method = @_public_only ? public_instance_method(target) : instance_method(target)
|
60
|
+
rescue NameError => e
|
61
|
+
raise unless e.message.include?('private')
|
57
62
|
|
58
|
-
|
59
|
-
if RUBY_VERSION >= '2.7'
|
60
|
-
proc do |*args, **kwargs, &blk|
|
61
|
-
instance_exec(*args, **kwargs, &block)
|
62
|
-
original_method.bind_call(self, *args, **kwargs, &blk)
|
63
|
-
end
|
64
|
-
else
|
65
|
-
proc do |*args, &blk|
|
66
|
-
instance_exec(*args, &block)
|
67
|
-
original_method.bind(self).call(*args, &blk)
|
63
|
+
raise TinyHooks::PrivateError, "Public only mode is on and hooks for private methods (#{target} for this time) are not available."
|
68
64
|
end
|
65
|
+
@_originals[target.to_sym] = original_method unless @_originals[target.to_sym]
|
66
|
+
|
67
|
+
undef_method(target)
|
68
|
+
define_method(target, &method_body(kind, original_method, terminator, binding.local_variable_get(:if), &block))
|
69
|
+
private target if is_private
|
69
70
|
end
|
70
|
-
end
|
71
71
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
72
|
+
# Restore original method
|
73
|
+
#
|
74
|
+
# @param [Symbol, String] target
|
75
|
+
def restore_original(target)
|
76
|
+
original_method = @_originals[target.to_sym] || instance_method(target)
|
77
|
+
|
78
|
+
undef_method(target)
|
79
|
+
define_method(target, original_method)
|
80
|
+
end
|
81
|
+
|
82
|
+
# Defines target for hooks
|
83
|
+
# @param include_pattern [Regexp]
|
84
|
+
# @param exclude_pattern [Regexp]
|
85
|
+
def target!(include_pattern: nil, exclude_pattern: nil)
|
86
|
+
raise ArgumentError if include_pattern.nil? && exclude_pattern.nil?
|
87
|
+
|
88
|
+
candidates = @_public_only ? instance_methods : instance_methods + private_instance_methods
|
89
|
+
@_targets = if include_pattern && exclude_pattern
|
90
|
+
targets = candidates.grep(include_pattern)
|
91
|
+
targets.grep_v(exclude_pattern)
|
92
|
+
elsif include_pattern
|
93
|
+
candidates.grep(include_pattern)
|
94
|
+
else
|
95
|
+
candidates.grep_v(exclude_pattern)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Enable public only mode
|
100
|
+
def public_only!
|
101
|
+
@_public_only = true
|
102
|
+
end
|
103
|
+
|
104
|
+
# Disable public only mode
|
105
|
+
def include_private!
|
106
|
+
@_public_only = false
|
107
|
+
end
|
108
|
+
|
109
|
+
private
|
110
|
+
|
111
|
+
def method_body(kind, original_method, terminator, if_proc, &block)
|
112
|
+
case kind.to_sym
|
113
|
+
when :before then _before(original_method, terminator: terminator, if_proc: if_proc, &block)
|
114
|
+
when :after then _after(original_method, if_proc: if_proc, &block)
|
115
|
+
when :around then _around(original_method, if_proc: if_proc, &block)
|
116
|
+
else
|
117
|
+
raise Error, "#{kind} is not supported."
|
77
118
|
end
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
119
|
+
end
|
120
|
+
|
121
|
+
def _before(original_method, terminator:, if_proc:, &block)
|
122
|
+
if RUBY_VERSION >= '2.7'
|
123
|
+
proc do |*args, **kwargs, &blk|
|
124
|
+
if if_proc.nil? || instance_exec(&if_proc) != false
|
125
|
+
hook_result = nil
|
126
|
+
abort_result = catch :abort do
|
127
|
+
hook_result = instance_exec(*args, **kwargs, &block)
|
128
|
+
true
|
129
|
+
end
|
130
|
+
return if abort_result.nil? && terminator == :abort
|
131
|
+
return if hook_result == false && terminator == :return_false
|
132
|
+
end
|
133
|
+
|
134
|
+
original_method.bind_call(self, *args, **kwargs, &blk)
|
135
|
+
end
|
136
|
+
else
|
137
|
+
proc do |*args, &blk|
|
138
|
+
if if_proc.nil? || instance_exec(&if_proc) != false
|
139
|
+
hook_result = nil
|
140
|
+
abort_result = catch :abort do
|
141
|
+
hook_result = instance_exec(*args, &block)
|
142
|
+
true
|
143
|
+
end
|
144
|
+
return if abort_result.nil? && terminator == :abort
|
145
|
+
return if hook_result == false && terminator == :return_false
|
146
|
+
end
|
147
|
+
|
148
|
+
original_method.bind(self).call(*args, &blk)
|
149
|
+
end
|
82
150
|
end
|
83
151
|
end
|
84
|
-
end
|
85
152
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
153
|
+
def _after(original_method, if_proc:, &block)
|
154
|
+
if RUBY_VERSION >= '2.7'
|
155
|
+
proc do |*args, **kwargs, &blk|
|
156
|
+
original_method.bind_call(self, *args, **kwargs, &blk)
|
157
|
+
instance_exec(*args, **kwargs, &block) if if_proc.nil? || instance_exec(&if_proc) != false
|
158
|
+
end
|
159
|
+
else
|
160
|
+
proc do |*args, &blk|
|
161
|
+
original_method.bind(self).call(*args, &blk)
|
162
|
+
instance_exec(*args, &block) if if_proc.nil? || instance_exec(&if_proc) != false
|
163
|
+
end
|
91
164
|
end
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
165
|
+
end
|
166
|
+
|
167
|
+
def _around(original_method, if_proc:, &block)
|
168
|
+
if RUBY_VERSION >= '2.7'
|
169
|
+
proc do |*args, **kwargs, &blk|
|
170
|
+
wrapper = -> { original_method.bind_call(self, *args, **kwargs, &blk) }
|
171
|
+
instance_exec(wrapper, *args, **kwargs, &block) if if_proc.nil? || instance_exec(&if_proc) != false
|
172
|
+
end
|
173
|
+
else
|
174
|
+
proc do |*args, &blk|
|
175
|
+
wrapper = -> { original_method.bind(self).call(*args, &blk) }
|
176
|
+
instance_exec(wrapper, *args, &block) if if_proc.nil? || instance_exec(&if_proc) != false
|
177
|
+
end
|
96
178
|
end
|
97
179
|
end
|
180
|
+
|
181
|
+
def inherited(subclass)
|
182
|
+
super
|
183
|
+
subclass.instance_variable_set(:@_originals, instance_variable_get(:@_originals).clone)
|
184
|
+
subclass.instance_variable_set(:@_targets, instance_variable_get(:@_targets).clone)
|
185
|
+
subclass.instance_variable_set(:@_public_only, instance_variable_get(:@_public_only).clone)
|
186
|
+
end
|
98
187
|
end
|
99
188
|
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: 0.
|
4
|
+
version: 1.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-05-09 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: Simple, tiny and general hooks control.
|
14
14
|
email:
|
@@ -26,6 +26,7 @@ files:
|
|
26
26
|
- LICENSE.txt
|
27
27
|
- README.md
|
28
28
|
- Rakefile
|
29
|
+
- benchmark/compare_to_as_callbacks.rb
|
29
30
|
- bin/console
|
30
31
|
- bin/setup
|
31
32
|
- lib/tiny_hooks.rb
|