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.
104 105 106 |
# File 'lib/archspec/model.rb', line 104 def components @components end |
#constants ⇒ Object (readonly)
Returns the value of attribute constants.
104 105 106 |
# File 'lib/archspec/model.rb', line 104 def constants @constants end |
#edges ⇒ Object (readonly)
Returns the value of attribute edges.
104 105 106 |
# File 'lib/archspec/model.rb', line 104 def edges @edges end |
#files ⇒ Object (readonly)
Returns the value of attribute files.
104 105 106 |
# File 'lib/archspec/model.rb', line 104 def files @files end |
#root ⇒ Object (readonly)
Returns the value of attribute root.
104 105 106 |
# File 'lib/archspec/model.rb', line 104 def root @root end |
Instance Method Details
#add_constant(name:, kind:, path:, location:) ⇒ Object
125 126 127 128 129 130 131 132 133 134 |
# File 'lib/archspec/model.rb', line 125 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
136 137 138 |
# File 'lib/archspec/model.rb', line 136 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
115 116 117 118 119 120 121 122 123 |
# File 'lib/archspec/model.rb', line 115 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
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 |
# File 'lib/archspec/model.rb', line 155 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
252 253 254 255 256 257 258 259 260 |
# File 'lib/archspec/model.rb', line 252 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
244 245 246 247 248 249 250 |
# File 'lib/archspec/model.rb', line 244 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
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 |
# File 'lib/archspec/model.rb', line 225 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
185 186 187 188 189 190 191 |
# File 'lib/archspec/model.rb', line 185 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
179 180 181 182 183 |
# File 'lib/archspec/model.rb', line 179 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
144 145 146 |
# File 'lib/archspec/model.rb', line 144 def constants_for_path(path) constants.select { |constant| constant.path == path } end |
#constants_named(name) ⇒ Object
140 141 142 |
# File 'lib/archspec/model.rb', line 140 def constants_named(name) @constants_by_name[normalize_constant(name)] end |
#dependency_edges ⇒ Object
193 194 195 |
# File 'lib/archspec/model.rb', line 193 def dependency_edges edges.select { |edge| DEPENDENCY_EDGE_TYPES.include?(edge.type) } end |
#method_definitions_for_component(name) ⇒ Object
148 149 150 151 152 153 |
# File 'lib/archspec/model.rb', line 148 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
207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 |
# File 'lib/archspec/model.rb', line 207 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
262 263 264 |
# File 'lib/archspec/model.rb', line 262 def suppressed?(diagnostic) files[diagnostic.location.path]&.suppressions&.any? { |suppression| suppression.matches?(diagnostic) } end |
#target_components_for(edge) ⇒ Object
197 198 199 200 201 202 203 204 205 |
# File 'lib/archspec/model.rb', line 197 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 |