betterlint 1.14.0 → 1.15.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: eba1c8a88e6cefd6fc8db168dfe4b9f41f30989c4716fdc0338e99614a40315d
4
- data.tar.gz: 9b176cbf47cf1f1d65547624b44531dcb0328832a1dfc62c12b3f33c0222f1e3
3
+ metadata.gz: 834ddcd8893844db2ccbff0a1c0379f40e47f12fbbff9859831c342df1266d34
4
+ data.tar.gz: 8b8b7c4d49d7a961f042fc6941f6f55085b77a502441eb1c6ba7dfb8aa1af9cc
5
5
  SHA512:
6
- metadata.gz: 00b1ed1bdec96493c6c9332228a2cff4a1134926c27d72ea0c13439f454af5668258a1722ef53520ffa2786472c59b1c09e12ee5abdce8e7fdc1d1d91ecb09df
7
- data.tar.gz: 7d159d6ee74e8a6f5b30f3e922e9d13ae1d5a26c779c12df0750daad6e82b68d2caecda1a8e473c7b983af45d96aca3635239906d03592e3e790fbf09952dec5
6
+ metadata.gz: d1db7904622a80b0271e05f33f36c4667f720a9fd13253422dba34c70ecd2ef0f6973255d5506475cd942e3ab72b353a61121da4ed4fa871d2b546228881324f
7
+ data.tar.gz: 3cf27adfc7561156c5a11a5a54c58b1bb471e5e944aed955220aaa8c383bf0e155eeb56aa514d537da317e08943ac9b330bb510d8fcf85cc8f6bbe6d7dcbc1dc
data/README.md CHANGED
@@ -130,6 +130,50 @@ end
130
130
  ```
131
131
 
132
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.
133
177
 
134
178
  ### Betterment/UnsafeJob
135
179
 
@@ -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
@@ -10,6 +10,7 @@ require 'rubocop/cop/betterment/dynamic_params'
10
10
  require 'rubocop/cop/betterment/unscoped_find'
11
11
  require 'rubocop/cop/betterment/unsafe_job'
12
12
  require 'rubocop/cop/betterment/timeout'
13
+ require 'rubocop/cop/betterment/internals_protection'
13
14
  require 'rubocop/cop/betterment/memoization_with_arguments'
14
15
  require 'rubocop/cop/betterment/non_standard_actions'
15
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.14.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-09-26 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
@@ -113,6 +113,7 @@ files:
113
113
  - lib/rubocop/cop/betterment/fetch_boolean.rb
114
114
  - lib/rubocop/cop/betterment/hardcoded_id.rb
115
115
  - lib/rubocop/cop/betterment/implicit_redirect_type.rb
116
+ - lib/rubocop/cop/betterment/internals_protection.rb
116
117
  - lib/rubocop/cop/betterment/memoization_with_arguments.rb
117
118
  - lib/rubocop/cop/betterment/non_standard_actions.rb
118
119
  - lib/rubocop/cop/betterment/redirect_status.rb
@@ -133,10 +134,10 @@ licenses:
133
134
  - MIT
134
135
  metadata:
135
136
  homepage_uri: https://github.com/Betterment/betterlint
136
- source_code_uri: https://github.com/Betterment/betterlint/tree/v1.14.0
137
- changelog_uri: https://github.com/Betterment/betterlint/blob/v1.14.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
138
139
  bug_tracker_uri: https://github.com/Betterment/betterlint/issues
139
- documentation_uri: https://www.rubydoc.info/gems/betterlint/1.14.0
140
+ documentation_uri: https://www.rubydoc.info/gems/betterlint/1.15.0
140
141
  rubygems_mfa_required: 'true'
141
142
  post_install_message:
142
143
  rdoc_options: []
@@ -153,7 +154,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
153
154
  - !ruby/object:Gem::Version
154
155
  version: '0'
155
156
  requirements: []
156
- rubygems_version: 3.5.18
157
+ rubygems_version: 3.5.21
157
158
  signing_key:
158
159
  specification_version: 4
159
160
  summary: Betterment rubocop configuration