slayer 0.4.0.beta4 → 0.5.0.beta
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 +5 -5
- data/.github/workflows/release.yml +28 -0
- data/.github/workflows/test.yml +28 -0
- data/.gitignore +1 -0
- data/.rubocop.yml +13 -18
- data/Dockerfile +1 -2
- data/README.md +39 -47
- data/Rakefile +0 -11
- data/lib/slayer/command.rb +65 -8
- data/lib/slayer/compat/compat_040.rb +64 -0
- data/lib/slayer/cops/return_matcher.rb +45 -0
- data/lib/slayer/minitest.rb +6 -7
- data/lib/slayer/result.rb +5 -5
- data/lib/slayer/result_matcher.rb +24 -24
- data/lib/slayer/rspec.rb +20 -4
- data/lib/slayer/version.rb +1 -1
- data/lib/slayer.rb +0 -2
- data/slayer.gemspec +5 -8
- metadata +19 -64
- data/.hound.yml +0 -2
- data/.rubocop_todo.yml +0 -48
- data/.travis.yml +0 -6
- data/CHANGELOG.md +0 -9
- data/lib/slayer/hook.rb +0 -154
- data/lib/slayer/service.rb +0 -136
@@ -10,16 +10,16 @@ module Slayer
|
|
10
10
|
#
|
11
11
|
# == Matching based on success or failure
|
12
12
|
#
|
13
|
-
# The ResultMatcher matches calls to {#
|
14
|
-
# for {Result#
|
15
|
-
# for {Result#
|
13
|
+
# The ResultMatcher matches calls to {#ok} to a {Result} that returns +true+
|
14
|
+
# for {Result#ok?}, calls to {#err} to a {Result} that returns +true+
|
15
|
+
# for {Result#err?}, and calls to {#all} to a {Result} in either state.
|
16
16
|
#
|
17
|
-
# A matching call to {#
|
17
|
+
# A matching call to {#ok} or {#err} takes precedence over matching calls to {#all}
|
18
18
|
#
|
19
19
|
# == Matching based on status
|
20
20
|
#
|
21
21
|
# Additionally, the ResultMatcher can also match by the {Result#status}. If a status
|
22
|
-
# or statuses is passed to {#
|
22
|
+
# or statuses is passed to {#ok}, {#err}, or {#all}, these will only be invoked if the
|
23
23
|
# status of the {Result} matches the passed in status.
|
24
24
|
#
|
25
25
|
# If the default block is the same as the block for one of the statuses the status +:default+
|
@@ -29,11 +29,11 @@ module Slayer
|
|
29
29
|
# == Both pass and fail must be handled
|
30
30
|
#
|
31
31
|
# If the block form of a {Command.call} is invoked, both the block must handle the default
|
32
|
-
# status for both a {Result#
|
32
|
+
# status for both a {Result#ok?} and a {Result#err?}. If both are not handled,
|
33
33
|
# the matching block will not be invoked and a {ResultNotHandledError} will be
|
34
34
|
# raised.
|
35
35
|
#
|
36
|
-
# @example Matcher invokes the matching pass block, with precedence given to {#
|
36
|
+
# @example Matcher invokes the matching pass block, with precedence given to {#ok} and {#err}
|
37
37
|
# # Call produces a successful Result
|
38
38
|
# SuccessCommand.call do |m|
|
39
39
|
# m.pass { puts "Pass!" }
|
@@ -105,8 +105,8 @@ module Slayer
|
|
105
105
|
|
106
106
|
@status = result.status || :default
|
107
107
|
|
108
|
-
@
|
109
|
-
@
|
108
|
+
@handled_default_ok = false
|
109
|
+
@handled_default_err = false
|
110
110
|
|
111
111
|
# These are set to false if they are never set. If they are set to `nil` that
|
112
112
|
# means the block intentionally passed `nil` as the block to be executed.
|
@@ -125,12 +125,12 @@ module Slayer
|
|
125
125
|
# not matched by other matchers.
|
126
126
|
#
|
127
127
|
# If no value is provided for statuses it defaults to +:default+.
|
128
|
-
def
|
128
|
+
def ok(*statuses, &block)
|
129
129
|
statuses << :default if statuses.empty?
|
130
|
-
@
|
130
|
+
@handled_default_ok ||= statuses.include?(:default)
|
131
131
|
|
132
|
-
block_is_match = @result.
|
133
|
-
block_is_default = @result.
|
132
|
+
block_is_match = @result.ok? && statuses.include?(@status)
|
133
|
+
block_is_default = @result.ok? && statuses.include?(:default)
|
134
134
|
|
135
135
|
@matching_block = block if block_is_match
|
136
136
|
@default_block = block if block_is_default
|
@@ -144,19 +144,19 @@ module Slayer
|
|
144
144
|
# not matched by other matchers.
|
145
145
|
#
|
146
146
|
# If no value is provided for statuses it defaults to +:default+.
|
147
|
-
def
|
147
|
+
def err(*statuses, &block)
|
148
148
|
statuses << :default if statuses.empty?
|
149
|
-
@
|
149
|
+
@handled_default_err ||= statuses.include?(:default)
|
150
150
|
|
151
|
-
block_is_match = @result.
|
152
|
-
block_is_default = @result.
|
151
|
+
block_is_match = @result.err? && statuses.include?(@status)
|
152
|
+
block_is_default = @result.err? && statuses.include?(:default)
|
153
153
|
|
154
154
|
@matching_block = block if block_is_match
|
155
155
|
@default_block = block if block_is_default
|
156
156
|
end
|
157
157
|
|
158
158
|
# Provide a block that should be invoked for any {Result}. This has a lower precedence that
|
159
|
-
# either {#
|
159
|
+
# either {#ok} or {#err}.
|
160
160
|
#
|
161
161
|
# @param statuses [Array<status>] Statuses that should be compared to the {Result}. If
|
162
162
|
# any of provided statuses match the {Result} this block will be considered a match.
|
@@ -166,8 +166,8 @@ module Slayer
|
|
166
166
|
# If no value is provided for statuses it defaults to +:default+.
|
167
167
|
def all(*statuses, &block)
|
168
168
|
statuses << :default if statuses.empty?
|
169
|
-
@
|
170
|
-
@
|
169
|
+
@handled_default_ok ||= statuses.include?(:default)
|
170
|
+
@handled_default_err ||= statuses.include?(:default)
|
171
171
|
|
172
172
|
block_is_match = statuses.include?(@status)
|
173
173
|
block_is_default = statuses.include?(:default)
|
@@ -186,7 +186,7 @@ module Slayer
|
|
186
186
|
#
|
187
187
|
# @api private
|
188
188
|
def handled_defaults?
|
189
|
-
return @
|
189
|
+
return @handled_default_ok && @handled_default_err
|
190
190
|
end
|
191
191
|
|
192
192
|
# Executes the provided block that best matched the {Result}. If no block matched
|
@@ -213,8 +213,8 @@ module Slayer
|
|
213
213
|
|
214
214
|
private
|
215
215
|
|
216
|
-
|
217
|
-
|
218
|
-
|
216
|
+
def run_block(block)
|
217
|
+
block&.call(@result.value, @result, @command) # explicit nil should fail this test
|
218
|
+
end
|
219
219
|
end
|
220
220
|
end
|
data/lib/slayer/rspec.rb
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
require 'rspec/expectations'
|
2
|
+
|
2
3
|
# rubocop:disable Metrics/BlockLength
|
3
|
-
RSpec::Matchers.define :
|
4
|
+
RSpec::Matchers.define :be_ok_result do
|
4
5
|
match do |result|
|
5
6
|
status_matches = @status.nil? || @status == result.status
|
6
7
|
message_matches = @message.nil? || @message == result.message
|
7
8
|
value_matches = @value.nil? || @value == result.value
|
8
9
|
|
9
|
-
result.
|
10
|
+
result.ok? && status_matches && message_matches && value_matches
|
10
11
|
end
|
11
12
|
|
12
13
|
chain :with_status do |status|
|
@@ -21,6 +22,12 @@ RSpec::Matchers.define :be_success_result do
|
|
21
22
|
@value = value
|
22
23
|
end
|
23
24
|
|
25
|
+
chain :with do |args|
|
26
|
+
@status = args[:status] if args.key? :status
|
27
|
+
@message = args[:message] if args.key? :message
|
28
|
+
@value = args[:value] if args.key? :value
|
29
|
+
end
|
30
|
+
|
24
31
|
# :nocov:
|
25
32
|
failure_message do |result|
|
26
33
|
return 'expected command to succeed' if @status.nil? && @value.nil? && @message.nil?
|
@@ -33,18 +40,19 @@ RSpec::Matchers.define :be_success_result do
|
|
33
40
|
return "expected command not to have message: #{@message}" if !@message.nil? && result.message == @message
|
34
41
|
return "expected command not to have value: #{@value}" if !@value.nil? && result.value == @value
|
35
42
|
return "expected command not to have status :#{@status}" if !@status.nil? && result.status == @status
|
43
|
+
|
36
44
|
return 'expected command to fail'
|
37
45
|
end
|
38
46
|
# :nocov:
|
39
47
|
end
|
40
48
|
|
41
|
-
RSpec::Matchers.define :
|
49
|
+
RSpec::Matchers.define :be_err_result do
|
42
50
|
match do |result|
|
43
51
|
status_matches = @status.nil? || @status == result.status
|
44
52
|
message_matches = @message.nil? || @message == result.message
|
45
53
|
value_matches = @value.nil? || @value == result.value
|
46
54
|
|
47
|
-
result.
|
55
|
+
result.err? && status_matches && message_matches && value_matches
|
48
56
|
end
|
49
57
|
|
50
58
|
chain :with_status do |status|
|
@@ -59,6 +67,12 @@ RSpec::Matchers.define :be_failed_result do
|
|
59
67
|
@value = value
|
60
68
|
end
|
61
69
|
|
70
|
+
chain :with do |args|
|
71
|
+
@status = args[:status] if args.key? :status
|
72
|
+
@message = args[:message] if args.key? :message
|
73
|
+
@value = args[:value] if args.key? :value
|
74
|
+
end
|
75
|
+
|
62
76
|
# :nocov:
|
63
77
|
failure_message do |result|
|
64
78
|
return 'expected command to fail' if @status.nil? && @value.nil? && @message.nil?
|
@@ -71,8 +85,10 @@ RSpec::Matchers.define :be_failed_result do
|
|
71
85
|
return "expected command to have message: #{@message}" if !@message.nil? && result.message == @message
|
72
86
|
return "expected command to have value: #{@value}" if !@value.nil? && result.value == @value
|
73
87
|
return "expected command to have status :#{@status}" if !@status.nil? && result.status == @status
|
88
|
+
|
74
89
|
return 'expected command to succeed'
|
75
90
|
end
|
76
91
|
# :nocov:
|
77
92
|
end
|
93
|
+
|
78
94
|
# rubocop:enable Metrics/BlockLength
|
data/lib/slayer/version.rb
CHANGED
data/lib/slayer.rb
CHANGED
data/slayer.gemspec
CHANGED
@@ -18,18 +18,15 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
|
-
spec.add_dependency 'virtus', '~>
|
21
|
+
spec.add_dependency 'virtus', '~> 2.0'
|
22
22
|
|
23
23
|
spec.add_development_dependency 'bundler', '~> 2.0'
|
24
24
|
spec.add_development_dependency 'byebug', '~> 9.0'
|
25
|
-
spec.add_development_dependency 'chandler'
|
26
|
-
spec.add_development_dependency 'coveralls'
|
27
25
|
spec.add_development_dependency 'minitest', '~> 5.0'
|
28
26
|
spec.add_development_dependency 'minitest-reporters', '~> 1.1'
|
29
|
-
spec.add_development_dependency 'rspec', '~> 3.
|
30
|
-
spec.add_development_dependency '
|
31
|
-
spec.add_development_dependency '
|
32
|
-
spec.add_development_dependency '
|
33
|
-
spec.add_development_dependency 'simplecov', '~> 0.13'
|
27
|
+
spec.add_development_dependency 'rspec', '~> 3.12'
|
28
|
+
spec.add_development_dependency 'rake', '~> 13.0'
|
29
|
+
spec.add_development_dependency 'rubocop', '= 1.38.0'
|
30
|
+
spec.add_development_dependency 'simplecov'
|
34
31
|
spec.add_development_dependency 'yard', '~> 0.9'
|
35
32
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: slayer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0.beta
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Wyatt Kirby
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2022-12-20 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: virtus
|
@@ -17,14 +17,14 @@ dependencies:
|
|
17
17
|
requirements:
|
18
18
|
- - "~>"
|
19
19
|
- !ruby/object:Gem::Version
|
20
|
-
version: '
|
20
|
+
version: '2.0'
|
21
21
|
type: :runtime
|
22
22
|
prerelease: false
|
23
23
|
version_requirements: !ruby/object:Gem::Requirement
|
24
24
|
requirements:
|
25
25
|
- - "~>"
|
26
26
|
- !ruby/object:Gem::Version
|
27
|
-
version: '
|
27
|
+
version: '2.0'
|
28
28
|
- !ruby/object:Gem::Dependency
|
29
29
|
name: bundler
|
30
30
|
requirement: !ruby/object:Gem::Requirement
|
@@ -53,34 +53,6 @@ dependencies:
|
|
53
53
|
- - "~>"
|
54
54
|
- !ruby/object:Gem::Version
|
55
55
|
version: '9.0'
|
56
|
-
- !ruby/object:Gem::Dependency
|
57
|
-
name: chandler
|
58
|
-
requirement: !ruby/object:Gem::Requirement
|
59
|
-
requirements:
|
60
|
-
- - ">="
|
61
|
-
- !ruby/object:Gem::Version
|
62
|
-
version: '0'
|
63
|
-
type: :development
|
64
|
-
prerelease: false
|
65
|
-
version_requirements: !ruby/object:Gem::Requirement
|
66
|
-
requirements:
|
67
|
-
- - ">="
|
68
|
-
- !ruby/object:Gem::Version
|
69
|
-
version: '0'
|
70
|
-
- !ruby/object:Gem::Dependency
|
71
|
-
name: coveralls
|
72
|
-
requirement: !ruby/object:Gem::Requirement
|
73
|
-
requirements:
|
74
|
-
- - ">="
|
75
|
-
- !ruby/object:Gem::Version
|
76
|
-
version: '0'
|
77
|
-
type: :development
|
78
|
-
prerelease: false
|
79
|
-
version_requirements: !ruby/object:Gem::Requirement
|
80
|
-
requirements:
|
81
|
-
- - ">="
|
82
|
-
- !ruby/object:Gem::Version
|
83
|
-
version: '0'
|
84
56
|
- !ruby/object:Gem::Dependency
|
85
57
|
name: minitest
|
86
58
|
requirement: !ruby/object:Gem::Requirement
|
@@ -115,70 +87,56 @@ dependencies:
|
|
115
87
|
requirements:
|
116
88
|
- - "~>"
|
117
89
|
- !ruby/object:Gem::Version
|
118
|
-
version: '3.
|
119
|
-
type: :development
|
120
|
-
prerelease: false
|
121
|
-
version_requirements: !ruby/object:Gem::Requirement
|
122
|
-
requirements:
|
123
|
-
- - "~>"
|
124
|
-
- !ruby/object:Gem::Version
|
125
|
-
version: '3.5'
|
126
|
-
- !ruby/object:Gem::Dependency
|
127
|
-
name: mocha
|
128
|
-
requirement: !ruby/object:Gem::Requirement
|
129
|
-
requirements:
|
130
|
-
- - "~>"
|
131
|
-
- !ruby/object:Gem::Version
|
132
|
-
version: '1.2'
|
90
|
+
version: '3.12'
|
133
91
|
type: :development
|
134
92
|
prerelease: false
|
135
93
|
version_requirements: !ruby/object:Gem::Requirement
|
136
94
|
requirements:
|
137
95
|
- - "~>"
|
138
96
|
- !ruby/object:Gem::Version
|
139
|
-
version: '
|
97
|
+
version: '3.12'
|
140
98
|
- !ruby/object:Gem::Dependency
|
141
99
|
name: rake
|
142
100
|
requirement: !ruby/object:Gem::Requirement
|
143
101
|
requirements:
|
144
102
|
- - "~>"
|
145
103
|
- !ruby/object:Gem::Version
|
146
|
-
version: '
|
104
|
+
version: '13.0'
|
147
105
|
type: :development
|
148
106
|
prerelease: false
|
149
107
|
version_requirements: !ruby/object:Gem::Requirement
|
150
108
|
requirements:
|
151
109
|
- - "~>"
|
152
110
|
- !ruby/object:Gem::Version
|
153
|
-
version: '
|
111
|
+
version: '13.0'
|
154
112
|
- !ruby/object:Gem::Dependency
|
155
113
|
name: rubocop
|
156
114
|
requirement: !ruby/object:Gem::Requirement
|
157
115
|
requirements:
|
158
116
|
- - '='
|
159
117
|
- !ruby/object:Gem::Version
|
160
|
-
version:
|
118
|
+
version: 1.38.0
|
161
119
|
type: :development
|
162
120
|
prerelease: false
|
163
121
|
version_requirements: !ruby/object:Gem::Requirement
|
164
122
|
requirements:
|
165
123
|
- - '='
|
166
124
|
- !ruby/object:Gem::Version
|
167
|
-
version:
|
125
|
+
version: 1.38.0
|
168
126
|
- !ruby/object:Gem::Dependency
|
169
127
|
name: simplecov
|
170
128
|
requirement: !ruby/object:Gem::Requirement
|
171
129
|
requirements:
|
172
|
-
- - "
|
130
|
+
- - ">="
|
173
131
|
- !ruby/object:Gem::Version
|
174
|
-
version: '0
|
132
|
+
version: '0'
|
175
133
|
type: :development
|
176
134
|
prerelease: false
|
177
135
|
version_requirements: !ruby/object:Gem::Requirement
|
178
136
|
requirements:
|
179
|
-
- - "
|
137
|
+
- - ">="
|
180
138
|
- !ruby/object:Gem::Version
|
181
|
-
version: '0
|
139
|
+
version: '0'
|
182
140
|
- !ruby/object:Gem::Dependency
|
183
141
|
name: yard
|
184
142
|
requirement: !ruby/object:Gem::Requirement
|
@@ -202,13 +160,11 @@ extensions: []
|
|
202
160
|
extra_rdoc_files: []
|
203
161
|
files:
|
204
162
|
- ".githooks/pre-push"
|
163
|
+
- ".github/workflows/release.yml"
|
164
|
+
- ".github/workflows/test.yml"
|
205
165
|
- ".gitignore"
|
206
|
-
- ".hound.yml"
|
207
166
|
- ".rspec"
|
208
167
|
- ".rubocop.yml"
|
209
|
-
- ".rubocop_todo.yml"
|
210
|
-
- ".travis.yml"
|
211
|
-
- CHANGELOG.md
|
212
168
|
- Dockerfile
|
213
169
|
- Gemfile
|
214
170
|
- LICENSE.txt
|
@@ -226,14 +182,14 @@ files:
|
|
226
182
|
- lib/ext/string_ext.rb
|
227
183
|
- lib/slayer.rb
|
228
184
|
- lib/slayer/command.rb
|
185
|
+
- lib/slayer/compat/compat_040.rb
|
186
|
+
- lib/slayer/cops/return_matcher.rb
|
229
187
|
- lib/slayer/errors.rb
|
230
188
|
- lib/slayer/form.rb
|
231
|
-
- lib/slayer/hook.rb
|
232
189
|
- lib/slayer/minitest.rb
|
233
190
|
- lib/slayer/result.rb
|
234
191
|
- lib/slayer/result_matcher.rb
|
235
192
|
- lib/slayer/rspec.rb
|
236
|
-
- lib/slayer/service.rb
|
237
193
|
- lib/slayer/version.rb
|
238
194
|
- slayer.gemspec
|
239
195
|
- slayer_logo.png
|
@@ -256,8 +212,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
256
212
|
- !ruby/object:Gem::Version
|
257
213
|
version: 1.3.1
|
258
214
|
requirements: []
|
259
|
-
|
260
|
-
rubygems_version: 2.6.13
|
215
|
+
rubygems_version: 3.2.3
|
261
216
|
signing_key:
|
262
217
|
specification_version: 4
|
263
218
|
summary: A killer service layer
|
data/.hound.yml
DELETED
data/.rubocop_todo.yml
DELETED
@@ -1,48 +0,0 @@
|
|
1
|
-
# This configuration was generated by
|
2
|
-
# `rubocop --auto-gen-config`
|
3
|
-
# on 2017-02-13 09:02:04 -0800 using RuboCop version 0.47.1.
|
4
|
-
# The point is for the user to remove these configuration records
|
5
|
-
# one by one as the offenses are removed from the code base.
|
6
|
-
# Note that changes in the inspected code, or installation of new
|
7
|
-
# versions of RuboCop, may require this file to be generated again.
|
8
|
-
|
9
|
-
# Offense count: 2
|
10
|
-
Lint/HandleExceptions:
|
11
|
-
Exclude:
|
12
|
-
- 'lib/slayer/command.rb'
|
13
|
-
- 'test/lib/result_matcher_test.rb'
|
14
|
-
|
15
|
-
# Offense count: 2
|
16
|
-
# Cop supports --auto-correct.
|
17
|
-
# Configuration parameters: AllowUnusedKeywordArguments, IgnoreEmptyMethods.
|
18
|
-
Lint/UnusedMethodArgument:
|
19
|
-
Exclude:
|
20
|
-
- 'lib/slayer/form.rb'
|
21
|
-
|
22
|
-
# Offense count: 1
|
23
|
-
Metrics/AbcSize:
|
24
|
-
Max: 19
|
25
|
-
|
26
|
-
# Offense count: 1
|
27
|
-
Metrics/CyclomaticComplexity:
|
28
|
-
Max: 9
|
29
|
-
|
30
|
-
# Offense count: 5
|
31
|
-
# Configuration parameters: CountComments.
|
32
|
-
Metrics/MethodLength:
|
33
|
-
Max: 18
|
34
|
-
|
35
|
-
# Offense count: 1
|
36
|
-
Metrics/PerceivedComplexity:
|
37
|
-
Max: 10
|
38
|
-
|
39
|
-
# Offense count: 6
|
40
|
-
Style/Documentation:
|
41
|
-
Exclude:
|
42
|
-
- 'spec/**/*'
|
43
|
-
- 'test/**/*'
|
44
|
-
- 'lib/ext/**/*'
|
45
|
-
- 'lib/slayer/command.rb'
|
46
|
-
- 'lib/slayer/errors.rb'
|
47
|
-
- 'lib/slayer/form.rb'
|
48
|
-
- 'lib/slayer/result.rb'
|
data/.travis.yml
DELETED
data/CHANGELOG.md
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
# Changelog
|
2
|
-
All notable changes to this project will be documented in this file.
|
3
|
-
|
4
|
-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
|
-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
|
-
|
7
|
-
## [Unreleased]
|
8
|
-
### Added
|
9
|
-
- Added documentation for CHANGELOG to README
|
data/lib/slayer/hook.rb
DELETED
@@ -1,154 +0,0 @@
|
|
1
|
-
module Slayer
|
2
|
-
# Hook adds the ability to wrap all calls to a class in a wrapper method. The
|
3
|
-
# wrapper method is provided with a block that can be used to invoke the called
|
4
|
-
# method.
|
5
|
-
#
|
6
|
-
# The methods #skip_hook and #only_hook can be used to control which methods are
|
7
|
-
# and are not wrapped with the hook call.
|
8
|
-
#
|
9
|
-
# @example Including Hook on a class.
|
10
|
-
# class MyHookedClass
|
11
|
-
# include Hook
|
12
|
-
#
|
13
|
-
# hook :say_hello_and_goodbye
|
14
|
-
#
|
15
|
-
# def self.say_hello_and_goodbye
|
16
|
-
# puts "hello!"
|
17
|
-
#
|
18
|
-
# yield # calls hooked method
|
19
|
-
#
|
20
|
-
# puts "goodbye!"
|
21
|
-
# end
|
22
|
-
#
|
23
|
-
# def self.say_something
|
24
|
-
# puts "something"
|
25
|
-
# end
|
26
|
-
# end
|
27
|
-
#
|
28
|
-
# MyHookedClass.say_something
|
29
|
-
# # => "hello!"
|
30
|
-
# # "something"
|
31
|
-
# # "goodbye!"
|
32
|
-
#
|
33
|
-
# @example Skipping Hooks
|
34
|
-
#
|
35
|
-
# skip_hook :say_something, :do_something # the hook method will not be invoked for
|
36
|
-
# # these methods. They will be called directly.
|
37
|
-
#
|
38
|
-
# @example Only hooking
|
39
|
-
#
|
40
|
-
# only_hook :see_something, :hear_something # These are the only methods that will be
|
41
|
-
# # be hooked. All other methods will be
|
42
|
-
# # called directly.
|
43
|
-
module Hook
|
44
|
-
def self.included(klass)
|
45
|
-
klass.extend ClassMethods
|
46
|
-
end
|
47
|
-
|
48
|
-
# Everything in Hook::ClassMethods automatically get extended onto the
|
49
|
-
# class that includes Hook.
|
50
|
-
module ClassMethods
|
51
|
-
# Define the method that will be invoked whenever another method is invoked.
|
52
|
-
# This should be a class method.
|
53
|
-
def hook(hook_method)
|
54
|
-
@__hook = hook_method
|
55
|
-
end
|
56
|
-
|
57
|
-
# Define the set of methods that should always be called directly, and should
|
58
|
-
# never be hooked
|
59
|
-
def skip_hook(*hook_skips)
|
60
|
-
@__hook_skips = hook_skips
|
61
|
-
end
|
62
|
-
|
63
|
-
def singleton_skip_hook(*hook_skips)
|
64
|
-
@__singleton_hook_skips = hook_skips
|
65
|
-
end
|
66
|
-
|
67
|
-
# If only_hook is called then only the methods provided will be hooked. All
|
68
|
-
# other methods will be called directly.
|
69
|
-
def only_hook(*hook_only)
|
70
|
-
@__hook_only = hook_only
|
71
|
-
end
|
72
|
-
|
73
|
-
private
|
74
|
-
|
75
|
-
def __hook
|
76
|
-
@__hook ||= nil
|
77
|
-
end
|
78
|
-
|
79
|
-
def __hook_skips
|
80
|
-
@__hook_skips ||= []
|
81
|
-
end
|
82
|
-
|
83
|
-
def __singleton_hook_skips
|
84
|
-
@__singleton_hook_skips ||= []
|
85
|
-
end
|
86
|
-
|
87
|
-
def __hook_only
|
88
|
-
@__hook_only ||= nil
|
89
|
-
end
|
90
|
-
|
91
|
-
def __current_methods
|
92
|
-
@__current_methods ||= nil
|
93
|
-
end
|
94
|
-
|
95
|
-
def run_hook(name, instance, passed_block, &block)
|
96
|
-
if __hook
|
97
|
-
send(__hook, name, instance, passed_block, &block)
|
98
|
-
else
|
99
|
-
# rubocop:disable Performance/RedundantBlockCall
|
100
|
-
block.call
|
101
|
-
# rubocop:enable Performance/RedundantBlockCall
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
def singleton_method_added(name)
|
106
|
-
insert_hook_for(name,
|
107
|
-
define_method_fn: :define_singleton_method,
|
108
|
-
hook_target: self,
|
109
|
-
alias_target: singleton_class,
|
110
|
-
skip_methods: blacklist(__singleton_hook_skips))
|
111
|
-
end
|
112
|
-
|
113
|
-
def method_added(name)
|
114
|
-
insert_hook_for(name,
|
115
|
-
define_method_fn: :define_method,
|
116
|
-
hook_target: self,
|
117
|
-
alias_target: self,
|
118
|
-
skip_methods: blacklist(__hook_skips))
|
119
|
-
end
|
120
|
-
|
121
|
-
def insert_hook_for(name, define_method_fn:, hook_target:, alias_target:, skip_methods: [])
|
122
|
-
return if __current_methods && __current_methods.include?(name)
|
123
|
-
|
124
|
-
with_hooks = :"__#{name}_with_hooks"
|
125
|
-
without_hooks = :"__#{name}_without_hooks"
|
126
|
-
|
127
|
-
return if __hook_only && !__hook_only.include?(name.to_sym)
|
128
|
-
return if skip_methods.include? name.to_sym
|
129
|
-
|
130
|
-
@__current_methods = [name, with_hooks, without_hooks]
|
131
|
-
send(define_method_fn, with_hooks) do |*args, &block|
|
132
|
-
hook_target.send(:run_hook, name, self, block) do
|
133
|
-
send(without_hooks, *args)
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
alias_target.send(:alias_method, without_hooks, name)
|
138
|
-
alias_target.send(:alias_method, name, with_hooks)
|
139
|
-
|
140
|
-
@__current_methods = nil
|
141
|
-
end
|
142
|
-
|
143
|
-
def blacklist(additional = [])
|
144
|
-
list = Object.methods(false) + Object.private_methods(false)
|
145
|
-
list += Slayer::Hook::ClassMethods.private_instance_methods(false)
|
146
|
-
list += Slayer::Hook::ClassMethods.instance_methods(false)
|
147
|
-
list += additional
|
148
|
-
list << __hook if __hook
|
149
|
-
|
150
|
-
list
|
151
|
-
end
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|