rubocop-canon 0.1.0 → 0.2.1

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ec9b67b96a586164140535f5e47bae25d02443a7f37094b0dd6fe8c2256d7d57
4
- data.tar.gz: 79bebd07f1aec3b30061849a9c0166640eef0728756dc6eb477d47e17c5a9c69
3
+ metadata.gz: 24d224c2ecf5e361f702c300a79778baf8ad63247fb726cb8c39b89534d7dd3f
4
+ data.tar.gz: 8e4e0ca31f844a61bdba984c2efd2ccadb2d0ddea11693f38dc452c9b356e40c
5
5
  SHA512:
6
- metadata.gz: 49faab8220e3b5c1fe4004566a664c19051848136a628245e7e30da3e5b072fed9b349089681831cd5efac7052e40dd788986e9827ee16569617c26a304b541c
7
- data.tar.gz: d01f901f2b9748cf4617eb75a633930dd8c9049c1c45bd4c6379cce308ac1678f5fc41facb34a4d5da664b9ea1dbcfb5fbad15232eb90437921cf79f1fdea79b
6
+ metadata.gz: b1eea01a2c5652d7513ccfe08b048cd61817f4f20c3dcb757c5e7b0e96ac99c92d0b52f1840d40b69a9c3c5b2eced42444c22ae29ed3461eedc83c1dbd4b5081
7
+ data.tar.gz: 98f0a6237ae0b71995af7bf03e8d8b46bcf83ebd92f3fa0ddf81f607a503ab71061b365f2e19a05279702791998be00d7cd1d621224dcdd965ae79d2d3fc2d84
data/README.md CHANGED
@@ -6,7 +6,10 @@ Deterministic RuboCop cops that reduce Ruby code to canonical form. Given any in
6
6
 
7
7
  | Cop | What it does |
8
8
  |-----|-------------|
9
+ | `Canon/BlockPhases` | A block body ends in one unbroken run of calls, opened by one blank line |
10
+ | `Canon/DeclarationGroups` | One blank line between declaration groups, none inside a group |
9
11
  | `Canon/KeywordShorthand` | `foo(bar: bar)` becomes `foo(bar:)` |
12
+ | `Canon/MethodBodyBlankLines` | One blank line around a method body's standalone calls, none anywhere else |
10
13
  | `Canon/SortHash` | `{b: 1, a: 2}` becomes `{a: 2, b: 1}` |
11
14
  | `Canon/SortKeywords` | `method(z: 1, a: 2)` becomes `method(a: 2, z: 1)` |
12
15
  | `Canon/SortMethodArguments` | `attr_reader :z, :a` becomes `attr_reader :a, :z` |
@@ -29,6 +32,32 @@ plugins:
29
32
 
30
33
  ## Configuration
31
34
 
35
+ The blank-line cops are driven by name lists:
36
+
37
+ ```yaml
38
+ Canon/BlockPhases:
39
+ Blocks: # only check blocks of these methods (required)
40
+ - it
41
+ TrailingMethods: # calls that make up the trailing phase (required)
42
+ - expect
43
+ MaxPhases: 3 # phases allowed before the trailing one
44
+
45
+ Canon/DeclarationGroups:
46
+ GroupedMethods: # method names that form one group
47
+ - [belongs_to, has_many, has_one]
48
+ - [validate, validates]
49
+
50
+ Canon/MethodBodyBlankLines:
51
+ SeparatedMethods: # calls that take a blank line beside them
52
+ - expose
53
+ - mail
54
+ - errors.add # dotted: matches a receiver and method together
55
+ ```
56
+
57
+ `Canon/BlockPhases` does nothing without both lists. `Canon/DeclarationGroups` treats every
58
+ method name as its own group until `GroupedMethods` merges them. `Canon/MethodBodyBlankLines`
59
+ only removes blank lines until `SeparatedMethods` says where they belong.
60
+
32
61
  `Canon/SortHash` and the three sort cops accept:
33
62
 
34
63
  ```yaml
@@ -49,7 +78,7 @@ Canon/SortMethodArguments:
49
78
  - delegate
50
79
  ```
51
80
 
52
- `Canon/SortKeywords` and `Canon/SortMethodArguments` are disabled by default. They require a `Methods` list to function.
81
+ Every cop is disabled by default. Enable the ones you want in your `.rubocop.yml`.
53
82
 
54
83
  ## License
55
84
 
