slayer 0.4.0.beta3 → 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
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
|