betterlint 1.13.0 → 1.15.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: 5c4835d2317f75072a423ec551933a1eaccccf5e9e625f98973a0c7750d9a8a8
4
- data.tar.gz: c40e7571c5cab59a0fbe006b8cd8dc8117bc16010ec9a0a7c33798e62c550f4a
3
+ metadata.gz: 834ddcd8893844db2ccbff0a1c0379f40e47f12fbbff9859831c342df1266d34
4
+ data.tar.gz: 8b8b7c4d49d7a961f042fc6941f6f55085b77a502441eb1c6ba7dfb8aa1af9cc
5
5
  SHA512:
6
- metadata.gz: e1fd9a6bfdcbc7fa683bee7200f6ff8d097b655f18757023f011ddffd9205d33b93649d721825f209e106f98bc2a1e1e32632e8ee98ab3bf4e26e62db875410b
7
- data.tar.gz: 781b01893ca3ad33f4b64842fc48d27577cc0d16401d29f135d0f596a570ece65f6e37dc7aeb06f45ccd50fea846d9266a8eeb1ed199d99daff62f3a30fd1f42
6
+ metadata.gz: d1db7904622a80b0271e05f33f36c4667f720a9fd13253422dba34c70ecd2ef0f6973255d5506475cd942e3ab72b353a61121da4ed4fa871d2b546228881324f
7
+ data.tar.gz: 3cf27adfc7561156c5a11a5a54c58b1bb471e5e944aed955220aaa8c383bf0e155eeb56aa514d537da317e08943ac9b330bb510d8fcf85cc8f6bbe6d7dcbc1dc
data/README.md CHANGED
@@ -101,6 +101,15 @@ Betterment/UnscopedFind:
101
101
  - ZipCode
102
102
  ```
103
103
 
104
+ ### Betterment/DirectDelayedEnqueue
105
+
106
+ This cop flags code that uses `Object#delay` or `Delayed::Job.enqueue`. Please use `ActiveJob` instead.
107
+
108
+ ```ruby
109
+ user.delay.save!
110
+ Delayed::Job.enqueue(MyJob.new)
111
+ ```
112
+
104
113
  ### Betterment/DynamicParams
105
114
 
106
115
  This cop flags code that accesses parameters whose names may be dynamically generated, such as a list of parameters in an a global variable or a return value from a method. In some cases, dynamically accessing parameter names can obscure what the client is expected to send and may make it difficult to reason about the code, both manually and programmatically. For example:
