slayer 0.4.0.beta4 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 5fdd7e7da2630dcab6357d4aa3391c24d203cd5b
4
- data.tar.gz: 61d1b9ba39d0751fc579dadaf106c31d77358e24
2
+ SHA256:
3
+ metadata.gz: f88c862cf4596840150934c88372a6cda656557e34da5a3918e98f19b159c645
4
+ data.tar.gz: 84d431982bad263f5a8cad99e3cd7fdb62610b9b75a80621c8ad457d01f93600
5
5
  SHA512:
6
- metadata.gz: fc1f3e004f7403938b3b8505a527211e3ed04b72e0674e2d8f36a00300815f158057e79354eb743f68e0295d6c59c07715dd7dc7ac7d8ed6cc86daf86be5c1ac
7
- data.tar.gz: 10cc9fe7a69d05478255729b97575d31f1b1daa6360e6de049d4789b1f0f0a45ebec16ece1895a310ce9d9cd9b3bdda12ee119e3665b1007b1abbcbdf41bbd0d
6
+ metadata.gz: e390d010a072ec169f4c8a776547a4e3d562599db94a11a91534c7f0a2cc0667ce10c94fcfe765de658edb1bd477f38a4ac14b1972cbc183c03c666391193fbf
7
+ data.tar.gz: 13adcd8d46c0e28e1f4c243426ca1ad628157ca056d8298932dbdbf946d288661358a1c6e2a6ebbcf2e497a9efea45811579cb6063c28e46e80410f1a6f6d0e4
@@ -0,0 +1,27 @@
1
+ # .github/workflows/release.yml
2
+
3
+ name: Release
4
+
5
+ on:
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ release:
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v2
13
+ - uses: ruby/setup-ruby@v1
14
+ with:
15
+ ruby-version: 3.0.0
16
+ - run: bundle install
17
+ - name: publish gem
18
+ run: |
19
+ mkdir -p $HOME/.gem
20
+ touch $HOME/.gem/credentials
21
+ chmod 0600 $HOME/.gem/credentials
22
+ printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
23
+ gem build *.gemspec
24
+ gem push *.gem
25
+ env:
26
+ GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
27
+
@@ -0,0 +1,28 @@
1
+ name: Test & Lint
2
+
3
+ on:
4
+ push:
5
+ branches: [main]
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
@@ -1,6 +1,7 @@
1
1
  /.bundle/
2
2
  /.yardoc
3
3
  /_yardoc/
4
+ /vendor/
4
5
  /coverage/
5
6
  /doc/
6
7
  /pkg/
data/.rubocop.yml CHANGED
@@ -1,7 +1,7 @@
1
- inherit_from: .rubocop_todo.yml
2
-
3
1
  AllCops:
4
- TargetRubyVersion: 2.1
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
- Style/BracesAroundHashParameters:
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/UncommunicativeMethodParamName:
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
@@ -1,5 +1,4 @@
1
- FROM ruby:2.5.1-alpine
2
- MAINTAINER wyatt@apsis.io
1
+ FROM ruby:3.1.2-alpine
3
2
 
4
3
  RUN apk add --no-cache --update \
5
4
  bash \
data/README.md CHANGED
@@ -6,9 +6,11 @@
6
6
 
7
7
  Slayer is intended to operate as a minimal service layer for your ruby application. To achieve this, Slayer provides base classes for business logic.
8
8
 
9
+ **Slayer is still under development, and not yet ready for production use. We are targetting a stable API with the 0.4.0 launch, so expect breaking changes until then.**
10
+
9
11
  ## Application Structure
10
12
 
11
- Slayer provides 3 base classes for organizing your business logic: `Forms`, `Commands`, and `Services`. Each of these has a distinct role in your application's structure.
13
+ 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
14
 
13
15
  ### Forms
14
16
 
@@ -16,16 +18,12 @@ Slayer provides 3 base classes for organizing your business logic: `Forms`, `Com
16
18
 
17
19
  ### Commands
18
20
 
19
- `Slayer::Commands` are the bread and butter of your application's business logic, and a specific implementation of the `Slayer::Service` object. `Commands` are where you compose services, and perform one-off business logic tasks. In our applications, we usually create a single `Command` per `Controller` endpoint.
21
+ `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
22
 
