rubocop-inclusivity 1.0.0 → 1.1.0

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
2
  SHA256:
3
- metadata.gz: 2b6237d1e9d200e8c88398b425785f2b71e01d2cf7ff8853c208efd83a23449f
4
- data.tar.gz: 29777d338a59293c206a6d3233a829c375a344cae93d554c06aa95296ca91e21
3
+ metadata.gz: 3833b1927a9e751644572e0cdad9b33db8e34fab732c39ba6baafa66eec33016
4
+ data.tar.gz: c38bfbc149c5553cde194d39382243e9563c842a1fbb28ba4d1c8e31a4211b5a
5
5
  SHA512:
6
- metadata.gz: 4ab213c6f39cac2db6df37d991fccbb6121f12d71293c8970e5b5fcadf44a5a8c8a7510a62a9a57527a419702796c31d27816625b007037def98641edb81e18a
7
- data.tar.gz: 3a46b067de2c5bbc3f968c04ff22f511f77d3e8087a3a167ef739b4193e72dd9fb6a44bdebdddc03e338d7874aadd97fa39b8aeffcfc3caeed6969ae19f2dab5
6
+ metadata.gz: 7e02c15c0180190c147d446273a13716510bd633e56d7f8b1666bf2331bef2395454a740bedb2d08c7d9f10e34ed74de8037f1cbb8d70d126a235e7afb82d946
7
+ data.tar.gz: f7613cb5850619a21bb8da40f603a7fd3a77bf6192557b3002aacac8b5b473e619150553a830cc42a9055484c515b37e01adddd9dfb4b1b907bbc1c2db8b7c67
@@ -11,7 +11,7 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
  steps:
13
13
  - uses: actions/checkout@v2
14
- - uses: actions/setup-ruby@v1
14
+ - uses: ruby/setup-ruby@v1
15
15
  - name: Run RSpec
16
16
  run: |
17
17
  gem install bundler
data/.gitignore CHANGED
@@ -7,5 +7,7 @@
7
7
  /spec/reports/
8
8
  /tmp/
9
9
 
10
+ .byebug_history
11
+
10
12
  # rspec failure tracking
11
13
  .rspec_status
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 3.0.3
data/Gemfile.lock CHANGED
@@ -1,95 +1,70 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- rubocop-inclusivity (1.0.0)
5
- rubocop
4
+ rubocop-inclusivity (1.1.0)
5
+ activesupport (>= 4)
6
+ rubocop (> 0.87.0)
6
7
 
7
8
  GEM
8
9
  remote: https://rubygems.org/
9
10
  specs:
10
- activesupport (6.0.2.2)
11
+ activesupport (7.0.2.3)
11
12
  concurrent-ruby (~> 1.0, >= 1.0.2)
12
- i18n (>= 0.7, < 2)
13
- minitest (~> 5.1)
14
- tzinfo (~> 1.1)
15
- zeitwerk (~> 2.2)
16
- addressable (2.7.0)
17
- public_suffix (>= 2.0.2, < 5.0)
18
- ast (2.4.0)
19
- byebug (9.1.0)
20
- concurrent-ruby (1.1.6)
21
- diff-lcs (1.3)
22
- faraday (0.17.3)
23
- multipart-post (>= 1.2, < 3)
24
- faraday-http-cache (2.0.0)
25
- faraday (~> 0.8)
26
- github_changelog_generator (1.15.0)
27
- activesupport
28
- faraday-http-cache
29
- multi_json
30
- octokit (~> 4.6)
31
- rainbow (>= 2.2.1)
32
- rake (>= 10.0)
33
- retriable (~> 3.0)
34
- i18n (1.8.2)
13
+ i18n (>= 1.6, < 2)
14
+ minitest (>= 5.1)
15
+ tzinfo (~> 2.0)
16
+ ast (2.4.1)
17
+ byebug (11.1.3)
18
+ concurrent-ruby (1.1.9)
19
+ diff-lcs (1.4.4)
20
+ i18n (1.10.0)
35
21
  concurrent-ruby (~> 1.0)
36
- jaro_winkler (1.5.4)
37
- minitest (5.14.0)
38
- multi_json (1.14.1)
39
- multipart-post (2.1.1)
40
- octokit (4.18.0)
41
- faraday (>= 0.9)
42
- sawyer (~> 0.8.0, >= 0.5.3)
43
- parallel (1.19.1)
44
- parser (2.7.1.0)
45
- ast (~> 2.4.0)
46
- public_suffix (4.0.3)
22
+ minitest (5.15.0)
23
+ parallel (1.20.1)
24
+ parser (3.0.0.0)
25
+ ast (~> 2.4.1)
47
26
  rainbow (3.0.0)