@@ -121,6 +130,50 @@ end
121
130
  ```
122
131
 
123
132
  All three `params.permit` calls will be flagged.
133
+ ### Betterment/InternalsProtection
134
+
135
+ This cop is not enabled by default, and must be enabled from your `.rubocop.yml` file:
136
+
137
+ ```yaml
138
+ Betterment/InternalsProtection:
139
+ Enabled: true
140
+ ```
141
+
142
+ This cop enforces constants defined within `Internals` modules from being referenced from other modules.
143
+
144
+ For example, if you have a `Widget` ActiveRecord class that you don't want to be used directly,
145
+ you can place it into an Internals module such as:
146
+ ```ruby
147
+ class Widgets::Internals::Widget < ApplicationRecord
148
+ end
149
+ ```
150
+
151
+ Code outside of the `Widgets` module will not be allowed to reference Widget:
152
+ ```ruby
153
+ class WidgetsController < ApplicationController
154
+ def create
155
+ Widgets::Internals::Widget.create!
156
+ ^^^^^^^^^^^^^^^^^^ Internal constants may only be referenced from code within its containing module. [...]
157
+ end
158
+ end
159
+ ```
160
+
161
+ Non-Internal code within the module can be used to manage access.
162
+ ```ruby
163
+ class Widgets::WidgetCreation
164
+ def save!
165
+ Widgets::Internals::Widget.create!
166
+ end
167
+ end
168
+
169
+ class WidgetsController < ApplicationController
170
+ def create
171
+ Widgets::WidgetCreation.new.save!
172
+ end
173
+ end
174
+ ```
175
+
176
+ This cop inspects all direct constant references, and the `class_name` attribute on ActiveRecord associations.
124
177
 
125
178
  ### Betterment/UnsafeJob
126
179
 
data/config/default.yml CHANGED
@@ -32,6 +32,9 @@ Betterment/AuthorizationInController:
32
32
  Enabled: false
33
33
  StyleGuide: '#bettermentauthorizationincontroller'
34
34
 
35
+ Betterment/DirectDelayedEnqueue:
36
+ StyleGuide: '#bettermentdirectdelayedenqueue'
37
+
35
38
  Betterment/DynamicParams:
36
39
  StyleGuide: '#bettermentdynamicparams'
37
40
 
@@ -98,15 +101,43 @@ FactoryBot/ConsistentParenthesesStyle:
98
101
  FactoryBot/SyntaxMethods:
99
102
  Enabled: false
100
103
 
104
+ Layout/ArgumentAlignment:
105
+ EnforcedStyle: with_fixed_indentation
106
+
107
+ Layout/ArrayAlignment:
108
+ EnforcedStyle: with_fixed_indentation
109
+
101
110
  Layout/CaseIndentation:
102
- IndentOneStep: true
111
+ EnforcedStyle: end
112
+ IndentOneStep: false
103
113
 
104
114
  Layout/ClosingParenthesisIndentation:
105
115
  Enabled: true
106
116
 
117
+ Layout/EndAlignment:
118
+ EnforcedStyleAlignWith: variable
119
+
120
+ Layout/FirstArgumentIndentation:
121
+ EnforcedStyle: consistent
122
+
107
123
  Layout/FirstArrayElementIndentation:
108
124
  EnforcedStyle: consistent
109
125
 
126
+ Layout/FirstHashElementIndentation:
127
+ EnforcedStyle: consistent
128
+
129
+ Layout/FirstParameterIndentation:
130
+ Enabled: false
131
+
132
+ Layout/LineContinuationLeadingSpace:
133
+ Enabled: false
134
+
135
+ Layout/LineContinuationSpacing:
136
+ Enabled: true
137
+
138
+ Layout/LineEndStringConcatenationIndentation:
139
+ Enabled: false
140
+
110
141
  Layout/LineLength:
111
142
  Max: 140
112
143
 
@@ -117,11 +148,11 @@ Layout/MultilineOperationIndentation:
117
148
  EnforcedStyle: indented
118
149
 
119
150
  Layout/ParameterAlignment:
120
- Enabled: false
151
+ Enabled: true
152
+ EnforcedStyle: with_fixed_indentation
121
153
 
122
- # Disabling because of a bug in rubocop: https://github.com/rubocop-hq/rubocop/issues/6918
123
154
  Layout/RescueEnsureAlignment:
124
- Enabled: false
155
+ Enabled: true
125
156
 
126
157
  Lint/AmbiguousBlockAssociation:
127
158
  Exclude:
@@ -0,0 +1,22 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Betterment
6
+ class DirectDelayedEnqueue < Base
7
+ DELAY_MESSAGE = 'Please use Active Job instead of using `Object#delay`'
8
+ ENQUEUE_MESSAGE = 'Please use Active Job instead of using `Delayed::Job.enqueue`'
9
+
10
+ # @!method enqueue?(node)
11
+ def_node_matcher :enqueue?, <<-PATTERN
12
+ (send (const (const nil? :Delayed) :Job) :enqueue ...)
13
+ PATTERN
14
+
15
+ def on_send(node)
16
+ add_offense(node, message: DELAY_MESSAGE) if node.method?(:delay)
17
+ add_offense(node, message: ENQUEUE_MESSAGE) if enqueue?(node)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,85 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RuboCop
4
+ module Cop
5
+ module Betterment
6
+ class InternalsProtection < Base
7
+ MSG = <<~END.gsub(/\s+/, " ")
8
+ Internal constants may only be referenced from code within its containing module.
9
+ Constants defined within a module's Internals submodule may only be referenced by code in that module,
10
+ or nested classes and modules
11
+ (e.g. MyModule::Internals::MyClass may only be referenced from code in MyModule or MyModule::MyPublicClass).
12
+ END
13
+
14
+ # @!method association_with_class_name(node)
15
+ def_node_matcher :association_with_class_name, <<-PATTERN
16
+ (send nil? {:has_many :has_one :belongs_to} ... (hash <(pair (sym :class_name) ${str}) ...>))
17
+ PATTERN
18
+
19
+ # @!method rspec_describe(node)
20
+ def_node_matcher :rspec_describe, <<-PATTERN
21
+ (block (send (const nil? :RSpec) :describe ${const | str}) ...)
22
+ PATTERN
23
+
24
+ def on_const(node)
25
+ if node.children[1] == :Internals
26
+ module_path = const_path(node)
27
+
28
+ ensure_allowed_reference!(node, module_path)
29
+ end
30
+ end
31
+
32
+ def on_send(node)
33
+ class_name_node = association_with_class_name(node)
34
+ return unless class_name_node
35
+
36
+ full_path = string_path(class_name_node)
37
+ internals_index = full_path.find_index(:Internals)
38
+ if internals_index
39
+ module_path = full_path.take(internals_index)
40
+ ensure_allowed_reference!(class_name_node, module_path)
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def ensure_allowed_reference!(node, module_path)
47
+ return if module_path.empty?
48
+
49
+ unless definition_context_path(node).each_cons(module_path.size).any?(module_path)
50
+ add_offense(node)
51
+ end
52
+ end
53
+
54
+ def const_path(const_node)
55
+ const_node.each_descendant(:const, :cbase).map { |n| n.children[1] }.reverse
56
+ end
57
+
58
+ def string_path(string_node)
59
+ string_node.children[0].split('::').map { |name| name == '' ? nil : name.to_sym }
60
+ end
61
+
62
+ def definition_context_path(node)
63
+ rspec_context_path(node) || module_class_definition_context_path(node)
64
+ end
65
+
66
+ def module_class_definition_context_path(node)
67
+ node.each_ancestor(:class, :module).flat_map { |anc|
68
+ anc.children[0].each_node(:const, :cbase).map { |c| c.children[1] }
69
+ }.push(nil).reverse
70
+ end
71
+
72
+ def rspec_context_path(node)
73
+ rspec_described_class = node.each_ancestor(:block).filter_map { |ancestor|
74
+ rspec_describe(ancestor)
75
+ }.first
76
+ case rspec_described_class&.type
77
+ when :const then const_path(rspec_described_class)
78
+ when :str then string_path(rspec_described_class)
79
+ else nil # rubocop:disable Style/EmptyElse
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -53,10 +53,10 @@ module RuboCop
53
53
  if route