21
23
  `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
24
 
23
25
  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
26
 
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
27
  ## Installation
30
28
 
31
29
  Add this line to your application's Gemfile:
@@ -50,7 +48,7 @@ $ gem install slayer
50
48
 
51
49
  ### Commands
52
50
 
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 `success?` or `failure?`, a 'value' payload object, a 'status' value, and a user presentable `message`.
51
+ 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
52
 
55
53
  ```ruby
56
54
  # A Command that passes when given the string "foo"
@@ -58,10 +56,10 @@ Slayer Commands should implement `call`, which will `pass` or `fail` the service
58
56
  class FooCommand < Slayer::Command
59
57
  def call(foo:)
60
58
  unless foo == "foo"
61
- flunk! value: foo, message: "Argument must be foo!"
59
+ return err value: foo, message: "Argument must be foo!"
62
60
  end
63
61
 
64
- pass! value: foo
62
+ ok value: foo
65
63
  end
66
64
  end
67
65
  ```
@@ -70,11 +68,11 @@ Handling the results of a command can be done in two ways. The primary way is th
70
68
 
71
69
  ```ruby
72
70
  FooCommand.call(foo: "foo") do |m|
73
- m.pass do |result|
71
+ m.ok do |value|
74
72
  puts "This code runs on success"
75
73
  end
76
74
 
77
- m.fail do |result|
75
+ m.err do |_value, result|
78
76
  puts "This code runs on failure. Message: #{result.message}"
79
77
  end
80
78
 
@@ -92,10 +90,10 @@ The second is less comprehensive, but can be useful for very simple commands. Th
92
90
 
93
91
  ```ruby
94
92
  result = FooCommand.call(foo: "foo")
95
- puts result.success? # => true
93
+ puts result.ok? # => true
96
94
 
97
95
  result = FooCommand.call(foo: "bar")
98
- puts result.success? # => false
96
+ puts result.ok? # => false
99
97
  ```
100
98
 
101
99
  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 +103,7 @@ Here's a more complex example demonstrating how the command pattern can be used
105
103
  class CreateUserCommand < Slayer::Command
106
104
  def call(create_user_form:)
107
105
  unless arguments_valid?(create_user_form)
108
- flunk! value: create_user_form, status: :arguments_invalid
106
+ return err value: create_user_form, status: :arguments_invalid
109
107
  end
110
108
 
111
109
  user = nil
@@ -114,10 +112,10 @@ class CreateUserCommand < Slayer::Command
114
112
  end
115
113
 
116
114
  unless user.persisted?
117
- flunk! message: I18n.t('user.create.error'), status: :unprocessible_entity
115
+ return err message: I18n.t('user.create.error'), status: :unprocessible_entity
118
116
  end
119
117
 
120
- pass! value: user
118
+ ok value: user
121
119
  end
122
120
 
123
121
  def arguments_valid?(create_user_form)
@@ -133,17 +131,17 @@ class UsersController < ApplicationController
133
131
  @create_user_form = CreateUserForm.from_params(create_user_params)
134
132
 
135
133
  CreateUserCommand.call(create_user_form: @create_user_form) do |m|
136
- m.pass do |user|
134
+ m.ok do |user|
137
135
  auto_login(user)
138
136
  redirect_to root_path, notice: t('user.create.success')
139
137
  end
140
138
 
141
- m.fail(:arguments_invalid) do |result|
139
+ m.err(:arguments_invalid) do |_user, result|
142
140
  flash[:error] = result.errors.full_messages.to_sentence
143
141
  render :new, status: :unprocessible_entity
144
142
  end
145
143
 
146
- m.fail do |result|
144
+ m.err do |_user, result|
147
145
  flash[:error] = t('user.create.error')
148
146
  render :new, status: :bad_request
149
147
  end
@@ -167,24 +165,24 @@ end
167
165
 
168
166
  The result matcher is an object that is used to handle `Slayer::Result` objects based on their status.
169
167
 
170
- #### Handlers: `pass`, `fail`, `all`, `ensure`
168
+ #### Handlers: `ok`, `err`, `all`, `ensure`
171
169
 
172
- The result matcher block can take 4 types of handler blocks: `pass`, `fail`, `all`, and `ensure`. They operate as you would expect based on their names.
170
+ 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
171
 
174
- * The `pass` block runs if the command was successful.
175
- * The `fail` block runs if the command was `flunked`.
176
- * The `all` block runs on any type of result --- `pass` or `fail` --- unless the result has already been handled.
172
+ * The `ok` block runs if the command was successful.
173
+ * The `err` block runs if the command was `koed`.
174
+ * The `all` block runs on any type of result --- `ok` or `err` --- unless the result has already been handled.
177
175
  * The `ensure` block always runs after the result has been handled.
