Module: MetzScan::Analyzers::PackageDependencyPressure::SharedDependencyTriage

Defined in:
lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb

Constant Summary collapse

CATEGORY =
"shared_dependency"
CONFIDENCE =
"low"
TRIAGE_SEVERITY =
"shared dependency"
TRIAGE_SUMMARY =
"Shared dependency signal; review broad APIs or infrastructure hubs only when " \
"they change often or force package-specific callers to know too much."
WHY =
"Broad shared dependencies are often intentional, but they can still reveal global APIs " \
"that many packages must understand."
SUGGESTED_NEXT_MOVES =
[
  "Leave stable shared APIs alone when callers use a narrow, intentional interface.",
  "Extract package-owned adapters when broad APIs force repeated package-specific knowledge."
].freeze

Class Method Summary collapse

Class Method Details

.attributes(status) ⇒ Object



37
38
39
40
# File 'lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb', line 37

def attributes(status)
  { project_analyzer_status: status, confidence: CONFIDENCE,
    triage_severity: TRIAGE_SEVERITY, triage_summary: TRIAGE_SUMMARY }
end

.constant_like_tail?(segment) ⇒ Boolean

Returns:

  • (Boolean)


65
66
67
# File 'lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb', line 65

def constant_like_tail?(segment)
  segment.to_s.match?(/\A[A-Z][A-Z0-9_]*\z/) && segment.to_s.include?("_")
end

.conventional_declaration_path?(declaration) ⇒ Boolean

Returns:

  • (Boolean)


98
99
100
# File 'lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb', line 98

def conventional_declaration_path?(declaration)
  normalized_path(declaration.path).end_with?(conventional_path_suffix(declaration.name))
end

.conventional_domain_model?(declaration) ⇒ Boolean

Returns:

  • (Boolean)


81
82
83
84
# File 'lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb', line 81

def conventional_domain_model?(declaration)
  PackageMap.package_for(declaration.path) == "app/models" &&
    conventional_declaration_path?(declaration)
end

.conventional_path_suffix(name) ⇒ Object



106
107
108
# File 'lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb', line 106

def conventional_path_suffix(name)
  "#{name_segments(name).map { |segment| underscore(segment) }.join('/')}.rb"
end

.conventional_value_object?(declaration) ⇒ Boolean

Returns:

  • (Boolean)


86
87
88
89
90
# File 'lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb', line 86

def conventional_value_object?(declaration)
  PackageMap.package_for(declaration.path).to_s.start_with?("lib/") &&
    name_segments(declaration.name).last.to_s.match?(VALUE_OBJECT_SEGMENT_PATTERN) &&
    conventional_declaration_path?(declaration)
end

.declaration_path_matches_tail?(declaration) ⇒ Boolean

Returns:

  • (Boolean)


102
103
104
# File 'lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb', line 102

def declaration_path_matches_tail?(declaration)
  normalized_path(declaration.path).end_with?("#{underscore(name_segments(declaration.name).last)}.rb")
end

.error_like_tail?(segment) ⇒ Boolean

Returns:

  • (Boolean)


69
70
71
# File 'lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb', line 69

def error_like_tail?(segment)
  segment.to_s.match?(ERROR_SUFFIX_PATTERN)
end

.infrastructure_lib_segment?(package_parts) ⇒ Boolean

Returns:

  • (Boolean)


77
78
79
# File 'lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb', line 77

def infrastructure_lib_segment?(package_parts)
  File.basename(package_parts[1].to_s, ".rb").match?(INFRASTRUCTURE_LIB_SEGMENT_PATTERN)
end

.name_segments(name) ⇒ Object



61
62
63
# File 'lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb', line 61

def name_segments(name)
  name.to_s.split("::")
end

.normalized_path(path) ⇒ Object



118
119
120
# File 'lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb', line 118

def normalized_path(path)
  path.to_s.split(File::SEPARATOR).join("/")
end

.protocol_manager_surface?(declaration) ⇒ Boolean

Returns:

  • (Boolean)


92
93
94
95
96
# File 'lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb', line 92

def protocol_manager_surface?(declaration)
  PackageMap.package_for(declaration.path) == "app/lib" &&
    name_segments(declaration.name).last.to_s.match?(PROTOCOL_MANAGER_SUFFIX_PATTERN) &&
    declaration_path_matches_tail?(declaration)
end

.root_lib_file?(package_parts) ⇒ Boolean

Returns:

  • (Boolean)


73
74
75
# File 'lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb', line 73

def root_lib_file?(package_parts)
  package_parts[1].to_s.end_with?(".rb")
end

.shared?(declaration) ⇒ Boolean

Returns:

  • (Boolean)


33
34
35
# File 'lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb', line 33

def shared?(declaration)
  shared_name?(declaration.name) || shared_path?(declaration.path) || shared_surface?(declaration)
end

.shared_name?(name) ⇒ Boolean

Returns:

  • (Boolean)


42
43
44
45
46
# File 'lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb', line 42

def shared_name?(name)
  segments = name_segments(name)
  segments.any? { |segment| segment.match?(NAME_SEGMENT_PATTERN) } ||
    constant_like_tail?(segments.last) || error_like_tail?(segments.last)
end

.shared_path?(path) ⇒ Boolean

Returns:

  • (Boolean)


48
49
50
51
52
53
# File 'lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb', line 48

def shared_path?(path)
  package_parts = PackageMap.package_parts(path)
  return false unless package_parts.first == "lib"

  root_lib_file?(package_parts) || infrastructure_lib_segment?(package_parts)
end

.shared_surface?(declaration) ⇒ Boolean

Returns:

  • (Boolean)


55
56
57
58
59
# File 'lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb', line 55

def shared_surface?(declaration)
  conventional_domain_model?(declaration) ||
    conventional_value_object?(declaration) ||
    protocol_manager_surface?(declaration)
end

.underscore(segment) ⇒ Object



110
111
112
113
114
115
116
# File 'lib/metz_scan/analyzers/package_dependency_pressure/shared_dependency_triage.rb', line 110

def underscore(segment)
  segment.to_s
         .gsub(/([A-Z\d]+)([A-Z][a-z])/, '\\1_\\2')
         .gsub(/([a-z\d])([A-Z])/, '\\1_\\2')
         .tr("-", "_")
         .downcase
end