48
27
  rake (12.3.3)
49
- retriable (3.1.2)
28
+ regexp_parser (2.0.3)
50
29
  rexml (3.2.4)
51
- rspec (3.9.0)
52
- rspec-core (~> 3.9.0)
53
- rspec-expectations (~> 3.9.0)
54
- rspec-mocks (~> 3.9.0)
55
- rspec-core (3.9.1)
56
- rspec-support (~> 3.9.1)
57
- rspec-expectations (3.9.1)
30
+ rspec (3.10.0)
31
+ rspec-core (~> 3.10.0)
32
+ rspec-expectations (~> 3.10.0)
33
+ rspec-mocks (~> 3.10.0)
34
+ rspec-core (3.10.1)
35
+ rspec-support (~> 3.10.0)
36
+ rspec-expectations (3.10.1)
58
37
  diff-lcs (>= 1.2.0, < 2.0)
59
- rspec-support (~> 3.9.0)
60
- rspec-mocks (3.9.1)
38
+ rspec-support (~> 3.10.0)
39
+ rspec-mocks (3.10.1)
61
40
  diff-lcs (>= 1.2.0, < 2.0)
62
- rspec-support (~> 3.9.0)
63
- rspec-support (3.9.2)
64
- rubocop (0.80.1)
65
- jaro_winkler (~> 1.5.1)
41
+ rspec-support (~> 3.10.0)
42
+ rspec-support (3.10.1)
43
+ rubocop (1.8.1)
66
44
  parallel (~> 1.10)
67
- parser (>= 2.7.0.1)
45
+ parser (>= 3.0.0.0)
68
46
  rainbow (>= 2.2.2, < 4.0)
47
+ regexp_parser (>= 1.8, < 3.0)
69
48
  rexml
49
+ rubocop-ast (>= 1.2.0, < 2.0)
70
50
  ruby-progressbar (~> 1.7)
71
- unicode-display_width (>= 1.4.0, < 1.7)
51
+ unicode-display_width (>= 1.4.0, < 3.0)
52
+ rubocop-ast (1.4.0)
53
+ parser (>= 2.7.1.5)
72
54
  rubocop-performance (1.5.2)
73
55
  rubocop (>= 0.71.0)
74
- ruby-progressbar (1.10.1)
75
- sawyer (0.8.2)
76
- addressable (>= 2.3.5)
77
- faraday (> 0.8, < 2.0)
78
- standard (0.2.3)
79
- rubocop (~> 0.80.1)
80
- rubocop-performance (~> 1.5.2)
81
- thread_safe (0.3.6)
82
- tzinfo (1.2.7)
83
- thread_safe (~> 0.1)
84
- unicode-display_width (1.6.1)
85
- zeitwerk (2.3.0)
56
+ ruby-progressbar (1.11.0)
57
+ standard (0.0.36)
58
+ rubocop (>= 0.63)
59
+ tzinfo (2.0.4)
60
+ concurrent-ruby (~> 1.0)
61
+ unicode-display_width (2.0.0)
86
62
 
87
63
  PLATFORMS
88
64
  ruby
89
65
 
90
66
  DEPENDENCIES
91
67
  byebug
92
- github_changelog_generator
93
68
  rake (~> 12.0)
94
69
  rspec
95
70
  rubocop-inclusivity!
@@ -97,4 +72,4 @@ DEPENDENCIES
97
72
  standard
98
73
 
99
74
  BUNDLED WITH
100
- 2.1.4
75
+ 2.3.5
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0
1
+ 1.1.0
data/config/default.yml CHANGED
@@ -4,6 +4,7 @@ Inclusivity/Race:
4
4
  accurate and inclusive words.
5
5
  Reference: https://github.com/rails/rails/issues/33677
6
6
  Enabled: true
7
+ Allowlist:
7
8
  Offenses:
8
9
  whitelist:
9
10
  - allowlist
@@ -1,5 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require "active_support"
4
+
5
+ # https://github.com/rubocop-hq/rubocop-ast/blob/5cd306f40e5d5ba4dacf78c698354747cdac7825/docs/modules/ROOT/pages/node_types.adoc
6
+
3
7
  module RuboCop
4
8
  module Cop
5
9
  module Inclusivity
@@ -16,41 +20,152 @@ module RuboCop
16
20
  # # good
17
21
  # banlist = 1
18
22
  #
