rubocop-mdsol 0.2.0 → 0.3.0

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: 92834a11847f8ca55b8c2f90149fce6a66bf68ab3786a4d9b4f85b3230ccd63f
4
- data.tar.gz: 602685a360aad1801893619d31b192094530807727545d876c0365c78e6a52d5
3
+ metadata.gz: 9ea1fee9117692bb17d96ebfbf59955dbfd329010bf8802a65d3f45b89f238f5
4
+ data.tar.gz: caabfe16c4b05113edec4ace5265e7d00dd70c539e66a6a0ef4cf50e0e31bb8d
5
5
  SHA512:
6
- metadata.gz: 20b149d79d773a80530631cf32e9b1e42eb39dbc65cea2ef3e43cb6695effedb1e187a94d6df0e8e81c75d754c022240cd4c58cd68ab23dacf1da08ff49b1b28
7
- data.tar.gz: d5d84bfd222b879e17c71a8557b4e8b3f75921d33215138555f2d74fae4068c143daec2ed2449529263920ab49caeb5a03535d9994d77a425cb758a25e6a16df
6
+ metadata.gz: 1f0ddcd460d42ad310c8dd794eba171fa5895ecd6778def44faf6d4cf9dbe53d2aad2561cb3664deb52d16b08abe0be7881c6a8feb3a8a9b4fe328029bdeac0f
7
+ data.tar.gz: ea26682188a2187fcbb4760a6d32d70215f625e5e38c753220ad6daefeea9412c56b79b663dba36c95fb30067dbbcd0b82dc21f5a5b01917698f067e52c4b5bb
data/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ # 0.3.0
2
+ - Add custom cops: [`mdsol/log_with_data`](lib/rubocop/cop/mdsol/log_with_data.rb) and [`mdsol/unless_not_equal`](lib/rubocop/cop/mdsol/unless_not_equal.rb).
3
+ - Drop Ruby 2.4, 2.5, and 2.6 support.
4
+
1
5
  # 0.2.0
2
6
  - Add rubocop-rspec.yml.
3
7
 
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
data/README.md CHANGED
@@ -60,6 +60,23 @@ inherit_gem:
60
60
  # your customizations here...
