Class: Async::Matrix::Api::PathTree
- Inherits:
-
Object
- Object
- Async::Matrix::Api::PathTree
- Defined in:
- lib/async/matrix/api/path_tree.rb
Overview
A trie of valid Matrix API paths, built from OpenAPI 3.1.0 YAML schemas.
Each leaf node stores the set of HTTP methods valid at that path. Template segments like roomId become wildcard nodes that match any value.
Example:
tree = PathTree.load
tree.match(["_matrix", "client", "v3", "rooms", "!abc:ex.com", "ban"], "POST")
# => { valid: true, operation_id: "ban", methods: ["post"] }
Defined Under Namespace
Classes: Node
Constant Summary collapse
- SCHEMA_DIR =
Pathname.new(File.("../../../../data/matrix-spec/api/client-server", __dir__))
Instance Attribute Summary collapse
-
#root ⇒ Object
readonly
Returns the value of attribute root.
Class Method Summary collapse
-
.load(schema_dir: SCHEMA_DIR) ⇒ Object
Load all OpenAPI schemas from data/ and build the tree.
Instance Method Summary collapse
-
#initialize ⇒ PathTree
constructor
A new instance of PathTree.
-
#insert(segments, method, operation_id = nil) ⇒ Object
Insert a path (as array of segments) with an HTTP method into the trie.
-
#load_schema(path) ⇒ Object
Parse a single OpenAPI YAML file and insert its paths into the tree.
-
#match(segments, method = nil) ⇒ Object
Match a concrete path (array of segments) against the trie.
Constructor Details
Instance Attribute Details
#root ⇒ Object (readonly)
Returns the value of attribute root.
36 37 38 |
# File 'lib/async/matrix/api/path_tree.rb', line 36 def root @root end |
Class Method Details
.load(schema_dir: SCHEMA_DIR) ⇒ Object
Load all OpenAPI schemas from data/ and build the tree.
43 44 45 46 47 48 49 |
# File 'lib/async/matrix/api/path_tree.rb', line 43 def self.load(schema_dir: SCHEMA_DIR) tree = new Pathname.glob(schema_dir / "*.yaml").each do |path| tree.load_schema(path) end tree end |
Instance Method Details
#insert(segments, method, operation_id = nil) ⇒ Object
Insert a path (as array of segments) with an HTTP method into the trie.
76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 |
# File 'lib/async/matrix/api/path_tree.rb', line 76 def insert(segments, method, operation_id = nil) node = @root segments.each do |segment| if segment.start_with?("{") && segment.end_with?("}") # Wildcard segment — matches any value node.wildcard ||= Node.new node = node.wildcard else node.children[segment] ||= Node.new node = node.children[segment] end end node.methods << method.downcase unless node.methods.include?(method.downcase) node.operation_ids[method.downcase] = operation_id if operation_id end |
#load_schema(path) ⇒ Object
Parse a single OpenAPI YAML file and insert its paths into the tree.
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'lib/async/matrix/api/path_tree.rb', line 52 def load_schema(path) doc = YAML.safe_load(File.read(path), permitted_classes: [Symbol], aliases: true) return unless doc.is_a?(Hash) base_path = extract_base_path(doc) paths = doc["paths"] return unless paths.is_a?(Hash) paths.each do |path_template, methods_hash| next unless methods_hash.is_a?(Hash) # Build full path: basePath + path_template full_path = "#{base_path}#{path_template.strip}" segments = full_path.split("/").reject(&:empty?) methods_hash.each do |method, operation| next unless %w[get post put delete patch head].include?(method) operation_id = operation.is_a?(Hash) ? operation["operationId"] : nil insert(segments, method, operation_id) end end end |
#match(segments, method = nil) ⇒ Object
Match a concrete path (array of segments) against the trie. Returns a result hash.
94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 |
# File 'lib/async/matrix/api/path_tree.rb', line 94 def match(segments, method = nil) node = @root segments.each do |segment| if node.children.key?(segment) node = node.children[segment] elsif node.wildcard node = node.wildcard else return {valid: false, methods: [], operation_id: nil} end end if method method_down = method.downcase valid = node.methods.include?(method_down) {valid: valid, methods: node.methods, operation_id: node.operation_ids[method_down]} else {valid: node.methods.any?, methods: node.methods, operation_id: nil} end end |