inclusive-code 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +86 -2
- data/lib/rubocop/cop/inclusive_code.rb +49 -27
- data/lib/rubocop/inclusive_code/version.rb +1 -1
- metadata +5 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7fc5f8f716dd932818068d5b282bc52d1f7e93ad38eec1f16e100076347c42f8
|
4
|
+
data.tar.gz: 372a8b68115654589caf6ec9a6c03e94c96d5f2cced613065c15b0454214076a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6781133cb6fad6ef8bb373de9d7359a0d121901f58863adcf5f0498c0550059af3b3b6787cab808ca104392ef790e22e6fe4529805362c5c0c3c5332269004d5
|
7
|
+
data.tar.gz: aa2a97adf00529dbdf3fb6f525aaaa994e7988fe0242d47aeb934cd063fc448e0c8723ee22f91da792727c914b68b6fbbea78142fe271641e3242818450d64da
|
data/README.md
CHANGED
@@ -19,7 +19,7 @@ Configure your own set of flagged terms following the structure of the `inclusiv
|
|
19
19
|
|
20
20
|
Put this into your .rubocop.yml:
|
21
21
|
|
22
|
-
```
|
22
|
+
```yaml
|
23
23
|
require:
|
24
24
|
- inclusive-code
|
25
25
|
|
@@ -29,8 +29,92 @@ Flexport/InclusiveCode:
|
|
29
29
|
DisableAutoCorrect: false
|
30
30
|
```
|
31
31
|
|
32
|
-
You can run the cop on your entire codebase with `rubocop --only Flexport/InclusiveCode`.
|
32
|
+
You can run the cop on your entire codebase with `rubocop --only Flexport/InclusiveCode`.
|
33
33
|
|
34
34
|
You can run the cop on a specific file with `rubocop --only Flexport/InclusiveCode file_path`.
|
35
35
|
|
36
36
|
If you want to add inline `rubocop:disable` or `rubocop:todo` comments on all offenses, set `DisableAutoCorrect: true` in your .rubocop.yml, and run `rubocop --only Flexport/InclusiveCode --auto-correct --disable-uncorrectable`.
|
37
|
+
|
38
|
+
## Configuration
|
39
|
+
|
40
|
+
The inclusive-code gem includes an initial configuration to get your project started. You can customize this configuration to fit your needs.
|
41
|
+
|
42
|
+
### Flagging harmful terms
|
43
|
+
|
44
|
+
Rules are added under the key `flagged_term` as follows:
|
45
|
+
|
46
|
+
```yaml
|
47
|
+
---
|
48
|
+
flagged_terms:
|
49
|
+
some_harmful_term: {}
|
50
|
+
```
|
51
|
+
|
52
|
+
### Specifying harmful terminology
|
53
|
+
|
54
|
+
You can specify harmful terminology using basic keys (`another_harmful_term:`), string keys (`" his ":`), or using [regular expressions](https://rubular.com/r/HvcomHUBZ3KFCz) like `"white[-_\\s]*list":`. Please note that when specifying a regular expression, some characters (`\`) may need to be escaped. Examples:
|
55
|
+
|
56
|
+
```yaml
|
57
|
+
---
|
58
|
+
flagged_terms:
|
59
|
+
"white[-_\\s]*list": {}
|
60
|
+
"black[-_\\s]*list": {}
|
61
|
+
" him ": {}
|
62
|
+
master: {}
|
63
|
+
```
|
64
|
+
|
65
|
+
### Suggestions and Autocorrect
|
66
|
+
|
67
|
+
In a document titled [Terminology, Power and Offensive Language](https://tools.ietf.org/id/draft-knodel-terminology-01.html), the Internet Engineering Task Force (IETF) recommends that an editor or reviewer *should* "offer alternatives for offensive terminology as an important act of correcting larger editorial issues and clarifying technical concepts."
|
68
|
+
|
69
|
+
As this gem does some of the work of an editor or reviewer, it is appropriate that it should allow for communicating better alternatives when it finds harmful technology.
|
70
|
+
|
71
|
+
Here's how you can offer alternative suggestions:
|
72
|
+
|
73
|
+
```yaml
|
74
|
+
---
|
75
|
+
flagged_terms:
|
76
|
+
some_harmful_term:
|
77
|
+
suggestions:
|
78
|
+
- some_thoughtful_alternative
|
79
|
+
- some_other_thoughtful_alternative
|
80
|
+
```
|
81
|
+
|
82
|
+
When using autocorrect, the first item in the suggestions array will be used as the autocorrect term.
|
83
|
+
|
84
|
+
### Allowing exceptions
|
85
|
+
|
86
|
+
This gem supports two ways to specify exceptions to your rules: allowing specific terms, allowing specific files.
|
87
|
+
|
88
|
+
#### Allowing specific terms
|
89
|
+
|
90
|
+
You might want to do this to allow for an [Industry Term Exception](../README.md#industry-term-exemption). Here's how to allow certain terms using the `allowed:` key:
|
91
|
+
|
92
|
+
```yaml
|
93
|
+
---
|
94
|
+
flagged_terms:
|
95
|
+
master:
|
96
|
+
suggestions:
|
97
|
+
- main
|
98
|
+
allowed:
|
99
|
+
- master bill
|
100
|
+
- master air waybill
|
101
|
+
- master consol
|
102
|
+
- master shipment
|
103
|
+
```
|
104
|
+
|
105
|
+
#### Allowing specific files
|
106
|
+
|
107
|
+
You might want to do this when you wish to disallow some term, but you need to allow it in certain files. Perhaps you rely on some library which requires you to configure it using some harmful terminology. Here's how to allow occurrences of a harmful term when they occur within some file using the `allowed_files:` key:
|
108
|
+
|
109
|
+
```yaml
|
110
|
+
---
|
111
|
+
flagged_terms:
|
112
|
+
whitelist:
|
113
|
+
suggestions:
|
114
|
+
- allowlist
|
115
|
+
allowed_files:
|
116
|
+
- config/initializers/some_gem_config.rb
|
117
|
+
- .some_gem/*
|
118
|
+
```
|
119
|
+
|
120
|
+
This will result in allowing offenses in any files returned by `Dir.glob("{config/initializers/some_gem_config.rb,.some_gem/*}")`
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'active_support/core_ext/string'
|
2
4
|
|
3
5
|
module RuboCop
|
@@ -23,46 +25,56 @@ module RuboCop
|
|
23
25
|
class InclusiveCode < Cop
|
24
26
|
include RangeHelp
|
25
27
|
|
26
|
-
SEVERITY = 'warning'
|
28
|
+
SEVERITY = 'warning'
|
29
|
+
|
30
|
+
FLAG_ONLY_MSG = '🚫 Use of non_inclusive word: `%<non_inclusive_word>s`.'
|
31
|
+
FULL_FLAG_MSG = "#{FLAG_ONLY_MSG} Consider using these suggested alternatives: `%<suggestions>s`."
|
27
32
|
|
28
|
-
|
33
|
+
ALLOWED_TERM_MASK_CHAR = '*'.freeze
|
29
34
|
|
30
35
|
def initialize(config = nil, options = nil, source_file = nil)
|
31
36
|
super(config, options)
|
32
|
-
|
37
|
+
|
33
38
|
source_file ||= YAML.load_file(cop_config['GlobalConfigPath'])
|
34
39
|
@non_inclusive_words_alternatives_hash = source_file['flagged_terms']
|
35
40
|
@all_non_inclusive_words = @non_inclusive_words_alternatives_hash.keys
|
36
|
-
@non_inclusive_words_regex =
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
@
|
41
|
-
[
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
@allowed_regex =
|
41
|
+
@non_inclusive_words_regex = concatenated_regex(@all_non_inclusive_words)
|
42
|
+
@allowed_terms = {}
|
43
|
+
@allowed_files = {}
|
44
|
+
|
45
|
+
@all_non_inclusive_words.each do |word|
|
46
|
+
@allowed_terms[word] = get_allowed_string(word)
|
47
|
+
@allowed_files[word] = source_file['flagged_terms'][word]['allowed_files'] || []
|
48
|
+
end
|
49
|
+
@allowed_regex = @allowed_terms.values.reject(&:blank?).join('|')
|
50
|
+
|
51
|
+
@allowed_regex = if @allowed_regex.blank?
|
52
|
+
Regexp.new(/^$/)
|
53
|
+
else
|
54
|
+
Regexp.new(@allowed_regex, Regexp::IGNORECASE)
|
55
|
+
end
|
47
56
|
end
|
48
57
|
|
49
58
|
def investigate(processed_source)
|
59
|
+
non_inclusive_words_for_current_file = @all_non_inclusive_words.reject do |non_inclusive_word|
|
60
|
+
Dir.glob("{#{@allowed_files[non_inclusive_word].join(',')}}").include?(processed_source.path)
|
61
|
+
end
|
62
|
+
|
50
63
|
processed_source.lines.each_with_index do |line, line_number|
|
51
64
|
next unless line.match(@non_inclusive_words_regex)
|
52
65
|
|
53
|
-
|
54
|
-
allowed = @
|
55
|
-
scan_regex =
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
end
|
66
|
+
non_inclusive_words_for_current_file.each do |non_inclusive_word|
|
67
|
+
allowed = @allowed_terms[non_inclusive_word]
|
68
|
+
scan_regex = /(?=#{non_inclusive_word})/i
|
69
|
+
if allowed.present?
|
70
|
+
line = line.gsub(/(#{allowed})/i){ |match| ALLOWED_TERM_MASK_CHAR * match.size }
|
71
|
+
end
|
60
72
|
locations = line.enum_for(
|
61
73
|
:scan,
|
62
74
|
scan_regex
|
63
75
|
).map { Regexp.last_match&.offset(0)&.first }
|
64
76
|
|
65
|
-
non_inclusive_words = line.scan
|
77
|
+
non_inclusive_words = line.scan(/#{non_inclusive_word}/i)
|
66
78
|
|
67
79
|
locations = locations.zip(non_inclusive_words).to_h
|
68
80
|
next if locations.blank?
|
@@ -87,7 +99,7 @@ module RuboCop
|
|
87
99
|
path = processed_source.path
|
88
100
|
return if path.nil?
|
89
101
|
|
90
|
-
non_inclusive_words_match = path.match(
|
102
|
+
non_inclusive_words_match = path.match(concatenated_regex(non_inclusive_words_for_current_file))
|
91
103
|
return unless non_inclusive_words_match && !path.match(@allowed_regex)
|
92
104
|
|
93
105
|
range = source_range(processed_source.buffer, 1, 0)
|
@@ -124,26 +136,36 @@ module RuboCop
|
|
124
136
|
|
125
137
|
private
|
126
138
|
|
139
|
+
def concatenated_regex(non_inclusive_words)
|
140
|
+
Regexp.new(
|
141
|
+
non_inclusive_words.join('|'),
|
142
|
+
Regexp::IGNORECASE
|
143
|
+
)
|
144
|
+
end
|
145
|
+
|
127
146
|
def get_allowed_string(non_inclusive_word)
|
128
|
-
allowed = @non_inclusive_words_alternatives_hash[non_inclusive_word]
|
147
|
+
allowed = @non_inclusive_words_alternatives_hash[non_inclusive_word].fetch('allowed') { [] }
|
129
148
|
snake_case = allowed.map { |e| e.tr(' ', '_').underscore }
|
130
149
|
pascal_case = snake_case.map(&:camelize)
|
131
150
|
(allowed + snake_case + pascal_case).join('|')
|
132
151
|
end
|
133
152
|
|
134
153
|
def correction_for_word(word_to_correct)
|
135
|
-
_, correction = @non_inclusive_words_alternatives_hash.detect
|
154
|
+
_, correction = @non_inclusive_words_alternatives_hash.detect do |correction_key, _|
|
155
|
+
Regexp.new(correction_key, Regexp::IGNORECASE).match?(word_to_correct.downcase)
|
156
|
+
end
|
136
157
|
|
137
158
|
correction || {}
|
138
159
|
end
|
139
160
|
|
140
161
|
def create_message(non_inclusive_word)
|
141
162
|
correction = correction_for_word(non_inclusive_word)
|
163
|
+
suggestions = correction.fetch('suggestions') { [] }.join(', ')
|
142
164
|
|
143
165
|
format(
|
144
|
-
|
166
|
+
suggestions.present? ? FULL_FLAG_MSG : FLAG_ONLY_MSG,
|
145
167
|
non_inclusive_word: non_inclusive_word,
|
146
|
-
suggestions:
|
168
|
+
suggestions: suggestions
|
147
169
|
)
|
148
170
|
end
|
149
171
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: inclusive-code
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.5
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Flexport Engineering
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-
|
11
|
+
date: 2021-05-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -55,7 +55,8 @@ files:
|
|
55
55
|
homepage: https://github.com/flexport/rubocop-flexport
|
56
56
|
licenses:
|
57
57
|
- MIT
|
58
|
-
metadata:
|
58
|
+
metadata:
|
59
|
+
source_code_uri: https://github.com/flexport/inclusive-code
|
59
60
|
post_install_message:
|
60
61
|
rdoc_options: []
|
61
62
|
require_paths:
|
@@ -71,8 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
71
72
|
- !ruby/object:Gem::Version
|
72
73
|
version: '0'
|
73
74
|
requirements: []
|
74
|
-
|
75
|
-
rubygems_version: 2.7.7
|
75
|
+
rubygems_version: 3.1.4
|
76
76
|
signing_key:
|
77
77
|
specification_version: 4
|
78
78
|
summary: Inclusive Language RuboCop.
|