61
61
  ```
62
62
 
63
+ ### Custom Cops
64
+
65
+ To activate the custom cops (`mdsol/*`) shipped with this Gem:
66
+
67
+ ```yaml
68
+ require: rubocop-mdsol
69
+
70
+ # or using the array notation to specify multiple extensions:
71
+
72
+ require:
73
+ - rubocop-mdsol
74
+ - rubocop-rails
75
+ ```
76
+
77
+ All cops are located under
78
+ [`lib/rubocop/cop/mdsol`](lib/rubocop/cop/mdsol) and contain examples/documentation.
79
+
63
80
 
64
81
  ## Recommended customizations
65
82
 
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ task default: %i[]
5
+
6
+ require "rspec/core/rake_task"
7
+
8
+ RSpec::Core::RakeTask.new(:spec) do |spec|
9
+ spec.pattern = FileList["spec/**/*_spec.rb"]
10
+ end
11
+
12
+ desc "Generate a new cop with a template"
13
+ task :new_cop, [:cop] do |_task, args|
14
+ require "rubocop"
15
+
16
+ cop_name = args.fetch(:cop) do
17
+ warn "usage: bundle exec rake new_cop[Department/Name]"
18
+ exit!
19
+ end
20
+
21
+ generator = RuboCop::Cop::Generator.new(cop_name)
22
+
23
+ generator.write_source
24
+ generator.write_spec
25
+ generator.inject_require(root_file_path: "lib/rubocop/cop/mdsol_cops.rb")
26
+ generator.inject_config(config_file_path: "config/default.yml")
27
+
28
+ puts generator.todo
29
+ end
@@ -0,0 +1,9 @@
1
+ Mdsol/LogWithData:
2
+ Description: Favor the `logger.<level>_with_data` method for structured logging.
3
+ Enabled: true
4
+ VersionAdded: 0.3.0
5
+
6
+ Mdsol/UnlessNotEqual:
7
+ Description: Avoid double negative condition.
8
+ Enabled: true
9
+ VersionAdded: 0.3.0
@@ -0,0 +1,81 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Mdsol
6
+ # Favor the logger.<level>_with_data method for structured logging.
7
+ #
8
+ # @example
9
+ # # bad - string interpolations exceed the threshold (configurable via AllowedStringInterpolations setting)
10
+ # Rails.logger.info("a message with more than 2 interpolations: #{foo} #{bar} #{baz}")
11
+ #
12
+ # # bad - string interpolation contains Enumerable methods (:map, :collect)
13
+ # Rails.logger.info("Deleted the following records: #{records.map(&:id)}")
14
+ #
15
+ # # good
16
+ # Rails.logger.info("a message")
17
+ #
18
+ # # good - string interpolations are within the threshold (configurable via AllowedStringInterpolations setting)
19
+ # Rails.logger.info("a message with 2 interpolations: #{foo} #{bar}")
20
+ #
21
+ # # good
22
+ # Rails.logger.info_with_data("Created the record", record_id: record.id, status: status)
23
+ #
24
+ # # good
25
+ # Rails.logger.info_with_data("Deleted the following records", record_ids: results.records.map(&:id))
26
+ class LogWithData < Base
27
+ MSG = "Use `logger.%<method>s_with_data(msg, data_hash)` instead. See https://github.com/mdsol/astinus#using-context-data-logging"
28
+
29
+ RESTRICT_ON_SEND = %i[logger debug info warn error].freeze
30
+ FORBIDDEN_ENUMERABLE_METHODS = %i[map collect].freeze
31
+ DEFAULT_STRING_INTERPOLATION_THRESHOLD = 2
32
+
33
+ # AST for code: `Rails.logger.debug("study creation request #{request} was status #{status}")`
34
+ # (send
35
+ # (send
36
+ # (const nil :Rails) :logger) :debug
37
+ # (dstr
38
+ # (str "study creation request ")
39
+ # (begin
40
+ # (send nil :request))
41
+ # (str " was status ")
42
+ # (begin
43
+ # (send nil :status))))
44
+ def_node_matcher :logger_with_interpolation, <<~PATTERN
45
+ (send (send (const nil? $_) :logger) $_ $(dstr ...))
46
+ PATTERN
47
+
48
+ def on_send(node)
49
+ logger_with_interpolation(node) do |logger_owner, log_method, log_message|
50
+ return unless registered_logger_owners.include?(logger_owner.to_s)
51
+
52
+ if interpoloations_exceed_threshold?(log_message) || contain_forbidden_enumerable_method?(log_message)
53
+ msg = format(MSG, method: log_method.to_s)
54
+ add_offense(node, message: msg)
55
+ end
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ def registered_logger_owners
62
+ @registered_logger_owners ||= (["Rails"] + cop_config["LoggerOwners"].to_a).uniq
63
+ end
64
+
65
+ def interpoloations_exceed_threshold?(log_message_node)
66
+ log_message_node.child_nodes.count { |node| node.type == :begin } > string_interpolations_threshold
67
+ end
68
+
69
+ def string_interpolations_threshold
70
+ cop_config["AllowedStringInterpolations"] || DEFAULT_STRING_INTERPOLATION_THRESHOLD
71
+ end
72
+
73
+ def contain_forbidden_enumerable_method?(log_message_node)
74
+ log_message_node.each_descendant.any? do |descedant|
75
+ descedant.send_type? && FORBIDDEN_ENUMERABLE_METHODS.include?(descedant.method_name)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Mdsol
6
+ # Avoid double negative condition.
7
+ #
8
+ # @example
9
+ # # bad
10
+ # do_something unless a != b
11
+ #
12
+ # # good
13
+ # do_something if a == b
14
+ #
15
+ # Credit to Warut: https://github.com/mdsol/tenjin/pull/621#discussion_r748106992
16
+ class UnlessNotEqual < Base
17
+ extend AutoCorrector
18
+
19
+ MSG = "Avoid double negative. Write `%<correction>s` instead."
20
+
21
+ # AST for: `do_something unless a != b`
22
+ # (if
23
+ # (send
24
+ # (send nil :a) :!=
25
+ # (send nil :b)) nil
26
+ # (send nil :do_something))
27
+ def_node_matcher :unless_not_equal_match, <<~PATTERN
28
+ (if $(send $(...) :!= $(...)) ...)
29
+ PATTERN
30
+
31
+ def on_if(node)
32
+ return if node.ternary? || node.if? || node.elsif?
33
+
34
+ unless_not_equal_match(node) do |unless_body_node, left, right|
35
+ correction = "if #{left.source} == #{right.source}"
36
+ msg = format(MSG, correction: correction)
37
+ range = range_with_unless_modifier(unless_body_node)
38
+
39
+ add_offense(range, message: msg) do |corrector|
40
+ corrector.replace(range, correction)
41
+ end
42
+ end
43
+ end
44
+
45
+ private
46
+
47
+ def range_with_unless_modifier(unless_body_node)
48
+ leading_unless = "unless "
49
+ expand_range_to_left_by(unless_body_node, leading_unless.size)
50
+ end
51
+
52
+ def expand_range_to_left_by(node, length)
53
+ source_range = node.source_range
54
+ source_buffer = source_range.source_buffer
55
+ Parser::Source::Range.new(source_buffer, source_range.begin_pos - length, source_range.end_pos)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,2 @@
1
+ require_relative "mdsol/log_with_data"
2
+ require_relative "mdsol/unless_not_equal"
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ # The original code is from https://github.com/rubocop/rubocop-rspec/blob/master/lib/rubocop/rspec/inject.rb
4
+ # See https://github.com/rubocop/rubocop-rspec/blob/master/MIT-LICENSE.md
5
+ module RuboCop
6
+ module Mdsol
7
+ # Because RuboCop doesn't yet support plugins, we have to monkey patch in a
8
+ # bit of our configuration.
9
+ module Inject
10
+ def self.defaults!
11
+ path = CONFIG_DEFAULT.to_s
12
+ hash = ConfigLoader.send(:load_yaml_configuration, path)
13
+ config = Config.new(hash, path).tap(&:make_excludes_absolute)
14
+ puts "configuration from #{path}" if ConfigLoader.debug?
15
+ config = ConfigLoader.merge_with_default(config, path)
16
+ ConfigLoader.instance_variable_set(:@default_configuration, config)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Mdsol
5
+ VERSION = "0.3.0"
6
+ end
7
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "mdsol/version"
4
+
5
+ module RuboCop
6
+ module Mdsol
7
+ class Error < StandardError; end
8
+ # Your code goes here...
9
+ PROJECT_ROOT = Pathname.new(__dir__).parent.parent.expand_path.freeze
10
+ CONFIG_DEFAULT = PROJECT_ROOT.join("config", "default.yml").freeze
11
+ CONFIG = YAML.safe_load(CONFIG_DEFAULT.read).freeze
12
+
13
+ private_constant(:CONFIG_DEFAULT, :PROJECT_ROOT)
14
+ end
15
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rubocop"
4
+
5
+ require_relative "rubocop/mdsol"
6
+ require_relative "rubocop/mdsol/version"
7
+ require_relative "rubocop/mdsol/inject"
8
+
9
+ RuboCop::Mdsol::Inject.defaults!
10
+
11
+ require_relative "rubocop/cop/mdsol_cops"
@@ -1,8 +1,10 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative "lib/rubocop/mdsol/version"
4
+
3
5
  Gem::Specification.new do |spec|
