File: hook_argument.rb

package info (click to toggle)
ruby-rubocop-rspec 2.16.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, forky, sid, trixie
  • size: 1,892 kB
  • sloc: ruby: 22,283; makefile: 4
file content (131 lines) | stat: -rw-r--r-- 3,383 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
# frozen_string_literal: true

module RuboCop
  module Cop
    module RSpec
      # Checks the arguments passed to `before`, `around`, and `after`.
      #
      # This cop checks for consistent style when specifying RSpec
      # hooks which run for each example. There are three supported
      # styles: "implicit", "each", and "example." All styles have
      # the same behavior.
      #
      # @example `EnforcedStyle: implicit` (default)
      #   # bad
      #   before(:each) do
      #     # ...
      #   end
      #
      #   # bad
      #   before(:example) do
      #     # ...
      #   end
      #
      #   # good
      #   before do
      #     # ...
      #   end
      #
      # @example `EnforcedStyle: each`
      #   # bad
      #   before(:example) do
      #     # ...
      #   end
      #
      #   # bad
      #   before do
      #     # ...
      #   end
      #
      #   # good
      #   before(:each) do
      #     # ...
      #   end
      #
      # @example `EnforcedStyle: example`
      #   # bad
      #   before(:each) do
      #     # ...
      #   end
      #
      #   # bad
      #   before do
      #     # ...
      #   end
      #
      #   # good
      #   before(:example) do
      #     # ...
      #   end
      #
      class HookArgument < Base
        extend AutoCorrector
        include ConfigurableEnforcedStyle

        IMPLICIT_MSG = 'Omit the default `%<scope>p` argument for RSpec hooks.'
        EXPLICIT_MSG = 'Use `%<scope>p` for RSpec hooks.'

        # @!method scoped_hook(node)
        def_node_matcher :scoped_hook, <<-PATTERN
          ({block numblock} $(send _ #Hooks.all (sym ${:each :example})) ...)
        PATTERN

        # @!method unscoped_hook(node)
        def_node_matcher :unscoped_hook, <<-PATTERN
          ({block numblock} $(send _ #Hooks.all) ...)
        PATTERN

        def on_block(node)
          hook(node) do |method_send, scope_name|
            return correct_style_detected if scope_name.equal?(style)
            return check_implicit(method_send) unless scope_name

            style_detected(scope_name)
            msg = explicit_message(scope_name)
            add_offense(method_send, message: msg) do |corrector|
              scope = implicit_style? ? '' : "(#{style.inspect})"
              corrector.replace(argument_range(method_send), scope)
            end
          end
        end

        alias on_numblock on_block

        private

        def check_implicit(method_send)
          style_detected(:implicit)
          return if implicit_style?

          msg = explicit_message(nil)
          add_offense(method_send.loc.selector, message: msg) do |corrector|
            scope = "(#{style.inspect})"
            corrector.replace(argument_range(method_send), scope)
          end
        end

        def explicit_message(scope)
          if implicit_style?
            format(IMPLICIT_MSG, scope: scope)
          else
            format(EXPLICIT_MSG, scope: style)
          end
        end

        def implicit_style?
          style.equal?(:implicit)
        end

        def hook(node, &block)
          scoped_hook(node, &block) || unscoped_hook(node, &block)
        end

        def argument_range(send_node)
          send_node.loc.selector.end.with(
            end_pos: send_node.loc.expression.end_pos
          )
        end
      end
    end
  end
end