slayer 0.4.0.beta4 → 0.5.0.beta
Sign up to get free protection for your applications and to get access to all the features.
- 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
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: bc095dc4fad61e836560eb30851324c846f8a5a1b3fd51bdc48d692ab576d8a3
|
4
|
+
data.tar.gz: 9a77c6b602b544462311a897bb53685434a51dde783602274fce9d83096f3644
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3a95c4d4b95bffc5d4f129e07fae690fcccd3543c6f7f745471aeaa8093bbfd9e5b377ffc931d52c0bb0f4f3048330f7b9421233246f633dde19de52526a6851
|
7
|
+
data.tar.gz: 2de8ba806e4210a197ec87323f8a48777f6e024e9546170cee4ce15a651cfad2dc62134f3e4021003fde1ef378e57771778d98eec49a9747110955c675956926
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# .github/workflows/release.yml
|
2
|
+
|
3
|
+
name: Release
|
4
|
+
|
5
|
+
on:
|
6
|
+
workflow_dispatch:
|
7
|
+
pull_request:
|
8
|
+
|
9
|
+
jobs:
|
10
|
+
release:
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
steps:
|
13
|
+
- uses: actions/checkout@v2
|
14
|
+
- uses: ruby/setup-ruby@v1
|
15
|
+
with:
|
16
|
+
ruby-version: 3.0.0
|
17
|
+
- run: bundle install
|
18
|
+
- name: publish gem
|
19
|
+
run: |
|
20
|
+
mkdir -p $HOME/.gem
|
21
|
+
touch $HOME/.gem/credentials
|
22
|
+
chmod 0600 $HOME/.gem/credentials
|
23
|
+
printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
|
24
|
+
gem build *.gemspec
|
25
|
+
gem push *.gem
|
26
|
+
env:
|
27
|
+
GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
|
28
|
+
|
@@ -0,0 +1,28 @@
|
|
1
|
+
name: Test & Lint
|
2
|
+
|
3
|
+
on:
|
4
|
+
push:
|
5
|
+
branches: [master]
|
6
|
+
pull_request:
|
7
|
+
|
8
|
+
jobs:
|
9
|
+
test:
|
10
|
+
|
11
|
+
runs-on: ubuntu-latest
|
12
|
+
|
13
|
+
strategy:
|
14
|
+
matrix:
|
15
|
+
ruby-version: ['3.1', '3.0', '2.7']
|
16
|
+
|
17
|
+
steps:
|
18
|
+
- uses: actions/checkout@v3
|
19
|
+
- name: Set up Ruby
|
20
|
+
uses: ruby/setup-ruby@359bebbc29cbe6c87da6bc9ea3bc930432750108
|
21
|
+
with:
|
22
|
+
ruby-version: ${{ matrix.ruby-version }}
|
23
|
+
- name: Install dependencies
|
24
|
+
run: bundle install
|
25
|
+
- name: Rubocop
|
26
|
+
run: rubocop
|
27
|
+
- name: Run tests
|
28
|
+
run: bundle exec rake
|
data/.gitignore
CHANGED
data/.rubocop.yml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
|
-
inherit_from: .rubocop_todo.yml
|
2
|
-
|
3
1
|
AllCops:
|
4
|
-
|
2
|
+
NewCops: enable
|
3
|
+
SuggestExtensions: false
|
4
|
+
TargetRubyVersion: 3.1
|
5
5
|
Include:
|
6
6
|
- 'lib/**/*.rb'
|
7
7
|
- 'test/**/*.rb'
|
@@ -12,6 +12,14 @@ AllCops:
|
|
12
12
|
- 'bin/**/*'
|
13
13
|
- 'test/fixtures/**/*.rb'
|
14
14
|
|
15
|
+
Style/HashSyntax:
|
16
|
+
EnforcedShorthandSyntax: never
|
17
|
+
|
18
|
+
Style/Documentation:
|
19
|
+
Enabled: false
|
20
|
+
|
21
|
+
Naming/BlockForwarding:
|
22
|
+
Enabled: false
|
15
23
|
|
16
24
|
Style/RedundantSelf:
|
17
25
|
Enabled: false
|
@@ -34,13 +42,7 @@ Style/FrozenStringLiteralComment:
|
|
34
42
|
Layout/CommentIndentation:
|
35
43
|
Enabled: false
|
36
44
|
|
37
|
-
|
38
|
-
Enabled: false
|
39
|
-
|
40
|
-
Layout/IndentationConsistency:
|
41
|
-
EnforcedStyle: rails
|
42
|
-
|
43
|
-
Metrics/LineLength:
|
45
|
+
Layout/LineLength:
|
44
46
|
Max: 120
|
45
47
|
|
46
48
|
Metrics/ClassLength:
|
@@ -49,17 +51,10 @@ Metrics/ClassLength:
|
|
49
51
|
Layout/EmptyLineBetweenDefs:
|
50
52
|
AllowAdjacentOneLineDefs: true
|
51
53
|
|
52
|
-
Naming/
|
54
|
+
Naming/MethodParameterName:
|
53
55
|
AllowedNames:
|
54
56
|
- _
|
55
57
|
|
56
|
-
# Temporarily disabled until this can be resolved in the todo file
|
57
|
-
# Style/Documentation:
|
58
|
-
# Exclude:
|
59
|
-
# - 'spec/**/*'
|
60
|
-
# - 'test/**/*'
|
61
|
-
# - 'lib/ext/**/*'
|
62
|
-
|
63
58
|
Style/ClassVars:
|
64
59
|
Exclude:
|
65
60
|
- 'lib/slayer/service.rb'
|
data/Dockerfile
CHANGED
data/README.md
CHANGED
@@ -8,7 +8,7 @@ Slayer is intended to operate as a minimal service layer for your ruby applicati
|
|
8
8
|
|
9
9
|
## Application Structure
|
10
10
|
|
11
|
-
Slayer provides
|
11
|
+
Slayer provides 2 base classes for organizing your business logic: `Forms` and `Commands`. These each have a distinct role in your application's structure.
|
12
12
|
|
13
13
|
### Forms
|
14
14
|
|
@@ -16,16 +16,12 @@ Slayer provides 3 base classes for organizing your business logic: `Forms`, `Com
|
|
16
16
|
|
17
17
|
### Commands
|
18
18
|
|
19
|
-
`Slayer::Commands` are the bread and butter of your application's business logic
|
19
|
+
`Slayer::Commands` are the bread and butter of your application's business logic. `Commands` wrap logic into easily tested, isolated, composable classes. In our applications, we usually create a single `Command` per `Controller` endpoint.
|
20
20
|
|
21
21
|
`Slayer::Commands` must implement a `call` method, which always return a structured `Slayer::Result` object making operating on results straightforward. The `call` method can also take a block, which provides `Slayer::ResultMatcher` object, and enforces handling of both `pass` and `fail` conditions for that result.
|
22
22
|
|
23
23
|
This helps provide confidence that your core business logic is behaving in expected ways, and helps coerce you to develop in a clean and testable way.
|
24
24
|
|
25
|
-
### Services
|
26
|
-
|
27
|
-
`Slayer::Service`s are the base class of `Slayer::Command`s, and encapsulate re-usable chunks of application logic. `Services` also return structured `Slayer::Result` objects.
|
28
|
-
|
29
25
|
## Installation
|
30
26
|
|
31
27
|
Add this line to your application's Gemfile:
|
@@ -50,7 +46,7 @@ $ gem install slayer
|
|
50
46
|
|
51
47
|
### Commands
|
52
48
|
|
53
|
-
Slayer Commands should implement `call`, which will `pass` or `fail` the service based on input. Commands return a `Slayer::Result` which has a predictable interface for determining `
|
49
|
+
Slayer Commands should implement `call`, which will `pass` or `fail` the service based on input. Commands return a `Slayer::Result` which has a predictable interface for determining `passed?` or `failed?`, a 'value' payload object, a 'status' value, and a user presentable `message`.
|
54
50
|
|
55
51
|
```ruby
|
56
52
|
# A Command that passes when given the string "foo"
|
@@ -58,10 +54,10 @@ Slayer Commands should implement `call`, which will `pass` or `fail` the service
|
|
58
54
|
class FooCommand < Slayer::Command
|
59
55
|
def call(foo:)
|
60
56
|
unless foo == "foo"
|
61
|
-
|
57
|
+
return err value: foo, message: "Argument must be foo!"
|
62
58
|
end
|
63
59
|
|
64
|
-
|
60
|
+
ok value: foo
|
65
61
|
end
|
66
62
|
end
|
67
63
|
```
|
@@ -70,11 +66,11 @@ Handling the results of a command can be done in two ways. The primary way is th
|
|
70
66
|
|
71
67
|
```ruby
|
72
68
|
FooCommand.call(foo: "foo") do |m|
|
73
|
-
m.
|
69
|
+
m.ok do |value|
|
74
70
|
puts "This code runs on success"
|
75
71
|
end
|
76
72
|
|
77
|
-
m.
|
73
|
+
m.err do |_value, result|
|
78
74
|
puts "This code runs on failure. Message: #{result.message}"
|
79
75
|
end
|
80
76
|
|
@@ -92,10 +88,10 @@ The second is less comprehensive, but can be useful for very simple commands. Th
|
|
92
88
|
|
93
89
|
```ruby
|
94
90
|
result = FooCommand.call(foo: "foo")
|
95
|
-
puts result.
|
91
|
+
puts result.ok? # => true
|
96
92
|
|
97
93
|
result = FooCommand.call(foo: "bar")
|
98
|
-
puts result.
|
94
|
+
puts result.ok? # => false
|
99
95
|
```
|
100
96
|
|
101
97
|
Here's a more complex example demonstrating how the command pattern can be used to encapuslate the logic for validating and creating a new user. This example is shown using a `rails` controller, but the same approach can be used regardless of the framework.
|
@@ -105,7 +101,7 @@ Here's a more complex example demonstrating how the command pattern can be used
|
|
105
101
|
class CreateUserCommand < Slayer::Command
|
106
102
|
def call(create_user_form:)
|
107
103
|
unless arguments_valid?(create_user_form)
|
108
|
-
|
104
|
+
return err value: create_user_form, status: :arguments_invalid
|
109
105
|
end
|
110
106
|
|
111
107
|
user = nil
|
@@ -114,10 +110,10 @@ class CreateUserCommand < Slayer::Command
|
|
114
110
|
end
|
115
111
|
|
116
112
|
unless user.persisted?
|
117
|
-
|
113
|
+
return err message: I18n.t('user.create.error'), status: :unprocessible_entity
|
118
114
|
end
|
119
115
|
|
120
|
-
|
116
|
+
ok value: user
|
121
117
|
end
|
122
118
|
|
123
119
|
def arguments_valid?(create_user_form)
|
@@ -133,17 +129,17 @@ class UsersController < ApplicationController
|
|
133
129
|
@create_user_form = CreateUserForm.from_params(create_user_params)
|
134
130
|
|
135
131
|
CreateUserCommand.call(create_user_form: @create_user_form) do |m|
|
136
|
-
m.
|
132
|
+
m.ok do |user|
|
137
133
|
auto_login(user)
|
138
134
|
redirect_to root_path, notice: t('user.create.success')
|
139
135
|
end
|
140
136
|
|
141
|
-
m.
|
137
|
+
m.err(:arguments_invalid) do |_user, result|
|
142
138
|
flash[:error] = result.errors.full_messages.to_sentence
|
143
139
|
render :new, status: :unprocessible_entity
|
144
140
|
end
|
145
141
|
|
146
|
-
m.
|
142
|
+
m.err do |_user, result|
|
147
143
|
flash[:error] = t('user.create.error')
|
148
144
|
render :new, status: :bad_request
|
149
145
|
end
|
@@ -167,24 +163,24 @@ end
|
|
167
163
|
|
168
164
|
The result matcher is an object that is used to handle `Slayer::Result` objects based on their status.
|
169
165
|
|
170
|
-
#### Handlers: `
|
166
|
+
#### Handlers: `ok`, `err`, `all`, `ensure`
|
171
167
|
|
172
|
-
The result matcher block can take 4 types of handler blocks: `
|
168
|
+
The result matcher block can take 4 types of handler blocks: `ok`, `err`, `all`, and `ensure`. They operate as you would expect based on their names.
|
173
169
|
|
174
|
-
* The `
|
175
|
-
* The `
|
176
|
-
* The `all` block runs on any type of result --- `
|
170
|
+
* The `ok` block runs if the command was successful.
|
171
|
+
* The `err` block runs if the command was `koed`.
|
172
|
+
* The `all` block runs on any type of result --- `ok` or `err` --- unless the result has already been handled.
|
177
173
|
* The `ensure` block always runs after the result has been handled.
|
178
174
|
|
179
175
|
#### Handler Params
|
180
176
|
|
181
|
-
Every handler in the result matcher block is given three arguments: `value`, `result`, and `command`. These encapsulate the `value` provided in the `
|
177
|
+
Every handler in the result matcher block is given three arguments: `value`, `result`, and `command`. These encapsulate the `value` provided in the `ok` or `return err` call from the `Command`, the returned `Slayer::Result` object, and the `Slayer::Command` instance that was just run:
|
182
178
|
|
183
179
|
```ruby
|
184
180
|
class NoArgCommand < Slayer::Command
|
185
181
|
def call
|
186
182
|
@instance_var = 'instance'
|
187
|
-
|
183
|
+
ok value: 'pass'
|
188
184
|
end
|
189
185
|
end
|
190
186
|
|
@@ -192,42 +188,39 @@ end
|
|
192
188
|
NoArgCommand.call do |m|
|
193
189
|
m.all do |value, result, command|
|
194
190
|
puts value # => 'pass'
|
195
|
-
puts result.
|
191
|
+
puts result.ok? # => true
|
196
192
|
puts command.instance_var # => 'instance'
|
197
193
|
end
|
198
|
-
|
194
|
+
end
|
199
195
|
```
|
200
196
|
|
201
197
|
#### Statuses
|
202
198
|
|
203
|
-
You can pass a `status` flag to both the `
|
199
|
+
You can pass a `status` flag to both the `ok` and `return err` methods that allows the result matcher to process different kinds of successes and failures differently:
|
204
200
|
|
205
201
|
```ruby
|
206
202
|
class StatusCommand < Slayer::Command
|
207
203
|
def call
|
208
|
-
|
209
|
-
|
210
|
-
|
204
|
+
return err message: "Extra specific ko", status: :extra_specific_err if extra_specific_err?
|
205
|
+
return err message: "Specific ko", status: :specific_err if specific_err?
|
206
|
+
return err message: "Generic ko" if generic_err?
|
207
|
+
|
208
|
+
return ok message: "Specific pass", status: :specific_pass if specific_pass?
|
211
209
|
|
212
|
-
|
213
|
-
pass! message: "Specific pass", status: :specific_pass
|
210
|
+
ok message: "Generic pass"
|
214
211
|
end
|
215
212
|
end
|
216
213
|
|
217
214
|
StatusCommand.call do |m|
|
218
|
-
m.
|
219
|
-
m.
|
220
|
-
m.
|
215
|
+
m.err { puts "generic err" }
|
216
|
+
m.err(:specific_err) { puts "specific err" }
|
217
|
+
m.err(:extra_specific_err) { puts "extra specific err" }
|
221
218
|
|
222
|
-
m.
|
223
|
-
m.
|
219
|
+
m.ok { puts "generic pass" }
|
220
|
+
m.ok(:specific_pass) { puts "specific pass" }
|
224
221
|
end
|
225
222
|
```
|
226
223
|
|
227
|
-
### Forms
|
228
|
-
|
229
|
-
### Services
|
230
|
-
|
231
224
|
## RSpec & Minitest Integrations
|
232
225
|
|
233
226
|
`Slayer` provides assertions and matchers that make testing your `Commands` simpler.
|
@@ -283,12 +276,12 @@ class MinitestCommandTest < Minitest::Test
|
|
283
276
|
@failed_result = MinitestCommand.call(should_pass: false)
|
284
277
|
end
|
285
278
|
|
286
|
-
def
|
279
|
+
def test_is_ok
|
287
280
|
assert_success @success_result, status: :no_status, message: 'message', value: 'value'
|
288
281
|
refute_failed @success_result, status: :no_status, message: 'message', value: 'value'
|
289
282
|
end
|
290
283
|
|
291
|
-
def
|
284
|
+
def test_is_err
|
292
285
|
assert_failed @failed_result, status: :no_status, message: 'message', value: 'value'
|
293
286
|
refute_success @failed_result, status: :no_status, message: 'message', value: 'value'
|
294
287
|
end
|
@@ -366,12 +359,11 @@ end
|
|
366
359
|
|
367
360
|
### Generators
|
368
361
|
|
369
|
-
Use generators to make sure your `Slayer` objects are always in the right place. `slayer_rails` includes generators for `Slayer::Form
|
362
|
+
Use generators to make sure your `Slayer` objects are always in the right place. `slayer_rails` includes generators for `Slayer::Form` and `Slayer::Command`.
|
370
363
|
|
371
364
|
```sh
|
372
365
|
$ bin/rails g slayer:form foo_form
|
373
366
|
$ bin/rails g slayer:command foo_command
|
374
|
-
$ bin/rails g slayer:service foo_service
|
375
367
|
```
|
376
368
|
|
377
369
|
## Development
|
data/Rakefile
CHANGED
@@ -3,15 +3,4 @@ require 'rspec/core/rake_task'
|
|
3
3
|
|
4
4
|
RSpec::Core::RakeTask.new(:spec)
|
5
5
|
|
6
|
-
if defined? Chandler
|
7
|
-
# Set Chandler options
|
8
|
-
Chandler::Tasks.configure do |config|
|
9
|
-
config.changelog_path = 'CHANGELOG.md'
|
10
|
-
config.github_repository = 'apsislabs/slayer'
|
11
|
-
end
|
12
|
-
|
13
|
-
# Add chandler as a prerequisite for `rake release`
|
14
|
-
task 'release:rubygem_push' => 'chandler:push'
|
15
|
-
end
|
16
|
-
|
17
6
|
task default: :spec
|
data/lib/slayer/command.rb
CHANGED
@@ -1,21 +1,78 @@
|
|
1
1
|
module Slayer
|
2
|
-
class Command
|
3
|
-
singleton_skip_hook :call
|
4
|
-
|
2
|
+
class Command
|
5
3
|
class << self
|
6
4
|
def call(*args, &block)
|
7
|
-
self.new
|
5
|
+
instance = self.new
|
6
|
+
|
7
|
+
begin
|
8
|
+
res = instance.call(*args, &block)
|
9
|
+
rescue ResultFailureError => e
|
10
|
+
res = e.result
|
11
|
+
end
|
12
|
+
|
13
|
+
raise CommandNotImplementedError unless res.is_a? Result
|
14
|
+
|
15
|
+
handle_match(res, instance, block) if block_given?
|
16
|
+
return res
|
17
|
+
end
|
18
|
+
ruby2_keywords :call if respond_to?(:ruby2_keywords, true)
|
19
|
+
|
20
|
+
def ok(value: nil, status: :default, message: nil)
|
21
|
+
Result.new(value, status, message)
|
22
|
+
end
|
23
|
+
|
24
|
+
def err(value: nil, status: :default, message: nil)
|
25
|
+
ok(value: value, status: status, message: message).fail
|
26
|
+
end
|
27
|
+
|
28
|
+
def err!(value: nil, status: :default, message: nil)
|
29
|
+
warn '[DEPRECATION] `err!` is deprecated. Please use `return err` instead.'
|
30
|
+
raise ResultFailureError, err(value: value, status: status, message: message)
|
8
31
|
end
|
9
32
|
|
10
33
|
private
|
11
34
|
|
12
|
-
def
|
13
|
-
|
14
|
-
|
15
|
-
|
35
|
+
def handle_match(res, instance, block)
|
36
|
+
matcher = Slayer::ResultMatcher.new(res, instance)
|
37
|
+
|
38
|
+
block.call(matcher)
|
39
|
+
|
40
|
+
# raise error if not all defaults were handled
|
41
|
+
unless matcher.handled_defaults?
|
42
|
+
raise(ResultNotHandledError, 'The pass or fail condition of a result was not handled')
|
43
|
+
end
|
44
|
+
|
45
|
+
begin
|
46
|
+
matcher.execute_matching_block
|
47
|
+
ensure
|
48
|
+
matcher.execute_ensure_block
|
49
|
+
end
|
16
50
|
end
|
17
51
|
end
|
18
52
|
|
53
|
+
def ok(*args)
|
54
|
+
self.class.ok(*args)
|
55
|
+
end
|
56
|
+
ruby2_keywords :ok if respond_to?(:ruby2_keywords, true)
|
57
|
+
|
58
|
+
def err(*args)
|
59
|
+
self.class.err(*args)
|
60
|
+
end
|
61
|
+
ruby2_keywords :err if respond_to?(:ruby2_keywords, true)
|
62
|
+
|
63
|
+
def err!(*args)
|
64
|
+
self.class.err!(*args)
|
65
|
+
end
|
66
|
+
ruby2_keywords :err! if respond_to?(:ruby2_keywords, true)
|
67
|
+
|
68
|
+
def try!(value: nil, status: nil, message: nil)
|
69
|
+
r = yield
|
70
|
+
err!(value: value, status: status || :default, message: message) unless r.is_a?(Result)
|
71
|
+
return r.value if r.ok?
|
72
|
+
|
73
|
+
err!(value: value || r.value, status: status || r.status, message: message || r.message)
|
74
|
+
end
|
75
|
+
|
19
76
|
def call
|
20
77
|
raise NotImplementedError, 'Commands must define method `#call`.'
|
21
78
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
# :nocov:
|
2
|
+
require 'minitest/assertions'
|
3
|
+
require 'rspec/expectations'
|
4
|
+
|
5
|
+
module Slayer
|
6
|
+
class Command
|
7
|
+
class << self
|
8
|
+
def pass(value: nil, status: :default, message: nil)
|
9
|
+
warn '[DEPRECATION] `pass` is deprecated. Please use `ok` instead.'
|
10
|
+
ok(value: value, status: status, message: message)
|
11
|
+
end
|
12
|
+
|
13
|
+
def flunk(value: nil, status: :default, message: nil)
|
14
|
+
warn '[DEPRECATION] `flunk` is deprecated. Please use `err` instead.'
|
15
|
+
err(value: value, status: status, message: message)
|
16
|
+
end
|
17
|
+
|
18
|
+
def flunk!(value: nil, status: :default, message: nil)
|
19
|
+
warn '[DEPRECATION] `flunk!` is deprecated. Please use `return err` instead.'
|
20
|
+
err!(value: value, status: status, message: message)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
alias pass ok
|
25
|
+
alias flunk err
|
26
|
+
alias flunk! err!
|
27
|
+
end
|
28
|
+
|
29
|
+
class Result
|
30
|
+
def success?
|
31
|
+
warn '[DEPRECATION] `success?` is deprecated. Please use `ok?` instead.'
|
32
|
+
ok?
|
33
|
+
end
|
34
|
+
|
35
|
+
def failure?
|
36
|
+
warn '[DEPRECATION] `failure?` is deprecated. Please use `err?` instead.'
|
37
|
+
err?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
class ResultMatcher
|
42
|
+
def pass(...)
|
43
|
+
warn '[DEPRECATION] `pass` is deprecated. Please use `ok` instead.'
|
44
|
+
ok(...)
|
45
|
+
end
|
46
|
+
|
47
|
+
def fail(...)
|
48
|
+
warn '[DEPRECATION] `fail` is deprecated. Please use `err` instead.'
|
49
|
+
err(...)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
module Minitest::Assertions
|
55
|
+
alias assert_success assert_ok
|
56
|
+
alias refute_failed assert_ok
|
57
|
+
alias assert_failed refute_ok
|
58
|
+
alias refute_success refute_ok
|
59
|
+
end
|
60
|
+
|
61
|
+
RSpec::Matchers.alias_matcher :be_failed_result, :be_err_result
|
62
|
+
RSpec::Matchers.alias_matcher :be_success_result, :be_ok_result
|
63
|
+
|
64
|
+
# :nocov:
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'rubocop'
|
2
|
+
|
3
|
+
module Slayer
|
4
|
+
class CommandReturn < RuboCop::Cop::Base
|
5
|
+
def_node_search :explicit_returns, 'return'
|
6
|
+
def_node_matcher :slayer_command?, '(class (const (const nil :Slayer) :Command) _)'
|
7
|
+
def_node_matcher :is_call_to_pass?, '(send nil :pass ?)'
|
8
|
+
def_node_matcher :is_call_to_flunk?, '(send nil :flunk! ?)'
|
9
|
+
|
10
|
+
def on_def(node)
|
11
|
+
return unless node.method?(:call)
|
12
|
+
return unless in_slayer_command?(node)
|
13
|
+
|
14
|
+
explicit_returns(node) do |n|
|
15
|
+
validate_return! n.child_nodes.first, n
|
16
|
+
end
|
17
|
+
|
18
|
+
# Temporarily does not look at implicit returns
|
19
|
+
#
|
20
|
+
# implicit_returns(node) do |node|
|
21
|
+
# validate_return! node
|
22
|
+
# end
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
# Continue traversing `node` until you get to the last expression.
|
28
|
+
# If that expression is a call to `.can_see?`, then add an offense.
|
29
|
+
def implicit_returns(_node)
|
30
|
+
raise 'Not Implemented Yet'
|
31
|
+
end
|
32
|
+
|
33
|
+
def in_slayer_command?(node)
|
34
|
+
node.ancestors.any?(&:slayer_command?)
|
35
|
+
end
|
36
|
+
|
37
|
+
def validate_return!(node, return_node = nil)
|
38
|
+
return if is_call_to_pass? node
|
39
|
+
return if is_call_to_flunk? node
|
40
|
+
|
41
|
+
add_offense(return_node || node,
|
42
|
+
message: 'call in Slayer::Command must return the result of `pass` or call `flunk!`')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/lib/slayer/minitest.rb
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
# :nocov:
|
2
2
|
require 'minitest/assertions'
|
3
|
-
# rubocop:disable Style/Documentation
|
4
3
|
# rubocop:disable Metrics/MethodLength
|
5
4
|
module Minitest::Assertions
|
6
|
-
def
|
7
|
-
assert result.
|
5
|
+
def assert_ok(result, status: nil, message: nil, value: nil)
|
6
|
+
assert result.ok?, 'Expected command to succeed.'
|
8
7
|
|
9
8
|
unless status.nil?
|
10
9
|
assert_equal(
|
@@ -30,10 +29,10 @@ module Minitest::Assertions
|
|
30
29
|
)
|
31
30
|
end
|
32
31
|
end
|
33
|
-
alias
|
32
|
+
alias refute_err assert_ok
|
34
33
|
|
35
|
-
def
|
36
|
-
refute result.
|
34
|
+
def refute_ok(result, status: nil, message: nil, value: nil)
|
35
|
+
refute result.ok?, 'Expected command to fail.'
|
37
36
|
|
38
37
|
unless status.nil?
|
39
38
|
refute_equal(
|
@@ -59,7 +58,7 @@ module Minitest::Assertions
|
|
59
58
|
)
|
60
59
|
end
|
61
60
|
end
|
62
|
-
alias
|
61
|
+
alias assert_err refute_ok
|
63
62
|
end
|
64
63
|
# rubocop:enable Style/Documentation
|
65
64
|
# rubocop:enable Metrics/MethodLength
|
data/lib/slayer/result.rb
CHANGED
@@ -8,16 +8,16 @@ module Slayer
|
|
8
8
|
@message = message
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
!
|
11
|
+
def ok?
|
12
|
+
!err?
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
@
|
15
|
+
def err?
|
16
|
+
@err ||= false
|
17
17
|
end
|
18
18
|
|
19
19
|
def fail
|
20
|
-
@
|
20
|
+
@err ||= true
|
21
21
|
self
|
22
22
|
end
|
23
23
|
end
|