data/config/default.yml CHANGED
@@ -1,11 +1,31 @@
1
+ Canon/BlockPhases:
2
+ Description: 'Separate a block body into phases, ending in a trailing run of calls.'
3
+ Enabled: false
4
+ VersionAdded: '0.2'
5
+ Blocks: []
6
+ MaxPhases: 3
7
+ TrailingMethods: []
8
+
9
+ Canon/DeclarationGroups:
10
+ Description: 'Separate declaration groups with one blank line and nothing inside a group.'
11
+ Enabled: false
12
+ VersionAdded: '0.2'
13
+ GroupedMethods: []
14
+
1
15
  Canon/KeywordShorthand:
2
16
  Description: 'Use Ruby 3.1+ keyword shorthand where a local variable matches the key name.'
3
- Enabled: true
17
+ Enabled: false
4
18
  VersionAdded: '0.1'
5
19
 
20
+ Canon/MethodBodyBlankLines:
21
+ Description: "One blank line around a method body's standalone calls, none anywhere else."
22
+ Enabled: false
23
+ VersionAdded: '0.2'
24
+ SeparatedMethods: []
25
+
6
26
  Canon/SortHash:
7
27
  Description: 'Sort hash keys alphabetically.'
8
- Enabled: true
28
+ Enabled: false
9
29
  VersionAdded: '0.1'
10
30
  ShorthandsFirst: false
11
31
  ExcludeMethods: []
@@ -25,5 +45,5 @@ Canon/SortMethodArguments:
25
45
 
26
46
  Canon/SortMethodDefinition:
27
47
  Description: 'Sort keyword arguments in method definitions alphabetically.'
28
- Enabled: true
48
+ Enabled: false
29
49
  VersionAdded: '0.1'
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module Canon
5
- VERSION = '0.1.0'
5
+ VERSION = '0.2.1'
6
6
  end
7
7
  end
