查找块
文档加载(或部分加载)后,您可以遍历文档以查找块节点。有两种查找块节点的方法。一种方法是从 Document 对象开始向下遍历树。所有块都可以从 Document 对象访问。但是,查找块的更快捷的方法是使用 find_by 方法,该方法会自动为您进行遍历。我们将从这里开始,然后查看如何使用自定义遍历方法。
find_by
每个块节点(已解析的块),包括 Document 对象,都提供 find_by 方法。此方法旨在帮助您快速查找后代块。由于某些块具有不同的模型,因此此方法可以帮助您导航文档,而无需担心这些细微差别。
find_by 方法仅查找块节点。它不查找内联节点。 |
如果您想查找已解析文档中的任何块,请在 Document 对象上调用 find_by 方法。否则,您可以调用相关块的祖先上的 find_by 方法来查找文档特定区域中的块。
此方法的返回值是一个扁平的块数组,按文档顺序排列,并且已匹配。这些块之间的关系仅通过它们各自的模型来保留。如果没有匹配的块,则方法返回一个空数组。
所有块
如果未提供任何参数,find_by 方法将返回从调用它的块开始的所有块。如果调用 Document 对象,它将返回文档中的所有块(不包括 AsciiDoc 表格单元格中的块),包括文档本身。这是一个例子
require 'asciidoctor'
doc = Asciidoctor.load_file 'input.adoc', safe: :safe
puts doc.find_by
这是一个查找第一个节中所有块的示例
doc.sections.first.find_by
请注意,find_by 方法始终将您开始的块作为第一个结果返回(假设它也匹配稍后介绍的提供的选择器)。如果要排除该块,请将其从结果中切片掉。
puts doc.find_by.slice 1..-1
如果您只查找第一个结果,可以从结果数组中提取它。
puts doc.find_by.first
默认情况下,并且为了向后兼容,find_by 方法不会深入遍历 AsciiDoc 表格单元格。如果您希望它在这些单元格中查找块,请将选择器 Hash 中的 :traverse_documents 键设置为 true。
all_blocks = doc.find_by traverse_documents: true
下一节将介绍如何过滤返回的块。
过滤块
使用 find_by 方法时,您可能正在查找特定的块。该方法接受一个可选的选择器(一个 Hash)和一个可选的块过滤器(一个 Ruby proc)。该方法将遍历整个树(如果 :traverse_documents 为 true,则包括 AsciiDoc 表格单元格)来查找块。默认情况下,它会向下遍历不匹配的块,尽管可以使用块过滤器控制此行为。
匹配块的最简单方法是使用选择器。选择器是一个 Hash,它接受四个预定义的符号键。
- :context
-
单个块 上下文(即块名称),例如
:paragraph。 - :style
-
单个块样式,例如
source。 - :id
-
ID。
- :role
-
单个角色。
如果指定了 :id,则该方法永远不会返回超过一个块,因为 ID 本身是全局唯一的。以下是如何使用 :id 选择器按 ID 查找块的示例。
match = (doc.find_by id: 'prerequisites').first
现在,假设我们要匹配所有是源块的列表块。我们可以通过组合 :context 和 :style 选择器来实现。
some_source_blocks = doc.find_by context: :listing, style: 'source'
由于字面块也可以是源块,如果我们想要所有源块,我们需要省略 :context 选择器。
all_source_blocks = doc.find_by style: 'source'
如果我们想要所有带有特定角色的块,我们可以使用 :role 选择器来查找它们。
blocks_with_role = doc.find_by role: 'try-it'
选择器 Hash 的设计有意简单,以便于查找块。如果您要查找的块无法使用该选择器进行描述,那么您将需要使用块过滤器。
块过滤器是一个 Ruby proc,它会在访问的每个块上运行。它接受候选块作为唯一参数(即,将候选块传递给 proc)。如果 proc 返回 true,则认为候选块已匹配。
以下是使用块过滤器查找所有顶层节的示例。
top_level_sections = doc.find_by {|block| block.context == :section && block.level == 1 }
通过将其与选择器结合使用,我们可以使其更有效率。
top_level_sections = doc.find_by(context: :section) {|section| section.level == 1 }
如果提供了 Ruby 块,则它将作为附加过滤器应用于选择器。换句话说,候选块必须同时匹配选择器和过滤器。
控制遍历
块过滤器的优势在于它还允许您控制遍历。过滤器方法可以返回以下任何关键字。
- true
- :accept
-
接受块,继续遍历。
- false
- :skip
-
跳过块,但遍历其子项。
- :reject
-
拒绝块,并且不遍历其子项。
- :prune
-
接受块,但不遍历其后代。
以下是匹配所有未包含在另一个块内的侧边栏的高效方法。
top_level_sidebars = doc.find_by do |block|
if block == block.document
:skip
elsif block.context == :sidebar
:prune
else
:reject
end
end
过滤器必须返回 :skip 而不是 :reject 来处理文档对象,否则将不会遍历任何块。
如果组合使用选择器和块过滤器,您将对遍历哪些节点的可控性会降低。因此,如果您将使用块过滤器来控制遍历,最好将所有逻辑都放在该过滤器中。