第十六天 - 检查你的列表俩次

Checking Your List Twice

从命令行了解 Raku

这是 Sniffles the Elf 的大好机会!在丝带矿山经过多年的苦差事后,他们终于被提升到了清单管理部门。作为一名闪亮的新助理尼斯名单审核员,Sniffles 正在走向重要时刻。

在 Sniffles 到达的第一天,他们的新老板格伦布尔先生正等着他。“好人清单管理很麻烦,当有人在服务器上洒了牛奶和饼干时,我们的数据被意外删除了。我们一直在忙着检查列表,我们忘了检查备份!现在我们必须从头开始重建一切!裁员后,我们有点人手不足,所以由你来挽救这一天。“

Sniffles,特别勤劳,津津乐道于这个问题。经过一些研究,他们意识到他们需要的所有数据都可用,他们只需要收集它。

他们的朋友在丝带矿山中,一位名叫 Hermie 的自称口述历史学家一直在谈论 Raku 有多么伟大。Sniffles 决定尝试一下。

就像拔牙?

Sniffles 首先用一种新语言抛出标准的第一个脚本:

use v6.d;

say "Nice List restored!!!";

该脚本运行并尽职尽责地打印出消息。距离圣诞节还有几天了,是时候认真对待 Raku文档了。

稍微浏览一下 Sniffles 的 Raku 命令行界面实用程序 页面。他们喜欢它描述的 MAIN 这个特殊子程序的外观。

say 'Started initializing nice lister.';
sub MAIN() { say "Nice List restored!!!" }
say 'Finished initializing nice lister.';

产生:

Started initializing nice lister.
Finished initializing nice lister.
Nice List restored!!!

好吧,至少那是他们的启动代码。Sniffles 抛弃了初始化消息,它们只是噪音。但他们确信这个 MAIN 函数必须有更多的技巧才能让 Hermie 如此兴奋。

回到文档…检查了Y分钟学会X语言的 Raku 页面MAIN 接近尾声的额外部分是金矿!Sniffles 对这个念头打了个寒颤。

“好的,所以如果我们提供 MAIN子 程序签名,Raku 会为我们处理命令行解析。更好的是,它会自动生成帮助内容,“他们对自己嘟囔道。

sub MAIN (
    :$list-of-all-kids,
    :$naughty-list
) { ... }

产生:

$ nice-list
Usage:
  nicelist [--list-of-all-kids=<Any>] [--naughty-list=<Any>]

运行脚本得到:

Stub code executed
  in sub MAIN at foo line 1
  in block <unit> at foo line 1

真棒。

但是开关名称有点长。由于 TheNorthPole.io 是一个专门的商店,Sniffles 认为他们可能不得不输入一堆。呸。如果您可以添加一些解释性文字,更短的名称将没有问题。Raku 支持使用 POD6 标记进行文字编程,因此可以轻松添加注释。

#| Rebuild the Nice List
sub MAIN (
    :$all,    #= path to file containing the list of all children
    :$naughty #= path to file containing the Naughty List
) { ... }

产生:

Usage:
  nicelist [--all=<Any>] [--naughty=<Any>] -- Rebuild the Nice List
  
    --all=<Any>        path to file containing the list of all children
    --naughty=<Any>    path to file containing the Naughty List

Sniffles 印象深刻,但他们知道参数验证是编写 CLI 的另一部分,可能会变得乏味。“Raku 最近为我做了什么?”他们想知道。

一种强大的,沉默的类型

Raku 有一个渐进式类型系统,包括编译和运行时类型检查。渐进类型允许 Sniffles 到目前为止忽略类型检查。他们添加了一些类型,看看发生了什么。

Sniffles 使用类型 smiley定义了 Str 的子集,该类型使用 whatevercode 来验证给定路径上是否存在文件。

subset FilePath of Str:D where *.IO.f;

#| Rebuild the Nice List
sub MAIN (
    FilePath :$all,    #= path to file containing the list of all children
    FilePath :$naughty #= path to file containing the Naughty List
) { ... }

他们运行这个脚本:

$nice-list  --naughty=naughty.kids --all=notAFile.bleh
Usage:
  nice-list [--all=<FilePath>] [--naughty=<FilePath>] -- Rebuild the Nice List
  
    --all=<FilePath>        path to file containing the list of all children
    --naughty=<FilePath>    path to file containing the Naughty List