@@ -0,0 +1,87 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Canon
6
+ # Shared mechanics for the cops that decide where a blank line belongs.
7
+ #
8
+ # A cop mixing this in supplies the decision — how many blank lines a
9
+ # pair of statements takes — and leaves the counting, the comment guard,
10
+ # the source ranges and the corrections here.
11
+ module BlankLineHelp
12
+ include RangeHelp
13
+
14
+ BLOCK_TYPES = %i[block numblock].freeze
15
+
16
+ private
17
+
18
+ def body_statements(node)
19
+ body = node.body
20
+ return [] if body.nil?
21
+ return body.children if body.begin_type?
22
+
23
+ [body]
24
+ end
25
+
26
+ def blank_lines_between(previous, following)
27
+ following.first_line - previous.last_line - 1
28
+ end
29
+
30
+ def comments_between?(previous, following)
31
+ processed_source.comments.any? do |comment|
32
+ comment.loc.line >= previous.last_line && comment.loc.line < following.first_line
33
+ end
34
+ end
35
+
36
+ def register_extra_blank_line(previous, message)
37
+ add_offense(blank_line_range(previous), message:) do |corrector|
38
+ corrector.remove(newline_range(previous))
39
+ end
40
+ end
41
+
42
+ def register_missing_blank_line(previous, following, message)
43
+ add_offense(first_line_range(following), message:) do |corrector|
44
+ corrector.insert_after(line_range(previous.last_line), "\n")
45
+ end
46
+ end
47
+
48
+ def call_chain(node)
49
+ chain = []
50
+ current = unwrap_block(node)
51
+
52
+ while current.respond_to?(:send_type?) && current.send_type?
53
+ chain.unshift(current.method_name.to_s)
54
+ current = unwrap_block(current.receiver)
55
+ end
56
+
57
+ chain
58
+ end
59
+
60
+ def unwrap_block(node)
61
+ return if node.nil?
62
+ return node.send_node if BLOCK_TYPES.include?(node.type)
63
+
64
+ node
65
+ end
66
+
67
+ def line_range(line)
68
+ processed_source.buffer.line_range(line)
69
+ end
70
+
71
+ def blank_line_range(previous)
72
+ line_range(previous.last_line + 1)
73
+ end
74
+
75
+ def newline_range(previous)
76
+ range_between(line_range(previous.last_line).end_pos, line_range(previous.last_line + 1).end_pos)
77
+ end
78
+
79
+ def first_line_range(node)
80
+ line_end = line_range(node.first_line).end_pos
81
+
82
+ range_between(node.source_range.begin_pos, [node.source_range.end_pos, line_end].min)
83
+ end
84
+ end
85
+ end
86
+ end
87
+ end
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Canon
6
+ # Enforces the phase structure of a block body.
7
+ #
8
+ # A body checked by this cop ends in a *trailing phase*: the run of
9
+ # statements that call one of `TrailingMethods`. Exactly one blank line
10
+ # opens that run, no blank line splits it, and the statements before it
11
+ # form at most `MaxPhases` - 1 further phases.
12
+ #
13
+ # `Blocks` names the methods whose blocks are checked and
14
+ # `TrailingMethods` the calls that make up the trailing phase, matched
15
+ # against the leftmost call in a chain. Both are empty by default, so
16
+ # the cop does nothing until it is configured.
17
+ #
18
+ # A body carrying more phases than `MaxPhases` allows keeps the last
19
+ # separator before the trailing phase and loses the earlier ones —
20
+ # everything ahead of the last leading statement is one phase.
21
+ #
22
+ # @example Blocks: ['step'], TrailingMethods: ['report']
23
+ # # bad — no blank line opens the trailing phase
24
+ # step 'totals the order' do
25
+ # order = build_order
26
+ # report order.total
27
+ # end
28
+ #
29
+ # # bad — a blank line splits the trailing phase
30
+ # step 'totals the order' do
31
+ # order = build_order
32
+ #
33
+ # report order.total
34
+ #
35
+ # report order.currency
36
+ # end
37
+ #
38
+ # # good
39
+ # step 'totals the order' do
40
+ # order = build_order
41
+ #
42
+ # report order.total
43
+ # report order.currency
44
+ # end
45
+ class BlockPhases < Base
46
+ extend AutoCorrector
47
+ include BlankLineHelp
48
+
49
+ MSG_EXTRA = 'Remove the blank line inside the trailing phase.'
50
+ MSG_MISSING = 'Add a blank line before the trailing phase.'
51
+ MSG_TOO_MANY_PHASES = 'Use at most %<max_phases>d phases in a block body.'
52
+
53
+ def on_block(node)
54
+ return unless configured_block?(node)
55
+
56
+ check_phases(node)
57
+ end
58
+
59
+ alias on_numblock on_block
60
+
61
+ private
62
+
63
+ def check_phases(node)
64
+ statements = body_statements(node)
65
+ return if statements.size < 2
66
+
67
+ trailing_start = detect_trailing_start(statements)
68
+ return if trailing_start.nil?
69
+
70
+ check_trailing_phase(statements, trailing_start)
71
+ check_leading_phases(statements, trailing_start)
72
+ end
73
+
74
+ def check_trailing_phase(statements, trailing_start)
75
+ statements[trailing_start..].each_cons(2) do |previous, following|
76
+ next if comments_between?(previous, following)
77
+ next if blank_lines_between(previous, following) != 1
78
+
79
+ register_extra_blank_line(previous, MSG_EXTRA)
80
+ end
81
+ end
82
+
83
+ def check_leading_phases(statements, trailing_start)
84
+ return if trailing_start.zero?
85
+
86
+ check_separator(statements[trailing_start - 1], statements[trailing_start])
87
+ check_phase_count(statements[..(trailing_start - 1)])
88
+ end
89
+
90
+ def check_separator(previous, following)
91
+ return if comments_between?(previous, following)
92
+ return if blank_lines_between(previous, following) == 1
93
+
94
+ register_missing_blank_line(previous, following, MSG_MISSING)
95
+ end
96
+
97
+ def check_phase_count(statements)
98
+ separators = leading_separators(statements)
99
+ return if separators.size < max_phases - 1
100
+
101
+ surplus = separators[0..-(max_phases - 1)]
102
+
103
+ surplus.each do |previous|
104
+ register_extra_blank_line(previous, format(MSG_TOO_MANY_PHASES, max_phases:))
105
+ end
106
+ end
107
+
108
+ def leading_separators(statements)
109
+ statements.each_cons(2).filter_map do |previous, following|
110
+ next if comments_between?(previous, following)
111
+
112
+ previous if blank_lines_between(previous, following) == 1
113
+ end
114
+ end
115
+
116
+ def detect_trailing_start(statements)
117
+ trailing_start = nil
118
+
119
+ statements.each_with_index.reverse_each do |statement, index|
120
+ break unless trailing?(statement)
121
+
122
+ trailing_start = index
123
+ end
124
+
125
+ trailing_start
126
+ end
127
+
128
+ def configured_block?(node)
129
+ return false unless node.send_node.receiver.nil?
130
+
131
+ block_methods.include?(node.method_name.to_s)
132
+ end
133
+
134
+ def trailing?(node)
135
+ chain = call_chain(node)
136
+ return false if chain.empty?
137
+
138
+ trailing_methods.include?(chain.first)
139
+ end
140
+
141
+ def block_methods
142
+ @block_methods ||= Array(cop_config['Blocks']).map(&:to_s)
143
+ end
144
+
145
+ def trailing_methods
146
+ @trailing_methods ||= Array(cop_config['TrailingMethods']).map(&:to_s)
147
+ end
148
+
149
+ def max_phases
150
+ cop_config['MaxPhases']
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,157 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Canon
6
+ # Enforces blank lines between declaration groups in a class, module or
7
+ # DSL block body.
8
+ #
9
+ # `GroupedMethods` is the whole specification: two declarations calling
10
+ # methods listed in the same group take no blank line between them, and
11
+ # two calling methods listed in different groups take exactly one. The
12
+ # cop says nothing about a method name it has not been given, so a DSL
13
+ # it does not know stays as its author wrote it.
14
+ #
15
+ # A block is a group of one whatever it calls, so a DSL block always
16
+ # stands apart from the declarations around it. Everything else the cop
17
+ # has not been told about — an unlisted method name, a constant — it
18
+ # leaves alone.
19
+ #
20
+ # @example GroupedMethods: [[belongs_to, has_many], [validate, validates]]
21
+ # # bad
22
+ # class Shift < ApplicationRecord
23
+ # belongs_to :site
24
+ #
25
+ # has_many :shift_assignments, dependent: :destroy
26
+ # validates :starts_at, presence: true
27
+ # end
28
+ #
29
+ # # good
30
+ # class Shift < ApplicationRecord
31
+ # belongs_to :site
32
+ # has_many :shift_assignments, dependent: :destroy
33
+ #
34
+ # validates :starts_at, presence: true
35
+ # end
36
+ #
37
+ # # good — neither name is listed, so the cop leaves the body alone
38
+ # class Filtering < Capability::Base
39
+ # request_transformer RequestTransformer
40
+ # api_builder APIBuilder
41
+ # end
42
+ class DeclarationGroups < Base
43
+ extend AutoCorrector
44
+ include BlankLineHelp
45
+
46
+ MSG_EXTRA = 'Remove the blank line between declarations of the same group.'
47
+ MSG_MISSING = 'Add a blank line between declaration groups.'
48
+ ACCESS_MODIFIERS = %i[private protected public module_function private_class_method].freeze
49
+ DEFINITION_TYPES = %i[def defs].freeze
50
+ BLOCK_TYPES = %i[block numblock].freeze
51
+ STANDALONE_TYPES = %i[block numblock def defs].freeze
52
+
53
+ def on_class(node)
54
+ check_declarations(node)
55
+ end
56
+
57
+ def on_module(node)
58
+ check_declarations(node)
59
+ end
60
+
61
+ def on_block(node)
62
+ return unless declaration_context?(node)
63
+
64
+ check_declarations(node)
65
+ end
66
+
67
+ alias on_numblock on_block
68
+
69
+ private
70
+
71
+ def check_declarations(node)
72
+ statements = body_statements(node)
73
+ return if statements.size < 2
74
+ return unless statements.all? { |statement| declaration?(statement) }
75
+
76
+ statements.each_cons(2) do |previous, following|
77
+ next if definitions?(previous, following)
78
+ next if access_modifier?(previous)
79
+ next if access_modifier?(following)
80
+ next unless grouped?(previous, following)
81
+
82
+ check_gap(previous, following, expected_blank_lines(previous, following))
83
+ end
84
+ end
85
+
86
+ def check_gap(previous, following, expected)
87
+ return if comments_between?(previous, following)
88
+
89
+ blank_lines = blank_lines_between(previous, following)
90
+ return if blank_lines == expected
91
+ return if blank_lines > 1
92
+
93
+ return register_extra_blank_line(previous, MSG_EXTRA) if expected.zero?
94
+
95
+ register_missing_blank_line(previous, following, MSG_MISSING)
96
+ end
97
+
98
+ def grouped?(previous, following)
99
+ return true if standalone?(previous)
100
+ return true if standalone?(following)
101
+ return false if group_key(previous).nil?
102
+
103
+ !group_key(following).nil?
104
+ end
105
+
106
+ def expected_blank_lines(previous, following)
107
+ return 1 if standalone?(previous)
108
+ return 1 if standalone?(following)
109
+ return 0 if group_key(previous) == group_key(following)
110
+
111
+ 1
112
+ end
113
+
114
+ def standalone?(node)
115
+ STANDALONE_TYPES.include?(node.type)
116
+ end
117
+
118
+ def group_key(node)
119
+ return unless node.send_type?
120
+
121
+ grouped_methods.index { |group| group.include?(node.method_name.to_s) }
122
+ end
123
+
124
+ def declaration_context?(node)
125
+ enclosing = node.each_ancestor(:def, :defs, :class, :module).first
126
+ return false if enclosing.nil?
127
+ return true if enclosing.class_type?
128
+
129
+ enclosing.module_type?
130
+ end
131
+
132
+ def declaration?(node)
133
+ return true if node.casgn_type?
134
+ return true if DEFINITION_TYPES.include?(node.type)
135
+ return node.send_node.receiver.nil? if BLOCK_TYPES.include?(node.type)
136
+
137
+ node.send_type? && node.receiver.nil?
138
+ end
139
+
140
+ def definitions?(previous, following)
141
+ DEFINITION_TYPES.include?(previous.type) && DEFINITION_TYPES.include?(following.type)
142
+ end
143
+
144
+ def access_modifier?(node)
145
+ return false unless node.send_type?
146
+ return false unless node.arguments.empty?
147
+
148
+ ACCESS_MODIFIERS.include?(node.method_name)
149
+ end
150
+
151
+ def grouped_methods
152
+ @grouped_methods ||= Array(cop_config['GroupedMethods']).map { |group| Array(group).map(&:to_s) }
153
+ end
154
+ end
155
+ end
156
+ end
157
+ end
@@ -0,0 +1,142 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Canon
6
+ # Enforces blank lines around a method body's standalone calls and
7
+ # nowhere else.
8
+ #
9
+ # `SeparatedMethods` names the calls that stand on their own —
10
+ # `authorize!`, `expose`, `mail`. Each takes exactly one blank line
11
+ # between it and any statement next to it. Every other pair of
12
+ # statements takes none, because a blank line there marks a step that
13
+ # wants a name: extract a method instead.
14
+ #
15
+ # The list is empty by default, so the cop only removes until it is
16
+ # configured.
17
+ #
18
+ # The position after a guard clause belongs to
19
+ # `Layout/EmptyLineAfterGuardClause`, so this cop leaves it alone.
20
+ #
21
+ # An entry matches a method name anywhere in the call chain. Write it
22
+ # dotted — `errors.add` — to match a receiver and method together, so
23
+ # `coverage_request.errors.add(...)` matches and a bare `add` does not.
24
+ #
25
+ # @example SeparatedMethods: ['authorize!', 'expose']
26
+ # # bad
27
+ # def index
28
+ # authorize!
29
+ # shift = find_shift
30
+ # employees = Employee.for_shift(shift)
31
+ # expose employees
32
+ # end
33
+ #
34
+ # # good
35
+ # def index
36
+ # authorize!
37
+ #
38
+ # shift = find_shift
39
+ # employees = Employee.for_shift(shift)
40
+ #
41
+ # expose employees
42
+ # end
43
+ #
44
+ # # good — no standalone call, so no blank line
45
+ # def reset
46
+ # Current.session = nil
47
+ # Current.account = nil
48
+ # end
49
+ #
50
+ # # good — the guard clause owns its own blank line
51
+ # def total
52
+ # items = order.items
53
+ # return 0 if items.empty?
54
+ #
55
+ # items.sum(&:amount)
56
+ # end
57
+ class MethodBodyBlankLines < Base
58
+ extend AutoCorrector
59
+ include BlankLineHelp
60
+
61
+ MSG_EXTRA = 'Remove the blank line inside the method body.'
62
+ MSG_MISSING = 'Add a blank line around the standalone call.'
63
+ JUMP_METHODS = %i[raise fail throw].freeze
64
+
65
+ def on_def(node)
66
+ check_method_body(node)
67
+ end
68
+
69
+ def on_defs(node)
70
+ check_method_body(node)
71
+ end
72
+
73
+ private
74
+
75
+ def check_method_body(node)
76
+ statements = body_statements(node)
77
+ return if statements.size < 2
78
+
79
+ statements.each_cons(2) do |previous, following|
80
+ next if guard?(previous)
81
+
82
+ check_gap(previous, following, expected_blank_lines(previous, following))
83
+ end
84
+ end
85
+
86
+ def check_gap(previous, following, expected)
87
+ return if comments_between?(previous, following)
88
+
89
+ blank_lines = blank_lines_between(previous, following)
90
+ return if blank_lines == expected
91
+ return if blank_lines > 1
92
+
93
+ return register_extra_blank_line(previous, MSG_EXTRA) if expected.zero?
94
+
95
+ register_missing_blank_line(previous, following, MSG_MISSING)
96
+ end
97
+
98
+ def expected_blank_lines(previous, following)
99
+ return 1 if separated?(previous)
100
+ return 1 if separated?(following)
101
+
102
+ 0
103
+ end
104
+
105
+ def separated?(node)
106
+ chain = call_chain(node)
107
+ return false if chain.empty?
108
+
109
+ separated_methods.any? { |name| matches_chain?(chain, name) }
110
+ end
111
+
112
+ def matches_chain?(chain, name)
113
+ return chain.include?(name) unless name.include?('.')
114
+
115
+ chain.each_cons(2).any? { |pair| pair.join('.') == name }
116
+ end
117
+
118
+ def guard?(node)
119
+ return true if jump?(node)
120
+ return false unless node.if_type?
121
+ return false unless node.modifier_form?
122
+ return true if jump?(node.if_branch)
123
+
124
+ jump?(node.else_branch)
125
+ end
126
+
127
+ def jump?(node)
128
+ return false if node.nil?
129
+ return true if node.return_type?
130
+ return true if node.break_type?
131
+ return true if node.next_type?
132
+
133
+ node.send_type? && JUMP_METHODS.include?(node.method_name)
134
+ end
135
+
136
+ def separated_methods
137
+ @separated_methods ||= Array(cop_config['SeparatedMethods']).map(&:to_s)
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
data/lib/rubocop-canon.rb CHANGED
@@ -3,7 +3,11 @@
3
3
  require 'rubocop'
