public function assemble($params = [])
    {
        $params = array_merge($this->defaults, $params);
        $parts  = $this->parts;

        $return = [];
        foreach ($parts as $part) {
            if (0 === strpos($part, '~') && 1 !== strpos($part, ':')) {
                continue;
            }
            if (0 === strpos($part, ':')) {
                if (! isset($params[ltrim($part, ':')])) {
                    throw new InvalidArgumentException(sprintf(
                        'Missing value for the placeholder "%s".',
                        $part
                    ));
                }
                $name = ltrim($part, ':');
                if (isset($this->constraints[$name])
                    && ! preg_match(
                            '#\A' . $this->constraints[$name] . '\Z#',
                            $params[$name]
                        )
                ) {
                    throw new InvalidArgumentException(sprintf(
                        'The value "%s" of parameter "%s" must be constrain '
                        . 'with regex "%s".',
                        $params[$name],
                        $name,
                        $this->constraints[$name]
                    ));
                }
                $return[] = Uri::encode($params[$name]);

                continue;
            }
            if (1 === strpos($part, ':')) {
                $name = ltrim($part, '~:');
                if (! isset($params[$name])) {
                    continue;
                }
                if (isset($this->constraints[$name])
                    && ! preg_match(
                            '#\A' . $this->constraints[$name] . '\Z#',
                            $params[$name]
                        )
                ) {
                    throw new InvalidArgumentException(sprintf(
                        'The value "%s" of parameter "%s" must be constrain '
                        . 'with regex "%s".',
                        $params[$name],
                        $name,
                        $this->constraints[$name]
                    ));
                }
                $return[] = Uri::encode($params[$name]);

                continue;
            }
            $return[] = Uri::encode(ltrim($part, '~'));
        }

        return '/' . implode('/', $return);
    }