rubocop-mdsol 0.2.0 → 0.3.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: 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