19
- class Race < Cop
23
+ class Race < Base
24
+ extend AutoCorrector
25
+ include ActiveSupport::Inflector
26
+
27
+ ALLOWLIST = "Allowlist"
28
+ OFFENSES = "Offenses"
29
+ DOUBLE_QUOTE = "\""
20
30
  MSG = "`%s` may be insensitive. Consider alternatives: %s"
31
+ PARTIAL = "partial"
32
+ SINGLE_QUOTE = "'"
33
+ SYMBOL = ":"
34
+ UTF_8 = "UTF-8"
35
+
36
+ def simple_substitution(node)
37
+ name, = *node
38
+ name && check(node.loc.name, name)
39
+ end
40
+ alias on_lvasgn simple_substitution
41
+ alias on_ivasgn simple_substitution
42
+ alias on_cvasgn simple_substitution
43
+ alias on_arg simple_substitution
44
+ alias on_optarg simple_substitution
45
+ alias on_restarg simple_substitution
46
+ alias on_kwoptarg simple_substitution
47
+ alias on_kwarg simple_substitution
48
+ alias on_kwrestarg simple_substitution
49
+ alias on_blockarg simple_substitution
50
+ alias on_lvar simple_substitution
51
+ alias on_cvar simple_substitution
52
+
53
+ def on_casgn(node)
54
+ _parent, constant_name, _value = *node
55
+ check(node.loc.name, constant_name) { |alternative| alternative.upcase }
56
+ end
21
57
 
22
- def on_lvasgn(node)
58
+ def on_sym(node)
23
59
  name, = *node
24
- return unless name
60
+ check(node.source_range, name) { |replacement| node.source[0] == SYMBOL ? ":#{replacement}" : replacement }
61
+ end
25
62
 
26
- check_name(node, name, node.loc.name)
63
+ def on_str(node)
64
+ name, = *node
65
+ check(node.source_range, name) do |replacement|
66
+ quote = determine_quote(node.source)
67
+ "#{quote}#{replacement.downcase}#{quote}"
68
+ end
69
+ end
70
+
71
+ def on_def(node)
72
+ name, _args, _forward_args = *node
73
+ check(node.loc.name, name)
74
+ end
75
+
76
+ def on_send(node)
77
+ match_methods_and_variables(node) do |variable_name|
78
+ check(node.loc.selector, variable_name.to_s.delete_suffix("="))
79
+ end
80
+ end
81
+
82
+ def on_const(node)
83
+ match_consts(node) do |const_name|
84
+ check(node.loc.name, const_name)
85
+ end
27
86
  end
28
- alias on_ivasgn on_lvasgn
29
- alias on_cvasgn on_lvasgn
30
- alias on_arg on_lvasgn
31
- alias on_optarg on_lvasgn
32
- alias on_restarg on_lvasgn
33
- alias on_kwoptarg on_lvasgn
34
- alias on_kwarg on_lvasgn
35
- alias on_kwrestarg on_lvasgn
36
- alias on_blockarg on_lvasgn
37
- alias on_lvar on_lvasgn
38
87
 
39
88
  private
40
89
 
41
- def check_name(node, name, name_range)
42
- if (alternatives = preferred_language(name))
43
- msg = message(name, alternatives)
44
- add_offense(node, location: name_range, message: msg)
90
+ def_node_matcher :match_methods_and_variables, <<~PATTERN
91
+ (send _ $_ ...)
92
+ PATTERN
93
+
94
+ def_node_matcher :match_consts, <<~PATTERN
95
+ (const ... $_)
96
+ PATTERN
97
+
98
+ def determine_quote(source)
99
+ return SINGLE_QUOTE if source[0] == SINGLE_QUOTE
100
+ DOUBLE_QUOTE if source[0] == DOUBLE_QUOTE
101
+ end
102
+
103
+ def check(range, input)
104
+ string = input.to_s.encode(Encoding.find(UTF_8), invalid: :replace, undef: :replace, replace: "")
105
+ alternatives = preferred_language(string)
106
+ return unless alternatives
107
+
108
+ add_offense(range, message: format(MSG, string, alternatives.join(", "))) do |corrector|
109
+ replacement = block_given? ? yield(alternatives.first) : alternatives.first
110
+ corrector.replace(range, replacement)
45
111
  end
46
112
  end
47
113
 
48
114
  def preferred_language(word)