54
54
  (path, param, value) = route
55
55
  action = case param
56
- when :to then value.first.split('#').last
57
- when :action then value.first
58
- else path
59
- end
56
+ when :to then value.first.split('#').last
57
+ when :action then value.first
58
+ else path
59
+ end
60
60
  add_offense(node, message: MSG_ROUTE_TO) unless allowed_action?(action)
61
61
  true
62
62
  end
@@ -26,10 +26,10 @@ module RuboCop
26
26
 
27
27
  def infer_status(responder)
28
28
  case extract_template(responder).to_s
29
- when 'new', 'edit'
30
- :unprocessable_entity
31
- else
32
- :ok
29
+ when 'new', 'edit'
30
+ :unprocessable_entity
31
+ else
32
+ :ok
33
33
  end
34
34
  end
35
35
 
@@ -39,25 +39,25 @@ module RuboCop
39
39
  return [node] if node.literal? || node.variable?
40
40
 
41
41
  case node.type
42
- when :begin
43
- get_return_values(node.children.last)
44
- when :block
45
- get_return_values(node.body)
46
- when :if
47
- if_rets = get_return_values(node.if_branch)
48
- else_rets = get_return_values(node.else_branch)
49
- if_rets + else_rets
50
- when :case
51
- cases = []
52
- node.each_when do |block|
53
- cases += get_return_values(block.body)
54
- end
42
+ when :begin
43
+ get_return_values(node.children.last)
44
+ when :block
45
+ get_return_values(node.body)
46
+ when :if
47
+ if_rets = get_return_values(node.if_branch)
48
+ else_rets = get_return_values(node.else_branch)
49
+ if_rets + else_rets
50
+ when :case
51
+ cases = []
52
+ node.each_when do |block|
53
+ cases += get_return_values(block.body)
54
+ end
55
55
 
