Compare commits

..

No commits in common. "176fd9425c8fb4607bcc50a409fe615234f64bd6" and "01781355ef452e06ced54a8164da6afb3db74f75" have entirely different histories.

13 changed files with 45 additions and 163 deletions

View File

@ -3,8 +3,6 @@
### Bug Fixes
* Avoid caching resource when called via `include_relative` tag (#9784)
* Fix logs containing IPv6 URLs (#9813)
* Do not treat colons in `url_placeholders` as URI delimiters (#9850)
### Documentation
@ -13,15 +11,6 @@
* Add Supranode to third-party deployment guide (#9786)
* Document the need for a `Gemfile` in deployment step of step-by-step walkthrough (#9805)
* Add Azion to the 3rd party deployment docs (#9811)
* Add ruby-erb prerequisite for Arch Linux installations (#9832)
### Development Fixes
* Improve readability of `post_url` tag (#9829)
### Minor Enhancements
* feat: Allowing post_url tag to receive liquid variables (#9776)
## 4.4.1 / 2025-01-29

View File

@ -40,7 +40,7 @@ sudo emerge --ask --verbose jekyll
### ArchLinux
```sh
sudo pacman -S ruby base-devel ruby-erb
sudo pacman -S ruby base-devel
```
### OpenSUSE

View File

@ -188,25 +188,3 @@ You can also use this tag to create a link to a post in Markdown as follows:
[Name of Link]({% post_url 2010-07-21-name-of-post %})
```
{% endraw %}
Now lets say you have a [datafile]({{ '/docs/datafiles/' | relative_url }}) `_data/cool_posts.yaml` used to keep track
of certain posts that you intend to be listed as say *Cool Posts*:
```yaml
- title: "An Awesome Post"
slug: "2010-07-21-name-of-post"
- title: "Another Awesome Post"
slug: "2016-07-26-name-of-post"
```
You may list such posts using the `post_url` tag as well (from {%- include docs_version_badge.html version="4.5.0" -%}):
{% raw %}
```liquid
Cool posts:
{%- for cool_post in site.data.cool_posts %}
- [{{ cool_post.title }}]({% post_url {{ cool_post.slug }} %})
{%- endfor %}
```
{% endraw %}

View File

@ -157,37 +157,3 @@ Feature: PostUrl Tag
But the _site directory should exist
And I should see "<p><a href=\"/cats%20and%20dogs/2019/02/04/hello-world.html\">Post 1</a></p>" in "_site/index.html"
And I should see "<p><a href=\"/2019/02/05/hello-again.html\">Post 2</a></p>" in "_site/index.html"
Scenario: Calling for a post via a liquid variable
Given I have a _posts directory
And I have the following post:
| title | date | content |
| Hello World | 2019-02-04 | Lorem ipsum dolor |
And I have an "index.md" page with content:
"""
{% assign value='2019-02-04-hello-world' %}
[Welcome]({% post_url {{ value }} %})
"""
When I run jekyll build
Then I should get a zero exit status
And the _site directory should exist
And I should see "<p><a href=\"/2019/02/04/hello-world.html\">Welcome</a></p>" in "_site/index.html"
Scenario: Calling for posts via a liquid variable in a for tag
Given I have a _posts directory
And I have the following post:
| title | date | content |
| Hello World | 2019-02-04 | Lorem ipsum dolor |
| We Meet Again | 2019-02-05 | Alpha beta gamma |
And I have an "index.md" page with content:
"""
{% assign posts = '2019-02-04-hello-world;2019-02-05-we-meet-again' | split: ';' %}
{%- for slug in posts -%}
[{{ slug }}]({% post_url {{ slug }} %})
{%- endfor %}
"""
When I run jekyll build
Then I should get a zero exit status
And the _site directory should exist
And I should see "<a href=\"/2019/02/04/hello-world.html\">2019-02-04-hello-world</a>" in "_site/index.html"
And I should see "<a href=\"/2019/02/05/we-meet-again.html\">2019-02-05-we-meet-again</a>" in "_site/index.html"

View File

@ -185,8 +185,7 @@ module Jekyll
#
# Returns true if the 'write' metadata is true, false otherwise.
def write?
!!metadata.fetch("output", false) &&
(site.target == metadata.fetch("target", site.target))
!!metadata.fetch("output", false)
end
# The URL template to render collection's documents at.

View File

@ -30,7 +30,6 @@ module Jekyll
"limit_posts" => 0,
"future" => false,
"unpublished" => false,
"target" => "default",
# Plugins
"whitelist" => [],

View File

@ -348,20 +348,17 @@ module Jekyll
end
# Determine whether this document should be written.
# Based on the Collection to which it belongs
# and site and document target.
# Based on the Collection to which it belongs.
#
# True if the document has a collection and if that collection's #write?
# method returns true, and if the site's Publisher will publish the document,
# and if the document's target matches the site target or is undefined.
# method returns true, and if the site's Publisher will publish the document.
# False otherwise.
#
# rubocop:disable Naming/MemoizedInstanceVariableName
def write?
return @write_p if defined?(@write_p)
@write_p = collection&.write? && site.publisher.publish?(self) &&
(site.target == data.fetch("target", site.target))
@write_p = collection&.write? && site.publisher.publish?(self)
end
# rubocop:enable Naming/MemoizedInstanceVariableName

View File

@ -15,7 +15,7 @@ module Jekyll
private delegate_method_as :data, :fallback_data
delegate_methods :id, :output, :content, :to_s, :relative_path, :url, :date
data_delegators "title", "categories", "tags", :target
data_delegators "title", "categories", "tags"
def collection
@obj.collection.label

View File

@ -180,7 +180,7 @@ module Jekyll
end
def write?
site.config["target"] == data.fetch("target", site.config["target"])
true
end
def excerpt_separator

View File

@ -6,7 +6,7 @@ module Jekyll
:file_read_opts, :future, :gems, :generators, :highlighter,
:include, :inclusions, :keep_files, :layouts, :limit_posts,
:lsi, :pages, :permalink_style, :plugin_manager, :plugins,
:reader, :safe, :target, :show_drafts, :static_files, :theme, :time,
:reader, :safe, :show_drafts, :static_files, :theme, :time,
:unpublished
attr_reader :cache_dir, :config, :dest, :filter_cache, :includes_load_paths,
@ -47,7 +47,7 @@ module Jekyll
def config=(config)
@config = config.clone
%w(safe lsi highlighter baseurl exclude include future unpublished target
%w(safe lsi highlighter baseurl exclude include future unpublished
show_drafts limit_posts keep_files).each do |opt|
send(:"#{opt}=", config[opt])
end
@ -360,7 +360,7 @@ module Jekyll
end
def each_site_file
pages.each { |page| yield (page) if page.write? }
pages.each { |page| yield page }
static_files.each { |file| yield(file) if file.write? }
collections.each_value { |coll| coll.docs.each { |doc| yield(doc) if doc.write? } }
end

View File

@ -3,39 +3,34 @@
module Jekyll
module Tags
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
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
def initialize(name)
@name = name
all, @path, @date, @slug = *name.delete_prefix("/").match(POST_PATH_MATCHER)
all, @path, @date, @slug = *name.sub(%r!^/!, "").match(MATCHER)
unless all
raise Jekyll::Errors::InvalidPostNameError,
"'#{name}' does not contain valid date and/or title."
end
basename_pattern = "#{date}-#{Regexp.escape(slug)}\\.[^.]+"
@name_regex = %r!\A_posts/#{path}#{basename_pattern}|\A#{path}_posts/?#{basename_pattern}!
@name_regex = %r!^_posts/#{path}#{basename_pattern}|^#{path}_posts/?#{basename_pattern}!
end
def post_date
@post_date ||= Utils.parse_date(date, "Path '#{name}' does not contain valid date.")
@post_date ||= Utils.parse_date(
date,
"'#{date}' does not contain valid date and/or title."
)
end
# Returns `MatchData` or `nil`.
def ==(other)
other.relative_path.match(@name_regex)
end
# Deprecated. To be removed in v5.0.
def deprecated_equality(other)
slug == post_slug(other) &&
post_date.year == other.date.year &&
@ -45,9 +40,9 @@ module Jekyll
private
# Construct the directory-aware post slug for a Jekyll::Document object.
# Construct the directory-aware post slug for a Jekyll::Post
#
# other - the Jekyll::Document object.
# other - the Jekyll::Post
#
# Returns the post slug with the subdirectory (relative to _posts)
def post_slug(other)
@ -63,71 +58,47 @@ module Jekyll
class PostUrl < Liquid::Tag
include Jekyll::Filters::URLFilters
def initialize(tag_name, markup, tokens)
def initialize(tag_name, post, tokens)
super
@markup = markup.strip
@template = Liquid::Template.parse(@markup) if @markup.include?("{{")
# Deprecated instance_variables.
# To be removed in Jekyll v5.0.
@orig_post = @markup
@post = nil
@orig_post = post.strip
begin
@post = PostComparer.new(@orig_post)
rescue StandardError => e
raise Jekyll::Errors::PostURLError, <<~MSG
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
def render(context)
@context = context
@resolved_markup = @template&.render(@context) || @markup
site = context.registers[:site]
begin
@post_comparer = PostComparer.new(@resolved_markup)
rescue StandardError
raise_markup_parse_error
end
# For backwards compatibility only; deprecated instance_variable.
# To be removed in Jekyll v5.0.
@post = @post_comparer
# First pass-through.
site.posts.docs.each do |post|
return relative_url(post) if @post_comparer == post
site.posts.docs.each do |document|
return relative_url(document) if @post == document
end
# First pass-through did not yield the requested post. Search again using legacy matching
# 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)
# New matching method did not match, fall back to old method
# with deprecation warning if this matches
log_legacy_usage_deprecation
return relative_url(post)
site.posts.docs.each do |document|
next unless @post.deprecated_equality document
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
raise_post_not_found_error
end
private
def raise_markup_parse_error
raise Jekyll::Errors::PostURLError, <<~MSG
Could not parse name of post #{@resolved_markup.inspect} in tag 'post_url'.
Make sure the correct name is given to the tag.
Could not find post "#{@orig_post}" in tag 'post_url'.
Make sure the post exists and the name is correct.
MSG
end
def raise_post_not_found_error
raise Jekyll::Errors::PostURLError, <<~MSG
Could not find post #{@resolved_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 #{@resolved_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

View File

@ -144,13 +144,7 @@ module Jekyll
# pct-encoded = "%" HEXDIG HEXDIG
# sub-delims = "!" / "$" / "&" / "'" / "(" / ")"
# / "*" / "+" / "," / ";" / "="
#
# `Addressable::URI::CharacterClassesRegexps::PATH` is used to encode
# non-alphanumeric characters such as "[", "]", etc.
Addressable::URI.encode_component(
path,
Addressable::URI::CharacterClassesRegexps::PATH
).encode("utf-8").sub("#", "%23")
Addressable::URI.encode(path).encode("utf-8").sub("#", "%23")
end
# Unescapes a URL path segment

View File

@ -80,16 +80,5 @@ class TestURL < JekyllUnitTest
).to_s
end
end
should "not treat colons in placeholders as uri delimiters" do
assert_equal "/foo/foo%20bar:foobar/", URL.new(
:template => "/:x/:y/",
:placeholders => { :x => "foo", :y => "foo bar:foobar" }
).to_s
end
should "unescape urls with colons" do
assert_equal "/foo/foo bar:foobar/", Jekyll::URL.unescape_path("/foo/foo%20bar:foobar/")
end
end
end