很多 Ruby gems 提供了可執(zhí)行的命令行作為它們的功能的一部分。想象一下離開了命令行你能使用 bunlder
或者 rake
嗎? ... 那就不是一個(gè) gem 了!
這些可執(zhí)行文件和我們?cè)谇皫渍驴吹降?web 和測(cè)試的支持庫沒有什么大的區(qū)別。通常來說,它們是獨(dú)立在一個(gè)單獨(dú)的類庫中并且不被入口文件包含用于加載。在本章中,我們將會(huì)集成一個(gè)可執(zhí)行命令行到我們的 mega_lotoo gem
來代理我們已經(jīng)寫好的 drawing 方法。
用例
我們想要?jiǎng)?chuàng)建一個(gè)命令行工具為 mega_lotto
來代理 #draw
方法在 MegaLotto::Drawing
并且返回這樣的東西:
$ mega_lotto
1
6
47
57
15
26
由于 #draw 方法已經(jīng)被實(shí)現(xiàn)了, 創(chuàng)建一個(gè)可執(zhí)行命令不會(huì)很難。
實(shí)現(xiàn)
bin/
目錄是一個(gè)配套的可執(zhí)行文件的標(biāo)準(zhǔn)位置。再一次看看我們的 mega_lotto.gemspec
注意下面行:
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
gemspec 定義了一個(gè)可執(zhí)行文件的列表,通過被提交到 git 并且位于 bin/
目錄下。 所以讓我們加入文件 bin/mega_lotto
。
由于它是一個(gè)腳本會(huì)被命令行執(zhí)行,我們有兩個(gè)重要的改變要做:
在文件的頂部加入
#!/usr/bin/env
并且使用命令chomd +x bin/mega_lotto
允許其被執(zhí)行(假設(shè)你在 linux 或者 macOS 平臺(tái)上)因?yàn)檫@個(gè)文件是獨(dú)立于 gem 的其他部分的,我們不得不加載必要的依賴
#!/usr/bin/env ruby
require_relative "../lib/mega_lotto/drawing"
現(xiàn)在讓我們代理 #draw
方法:
#!/usr/bin/env ruby
require_relative "../lib/mega_lotto/drawing"
drawing = MegaLotto::Drawing.new.draw
puts drawing
讓我們提交我們的改變,并運(yùn)行 rake install
$ mega_lotto
8
11
9
38
43
注意: 如果你使用 Rbenv 來管理 Ruby 版本,你可能需要運(yùn)行 rbenv rehash
在可執(zhí)行文件可被得到前。
選項(xiàng)解析
很少有命令行不支持選項(xiàng)解析。如果你依賴于命令行,-h
是一個(gè)你可能經(jīng)常使用的選項(xiàng)。因?yàn)橹挥泻苌僖徊糠秩丝梢杂涀∥覀內(nèi)粘J褂玫目蓤?zhí)行文件的選項(xiàng)。
允許選項(xiàng)和參數(shù)被傳入命令行可以呈幾何級(jí)數(shù)地增強(qiáng)它的功能。幸運(yùn)的是, Ruby 已經(jīng)有了標(biāo)準(zhǔn)庫內(nèi)置的 OptonParser 類。我把實(shí)現(xiàn)的細(xì)節(jié)留給了其他資源, 但是這是值得一提的,因?yàn)槟愕目蓤?zhí)行文件可不會(huì)像上面提到的那么簡(jiǎn)單。
抽取 CLI 類
可執(zhí)行命令行需要加載更多的代碼而不是僅僅從主命名空間的一兩個(gè)方法。在這種情況下,通常需要分隔到一個(gè)單獨(dú)的文件,就像 lib/mega_lotto/cli.rb
。所以不是加載
require_relative "../lib/mega_lotto/drawing"
在可執(zhí)行文件中,而是包含 require_relative "../lib/mega_lotto/cli"
。
在可執(zhí)行命令中, 我們可以
添加 lib/mega_lotto/cli.rb
文件來負(fù)責(zé)加載依賴并且 lib/mega_lotto/cli.rb
文件可以解析選項(xiàng)(如果有的話)。
現(xiàn)實(shí)中的例子
Bundler 采取了后一種策略(和大多數(shù) gem 做法一樣)并且實(shí)現(xiàn)了一個(gè) cli.rb 類 來管理依賴和可執(zhí)行文件。在那里,功能被包裹來一個(gè)單獨(dú)的類中這樣就容易測(cè)試。測(cè)試 shell 腳本是大多數(shù) Ruby 程序員不擅長(zhǎng)的,所以我們要盡量把責(zé)任轉(zhuǎn)讓給 Ruby 本身。
Resque 是另一例子, 有一個(gè)單獨(dú)的 CLI 類的例子. 事實(shí)上, 看看可執(zhí)行文件的代碼.
非常簡(jiǎn)單, 不是嗎? 這就是我們希望我們的可執(zhí)行文件看上去的樣子 - 簡(jiǎn)單, 直接并且把所有的功能和錯(cuò)誤處理都放到獨(dú)立的 Ruby 類中。
總結(jié)
?寫一個(gè)可執(zhí)行命令行的結(jié)構(gòu)和最佳實(shí)踐超出了本書的范圍。有很多更好的資源關(guān)于這個(gè)主題,并且更加深入細(xì)節(jié),特別是 David Bryant Copeland 的書 <Building Awesome Command-Line Applications in Ruby>。然而,希望你現(xiàn)在能明白包含一個(gè)可執(zhí)行的命令到你的 gem 中是多么簡(jiǎn)單的一件事情。
在下一章,我們將會(huì)實(shí)現(xiàn)一個(gè)配置模式來提供額外的靈活的價(jià)值給我們的 gem 的用戶。