Sniffles 在没有争论和其他一些无效方式的情况下再次运行脚本。每次捕获无效输入并自动显示使用消息。 “非常好,”Sniffles 想道,“事实上,错误报告仍然很糟糕。如果你抛出一个参数就好像传入一个丢失的文件一样,你会得到相同的结果。”

精灵类型不匹配 - 弥补改进的错误处理

“Ugh! How do I get around this problem?” Sniffles shuffled around the docs some more. Multiple Dispatch and slurpy parameters. They added another subset and a couple of new definitions of MAIN:

subset FileNotFound of Str:D where !*.IO.f();
    
multi sub MAIN (
    FilePath :$all,    #= path to file containing the list of all children
    FilePath :$naughty #= path to file containing the Naughty List
) { ... }
    
multi sub MAIN (
    FileNotFound :$all,
    *%otherStuff
) {
    die "List of all children file does not exist";
}
    
multi sub MAIN (
    FileNotFound :$naughty,
    *%otherStuff
) {
    die "Naughty List file does not exist";
}

他们得到了:

Usage:
  nice-list [--all=<FilePath>] [--naughty=<FilePath>] -- Rebuild the Nice List
  nice-list [--all=<FileNotFound>] [--naughty=<FilePath>]
  nice-list [--all=<FilePath>] [--naughty=<FileNotFound>]
  
    --all=<FilePath>        path to file containing the list of all children
    --naughty=<FilePath>    path to file containing the Naughty List

哪个工作完美…除了现在他们在使用中有错误生成条目!双翘。Sniffles返回到CLI界面上的文章。将正确的特征添加到MAIN潜艇将使它们从自动生成的使用中消失:

multi sub MAIN (
    FileNotFound :$all,
    *%otherStuff
) is hidden-from-USAGE {
    die "List of all children file does not exist";
}

一团糟不见了!

我们不会去,直到我们得到一些!

Grumble 先生走了过来,他停下来看着 Sniffles 的屏幕。“那里有趣的工作,Sniffles。我们需要那个脚本,我们昨天需要它。哦,我们需要它能够审核现有的 Nice List 并重建一个。我们也需要这个。看到你。“在Sniffles眨眼之前他消失了。

Sniffles 认为,做一个爬行的功能比被迫吃无花果布丁更好。他们添加了这些命令:

#| Rebuild the Nice List
multi sub MAIN (
    'build',
    FilePath :$all,    #= path to file containing the list of all children
    FilePath :$naughty #= path to file containing the Naughty List
) { ... }
    
#| Compare all the lists for correctness
multi sub MAIN (
    'audit',
    FilePath :$all,     #= path to file containing the list of all children
    FilePath :$naughty, #= path to file containing the Naughty List
    FilePath :$nice,    #= path to file containing the Nice List
) { ... }

“好极了,”他们想,“但你必须像这样运行脚本 nicelist --all=foo --naughty=bar build。可怕。”

my %*SUB-MAIN-OPTS =
    :named-anywhere,    # allow named variables at any location 
;

“它被修复了!” Sniffles 在座位上跳起来了。

Usage:
  nicelist build [--all=<FilePath>] [--naughty=<FilePath>] -- Rebuild the Nice List
  nicelist audit [--all=<FilePath>] [--naughty=<FilePath>] [--nice=<FilePath>] -- Compare all the lists for correctness
  
    --all=<FilePath>        path to file containing the list of all children
    --naughty=<FilePath>    path to file containing the Naughty List
    --nice=<FilePath>       path to file containing the Nice List

跑步者走上了这条路。

好的,现在 Sniffles 拥有一个完美的框架来构建一个优秀的实用程序脚本。是时候实际写出实际的东西了。Sniffles 知道他们真的打算雪橇这个项目。

很快,Snuffles发现Raku的功能集帮助他们制作了一个功能强大,正确的脚本。他们创建了一个 Child ,在其上定义了身份操作,编写了一个用于加载列表数据的简洁 CSV 解析器和一个报告函数。内置的 Set 数据类型提供了操作符,可以轻松查找不合适的条目,甚至更容易重建 Nice List。

一旦完成,他们就恢复了 Nice List,并向 Grumbles 先生及其他团队发送了一封部门电子邮件,宣布他们取得了成功。当格罗布尔斯先生看到脚本有多好,它的用法和错误检查,仅此一次,他辜负了他们的期望。

为了表彰他们的辛勤工作和机智,Sniffles 被要求在圣诞老人最新工作室的开幕处剪彩。

comments powered by Disqus