4
4
  require_relative 'rubocop/canon/version'
5
5
  require_relative 'rubocop/canon/plugin'
6
+ require_relative 'rubocop/cop/canon/blank_line_help'
7
+ require_relative 'rubocop/cop/canon/block_phases'
8
+ require_relative 'rubocop/cop/canon/declaration_groups'
6
9
  require_relative 'rubocop/cop/canon/keyword_shorthand'
10
+ require_relative 'rubocop/cop/canon/method_body_blank_lines'
7
11
  require_relative 'rubocop/cop/canon/sort_hash'
8
12
  require_relative 'rubocop/cop/canon/sort_keywords'
9
13
  require_relative 'rubocop/cop/canon/sort_method_arguments'
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-canon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - skiftle
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-02-24 00:00:00.000000000 Z
11
+ date: 2026-09-06 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: lefthook
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '2.1'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '2.1'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: lint_roller
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -56,7 +70,11 @@ files:
56
70
  - lib/rubocop-canon.rb
57
71
  - lib/rubocop/canon/plugin.rb
58
72
  - lib/rubocop/canon/version.rb
73
+ - lib/rubocop/cop/canon/blank_line_help.rb
74
+ - lib/rubocop/cop/canon/block_phases.rb
75
+ - lib/rubocop/cop/canon/declaration_groups.rb
59
76
  - lib/rubocop/cop/canon/keyword_shorthand.rb
77
+ - lib/rubocop/cop/canon/method_body_blank_lines.rb
60
78
  - lib/rubocop/cop/canon/sort_hash.rb
61
79
  - lib/rubocop/cop/canon/sort_keywords.rb
62
80
  - lib/rubocop/cop/canon/sort_method_arguments.rb
@@ -84,7 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
102
  - !ruby/object:Gem::Version
85
103
  version: '0'
86
104
  requirements: []
87
- rubygems_version: 3.4.1
105
+ rubygems_version: 3.4.19
88
106
  signing_key:
89
107
  specification_version: 4
90
108
  summary: Deterministic RuboCop cops for canonical Ruby form