require "mountapi/error/route_not_found" module Mountapi # the router hold routes allow to add and find route for a request # Internaly the routes are store in hash to optimise search # The deep hash store routes by version / method / fragment_count class Router def initialize(routes = []) routes.each { |route| add(route) } end def add(route) routes = route_store_get(route.version, route.method, route.segment_count) route_store_set(route.version, route.method, route.segment_count, routes << route) end def find_route(url, http_method) url_parts = self.class.url_parts(url) routes = route_store_get(url_parts[:version], Route::Method.new(http_method), url_parts[:segments].count) result = routes.find { |route| route.match?(url, http_method) } result || not_found_error(url, http_method) end # extract version from url def self.url_parts(url) url_parts = url.split("/") if Mountapi.config.version_in_path root, version, *url_tail = url_parts {version: version, segments: url_tail.unshift(root)} else {version: '0.0.0', segments: url_parts} end end # Return an url for the operation_id def url_for(operation_id, *url_params) routes.find { |r| r.operation_id?(operation_id) }.to_url(*url_params) end # Routes list def routes routes_store.values.flat_map(&:values).flat_map(&:values).flatten end private def not_found_error(url, http_method) raise Error::RouteNotFound.new(url, http_method) end def route_store_get(version, method, segments_count) return [] unless version v = routes_store.fetch(version.to_sym, {}) m = v.fetch(method.to_sym, {}) m.fetch(segments_count, []) end def route_store_set(version, method, segments_count, routes) v = routes_store.fetch(version.to_sym) { |k| routes_store[k] = {} } m = v.fetch(method.to_sym) { |k| v[k] = {} } m[segments_count] = routes.sort end def routes_store @routes_store ||= {} end end end