Add a handler to the route list

        :param uri: path to match
        :param methods: sequence of accepted method names. If none are
            provided, any method is allowed
        :param handler: request handler function.
            When executed, it should provide a response object.
        :param name: user defined route name for url_for
        :return: Nothing

def _add(self, uri, methods, handler, host=None, name=None):
        """Add a handler to the route list

        :param uri: path to match
        :param methods: sequence of accepted method names. If none are
            provided, any method is allowed
        :param handler: request handler function.
            When executed, it should provide a response object.
        :param name: user defined route name for url_for
        :return: Nothing
        """
        if host is not None:
            if isinstance(host, str):
                uri = host + uri
                self.hosts.add(host)

            else:
                if not isinstance(host, Iterable):
                    raise ValueError(
                        "Expected either string or Iterable of "
                        "host strings, not {!r}".format(host)
                    )

                for host_ in host:
                    self.add(uri, methods, handler, host_, name)
                return

        # Dict for faster lookups of if method allowed
        if methods:
            methods = frozenset(methods)

        parameters = []
        parameter_names = set()
        properties = {"unhashable": None}

        def add_parameter(match):
            name = match.group(1)
            name, _type, pattern = self.parse_parameter_string(name)

            if name in parameter_names:
                raise ParameterNameConflicts(
                    "Multiple parameter named <{name}> "
                    "in route uri {uri}".format(name=name, uri=uri)
                )
            parameter_names.add(name)

            parameter = Parameter(name=name, cast=_type)
            parameters.append(parameter)

            # Mark the whole route as unhashable if it has the hash key in it
            if re.search(r"(^|[^^]){1}/", pattern):
                properties["unhashable"] = True
            # Mark the route as unhashable if it matches the hash key
            elif re.search(r"/", pattern):
                properties["unhashable"] = True

            return "({})".format(pattern)

        pattern_string = re.sub(self.parameter_pattern, add_parameter, uri)
        pattern = re.compile(r"^{}$".format(pattern_string))

        def merge_route(route, methods, handler):
            # merge to the existing route when possible.
            if not route.methods or not methods:
                # method-unspecified routes are not mergeable.
                raise RouteExists("Route already registered: {}".format(uri))
            elif route.methods.intersection(methods):
                # already existing method is not overloadable.
                duplicated = methods.intersection(route.methods)
                raise RouteExists(
                    "Route already registered: {} [{}]".format(
                        uri, ",".join(list(duplicated))
                    )
                )
            if isinstance(route.handler, CompositionView):
                view = route.handler
            else:
                view = CompositionView()
                view.add(route.methods, route.handler)
            view.add(methods, handler)
            route = route._replace(
                handler=view, methods=methods.union(route.methods)
            )
            return route

        if parameters:
            # TODO: This is too complex, we need to reduce the complexity
            if properties["unhashable"]:
                routes_to_check = self.routes_always_check
                ndx, route = self.check_dynamic_route_exists(
                    pattern, routes_to_check, parameters
                )
            else:
                routes_to_check = self.routes_dynamic[url_hash(uri)]
                ndx, route = self.check_dynamic_route_exists(
                    pattern, routes_to_check, parameters
                )
            if ndx != -1:
                # Pop the ndx of the route, no dups of the same route
                routes_to_check.pop(ndx)
        else:
            route = self.routes_all.get(uri)

        # prefix the handler name with the blueprint name
        # if available
        # special prefix for static files
        is_static = False
        if name and name.startswith("_static_"):
            is_static = True
            name = name.split("_static_", 1)[-1]

        if hasattr(handler, "__blueprintname__"):
            handler_name = "{}.{}".format(
                handler.__blueprintname__, name or handler.__name__
            )
        else:
            handler_name = name or getattr(handler, "__name__", None)

        if route:
            route = merge_route(route, methods, handler)
        else:
            route = Route(
                handler=handler,
                methods=methods,
                pattern=pattern,
                parameters=parameters,
                name=handler_name,
                uri=uri,
            )

        self.routes_all[uri] = route
        if is_static:
            pair = self.routes_static_files.get(handler_name)
            if not (pair and (pair[0] + "/" == uri or uri + "/" == pair[0])):
                self.routes_static_files[handler_name] = (uri, route)

        else:
            pair = self.routes_names.get(handler_name)
            if not (pair and (pair[0] + "/" == uri or uri + "/" == pair[0])):
                self.routes_names[handler_name] = (uri, route)

        if properties["unhashable"]:
            self.routes_always_check.append(route)
        elif parameters:
            self.routes_dynamic[url_hash(uri)].append(route)
        else:
            self.routes_static[uri] = route