178
176
 
179
177
  #### Handler Params
180
178
 
181
- Every handler in the result matcher block is given three arguments: `value`, `result`, and `command`. These encapsulate the `value` provided in the `pass!` or `flunk!` call from the `Command`, the returned `Slayer::Result` object, and the `Slayer::Command` instance that was just run:
179
+ 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
180
 
183
181
  ```ruby
184
182
  class NoArgCommand < Slayer::Command
185
183
  def call
186
184
  @instance_var = 'instance'
187
- pass value: 'pass'
185
+ ok value: 'pass'
188
186
  end
189
187
  end
190
188
 
@@ -192,42 +190,39 @@ end
192
190
  NoArgCommand.call do |m|
193
191
  m.all do |value, result, command|
194
192
  puts value # => 'pass'
195
- puts result.success? # => true
193
+ puts result.ok? # => true
196
194
  puts command.instance_var # => 'instance'
197
195
  end
198
- endpoint
196
+ end
199
197
  ```
200
198
 
201
199
  #### Statuses
202
200
 
203
- You can pass a `status` flag to both the `pass!` and `flunk!` methods that allows the result matcher to process different kinds of successes and failures differently:
201
+ 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
202
 
205
203
  ```ruby
206
204
  class StatusCommand < Slayer::Command
207
205
  def call
208
- flunk! message: "Generic flunk"
209
- flunk! message: "Specific flunk", status: :specific_flunk
210
- flunk! message: "Extra specific flunk", status: :extra_specific_flunk
206
+ return err message: "Extra specific ko", status: :extra_specific_err if extra_specific_err?
207
+ return err message: "Specific ko", status: :specific_err if specific_err?
208
+ return err message: "Generic ko" if generic_err?
209
+
210
+ return ok message: "Specific pass", status: :specific_pass if specific_pass?
211
211
 
212
- pass! message: "Generic pass"
213
- pass! message: "Specific pass", status: :specific_pass
212
+ ok message: "Generic pass"
214
213
  end
215
214
  end
216
215
 
217
216
  StatusCommand.call do |m|
218
- m.fail { puts "generic fail" }
219
- m.fail(:specific_flunk) { puts "specific flunk" }
220
- m.fail(:extra_specific_flunk) { puts "extra specific flunk" }
217
+ m.err { puts "generic err" }
218
+ m.err(:specific_err) { puts "specific err" }
219
+ m.err(:extra_specific_err) { puts "extra specific err" }
221
220
 
222
- m.pass { puts "generic pass" }
223
- m.pass(:specific_pass) { puts "specific pass" }
221
+ m.ok { puts "generic pass" }
222
+ m.ok(:specific_pass) { puts "specific pass" }
224
223
  end
225
224
  ```
226
225
 
227
- ### Forms
228
-
229
- ### Services
230
-
231
226
  ## RSpec & Minitest Integrations
232
227
 
233
228
  `Slayer` provides assertions and matchers that make testing your `Commands` simpler.
@@ -283,12 +278,12 @@ class MinitestCommandTest < Minitest::Test
283
278
  @failed_result = MinitestCommand.call(should_pass: false)
284
279
  end
285
280
 
286
- def test_is_success
281
+ def test_is_ok
287
282
  assert_success @success_result, status: :no_status, message: 'message', value: 'value'
288
283
  refute_failed @success_result, status: :no_status, message: 'message', value: 'value'
289
284
  end
290
285
 
291
- def test_is_failed
286
+ def test_is_err
292
287
  assert_failed @failed_result, status: :no_status, message: 'message', value: 'value'
293
288
  refute_success @failed_result, status: :no_status, message: 'message', value: 'value'
294
289
  end
@@ -366,12 +361,29 @@ end
366
361
 
367
362
  ### Generators
368
363
 
369
- Use generators to make sure your `Slayer` objects are always in the right place. `slayer_rails` includes generators for `Slayer::Form`, `Slayer::Command`, and `Slayer::Service` objects.
364
+ 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
365
 
