in doc-architect/doc-architect-core/src/main/java/com/docarchitect/core/scanner/impl/ruby/RailsRouteScanner.java [283:374]
private void parseRoutesWithContext(String content, String componentId, List<ApiEndpoint> apiEndpoints,
String pathPrefix, String namespacePrefix) {
String[] lines = content.split("\n");
List<ScopeContext> scopeStack = new ArrayList<>();
for (int i = 0; i < lines.length; i++) {
String line = lines[i].trim();
// Skip comments and empty lines
if (line.isEmpty() || isComment(line)) {
continue;
}
// Check for 'end' keyword to pop scope
if (END_PATTERN.matcher(line).matches() && !scopeStack.isEmpty()) {
scopeStack.remove(scopeStack.size() - 1);
continue;
}
// Get current context from scope stack
String currentPathPrefix = pathPrefix;
String currentNamespacePrefix = namespacePrefix;
if (!scopeStack.isEmpty()) {
ScopeContext currentScope = scopeStack.get(scopeStack.size() - 1);
currentPathPrefix = currentScope.pathPrefix;
currentNamespacePrefix = currentScope.namespacePrefix;
}
// Check for namespace
Matcher namespaceMatcher = NAMESPACE_PATTERN.matcher(line);
if (namespaceMatcher.find()) {
String namespace = namespaceMatcher.group(1);
String newPathPrefix = combinePaths(currentPathPrefix, "/" + namespace);
String newNamespacePrefix = combineNamespaces(currentNamespacePrefix, namespace);
scopeStack.add(new ScopeContext(newPathPrefix, newNamespacePrefix));
log.debug("Found namespace: {} (path prefix: {})", namespace, newPathPrefix);
continue;
}
// Check for scope
Matcher scopeMatcher = SCOPE_PATTERN.matcher(line);
if (scopeMatcher.find()) {
String scopePath = scopeMatcher.group(1);
String scopeModule = scopeMatcher.group(2); // may be null
String newPathPrefix = combinePaths(currentPathPrefix, scopePath);
String newNamespacePrefix = scopeModule != null ?
combineNamespaces(currentNamespacePrefix, scopeModule) : currentNamespacePrefix;
scopeStack.add(new ScopeContext(newPathPrefix, newNamespacePrefix));
log.debug("Found scope: {} (path prefix: {}, module: {})", scopePath, newPathPrefix, scopeModule);
continue;
}
// Check for resourceful routes
Matcher resourcesMatcher = RESOURCES_PATTERN.matcher(line);
if (resourcesMatcher.find()) {
String resourceName = resourcesMatcher.group(1);
addResourcefulRoutes(resourceName, currentPathPrefix, currentNamespacePrefix,
componentId, apiEndpoints, true);
continue;
}
// Check for singular resource
Matcher resourceMatcher = RESOURCE_PATTERN.matcher(line);
if (resourceMatcher.find()) {
String resourceName = resourceMatcher.group(1);
addResourcefulRoutes(resourceName, currentPathPrefix, currentNamespacePrefix,
componentId, apiEndpoints, false);
continue;
}
// Check for match route
Matcher matchMatcher = MATCH_ROUTE_PATTERN.matcher(line);
if (matchMatcher.find()) {
String path = matchMatcher.group(1);
String controllerAction = matchMatcher.group(2);
String method = matchMatcher.group(3).toUpperCase();
String fullPath = combinePaths(currentPathPrefix, path);
addCustomRoute(method, fullPath, controllerAction, componentId, apiEndpoints);
continue;
}
// Check for custom routes (get, post, put, patch, delete)
Matcher customMatcher = CUSTOM_ROUTE_PATTERN.matcher(line);
if (customMatcher.find()) {
String method = customMatcher.group(1).toUpperCase();
String path = customMatcher.group(2);
String controllerAction = customMatcher.group(3); // may be null
String fullPath = combinePaths(currentPathPrefix, path);
addCustomRoute(method, fullPath, controllerAction, componentId, apiEndpoints);
}
}
}