在这种情况下,您实际上并不需要“生成代码行”。正如@Uri 试图在他的回答中解释的那样,该方法的内容没有什么特别之处rule
;它只是简单的 Ruby 代码。因此,您可以在 Ruby 中执行的任何操作都可以在该规则方法中执行,包括读取文件、动态调用方法和调用对象上的方法。
让我分解您现有的代码,以便更好地解释同一问题的动态解决方案如何工作:
rule(:keyword) {
# Stuff here
}
这段代码在这里调用一个rule
方法并传递它:keyword
和一个代码块。在某些时候,parslet 将调用该块并检查其返回值。Parslet 可能会选择使用 调用块instance_exec
,这可以更改块正在执行的上下文,以使在块外部不可用的方法(例如str
,也许)在块内部可用。
str('keyword1')
在这里,在规则块的上下文中,您正在调用以str
字符串“keyword1”命名的方法,并获取结果。这里没什么特别的,这只是一个普通的方法调用。
str('keyword1') | str('keyword2')
在这里,|
操作符实际上只是在str('keyword1')
返回的任何内容上调用的方法。此代码等效于str('keyword1').send(:'|', str('keyword2'))
.
str('keyword1') |
str('keyword2') |
str('keyword2')
和以前一样,只是这次我们调用返回|
的任何内容str('keyword1').send(:'|', str('keyword2'))
。此方法调用的结果在调用块时返回给rule
方法。
所以现在您知道这一切是如何工作的,您可以根据文件的内容动态地执行完全相同的操作(str
使用每个关键字调用,并使用该|
方法“累加”结果):
rule(:keyword) {
File.readlines("keywords.txt").map(&:chomp).map { |k| str(k) }.inject(:|)
}
分解:
rule(:keyword) { # Call the rule method with the `:keyword` argument, and pass
# it this block of code.
File.readlines("keywords.txt"). # Get an array of strings containing all the
# keywords
map(&:chomp). # Remove surrounding whitespace from each keyword in the array,
# by calling `chomp` on them. (The strings returned by
# `File.readlines` include the newline character at the end of
# each string.)
map { |k| str(k) }. # Convert each keyword in the array into whatever is
# returned by calling `str` with that keyword.
inject(:|) # Reduce the returned objects to a single one using the `|`
# method on each object. (Equivalent to obj1 | obj2 | obj3...)
}
就是这样!看?无需生成任何代码行,只需执行真实代码正在执行的操作,而是动态执行!