371
366
  ```sh
372
367
  $ bin/rails g slayer:form foo_form
373
368
  $ bin/rails g slayer:command foo_command
374
- $ bin/rails g slayer:service foo_service
369
+ ```
370
+
371
+ ## Compatability
372
+
373
+ Backwards compatability with previous versions requires additional includes.
374
+
375
+ ```ruby
376
+ require 'slayer/compat/compat_040'
377
+ ```
378
+
379
+ If you use test matchers, you will have to separately require the compatability layer for your test runner:
380
+
381
+ ```ruby
382
+ require 'slayer/compat/minitest_compat_040'
383
+
384
+ # OR
385
+
386
+ require 'slayer/compat/rspec_compat_040'
375
387
  ```
376
388
 
377
389
  ## Development
@@ -397,3 +409,11 @@ Any PRs should be accompanied with documentation in `README.md`, and changes doc
397
409
  ## License
398
410
 
399
411
  The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
412
+
413
+ ---
414
+
415
+ # Built by Apsis
416
+
417
+ [![apsis](https://s3-us-west-2.amazonaws.com/apsiscdn/apsis.png)](https://www.apsis.io)
418
+
419
+ `slayer` was built by Apsis Labs. We love sharing what we build! Check out our [other libraries on Github](https://github.com/apsislabs), and if you like our work you can [hire us](https://www.apsis.io/work-with-us/) to build your vision.
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
@@ -1,21 +1,78 @@
1
1
  module Slayer
2
- class Command < Service
3
- singleton_skip_hook :call
4
-
2
+ class Command
5
3
  class << self
6
4
  def call(*args, &block)
7
- self.new.call(*args, &block)
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 inherited(klass)
13
- super(klass)
14
- klass.wrap_service_methods!
15
- klass.only_hook :call
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,52 @@
1
+ # :nocov:
2
+
3
+ module Slayer
4
+ class Command
5
+ class << self
6
+ def pass(value: nil, status: :default, message: nil)
7
+ warn '[DEPRECATION] `pass` is deprecated. Please use `ok` instead.'
8
+ ok(value: value, status: status, message: message)
9
+ end
10
+
11
+ def flunk(value: nil, status: :default, message: nil)
12
+ warn '[DEPRECATION] `flunk` is deprecated. Please use `err` instead.'
13
+ err(value: value, status: status, message: message)
14
+ end
15
+
16
+ def flunk!(value: nil, status: :default, message: nil)
17
+ warn '[DEPRECATION] `flunk!` is deprecated. Please use `return err` instead.'
18
+ err!(value: value, status: status, message: message)
19
+ end
20
+ end
21
+
22
+ alias pass ok
23
+ alias flunk err
24
+ alias flunk! err!
25
+ end
26
+
27
+ class Result
28
+ def success?
29
+ warn '[DEPRECATION] `success?` is deprecated. Please use `ok?` instead.'
30
+ ok?
31
+ end
32
+
33
+ def failure?
34
+ warn '[DEPRECATION] `failure?` is deprecated. Please use `err?` instead.'
35
+ err?
36
+ end
37
+ end
38
+
39
+ class ResultMatcher
40
+ def pass(...)
41
+ warn '[DEPRECATION] `pass` is deprecated. Please use `ok` instead.'
42
+ ok(...)
43
+ end
44
+
45
+ def fail(...)
46
+ warn '[DEPRECATION] `fail` is deprecated. Please use `err` instead.'
47
+ err(...)
48
+ end
49
+ end
50
+ end
51
+
52
+ # :nocov:
@@ -0,0 +1,12 @@
1
+ # :nocov:
2
+
3
+ require 'minitest/assertions'
4
+
5
+ module Minitest::Assertions
6
+ alias assert_success assert_ok
7
+ alias refute_failed assert_ok
8
+ alias assert_failed refute_ok
9
+ alias refute_success refute_ok
10
+ end
11
+
12
+ # :nocov:
@@ -0,0 +1,8 @@
1
+ # :nocov:
2
+
3
+ require 'rspec/expectations'
4
+
5
+ RSpec::Matchers.alias_matcher :be_failed_result, :be_err_result
6
+ RSpec::Matchers.alias_matcher :be_success_result, :be_ok_result
7
+
8
+ # :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
@@ -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 assert_success(result, status: nil, message: nil, value: nil)
7
- assert result.success?, 'Expected command to succeed.'
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 refute_failed assert_success
32
+ alias refute_err assert_ok
34
33
 
35
- def refute_success(result, status: nil, message: nil, value: nil)
36
- refute result.success?, 'Expected command to fail.'
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 assert_failed refute_success
61
+ alias assert_err refute_ok
63
62
  end
64
63
  # rubocop:enable Style/Documentation
65
64
  # rubocop:enable Metrics/MethodLength