处理 Jekyll 内容以将任何 post 标题的首次出现替换为具有该标题的 post 的超链接
Process Jekyll content to replace first occurrence of any post title with a hyperlink of the post with that title
我想做什么
我正在构建一个 Jekyll ruby 插件,它将用链接到 URL 的超链接替换 post 复制文本内容中任何单词的第一次出现70=]同名
我遇到的问题
我已经让这个工作了,但我无法找出 process_words
方法中的两个问题:
- 如何仅在 post 的主要内容副本文本中搜索 post 标题,而不是 post 或 table 之前的元标记内容(也是在 main post copy text 之前生成的)?我无法让它与 Nokigiri 一起工作,尽管这似乎是这里的首选工具。
- 如果 post 的 URL 不在
post.data['url']
,它在哪里?
- 此外,是否有更高效、更简洁的方法来执行此操作?
当前代码有效,但会替换第一次出现的代码,即使它是 HTML 属性的值,如锚点或元标记。
示例结果
我们有一个博客有 3 posts:
- 爱好
- 食物
- 自行车
而在 "Hobbies" post body 文本中,我们有一个句子,每个单词在 post 中首次出现,如下所示:
I love mountain biking and bicycles in general.
插件将处理该句子并将其输出为:
I love mountain biking and <a href="https://example.com/link/to/bicycles/">bicycles</a> in general.
我当前的代码(更新 1)
# _plugins/hyperlink_first_word_occurance.rb
require "jekyll"
require 'uri'
module Jekyll
# Replace the first occurance of each post title in the content with the post's title hyperlink
module HyperlinkFirstWordOccurance
POST_CONTENT_CLASS = "page__content"
BODY_START_TAG = "<body"
ASIDE_START_TAG = "<aside"
OPENING_BODY_TAG_REGEX = %r!<body(.*)>\s*!
CLOSING_ASIDE_TAG_REGEX = %r!</aside(.*)>\s*!
class << self
# Public: Processes the content and updates the
# first occurance of each word that also has a post
# of the same title, into a hyperlink.
#
# content - the document or page to be processes.
def process(content)
@title = content.data['title']
@posts = content.site.posts
content.output = if content.output.include? BODY_START_TAG
process_html(content)
else
process_words(content.output)
end
end
# Public: Determines if the content should be processed.
#
# doc - the document being processes.
def processable?(doc)
(doc.is_a?(Jekyll::Page) || doc.write?) &&
doc.output_ext == ".html" || (doc.permalink&.end_with?("/"))
end
private
# Private: Processes html content which has a body opening tag.
#
# content - html to be processes.
def process_html(content)
content.output = if content.output.include? ASIDE_START_TAG
head, opener, tail = content.output.partition(CLOSING_ASIDE_TAG_REGEX)
else
head, opener, tail = content.output.partition(POST_CONTENT_CLASS)
end
body_content, *rest = tail.partition("</body>")
processed_markup = process_words(body_content)
content.output = String.new(head) << opener << processed_markup << rest.join
end
# Private: Processes each word of the content and makes
# the first occurance of each word that also has a post
# of the same title, into a hyperlink.
#
# html = the html which includes all the content.
def process_words(html)
page_content = html
@posts.docs.each do |post|
post_title = post.data['title'] || post.name
post_title_lowercase = post_title.downcase
if post_title != @title
if page_content.include?(" " + post_title_lowercase + " ") ||
page_content.include?(post_title_lowercase + " ") ||
page_content.include?(post_title_lowercase + ",") ||
page_content.include?(post_title_lowercase + ".")
page_content = page_content.sub(post_title_lowercase, "<a href=\"#{ post.url }\">#{ post_title.downcase }</a>")
elsif page_content.include?(" " + post_title + " ") ||
page_content.include?(post_title + " ") ||
page_content.include?(post_title + ",") ||
page_content.include?(post_title + ".")
page_content = page_content.sub(post_title, "<a href=\"#{ post.data['url'] }\">#{ post_title }</a>")
end
end
end
page_content
end
end
end
end
Jekyll::Hooks.register %i[posts pages], :post_render do |doc|
# code to call after Jekyll renders a post
Jekyll::HyperlinkFirstWordOccurance.process(doc) if Jekyll::HyperlinkFirstWordOccurance.processable?(doc)
end
更新 1
根据@Keith Mifsud 的建议更新了我的代码。现在使用边栏的 aside
元素或 page__content
class 到 select body 内容进行处理。
还改进了检查和替换正确术语。
PS:我开始使用我的插件的代码库示例是 @Keith Mifsud's jekyll-target-blank plugin
这段代码看起来很熟悉 :) 我建议您查看 Rspecs 测试文件来测试您的问题:https://github.com/keithmifsud/jekyll-target-blank
我会尽力回答您的问题,抱歉,在撰写本文时我无法亲自测试这些问题。
如何只在post的主要内容复制文本中搜索post标题,而不是在post或table 的内容(也是在 main post copy text 之前生成的)?我无法让它与 Nokigiri 一起工作,尽管这似乎是这里的首选工具。
您的要求是:
1) 忽略 <body></body>
标签之外的内容。
这似乎已经在 process_html()
方法中实现了。此方法说明了 body_content
的唯一过程,它应该按原样工作。你有测试吗?你是如何调试它的?相同的字符串拆分在我的插件中有效。 IE。仅处理 body 中的内容。
2) 忽略目录 (TOC) Table 内的内容。
我建议您通过进一步拆分 body_content
变量来扩展 process_html()
方法。在目录的开始和结束标记之间搜索内容(通过 id、css class 等)并排除它,然后将其添加回 process_words
之前或之后的位置字符串。
3) 是否使用Nokigiri插件?
这个插件非常适合解析 html。我认为您正在解析字符串,然后创建 html。所以 vanilla Ruby 和 URI 插件应该足够了。如果你愿意,你仍然可以使用它,但它不会比在 ruby.
中拆分字符串更快
如果post的URL不在post.data['url'],它在哪里?
我认为你应该有一个方法来获取所有 post 标题,然后将 "words" 与数组匹配。您可以从文档本身 doc.site.posts
和 foreach post return 标题中获取所有 posts collection。 process_words()
方法可以检查每个作品以查看它是否与数组中的项目相匹配。但是,如果标题由多个单词组成怎么办?
此外,是否有更高效、更简洁的方法来执行此操作?
到目前为止一切顺利。我将从解决问题开始,然后重构速度和编码标准。
我再次建议您使用测试来帮助您。
如果我能提供更多帮助,请告诉我:)
我想做什么
我正在构建一个 Jekyll ruby 插件,它将用链接到 URL 的超链接替换 post 复制文本内容中任何单词的第一次出现70=]同名
我遇到的问题
我已经让这个工作了,但我无法找出 process_words
方法中的两个问题:
- 如何仅在 post 的主要内容副本文本中搜索 post 标题,而不是 post 或 table 之前的元标记内容(也是在 main post copy text 之前生成的)?我无法让它与 Nokigiri 一起工作,尽管这似乎是这里的首选工具。
- 如果 post 的 URL 不在
post.data['url']
,它在哪里? - 此外,是否有更高效、更简洁的方法来执行此操作?
当前代码有效,但会替换第一次出现的代码,即使它是 HTML 属性的值,如锚点或元标记。
示例结果
我们有一个博客有 3 posts:
- 爱好
- 食物
- 自行车
而在 "Hobbies" post body 文本中,我们有一个句子,每个单词在 post 中首次出现,如下所示:
I love mountain biking and bicycles in general.
插件将处理该句子并将其输出为:
I love mountain biking and <a href="https://example.com/link/to/bicycles/">bicycles</a> in general.
我当前的代码(更新 1)
# _plugins/hyperlink_first_word_occurance.rb
require "jekyll"
require 'uri'
module Jekyll
# Replace the first occurance of each post title in the content with the post's title hyperlink
module HyperlinkFirstWordOccurance
POST_CONTENT_CLASS = "page__content"
BODY_START_TAG = "<body"
ASIDE_START_TAG = "<aside"
OPENING_BODY_TAG_REGEX = %r!<body(.*)>\s*!
CLOSING_ASIDE_TAG_REGEX = %r!</aside(.*)>\s*!
class << self
# Public: Processes the content and updates the
# first occurance of each word that also has a post
# of the same title, into a hyperlink.
#
# content - the document or page to be processes.
def process(content)
@title = content.data['title']
@posts = content.site.posts
content.output = if content.output.include? BODY_START_TAG
process_html(content)
else
process_words(content.output)
end
end
# Public: Determines if the content should be processed.
#
# doc - the document being processes.
def processable?(doc)
(doc.is_a?(Jekyll::Page) || doc.write?) &&
doc.output_ext == ".html" || (doc.permalink&.end_with?("/"))
end
private
# Private: Processes html content which has a body opening tag.
#
# content - html to be processes.
def process_html(content)
content.output = if content.output.include? ASIDE_START_TAG
head, opener, tail = content.output.partition(CLOSING_ASIDE_TAG_REGEX)
else
head, opener, tail = content.output.partition(POST_CONTENT_CLASS)
end
body_content, *rest = tail.partition("</body>")
processed_markup = process_words(body_content)
content.output = String.new(head) << opener << processed_markup << rest.join
end
# Private: Processes each word of the content and makes
# the first occurance of each word that also has a post
# of the same title, into a hyperlink.
#
# html = the html which includes all the content.
def process_words(html)
page_content = html
@posts.docs.each do |post|
post_title = post.data['title'] || post.name
post_title_lowercase = post_title.downcase
if post_title != @title
if page_content.include?(" " + post_title_lowercase + " ") ||
page_content.include?(post_title_lowercase + " ") ||
page_content.include?(post_title_lowercase + ",") ||
page_content.include?(post_title_lowercase + ".")
page_content = page_content.sub(post_title_lowercase, "<a href=\"#{ post.url }\">#{ post_title.downcase }</a>")
elsif page_content.include?(" " + post_title + " ") ||
page_content.include?(post_title + " ") ||
page_content.include?(post_title + ",") ||
page_content.include?(post_title + ".")
page_content = page_content.sub(post_title, "<a href=\"#{ post.data['url'] }\">#{ post_title }</a>")
end
end
end
page_content
end
end
end
end
Jekyll::Hooks.register %i[posts pages], :post_render do |doc|
# code to call after Jekyll renders a post
Jekyll::HyperlinkFirstWordOccurance.process(doc) if Jekyll::HyperlinkFirstWordOccurance.processable?(doc)
end
更新 1
根据@Keith Mifsud 的建议更新了我的代码。现在使用边栏的 aside
元素或 page__content
class 到 select body 内容进行处理。
还改进了检查和替换正确术语。
PS:我开始使用我的插件的代码库示例是 @Keith Mifsud's jekyll-target-blank plugin
这段代码看起来很熟悉 :) 我建议您查看 Rspecs 测试文件来测试您的问题:https://github.com/keithmifsud/jekyll-target-blank
我会尽力回答您的问题,抱歉,在撰写本文时我无法亲自测试这些问题。
如何只在post的主要内容复制文本中搜索post标题,而不是在post或table 的内容(也是在 main post copy text 之前生成的)?我无法让它与 Nokigiri 一起工作,尽管这似乎是这里的首选工具。
您的要求是:
1) 忽略 <body></body>
标签之外的内容。
这似乎已经在 process_html()
方法中实现了。此方法说明了 body_content
的唯一过程,它应该按原样工作。你有测试吗?你是如何调试它的?相同的字符串拆分在我的插件中有效。 IE。仅处理 body 中的内容。
2) 忽略目录 (TOC) Table 内的内容。
我建议您通过进一步拆分 body_content
变量来扩展 process_html()
方法。在目录的开始和结束标记之间搜索内容(通过 id、css class 等)并排除它,然后将其添加回 process_words
之前或之后的位置字符串。
3) 是否使用Nokigiri插件? 这个插件非常适合解析 html。我认为您正在解析字符串,然后创建 html。所以 vanilla Ruby 和 URI 插件应该足够了。如果你愿意,你仍然可以使用它,但它不会比在 ruby.
中拆分字符串更快如果post的URL不在post.data['url'],它在哪里?
我认为你应该有一个方法来获取所有 post 标题,然后将 "words" 与数组匹配。您可以从文档本身 doc.site.posts
和 foreach post return 标题中获取所有 posts collection。 process_words()
方法可以检查每个作品以查看它是否与数组中的项目相匹配。但是,如果标题由多个单词组成怎么办?
此外,是否有更高效、更简洁的方法来执行此操作?
到目前为止一切顺利。我将从解决问题开始,然后重构速度和编码标准。
我再次建议您使用测试来帮助您。
如果我能提供更多帮助,请告诉我:)