49
- cop_config["Offenses"][word.to_s.downcase]
115
+ exclusive_language_matcher.match(word) do |match|
116
+ next if allow?(word)
117
+
118
+ offense = match[0].downcase
119
+ alternatives = cop_config["Offenses"].fetch(offense)
120
+ matcher = %r{#{offense}}i
121
+
122
+ alternatives.map do |alternative|
123
+ word.to_s.gsub(matcher) { |match| replace_match(match, alternative) }
124
+ end
125
+ end
126
+ end
127
+
128
+ def on_new_investigation
129
+ processed_source.comments.each do |comment|
130
+ start_position = comment.location.expression.to_range.first
131
+ comment.text.split(" ").each do |word|
132
+ alternatives = preferred_language(word)
133
+ next unless alternatives
134
+
135
+ buffer = @processed_source.buffer
136
+ end_position = start_position + word.to_s.size
137
+ range = Parser::Source::Range.new(buffer, start_position, end_position)
138
+ add_offense(range, message: format(MSG, word.to_s, alternatives.join(", "))) do |corrector|
139
+ corrector.replace(range, alternatives.first)
140
+ end
141
+ ensure
142
+ start_position += word.size + 1
143
+ end
144
+ end
145
+ end
146
+
147
+ def replace_match(word, alternative)
148
+ normalized_alternative = alternative.downcase
149
+ return normalized_alternative if word.downcase == word
150
+ return normalized_alternative.upcase if word.upcase == word
151
+ return underscore(normalized_alternative) if underscore(word) == word
152
+ return camelize(normalized_alternative) if camelize(word) == word
153
+ return camelize(normalized_alternative, false) if camelize(word, false) == word
154
+ word
155
+ end
156
+
157
+ def allow?(text)
158
+ allowlist.any? do |(allow, config)|
159
+ config.fetch(PARTIAL, false) ? text.to_s.match(%r{#{allow}}i) : text.to_s.downcase == allow.to_s.downcase
160
+ end
161
+ end
162
+
163
+ def allowlist
164
+ @_allowlist ||= cop_config[ALLOWLIST] || {}
50
165
  end
51
166
 
52
- def message(insensitive, alternatives)
53
- format(MSG, insensitive, alternatives.join(", "))
167
+ def exclusive_language_matcher
168
+ @_exclusive_language_matcher ||= %r{(#{cop_config[OFFENSES].keys.join("|")})}i
54
169
  end
55
170
  end
56
171
  end
@@ -26,5 +26,6 @@ Gem::Specification.new do |spec|
26
26
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
27
27
  spec.require_paths = ["lib"]
28
28
 
29
- spec.add_runtime_dependency "rubocop"
29
+ spec.add_runtime_dependency "rubocop", "> 0.87.0"
30
+ spec.add_runtime_dependency "activesupport", ">= 4"
30
31
  end
metadata CHANGED
@@ -1,30 +1,44 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-inclusivity
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chris Fung
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-06-19 00:00:00.000000000 Z
11
+ date: 2022-03-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">"
18
+ - !ruby/object:Gem::Version
19
+ version: 0.87.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">"
25
+ - !ruby/object:Gem::Version
26
+ version: 0.87.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: activesupport
15
29
  requirement: !ruby/object:Gem::Requirement
16
30
  requirements:
17
31
  - - ">="
18
32
  - !ruby/object:Gem::Version
19
- version: '0'
33
+ version: '4'
20
34
  type: :runtime
21
35
  prerelease: false
22
36
  version_requirements: !ruby/object:Gem::Requirement
23
37
  requirements:
24
38
  - - ">="
25
39
  - !ruby/object:Gem::Version
26
- version: '0'
27
- description:
40
+ version: '4'
41
+ description:
28
42
  email:
29
43
  - aergonaut@gmail.com
30
44
  executables: []
@@ -35,6 +49,7 @@ files:
35
49
  - ".gitignore"
36
50
  - ".rspec"
37
51
  - ".rubocop.yml"
52
+ - ".ruby-version"
38
53
  - ".travis.yml"
39
54
  - ".versionrc.json"
40
55
  - CHANGELOG.md
@@ -65,7 +80,7 @@ metadata:
65
80
  homepage_uri: https://github.com/aergonaut/rubocop-inclusivity
66
81
  source_code_uri: https://github.com/aergonaut/rubocop-inclusivity
67
82
  changelog_uri: https://github.com/aergonaut/rubocop-inclusivity/blob/master/CHANGELOG.md
68
- post_install_message:
83
+ post_install_message:
69
84
  rdoc_options: []
70
85
  require_paths:
71
86
  - lib
@@ -80,8 +95,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
95
  - !ruby/object:Gem::Version
81
96
  version: '0'
82
97
  requirements: []
83
- rubygems_version: 3.1.3
84
- signing_key:
98
+ rubygems_version: 3.1.6
99
+ signing_key:
85
100
  specification_version: 4
86
101
  summary: A RuboCop extension to promote inclusive language
87
102
  test_files: []