Improve readability of `post_url` tag (#9829)

Merge pull request 9829
This commit is contained in:
Ashwin Maroli 2025-06-02 20:37:02 +05:30 committed by GitHub
parent 84437a5052
commit 2a37caac83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1 changed files with 56 additions and 32 deletions

View File

@ -3,34 +3,39 @@
module Jekyll module Jekyll
module Tags module Tags
class PostComparer class PostComparer
# Deprecated (soft; No interpreter warnings).
# To be removed in v5.0.
# Use private constant `POST_PATH_MATCHER` instead.
MATCHER = %r!^(.+/)*(\d+-\d+-\d+)-(.*)$!.freeze MATCHER = %r!^(.+/)*(\d+-\d+-\d+)-(.*)$!.freeze
POST_PATH_MATCHER = %r!\A(.+/)*?(\d{2,4}-\d{1,2}-\d{1,2})-([^/]*)\z!.freeze
private_constant :POST_PATH_MATCHER
attr_reader :path, :date, :slug, :name attr_reader :path, :date, :slug, :name
def initialize(name) def initialize(name)
@name = name @name = name
all, @path, @date, @slug = *name.sub(%r!^/!, "").match(MATCHER) all, @path, @date, @slug = *name.delete_prefix("/").match(POST_PATH_MATCHER)
unless all unless all
raise Jekyll::Errors::InvalidPostNameError, raise Jekyll::Errors::InvalidPostNameError,
"'#{name}' does not contain valid date and/or title." "'#{name}' does not contain valid date and/or title."
end end
basename_pattern = "#{date}-#{Regexp.escape(slug)}\\.[^.]+" basename_pattern = "#{date}-#{Regexp.escape(slug)}\\.[^.]+"
@name_regex = %r!^_posts/#{path}#{basename_pattern}|^#{path}_posts/?#{basename_pattern}! @name_regex = %r!\A_posts/#{path}#{basename_pattern}|\A#{path}_posts/?#{basename_pattern}!
end end
def post_date def post_date
@post_date ||= Utils.parse_date( @post_date ||= Utils.parse_date(date, "Path '#{name}' does not contain valid date.")
date,
"'#{date}' does not contain valid date and/or title."
)
end end
# Returns `MatchData` or `nil`.
def ==(other) def ==(other)
other.relative_path.match(@name_regex) other.relative_path.match(@name_regex)
end end
# Deprecated. To be removed in v5.0.
def deprecated_equality(other) def deprecated_equality(other)
slug == post_slug(other) && slug == post_slug(other) &&
post_date.year == other.date.year && post_date.year == other.date.year &&
@ -40,9 +45,9 @@ module Jekyll
private private
# Construct the directory-aware post slug for a Jekyll::Post # Construct the directory-aware post slug for a Jekyll::Document object.
# #
# other - the Jekyll::Post # other - the Jekyll::Document object.
# #
# Returns the post slug with the subdirectory (relative to _posts) # Returns the post slug with the subdirectory (relative to _posts)
def post_slug(other) def post_slug(other)
@ -58,47 +63,66 @@ module Jekyll
class PostUrl < Liquid::Tag class PostUrl < Liquid::Tag
include Jekyll::Filters::URLFilters include Jekyll::Filters::URLFilters
def initialize(tag_name, post, tokens) def initialize(tag_name, markup, tokens)
super super
@orig_post = post.strip @markup = markup.strip
begin begin
@post = PostComparer.new(@orig_post) @post_comparer = PostComparer.new(@markup)
rescue StandardError => e rescue StandardError
raise Jekyll::Errors::PostURLError, <<~MSG raise_markup_parse_error
Could not parse name of post "#{@orig_post}" in tag 'post_url'.
Make sure the post exists and the name is correct.
#{e.class}: #{e.message}
MSG
end end
# Deprecated instance_variables.
# To be removed in Jekyll v5.0.
@orig_post = @markup
@post = @post_comparer
end end
def render(context) def render(context)
@context = context @context = context
site = context.registers[:site] site = context.registers[:site]
site.posts.docs.each do |document| # First pass-through.
return relative_url(document) if @post == document site.posts.docs.each do |post|
return relative_url(post) if @post_comparer == post
end end
# New matching method did not match, fall back to old method # First pass-through did not yield the requested post. Search again using legacy matching
# with deprecation warning if this matches # method. Log deprecation warning if a post is detected via this round.
site.posts.docs.each do |post|
next unless @post_comparer.deprecated_equality(post)
site.posts.docs.each do |document| log_legacy_usage_deprecation
next unless @post.deprecated_equality document return relative_url(post)
Jekyll::Deprecator.deprecation_message(
"A call to '{% post_url #{@post.name} %}' did not match a post using the new " \
"matching method of checking name (path-date-slug) equality. Please make sure " \
"that you change this tag to match the post's name exactly."
)
return relative_url(document)
end end
raise_post_not_found_error
end
private
def raise_markup_parse_error
raise Jekyll::Errors::PostURLError, <<~MSG raise Jekyll::Errors::PostURLError, <<~MSG
Could not find post "#{@orig_post}" in tag 'post_url'. Could not parse name of post #{@markup.inspect} in tag 'post_url'.
Make sure the post exists and the name is correct. Make sure the correct name is given to the tag.
MSG MSG
end end
def raise_post_not_found_error
raise Jekyll::Errors::PostURLError, <<~MSG
Could not find post #{@markup.inspect} in tag 'post_url'.
Make sure the post exists and the correct name is given to the tag.
MSG
end
def log_legacy_usage_deprecation
Jekyll::Deprecator.deprecation_message(
"A call to '{% post_url #{@markup} %}' did not match a post using the new matching " \
"method of checking name (path-date-slug) equality. Please make sure that you change " \
"this tag to match the post's name exactly."
)
end
end end
end end
end end