4
6
  spec.name = "rubocop-mdsol"
5
- spec.version = "0.2.0"
7
+ spec.version = RuboCop::Mdsol::VERSION
6
8
  spec.authors = ["Team Æ", "Team 10"]
7
9
  spec.email = ["ae@mdsol.com", "team10@mdsol.com"]
8
10
  spec.license = "MIT"
@@ -13,17 +15,15 @@ Gem::Specification.new do |spec|
13
15
  "changelog_uri" => "https://github.com/mdsol/rubocop-mdsol/blob/develop/CHANGELOG.md"
14
16
  }
15
17
 
16
- spec.files = Dir[
17
- "CHANGELOG.md",
18
- "MIT-LICENSE",
19
- "README.md",
20
- "rubocop-mdsol.gemspec",
21
- "rubocop-rails.yml",
22
- "rubocop-rspec.yml",
23
- "rubocop.yml"
24
- ]
18
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
19
+ f.match(/^(\.|bin|spec)/)
20
+ end
25
21
 
26
- spec.required_ruby_version = ">= 2.4.0"
22
+ spec.required_ruby_version = ">= 2.7.0"
23
+ spec.require_paths = ["lib"]
27
24
 
28
25
  spec.add_dependency "rubocop", "~> 1.0"
26
+ spec.add_development_dependency "rake", "~> 13.0"
27
+ spec.add_development_dependency "rspec", "~> 3.11"
28
+ spec.add_development_dependency "rubocop-rspec", "~> 2.11"
29
29
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rubocop-mdsol
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Team Æ
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2022-04-27 00:00:00.000000000 Z
12
+ date: 2022-06-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rubocop
@@ -25,6 +25,48 @@ dependencies:
25
25
  - - "~>"
26
26
  - !ruby/object:Gem::Version
27
27
  version: '1.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: '13.0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: '13.0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: rspec
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '3.11'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '3.11'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rubocop-rspec
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '2.11'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '2.11'
28
70
  description:
29
71
  email:
30
72
  - ae@mdsol.com
@@ -34,8 +76,18 @@ extensions: []
34
76
  extra_rdoc_files: []
35
77
  files:
36
78
  - CHANGELOG.md
79
+ - Gemfile
37
80
  - MIT-LICENSE
38
81
  - README.md
82
+ - Rakefile
83
+ - config/default.yml
84
+ - lib/rubocop-mdsol.rb
85
+ - lib/rubocop/cop/mdsol/log_with_data.rb
86
+ - lib/rubocop/cop/mdsol/unless_not_equal.rb
87
+ - lib/rubocop/cop/mdsol_cops.rb
88
+ - lib/rubocop/mdsol.rb
89
+ - lib/rubocop/mdsol/inject.rb
90
+ - lib/rubocop/mdsol/version.rb
39
91
  - rubocop-mdsol.gemspec
40
92
  - rubocop-rails.yml
41
93
  - rubocop-rspec.yml
@@ -54,14 +106,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
54
106
  requirements:
55
107
  - - ">="
56
108
  - !ruby/object:Gem::Version
57
- version: 2.4.0
109
+ version: 2.7.0
58
110
  required_rubygems_version: !ruby/object:Gem::Requirement
59
111
  requirements:
60
112
  - - ">="
61
113
  - !ruby/object:Gem::Version
62
114
  version: '0'
63
115
  requirements: []
64
- rubygems_version: 3.1.2
116
+ rubygems_version: 3.3.15
65
117
  signing_key:
66
118
  specification_version: 4
67
119
  summary: Base RuboCop configuration files for Ruby projects at Medidata