Class: ArchSpec::Graph
- Inherits:
-
Object
- Object
- ArchSpec::Graph
- Defined in:
- lib/archspec/model.rb
Constant Summary collapse
- DEPENDENCY_EDGE_TYPES =
%i[ references_constant inherits_from includes prepends extends ].freeze
Instance Attribute Summary collapse
-
#components ⇒ Object
readonly
Returns the value of attribute components.
-
#constants ⇒ Object
readonly
Returns the value of attribute constants.
-
#edges ⇒ Object
readonly
Returns the value of attribute edges.
-
#files ⇒ Object
readonly
Returns the value of attribute files.
-
#root ⇒ Object
readonly
Returns the value of attribute root.
Instance Method Summary collapse
- #add_constant(name:, kind:, path:, location:) ⇒ Object
- #add_edge(type:, from_path:, from_constant:, to:, location:, confidence: :high) ⇒ Object
- #add_file(path:, expected_constant:, parse_errors:, suppressions: []) ⇒ Object
- #assign_components(component_specs) ⇒ Object
- #component_assignment_reasons_for_constant(name) ⇒ Object
- #component_assignment_reasons_for_path(path) ⇒ Object
- #component_dependency_pairs(only: nil) ⇒ Object
- #component_names_for_constant(name) ⇒ Object
- #component_names_for_path(path) ⇒ Object
- #constants_for_path(path) ⇒ Object
- #constants_named(name) ⇒ Object
- #dependency_edges ⇒ Object
-
#initialize(root) ⇒ Graph
constructor
A new instance of Graph.
- #method_definitions_for_component(name) ⇒ Object
- #resolve_constant_reference(name, from_constant) ⇒ Object
- #suppressed?(diagnostic) ⇒ Boolean
- #target_components_for(edge) ⇒ Object
Constructor Details
Instance Attribute Details
#components ⇒ Object (readonly)
Returns the value of attribute components.
103 104 105 |
# File 'lib/archspec/model.rb', line 103 def components @components end |
#constants ⇒ Object (readonly)
Returns the value of attribute constants.
103 104 105 |
# File 'lib/archspec/model.rb', line 103 def constants @constants end |
#edges ⇒ Object (readonly)
Returns the value of attribute edges.
103 104 105 |
# File 'lib/archspec/model.rb', line 103 def edges @edges end |
#files ⇒ Object (readonly)
Returns the value of attribute files.
103 104 105 |
# File 'lib/archspec/model.rb', line 103 def files @files end |
#root ⇒ Object (readonly)
Returns the value of attribute root.
103 104 105 |
# File 'lib/archspec/model.rb', line 103 def root @root end |
Instance Method Details
#add_constant(name:, kind:, path:, location:) ⇒ Object
124 125 126 127 128 129 130 131 132 133 |
# File 'lib/archspec/model.rb', line 124 def add_constant(name:, kind:, path:, location:) normalized = normalize_constant(name) existing = @constants_by_name[normalized].find { |constant| constant.path == path && constant.kind == kind } return existing if existing constant = ConstantNode.new(name: normalized, kind: kind, path: path, location: location) constants << constant @constants_by_name[normalized] << constant constant end |
#add_edge(type:, from_path:, from_constant:, to:, location:, confidence: :high) ⇒ Object
135 136 137 |
# File 'lib/archspec/model.rb', line 135 def add_edge(type:, from_path:, from_constant:, to:, location:, confidence: :high) edges << Edge.new(type, from_path, from_constant, normalize_constant(to), location, confidence) end |
#add_file(path:, expected_constant:, parse_errors:, suppressions: []) ⇒ Object
114 115 116 117 118 119 120 121 122 |
# File 'lib/archspec/model.rb', line 114 def add_file(path:, expected_constant:, parse_errors:, suppressions: []) files[path] = SourceFile.new( root: root, path: path, expected_constant: expected_constant, parse_errors: parse_errors, suppressions: suppressions ) end |
#assign_components(component_specs) ⇒ Object
154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 |
# File 'lib/archspec/model.rb', line 154 def assign_components(component_specs) @components = {} component_specs.each do |spec| component = Component.new(spec.name) spec.file_patterns.each do |pattern| each_matching_file(pattern) { |path| component.add_file(path, reason: "matched file pattern #{pattern}") } end constants.each do |constant| matched_file = component.files.include?(constant.path) matched_constant = spec.matches_constant?(constant.name) next unless matched_file || matched_constant component.add_file(constant.path, reason: "defines #{constant.name}") if matched_constant component.add_constant(constant.name, reason: matched_file ? "defined in matched file" : "matched namespace/constant selector") end @components[component.name] = component end end |
#component_assignment_reasons_for_constant(name) ⇒ Object
250 251 252 253 254 255 256 257 258 |
# File 'lib/archspec/model.rb', line 250 def component_assignment_reasons_for_constant(name) normalized = normalize_constant(name) components.values.each_with_object({}) do |component, reasons| next unless component.constants.include?(normalized) reasons[component.name] = component.constant_reasons[normalized].to_a.sort end end |
#component_assignment_reasons_for_path(path) ⇒ Object
242 243 244 245 246 247 248 |
# File 'lib/archspec/model.rb', line 242 def component_assignment_reasons_for_path(path) components.values.each_with_object({}) do |component, reasons| next unless component.files.include?(path) reasons[component.name] = component.file_reasons[path].to_a.sort end end |
#component_dependency_pairs(only: nil) ⇒ Object
223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/archspec/model.rb', line 223 def component_dependency_pairs(only: nil) allowed_sources = Array(only).compact.map(&:to_sym).to_set pairs = Set.new dependency_edges.each do |edge| source_components = component_names_for_path(edge.from_path) source_components &= allowed_sources unless allowed_sources.empty? next if source_components.empty? target_components_for(edge).each do |target| source_components.each do |source| pairs.add([source, target]) unless source == target end end end pairs end |
#component_names_for_constant(name) ⇒ Object
183 184 185 186 187 188 189 |
# File 'lib/archspec/model.rb', line 183 def component_names_for_constant(name) normalized = normalize_constant(name) components.values.each_with_object(Set.new) do |component, names| names.add(component.name) if component.constants.include?(normalized) end end |
#component_names_for_path(path) ⇒ Object
177 178 179 180 181 |
# File 'lib/archspec/model.rb', line 177 def component_names_for_path(path) components.values.each_with_object(Set.new) do |component, names| names.add(component.name) if component.files.include?(path) end end |
#constants_for_path(path) ⇒ Object
143 144 145 |
# File 'lib/archspec/model.rb', line 143 def constants_for_path(path) constants.select { |constant| constant.path == path } end |
#constants_named(name) ⇒ Object
139 140 141 |
# File 'lib/archspec/model.rb', line 139 def constants_named(name) @constants_by_name[normalize_constant(name)] end |
#dependency_edges ⇒ Object
191 192 193 |
# File 'lib/archspec/model.rb', line 191 def dependency_edges edges.select { |edge| DEPENDENCY_EDGE_TYPES.include?(edge.type) } end |
#method_definitions_for_component(name) ⇒ Object
147 148 149 150 151 152 |
# File 'lib/archspec/model.rb', line 147 def method_definitions_for_component(name) component = components.fetch(name.to_sym) component.constants.flat_map { |constant_name| constants_named(constant_name) }.flat_map(&:method_definitions) rescue KeyError [] end |
#resolve_constant_reference(name, from_constant) ⇒ Object
205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 |
# File 'lib/archspec/model.rb', line 205 def resolve_constant_reference(name, from_constant) normalized = normalize_constant(name) candidates = [] if from_constant namespace = normalize_constant(from_constant).split("::") namespace.pop until namespace.empty? candidates << "#{namespace.join("::")}::#{normalized}" namespace.pop end end candidates << normalized candidates.find { |candidate| constants_named(candidate).any? } || normalized end |
#suppressed?(diagnostic) ⇒ Boolean
260 261 262 |
# File 'lib/archspec/model.rb', line 260 def suppressed?(diagnostic) files[diagnostic.location.path]&.suppressions&.any? { |suppression| suppression.matches?(diagnostic) } end |
#target_components_for(edge) ⇒ Object
195 196 197 198 199 200 201 202 203 |
# File 'lib/archspec/model.rb', line 195 def target_components_for(edge) return Set.new unless DEPENDENCY_EDGE_TYPES.include?(edge.type) resolved = resolve_constant_reference(edge.to, edge.from_constant) constants_named(resolved).each_with_object(Set.new) do |constant, names| names.merge(component_names_for_path(constant.path)) names.merge(component_names_for_constant(constant.name)) end end |