もしあなたがRubyプログラマーなら、Rake (故Jim Weirichによって作られたビルドユーティリティー) をほぼ確実に使ったことがあるだろう。しかし、Rakeがいかにパワフルでフレキシブルなツールになりうるかということは理解していないかもしれない。 実際、私は、Quarto (自作のe-book製作ツールチェーン)の土台としてRakeを使うと決めるまで理解していなかった。
この投稿はRakeについてのシリーズの一部で、基本から始めて高度な使用法に進む。 2013年の8月から9月に購読者に向け公開されたRubyTapasビデオのシリーズのひとつをもとにしている。 各投稿はビデオから始まり、ビデオより文字を好む人の ためにスクリプトが続く。
これらのエピソードを無料で公開するにあたり、より多くの人々が、 この広く行き渡っているが過小評価されているツールのすべての能力を知り愛するようになることを願う。もしあなたがRakeに感謝するのであれば、Jimへの追悼として the Weirich Fundへの寄付を検討して欲しい。
前回のエピソードでは、このRakefileを書きました。これは3つのMarkdownファイルから HTMLファイルへのビルドを自動化します。
task :default => :html
task :html => %W[ch1.html ch2.html ch3.html]
rule ".html" => ".md" do |t|
sh "pandoc -o #{t.name} #{t.source}"
end
本当は、処理するファイルを追加するたびに、このファイルを修正したくありません。 その代りに、自動的にビルドするファイルをRakefileに見つけさせたいのです。
いくつかの実験をするために、サンプルのプロジェクトディレクトリを作りました。 そこには4章分のMarkdownファイルと、サブディレクトリに1つの付録用のファイルが含まれていて、それらすべてがHTMLファイルにビルドされるべきものです。 また、それ以外のビルドしたくないファイルがいくつかあります。
~ch1.md
ファイルはエディタによって残された一時ファイルのようです。 また、scratchディレクトリには、無視すべきファイルが含まれています。
$ tree
.
├── ~ch1.md
├── ch1.md
├── ch2.md
├── ch3.md
├── ch4.markdown
├── scratch
│ └── test.md
├── subdir
│ └── appendix.md
└── temp.md
このプロジェクトはGitのバージョン管理下にあります。もしGitに、管理しているファイルの一覧を表示するよう告げれば、先程見たファイルのサブセットを確認できます。 特に、
temp.md
と呼ばれるファイルがないことに注意してください。このファイルは Gitに現在登録されおらず、今後も登録されることはないでしょう。 また、ビルドするファイルのリストからも除外されるべきものです。
$ git ls-files
ch1.md
ch2.md
ch3.md
ch4.markdown
scratch/test.md
subdir/appendix.md
~ch1.md
ビルドすべきファイルだけを自動的に発見するために、Rakeのfile listを使います。 file listがどんなもので何ができるのかを調べてみましょう。
file listを作るために、
Rake::FileList
クラスの添字演算子を使用し、 ファイルを表す文字列のリストを引き渡します。
require 'rake'
files = Rake::FileList["ch1.md", "ch2.md", "ch3.md"]
files # => ["ch1.md", "ch2.md", "ch3.md"]
ここまでは、あまりエキサイティングではありません。しかし、まだ始まったばかりです。 ファイルをひとつずつ列挙する代わりに、FileListを使ってshell globパターンを渡すことができます。パターン
*.md
を与えてみましょう。
require 'rake'
Dir.chdir "project"
files = Rake::FileList["*.md"]
files # => ["ch1.md", "temp.md", "ch3.md", "ch2.md", "~ch1.md"]
ここでFileListのパワーが分かり始めます。しかし、これは我々が望むファイルのリスト通りではりません。無視したいファイルがいくつか含まれ、必要なファイルがいくつか足りません。
足りないファイルから対処していきます。長い拡張子を使っているファイルを見つけるために
*.markdown
パターンを追加します。
require 'rake'
Dir.chdir "project"
files = Rake::FileList["*.md", "*.markdown"]
files # => ["ch1.md", "temp.md", "ch3.md", "ch2.md", "~ch1.md", "ch4.markdown"]
しかしまだ付録のファイルがありません。これを修正するために、globパターンをプロジェクトのディレクトリーツリー内のあらゆるレベルに一致するよう変更します。
require 'rake'
Dir.chdir "project"
files = Rake::FileList["**/*.md", "**/*.markdown"]
puts files
# >> ch1.md
# >> temp.md
# >> ch3.md
# >> ch2.md
# >> scratch/test.md
# >> ~ch1.md
# >> subdir/appendix.md
# >> ch4.markdown
これで、4章分と付録のすべてが見つかりましたが、同時に不要なものもたくさん 集めてしてしまいました。 ファイルの一覧を選別していきましょう。 このために、exclusionパターンを使用します。
文字
~
で始まるファイルを無視するところから始めます。
require 'rake'
Dir.chdir "project"
files = Rake::FileList["**/*.md", "**/*.markdown"]
files.exclude("~*")
puts files
# >> ch1.md
# >> temp.md
# >> ch3.md
# >> ch2.md
# >> scratch/test.md
# >> subdir/appendix.md
# >> ch4.markdown
次に
scratch
ディレクトリ内のファイルを無視します。それが可能なことを示すために、shell globの代わりに正規表現を使って除外します。
require 'rake'
Dir.chdir "project"
files = Rake::FileList["**/*.md", "**/*.markdown"]
files.exclude("~*")
files.exclude(/^scratch\//)
puts files
# >> ch1.md
# >> temp.md
# >> ch3.md
# >> ch2.md
# >> subdir/appendix.md
# >> ch4.markdown
まだ
temp.md
が残っています。先程見たように、このファイルはGitで管理されていません。 Gitで管理されていないファイルを無視するexclusionルールを作成したいと思います。 これを実現するために、.exclude
にブロックを渡します。その中に Gitがファイルを感知しているかどうかを判断する呪文を書きます。
require 'rake'
Dir.chdir "project"
files = Rake::FileList["**/*.md", "**/*.markdown"]
files.exclude("~*")
files.exclude(/^scratch\//)
files.exclude do |f|
`git ls-files #{f}`.empty?
end
puts files
# >> ch1.md
# >> ch3.md
# >> ch2.md
# >> subdir/appendix.md
# >> ch4.markdown
これにより一時ファイルが除外され、ついに取り扱いたいファイルの一覧だけが残りました。
次に、もう少しFileListの定義の意図がわかるよう、コードを修正します。 添字の省略記法を
FileList.new
に変更し、ブロックを引き渡します。 FileListはこのブロックに自身をyieldします。これは、ブロックの中にすべての exclusionが設定できることを意味します。
require 'rake'
Dir.chdir "project"
files = Rake::FileList.new("**/*.md", "**/*.markdown") do |fl|
fl.exclude("~*")
fl.exclude(/^scratch\//)
fl.exclude do |f|
`git ls-files #{f}`.empty?
end
end
puts files
# >> ch1.md
# >> ch3.md
# >> ch2.md
# >> subdir/appendix.md
# >> ch4.markdown
Rakefile
に戻る前に、ファイルの一覧に対して、もう1箇所変更する必要があります。 Rakefile
では、ビルドに対応するソースファイルではなく、ビルドされるファイルの一覧が必要でした。 入力ファイルの一覧を出力ファイルの一覧に変換んするために、#ext
メソッドを使用します。 そのメソッドに.html
ファイルの拡張子を渡すと、もとのMarkdown拡張子がすべて.html
に 置き換えられた、新たなファイルの一覧を返します。
require 'rake'
Dir.chdir "project"
files = Rake::FileList.new("**/*.md", "**/*.markdown") do |fl|
fl.exclude("~*")
fl.exclude(/^scratch\//)
fl.exclude do |f|
`git ls-files #{f}`.empty?
end
end
puts files.ext(".html")
# >> ch1.html
# >> ch3.html
# >> ch2.html
# >> subdir/appendix.html
# >> ch4.html
Rakefileに戻る準備ができました。ハードコードされた対象ファイルのリストを先程作ったFileListに置き換えます。
.md
または.markdown
のいずれの拡張子をもつMarkdownファイルもサポートしているので、 どちらのファイルからもHTMLファイルをビルドできることをRakeに告げるよう、あと1箇所変更する必要があります。今のところ、単にルールを重複することによってこれを実現します。 将来的に、この重複を避ける方法を見ていきます。
source_files = Rake::FileList.new("**/*.md", "**/*.markdown") do |fl|
fl.exclude("~*")
fl.exclude(/^scratch\//)
fl.exclude do |f|
`git ls-files #{f}`.empty?
end
end
task :default => :html
task :html => source_files.ext(".html")
rule ".html" => ".md" do |t|
sh "pandoc -o #{t.name} #{t.source}"
end
rule ".html" => ".markdown" do |t|
sh "pandoc -o #{t.name} #{t.source}"
end
rake
を実行すると、しかるべきHTMLファイルがすべてビルドされることを確認できます。
$ rake
pandoc -o ch1.html ch1.md
pandoc -o ch2.html ch2.md
pandoc -o ch3.html ch3.md
pandoc -o subdir/appendix.html subdir/appendix.md
pandoc -o ch4.html ch4.markdown
Rakeについて、今日はこれで十分でしょう。ハッピーハッキング!
Rakeについてのエピソード・文章を楽しんでいただけたことを願っている。もし今日、何かしら学んだのであれば、Jimの遺産である教育プログラムの継続を支援するため、the Weirich Fundに寄付することにより「恩送りすること(訳注:原文は"paying it forward")」を検討して欲しい。 もし、今回のようなビデオをもっと見たければ、RubyTapas をじっくり見てほしい。シリーズが完結するまで、ほぼ毎日公開していくつもりなので、ほどなくご確認を!
P.S. 特に興味深い方法でRakeを使っているなら、連絡して欲しい。
関連する投稿:
0 件のコメント:
コメントを投稿