56
- cases + get_return_values(node.else_branch)
57
- when :send
58
- [node]
59
- else
60
- []
56
+ cases + get_return_values(node.else_branch)
57
+ when :send
58
+ [node]
59
+ else
60
+ []
61
61
  end
62
62
  end
63
63
 
@@ -5,10 +5,12 @@ require 'rubocop/cop/betterment/utils/parser'
5
5
  require 'rubocop/cop/betterment/utils/method_return_table'
6
6
  require 'rubocop/cop/betterment/utils/hardcoded_attribute'
7
7
  require 'rubocop/cop/betterment/authorization_in_controller'
8
+ require 'rubocop/cop/betterment/direct_delayed_enqueue'
8
9
  require 'rubocop/cop/betterment/dynamic_params'
9
10
  require 'rubocop/cop/betterment/unscoped_find'
10
11
  require 'rubocop/cop/betterment/unsafe_job'
11
12
  require 'rubocop/cop/betterment/timeout'
13
+ require 'rubocop/cop/betterment/internals_protection'
12
14
  require 'rubocop/cop/betterment/memoization_with_arguments'
13
15
  require 'rubocop/cop/betterment/non_standard_actions'
14
16
  require 'rubocop/cop/betterment/site_prism_loaded'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: betterlint
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.13.0
4
+ version: 1.15.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Development
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2024-07-10 00:00:00.000000000 Z
11
+ date: 2024-11-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -108,10 +108,12 @@ files:
108
108
  - lib/rubocop/cop/betterment/active_job_performable.rb
109
109
  - lib/rubocop/cop/betterment/allowlist_blocklist.rb
110
110
  - lib/rubocop/cop/betterment/authorization_in_controller.rb
111
+ - lib/rubocop/cop/betterment/direct_delayed_enqueue.rb
111
112
  - lib/rubocop/cop/betterment/dynamic_params.rb
112
113
  - lib/rubocop/cop/betterment/fetch_boolean.rb
113
114
  - lib/rubocop/cop/betterment/hardcoded_id.rb
114
115
  - lib/rubocop/cop/betterment/implicit_redirect_type.rb
116
+ - lib/rubocop/cop/betterment/internals_protection.rb
115
117
  - lib/rubocop/cop/betterment/memoization_with_arguments.rb
116
118
  - lib/rubocop/cop/betterment/non_standard_actions.rb
117
119
  - lib/rubocop/cop/betterment/redirect_status.rb
@@ -132,10 +134,10 @@ licenses:
132
134
  - MIT
133
135
  metadata:
134
136
  homepage_uri: https://github.com/Betterment/betterlint
135
- source_code_uri: https://github.com/Betterment/betterlint/tree/v1.13.0
136
- changelog_uri: https://github.com/Betterment/betterlint/blob/v1.13.0/CHANGELOG.md
137
+ source_code_uri: https://github.com/Betterment/betterlint/tree/v1.15.0
138
+ changelog_uri: https://github.com/Betterment/betterlint/blob/v1.15.0/CHANGELOG.md
137
139
  bug_tracker_uri: https://github.com/Betterment/betterlint/issues
138
- documentation_uri: https://www.rubydoc.info/gems/betterlint/1.13.0
140
+ documentation_uri: https://www.rubydoc.info/gems/betterlint/1.15.0
139
141
  rubygems_mfa_required: 'true'
140
142
  post_install_message:
141
143
  rdoc_options: []
@@ -152,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
152
154
  - !ruby/object:Gem::Version
153
155
  version: '0'
154
156
  requirements: []
155
- rubygems_version: 3.5.14
157
+ rubygems_version: 3.5.21
156
158
  signing_key:
157
159
  specification_version: 4
158
160
  summary: Betterment rubocop configuration