package_protections 1.2.0 → 2.0.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 +4 -4
- data/lib/package_protections/private/configuration.rb +10 -0
- data/lib/package_protections/private.rb +1 -28
- data/lib/package_protections/rspec/support.rb +5 -1
- data/lib/package_protections.rb +8 -12
- data/lib/rubocop/cop/package_protections/namespaced_under_package_name/desired_zeitwerk_api.rb +110 -0
- data/lib/rubocop/cop/package_protections/namespaced_under_package_name.rb +104 -87
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2e7bd9febdd0619b714c112033b8bf61958e04368e8fb0c0e5f3498de54e988c
|
4
|
+
data.tar.gz: a0f7c5f79f3eb1279d084045c115345424dd2acfa22954eed321d2cf220bd40b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b8ff41de5e829270999baa91dc8df0fb3e810c6e8be39358cdbc6fd188a9d79ddf138f19b935a13360106c2a5e6c8300edffdf5fedf71e39c52826d2a6050a9e
|
7
|
+
data.tar.gz: f255780ea5ed693e66edcd028b4b8247419c612afddd1790f53078ecec3ca274e456c6ed16e62a0d83158c853f2be25282250c5b40b93b069da504469d25acf8
|
@@ -8,9 +8,13 @@ module PackageProtections
|
|
8
8
|
sig { params(protections: T::Array[ProtectionInterface]).void }
|
9
9
|
attr_writer :protections
|
10
10
|
|
11
|
+
sig { params(globally_permitted_namespaces: T::Array[String]).void }
|
12
|
+
attr_writer :globally_permitted_namespaces
|
13
|
+
|
11
14
|
sig { void }
|
12
15
|
def initialize
|
13
16
|
@protections = T.let(default_protections, T::Array[ProtectionInterface])
|
17
|
+
@globally_permitted_namespaces = T.let([], T::Array[String])
|
14
18
|
end
|
15
19
|
|
16
20
|
sig { returns(T::Array[ProtectionInterface]) }
|
@@ -18,9 +22,15 @@ module PackageProtections
|
|
18
22
|
@protections
|
19
23
|
end
|
20
24
|
|
25
|
+
sig { returns(T::Array[String]) }
|
26
|
+
def globally_permitted_namespaces
|
27
|
+
@globally_permitted_namespaces
|
28
|
+
end
|
29
|
+
|
21
30
|
sig { void }
|
22
31
|
def bust_cache!
|
23
32
|
@protections = default_protections
|
33
|
+
@globally_permitted_namespaces = []
|
24
34
|
end
|
25
35
|
|
26
36
|
sig { returns(T::Array[ProtectionInterface]) }
|
@@ -17,12 +17,6 @@ module PackageProtections
|
|
17
17
|
module Private
|
18
18
|
extend T::Sig
|
19
19
|
|
20
|
-
sig { returns(Private::Configuration) }
|
21
|
-
def self.config
|
22
|
-
@config = T.let(@config, T.nilable(Configuration))
|
23
|
-
@config ||= Private::Configuration.new
|
24
|
-
end
|
25
|
-
|
26
20
|
sig do
|
27
21
|
params(
|
28
22
|
packages: T::Array[ParsePackwerk::Package],
|
@@ -88,27 +82,6 @@ module PackageProtections
|
|
88
82
|
end
|
89
83
|
end
|
90
84
|
|
91
|
-
sig do
|
92
|
-
params(
|
93
|
-
package_names: T::Array[String],
|
94
|
-
all_packages: T::Array[ParsePackwerk::Package]
|
95
|
-
).returns(T::Array[ParsePackwerk::Package])
|
96
|
-
end
|
97
|
-
def self.packages_for_names(package_names, all_packages)
|
98
|
-
all_packages_indexed_by_name = {}
|
99
|
-
all_packages.each { |package| all_packages_indexed_by_name[package.name] = package }
|
100
|
-
|
101
|
-
package_names.map do |package_name|
|
102
|
-
clean_pack_name = package_name.gsub(%r{/$}, '')
|
103
|
-
package = all_packages_indexed_by_name[clean_pack_name]
|
104
|
-
if package.nil?
|
105
|
-
raise "Sorry, we couldn't find a package with name #{package_name}. Here are all of the package names we know about: #{all_packages.map(&:name).sort.inspect}"
|
106
|
-
end
|
107
|
-
|
108
|
-
package
|
109
|
-
end
|
110
|
-
end
|
111
|
-
|
112
85
|
sig { params(root_pathname: Pathname).returns(String) }
|
113
86
|
def self.rubocop_yml(root_pathname:)
|
114
87
|
protected_packages = Dir.chdir(root_pathname) { all_protected_packages }
|
@@ -143,7 +116,7 @@ module PackageProtections
|
|
143
116
|
def self.bust_cache!
|
144
117
|
@protected_packages_indexed_by_name = nil
|
145
118
|
@private_cop_config = nil
|
146
|
-
config.bust_cache!
|
119
|
+
PackageProtections.config.bust_cache!
|
147
120
|
end
|
148
121
|
|
149
122
|
sig { params(identifier: Identifier).returns(T::Hash[T.untyped, T.untyped]) }
|
@@ -7,11 +7,15 @@ require_relative 'matchers'
|
|
7
7
|
|
8
8
|
def get_resulting_rubocop
|
9
9
|
write_file('config/default.yml', <<~YML.strip)
|
10
|
-
<%= PackageProtections.rubocop_yml %>
|
10
|
+
<%= PackageProtections.rubocop_yml(root_pathname: Pathname.pwd) %>
|
11
11
|
YML
|
12
12
|
YAML.safe_load(ERB.new(File.read('config/default.yml')).result(binding))
|
13
13
|
end
|
14
14
|
|
15
15
|
RSpec.configure do |config|
|
16
16
|
config.include ApplicationFixtureHelper
|
17
|
+
|
18
|
+
config.before do
|
19
|
+
PackageProtections.bust_cache!
|
20
|
+
end
|
17
21
|
end
|
data/lib/package_protections.rb
CHANGED
@@ -44,13 +44,19 @@ module PackageProtections
|
|
44
44
|
|
45
45
|
sig { params(blk: T.proc.params(arg0: Private::Configuration).void).void }
|
46
46
|
def configure(&blk)
|
47
|
-
yield(
|
47
|
+
yield(PackageProtections.config)
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
51
|
sig { returns(T::Array[ProtectionInterface]) }
|
52
52
|
def self.all
|
53
|
-
|
53
|
+
config.protections
|
54
|
+
end
|
55
|
+
|
56
|
+
sig { returns(Private::Configuration) }
|
57
|
+
def self.config
|
58
|
+
@config = T.let(@config, T.nilable(Private::Configuration))
|
59
|
+
@config ||= Private::Configuration.new
|
54
60
|
end
|
55
61
|
|
56
62
|
#
|
@@ -116,16 +122,6 @@ module PackageProtections
|
|
116
122
|
Private.private_cop_config(identifier)
|
117
123
|
end
|
118
124
|
|
119
|
-
sig do
|
120
|
-
params(
|
121
|
-
package_names: T::Array[String],
|
122
|
-
all_packages: T::Array[ParsePackwerk::Package]
|
123
|
-
).returns(T::Array[ParsePackwerk::Package])
|
124
|
-
end
|
125
|
-
def self.packages_for_names(package_names, all_packages)
|
126
|
-
Private.packages_for_names(package_names, all_packages)
|
127
|
-
end
|
128
|
-
|
129
125
|
sig { void }
|
130
126
|
def self.bust_cache!
|
131
127
|
Private.bust_cache!
|
data/lib/rubocop/cop/package_protections/namespaced_under_package_name/desired_zeitwerk_api.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
# typed: strict
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module PackageProtections
|
6
|
+
class NamespacedUnderPackageName < Base
|
7
|
+
#
|
8
|
+
# This is a private class that represents API that we would prefer to be available somehow in Zeitwerk.
|
9
|
+
#
|
10
|
+
class DesiredZeitwerkApi
|
11
|
+
extend T::Sig
|
12
|
+
|
13
|
+
class NamespaceContext < T::Struct
|
14
|
+
const :current_namespace, String
|
15
|
+
const :expected_namespace, String
|
16
|
+
const :expected_filepath, String
|
17
|
+
end
|
18
|
+
|
19
|
+
#
|
20
|
+
# For now, this API includes `package_for_path`
|
21
|
+
# If this were truly zeitwerk API, it wouldn't include any mention of packs and it would likely not need the package at all
|
22
|
+
# Since it could get the actual namespace without knowing anything about packs.
|
23
|
+
# However, we would need to pass to it the desired namespace based on the pack name for it to be able to suggest
|
24
|
+
# a desired filepath.
|
25
|
+
# Likely this means that our own cop should determine the desired namespace and pass that in
|
26
|
+
# and this can determine actual namespace and how to get to expected.
|
27
|
+
#
|
28
|
+
sig { params(relative_filename: String, package_for_path: ParsePackwerk::Package).returns(T.nilable(NamespaceContext)) }
|
29
|
+
def for_file(relative_filename, package_for_path)
|
30
|
+
package_name = package_for_path.name
|
31
|
+
|
32
|
+
# Zeitwerk establishes a standard convention by which namespaces are defined.
|
33
|
+
# The package protections namespace checker is coupled to a specific assumption about how auto-loading works.
|
34
|
+
#
|
35
|
+
# Namely, we expect the following autoload paths: `packs/**/app/**/`
|
36
|
+
# Examples:
|
37
|
+
# 1) `packs/package_1/app/public/package_1/my_constant.rb` produces constant `Package1::MyConstant`
|
38
|
+
# 2) `packs/package_1/app/services/package_1/my_service.rb` produces constant `Package1::MyService`
|
39
|
+
# 3) `packs/package_1/app/services/package_1.rb` produces constant `Package1`
|
40
|
+
# 4) `packs/package_1/app/public/package_1.rb` produces constant `Package1`
|
41
|
+
#
|
42
|
+
# Described another way, we expect any part of the directory labeled NAMESPACE to establish a portion of the fully qualified runtime constant:
|
43
|
+
# `packs/**/app/**/NAMESPACE1/NAMESPACE2/[etc]`
|
44
|
+
#
|
45
|
+
# Therefore, for our implementation, we substitute out the non-namespace producing portions of the filename to count the number of namespaces.
|
46
|
+
# Note this will *not work* properly in applications that have different assumptions about autoloading.
|
47
|
+
|
48
|
+
path_without_package_base = relative_filename.gsub(%r{#{package_name}/app/}, '')
|
49
|
+
if path_without_package_base.include?('concerns')
|
50
|
+
autoload_folder_name = path_without_package_base.split('/').first(2).join('/')
|
51
|
+
else
|
52
|
+
autoload_folder_name = path_without_package_base.split('/').first
|
53
|
+
end
|
54
|
+
|
55
|
+
remaining_file_path = path_without_package_base.gsub(%r{\A#{autoload_folder_name}/}, '')
|
56
|
+
actual_namespace = get_actual_namespace(remaining_file_path, package_name)
|
57
|
+
|
58
|
+
if relative_filename.include?('app/')
|
59
|
+
app_or_lib = 'app'
|
60
|
+
elsif relative_filename.include?('lib/')
|
61
|
+
app_or_lib = 'lib'
|
62
|
+
end
|
63
|
+
|
64
|
+
absolute_desired_path = root_pathname.join(
|
65
|
+
package_name,
|
66
|
+
T.must(app_or_lib),
|
67
|
+
T.must(autoload_folder_name),
|
68
|
+
get_package_last_name(package_for_path),
|
69
|
+
remaining_file_path
|
70
|
+
)
|
71
|
+
|
72
|
+
relative_desired_path = absolute_desired_path.relative_path_from(root_pathname)
|
73
|
+
|
74
|
+
NamespaceContext.new(
|
75
|
+
current_namespace: actual_namespace,
|
76
|
+
expected_namespace: get_pack_based_namespace(package_for_path),
|
77
|
+
expected_filepath: relative_desired_path.to_s
|
78
|
+
)
|
79
|
+
end
|
80
|
+
|
81
|
+
sig { params(pack: ParsePackwerk::Package).returns(String) }
|
82
|
+
def get_pack_based_namespace(pack)
|
83
|
+
get_package_last_name(pack).camelize
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
sig { returns(Pathname) }
|
89
|
+
def root_pathname
|
90
|
+
Pathname.pwd
|
91
|
+
end
|
92
|
+
|
93
|
+
sig { params(pack: ParsePackwerk::Package).returns(String) }
|
94
|
+
def get_package_last_name(pack)
|
95
|
+
T.must(pack.name.split('/').last)
|
96
|
+
end
|
97
|
+
|
98
|
+
sig { params(remaining_file_path: String, package_name: String).returns(String) }
|
99
|
+
def get_actual_namespace(remaining_file_path, package_name)
|
100
|
+
# If the remaining file path is a ruby file (not a directory), then it establishes a global namespace
|
101
|
+
# Otherwise, per Zeitwerk's conventions as listed above, its a directory that establishes another global namespace
|
102
|
+
T.must(remaining_file_path.split('/').first).gsub('.rb', '').camelize
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
private_constant :DesiredZeitwerkApi
|
107
|
+
end
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -1,94 +1,109 @@
|
|
1
|
-
# typed:
|
1
|
+
# typed: strict
|
2
2
|
|
3
3
|
# For String#camelize
|
4
4
|
require 'active_support/core_ext/string/inflections'
|
5
|
+
require 'rubocop/cop/package_protections/namespaced_under_package_name/desired_zeitwerk_api'
|
5
6
|
|
6
7
|
module RuboCop
|
7
8
|
module Cop
|
8
9
|
module PackageProtections
|
10
|
+
#
|
11
|
+
# TODO:
|
12
|
+
# This class is in serious need of being split up into helpful abstractions.
|
13
|
+
# A really helpful abstraction would be one that takes a file path and can spit out information about
|
14
|
+
# namespacing, such as the exposed namespace, the file path for a different namespace, and more.
|
15
|
+
#
|
9
16
|
class NamespacedUnderPackageName < Base
|
10
17
|
extend T::Sig
|
11
18
|
|
12
19
|
include RangeHelp
|
13
20
|
include ::PackageProtections::RubocopProtectionInterface
|
14
21
|
|
22
|
+
sig { void }
|
15
23
|
def on_new_investigation
|
16
24
|
absolute_filepath = Pathname.new(processed_source.file_path)
|
17
25
|
relative_filepath = absolute_filepath.relative_path_from(Pathname.pwd)
|
18
26
|
relative_filename = relative_filepath.to_s
|
19
27
|
|
20
|
-
# This cop only works for files in `app`
|
21
|
-
return if !relative_filename.include?('app/')
|
22
|
-
|
23
|
-
match = relative_filename.match(%r{((#{::PackageProtections::EXPECTED_PACK_DIRECTORIES.join("|")})/.*?)/})
|
24
|
-
package_name = match && match[1]
|
25
|
-
|
26
|
-
return if package_name.nil?
|
27
|
-
|
28
|
-
return if relative_filepath.extname != '.rb'
|
29
|
-
|
30
|
-
# Zeitwerk establishes a standard convention by which namespaces are defined.
|
31
|
-
# The package protections namespace checker is coupled to a specific assumption about how auto-loading works.
|
32
|
-
#
|
33
|
-
# Namely, we expect the following autoload paths: `packs/**/app/**/`
|
34
|
-
# Examples:
|
35
|
-
# 1) `packs/package_1/app/public/package_1/my_constant.rb` produces constant `Package1::MyConstant`
|
36
|
-
# 2) `packs/package_1/app/services/package_1/my_service.rb` produces constant `Package1::MyService`
|
37
|
-
# 3) `packs/package_1/app/services/package_1.rb` produces constant `Package1`
|
38
|
-
# 4) `packs/package_1/app/public/package_1.rb` produces constant `Package1`
|
39
|
-
#
|
40
|
-
# Described another way, we expect any part of the directory labeled NAMESPACE to establish a portion of the fully qualified runtime constant:
|
41
|
-
# `packs/**/app/**/NAMESPACE1/NAMESPACE2/[etc]`
|
42
|
-
#
|
43
|
-
# Therefore, for our implementation, we substitute out the non-namespace producing portions of the filename to count the number of namespaces.
|
44
|
-
# Note this will *not work* properly in applications that have different assumptions about autoloading.
|
45
|
-
package_last_name = T.must(package_name.split('/').last)
|
46
|
-
path_without_package_base = relative_filename.gsub(%r{#{package_name}/app/}, '')
|
47
|
-
if path_without_package_base.include?('concerns')
|
48
|
-
autoload_folder_name = path_without_package_base.split('/').first(2).join('/')
|
49
|
-
else
|
50
|
-
autoload_folder_name = path_without_package_base.split('/').first
|
51
|
-
end
|
28
|
+
# This cop only works for files ruby files in `app`
|
29
|
+
return if !relative_filename.include?('app/') || relative_filepath.extname != '.rb'
|
52
30
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
if allowed_namespaces.include?(actual_namespace)
|
57
|
-
# No problem!
|
58
|
-
elsif allowed_namespaces.count == 1
|
59
|
-
single_allowed_namespace = allowed_namespaces.first
|
60
|
-
if relative_filepath.to_s.include?('app/')
|
61
|
-
app_or_lib = 'app'
|
62
|
-
elsif relative_filepath.to_s.include?('lib/')
|
63
|
-
app_or_lib = 'lib'
|
64
|
-
end
|
31
|
+
relative_filename = relative_filepath.to_s
|
32
|
+
package_for_path = ParsePackwerk.package_from_path(relative_filename)
|
33
|
+
return if package_for_path.nil?
|
65
34
|
|
66
|
-
|
35
|
+
namespace_context = self.class.desired_zeitwerk_api.for_file(relative_filename, package_for_path)
|
36
|
+
return if namespace_context.nil?
|
67
37
|
|
68
|
-
|
38
|
+
allowed_global_namespaces = Set.new([
|
39
|
+
namespace_context.expected_namespace,
|
40
|
+
*::PackageProtections.config.globally_permitted_namespaces
|
41
|
+
])
|
69
42
|
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
expected_namespace: package_last_name.camelize,
|
76
|
-
expected_path: relative_desired_path
|
77
|
-
)
|
78
|
-
)
|
43
|
+
package_name = package_for_path.name
|
44
|
+
actual_namespace = namespace_context.current_namespace
|
45
|
+
|
46
|
+
if allowed_global_namespaces.include?(actual_namespace)
|
47
|
+
# No problem!
|
79
48
|
else
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
49
|
+
package_enforces_namespaces = !::PackageProtections::ProtectedPackage.from(package_for_path).violation_behavior_for(NamespacedUnderPackageName::IDENTIFIER).fail_never?
|
50
|
+
expected_namespace = namespace_context.expected_namespace
|
51
|
+
relative_desired_path = namespace_context.expected_filepath
|
52
|
+
pack_owning_this_namespace = self.class.namespaces_to_packs[actual_namespace]
|
53
|
+
|
54
|
+
if package_enforces_namespaces
|
55
|
+
add_offense(
|
56
|
+
source_range(processed_source.buffer, 1, 0),
|
57
|
+
message: format(
|
58
|
+
'`%<package_name>s` prevents modules/classes that are not submodules of the package namespace. Should be namespaced under `%<expected_namespace>s` with path `%<expected_path>s`. See https://go/packwerk_cheatsheet_namespaces for more info.',
|
59
|
+
package_name: package_name,
|
60
|
+
expected_namespace: expected_namespace,
|
61
|
+
expected_path: relative_desired_path
|
62
|
+
)
|
86
63
|
)
|
87
|
-
|
64
|
+
elsif pack_owning_this_namespace
|
65
|
+
add_offense(
|
66
|
+
source_range(processed_source.buffer, 1, 0),
|
67
|
+
message: format(
|
68
|
+
'`%<pack_owning_this_namespace>s` prevents other packs from sitting in the `%<actual_namespace>s` namespace. This should be namespaced under `%<expected_namespace>s` with path `%<expected_path>s`. See https://go/packwerk_cheatsheet_namespaces for more info.',
|
69
|
+
package_name: package_name,
|
70
|
+
pack_owning_this_namespace: pack_owning_this_namespace,
|
71
|
+
expected_namespace: expected_namespace,
|
72
|
+
actual_namespace: actual_namespace,
|
73
|
+
expected_path: relative_desired_path
|
74
|
+
)
|
75
|
+
)
|
76
|
+
end
|
88
77
|
end
|
89
78
|
end
|
90
79
|
|
91
|
-
|
80
|
+
# We override `cop_configs` for this protection.
|
81
|
+
# The default behavior disables cops when a package has turned off a protection.
|
82
|
+
# However: namespace violations can occur even when one package has TURNED OFF their namespace protection
|
83
|
+
# but another package has it turned on. Therefore, all packages must always be opted in no matter what.
|
84
|
+
#
|
85
|
+
sig do
|
86
|
+
params(packages: T::Array[::PackageProtections::ProtectedPackage])
|
87
|
+
.returns(T::Array[::PackageProtections::RubocopProtectionInterface::CopConfig])
|
88
|
+
end
|
89
|
+
def cop_configs(packages)
|
90
|
+
include_paths = T.let([], T::Array[String])
|
91
|
+
packages.each do |p|
|
92
|
+
included_globs_for_pack.each do |glob|
|
93
|
+
include_paths << p.original_package.directory.join(glob).to_s
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
[
|
98
|
+
::PackageProtections::RubocopProtectionInterface::CopConfig.new(
|
99
|
+
name: cop_name,
|
100
|
+
enabled: include_paths.any?,
|
101
|
+
include_paths: include_paths
|
102
|
+
)
|
103
|
+
]
|
104
|
+
end
|
105
|
+
|
106
|
+
IDENTIFIER = T.let('prevent_this_package_from_creating_other_namespaces'.freeze, String)
|
92
107
|
|
93
108
|
sig { override.returns(String) }
|
94
109
|
def identifier
|
@@ -105,7 +120,11 @@ module RuboCop
|
|
105
120
|
|
106
121
|
# The reason for this is precondition is the `MultipleNamespacesProtection` assumes this to work properly.
|
107
122
|
# To remove this precondition, we need to modify `MultipleNamespacesProtection` to be more generalized!
|
108
|
-
|
123
|
+
is_root_package = package.name == ParsePackwerk::ROOT_PACKAGE_NAME
|
124
|
+
in_allowed_directory = ::PackageProtections::EXPECTED_PACK_DIRECTORIES.any? do |expected_package_directory|
|
125
|
+
package.directory.to_s.start_with?(expected_package_directory)
|
126
|
+
end
|
127
|
+
if in_allowed_directory || is_root_package
|
109
128
|
nil
|
110
129
|
else
|
111
130
|
"Package #{package.name} must be located in one of #{::PackageProtections::EXPECTED_PACK_DIRECTORIES.join(', ')} (or be the root) to use this protection"
|
@@ -121,15 +140,6 @@ module RuboCop
|
|
121
140
|
]
|
122
141
|
end
|
123
142
|
|
124
|
-
sig do
|
125
|
-
params(package: ::PackageProtections::ProtectedPackage).returns(T::Hash[T.untyped, T.untyped])
|
126
|
-
end
|
127
|
-
def custom_cop_config(package)
|
128
|
-
{
|
129
|
-
'GlobalNamespaces' => package.metadata['global_namespaces']
|
130
|
-
}
|
131
|
-
end
|
132
|
-
|
133
143
|
sig do
|
134
144
|
override.params(file: String).returns(String)
|
135
145
|
end
|
@@ -159,22 +169,29 @@ module RuboCop
|
|
159
169
|
MESSAGE
|
160
170
|
end
|
161
171
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
172
|
+
sig { returns(DesiredZeitwerkApi) }
|
173
|
+
def self.desired_zeitwerk_api
|
174
|
+
# This is cached at the class level so we will cache more expensive operations
|
175
|
+
# across rubocop requests.
|
176
|
+
@desired_zeitwerk_api ||= T.let(nil, T.nilable(DesiredZeitwerkApi))
|
177
|
+
@desired_zeitwerk_api ||= DesiredZeitwerkApi.new
|
168
178
|
end
|
169
179
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
180
|
+
sig { returns(T::Hash[String, String]) }
|
181
|
+
def self.namespaces_to_packs
|
182
|
+
@namespaces_to_packs = T.let(nil, T.nilable(T::Hash[String, String]))
|
183
|
+
@namespaces_to_packs ||= begin
|
184
|
+
all_packs_enforcing_namespaces = ParsePackwerk.all.reject do |p|
|
185
|
+
::PackageProtections::ProtectedPackage.from(p).violation_behavior_for(NamespacedUnderPackageName::IDENTIFIER).fail_never?
|
186
|
+
end
|
187
|
+
|
188
|
+
namespaces_to_packs = {}
|
189
|
+
all_packs_enforcing_namespaces.each do |package|
|
190
|
+
namespaces_to_packs[desired_zeitwerk_api.get_pack_based_namespace(package)] = package.name
|
191
|
+
end
|
175
192
|
|
176
|
-
|
177
|
-
|
193
|
+
namespaces_to_packs
|
194
|
+
end
|
178
195
|
end
|
179
196
|
end
|
180
197
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: package_protections
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Gusto Engineers
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-
|
11
|
+
date: 2022-09-12 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -192,6 +192,7 @@ files:
|
|
192
192
|
- lib/package_protections/rubocop_protection_interface.rb
|
193
193
|
- lib/package_protections/violation_behavior.rb
|
194
194
|
- lib/rubocop/cop/package_protections/namespaced_under_package_name.rb
|
195
|
+
- lib/rubocop/cop/package_protections/namespaced_under_package_name/desired_zeitwerk_api.rb
|
195
196
|
- lib/rubocop/cop/package_protections/typed_public_api.rb
|
196
197
|
homepage: https://github.com/rubyatscale/package_protections
|
197
198
|
licenses:
|