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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f0e9a66b10fa6c3d82c8faa54ddacb5953682d660b3751567aec4af6e6a1d945
4
- data.tar.gz: cdb7d1ec5ceaa41f4fa134c449a463251c59560a2d4ae6e8fe87d4e46b7acc6d
3
+ metadata.gz: 7fc5f8f716dd932818068d5b282bc52d1f7e93ad38eec1f16e100076347c42f8
4
+ data.tar.gz: 372a8b68115654589caf6ec9a6c03e94c96d5f2cced613065c15b0454214076a
5
5
  SHA512:
6
- metadata.gz: 87f0191b289b92d8b3241b05295d12b661fda42aa671519471b2bb494ed6ca3853d8b4a3aee662c8ba6828693fc50f4a219d60b8d6552069585571dacd99fd5d
7
- data.tar.gz: 1782766b518b793d0e702a2977c499f672a4ea1bbfc5009f1efbb628e3458ac536469265b95e9b705cdf6fdf64ddccab27319ee2ce07572ebb98f8517254778a
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'.freeze
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
- MSG = '🚫 Use of non_inclusive word: `%<non_inclusive_word>s`. Consider using these suggested alternatives: `%<suggestions>s`.'.freeze
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 = Regexp.new(
37
- @all_non_inclusive_words.join('|'),
38
- Regexp::IGNORECASE
39
- )
40
- @allowed = @all_non_inclusive_words.collect do |word|
41
- [
42
- word,
43
- get_allowed_string(word)
44
- ]
45
- end.to_h
46
- @allowed_regex = Regexp.new(@allowed.values.reject(&:blank?).join('|'), Regexp::IGNORECASE)
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
- @all_non_inclusive_words.each do |non_inclusive_word|
54
- allowed = @allowed[non_inclusive_word]
55
- scan_regex = if allowed.blank?
56
- /(?=#{non_inclusive_word})/i
57
- else
58
- /(?=#{non_inclusive_word})(?!(#{@allowed[non_inclusive_word]}))/i
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 /#{non_inclusive_word}/i
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(@non_inclusive_words_regex)
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]['allowed']
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 {|correction_key, _| Regexp.new(correction_key, Regexp::IGNORECASE).match? word_to_correct.downcase }
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
- MSG,
166
+ suggestions.present? ? FULL_FLAG_MSG : FLAG_ONLY_MSG,
145
167
  non_inclusive_word: non_inclusive_word,
146
- suggestions: correction.fetch('suggestions') {[]}.join(', ')
168
+ suggestions: suggestions
147
169
  )
148
170
  end
149
171
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module RuboCop
4
4
  module InclusiveCode
5
- VERSION = '0.1.4'
5
+ VERSION = '0.1.5'
6
6
  end
7
7
  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
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-04-22 00:00:00.000000000 Z
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
- rubyforge_project:
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.