class Jekyll::URL
def self.escape_path(path)
# => "/a%20b"
URL.escape_path("/a b")
Examples:
path - The path to be escaped.
Escapes a path to be a valid URL path segment
def self.escape_path(path) return path if path.empty? || %r!^[a-zA-Z0-9./-]+$!.match?(path) # Because URI.escape doesn't escape "?", "[" and "]" by default, # specify unsafe string (except unreserved, sub-delims, ":", "@" and "/"). # # URI path segment is defined in RFC 3986 as follows: # segment = *pchar # pchar = unreserved / pct-encoded / sub-delims / ":" / "@" # unreserved = ALPHA / DIGIT / "-" / "." / "_" / "~" # pct-encoded = "%" HEXDIG HEXDIG # sub-delims = "!" / "$" / "&" / "'" / "(" / ")" # / "*" / "+" / "," / ";" / "=" Addressable::URI.encode(path).encode("utf-8").sub("#", "%23") end
def self.unescape_path(path)
# => "/a b"
URL.unescape_path("/a%20b")
Examples:
path - The path to be unescaped.
Unescapes a URL path segment
def self.unescape_path(path) path = path.encode("utf-8") return path unless path.include?("%") Addressable::URI.unencode(path) end
def generate_url(template)
respective values in the given template
Internal: Generate the URL by replacing all placeholders with their
def generate_url(template) if @placeholders.is_a? Drops::UrlDrop generate_url_from_drop(template) else generate_url_from_hash(template) end end
def generate_url_from_drop(template)
def generate_url_from_drop(template) template.gsub(%r!:([a-z_]+)!) do |match| name = Regexp.last_match(1) pool = name.end_with?("_") ? [name, name.chomp!("_")] : [name] winner = pool.find { |key| @placeholders.key?(key) } if winner.nil? raise NoMethodError, "The URL template doesn't have #{pool.join(" or ")} keys. " \ "Check your permalink template!" end value = @placeholders[winner] value = "" if value.nil? replacement = self.class.escape_path(value) match.sub!(":#{winner}", replacement) end end
def generate_url_from_hash(template)
def generate_url_from_hash(template) @placeholders.inject(template) do |result, token| break result if result.index(":").nil? if token.last.nil? # Remove leading "/" to avoid generating urls with `//` result.gsub("/:#{token.first}", "") else result.gsub(":#{token.first}", self.class.escape_path(token.last)) end end end
def generated_permalink
Generates a URL from the permalink
def generated_permalink (@generated_permalink ||= generate_url(@permalink)) if @permalink end
def generated_url
Generates a URL from the template
def generated_url @generated_url ||= generate_url(@template) end
def initialize(options)
template. Instead, the given permalink will be
:permalink - If supplied, no URL will be generated from the
the placeholder ":year" with the current year.
{ "year" => Time.now.strftime("%Y") } would replace
replaced when used inside the template. E.g.
:placeholders - A hash containing the placeholders which will be
a placeholder is prefixed with a colon.
for example "/:path/:basename:output_ext", where
:template - The String used as template for URL generation,
options - One of :permalink or :template must be supplied.
def initialize(options) @template = options[:template] @placeholders = options[:placeholders] || {} @permalink = options[:permalink] if (@template || @permalink).nil? raise ArgumentError, "One of :template or :permalink must be supplied." end end
def possible_keys(key)
smart enough to know that so we have to make it an explicit
That should be :month and :day, but our key extraction regexp isn't
but the underscore is not part of the key, e.g. '/:month_:day'.
This poses a problem for keys which are followed by an underscore
We include underscores in keys to allow for 'i_month' and so forth.
def possible_keys(key) if key.end_with?("_") [key, key.chomp("_")] else [key] end end
def sanitize_url(str)
Returns a sanitized String URL, stripping "../../" and multiples of "/",
def sanitize_url(str) "/#{str}".gsub("..", "/").tap do |result| result.gsub!("./", "") result.squeeze!("/") end end
def to_s
The generated relative URL of the resource
def to_s sanitize_url(generated_permalink || generated_url) end