2014年6月24日火曜日

Rake Part 4: Pathmap 翻訳

Avdi GrimmLearn Advanced Rake in 7 Episodesを、本人の許可を得て翻訳します。以下、Part 4の翻訳です。



もしあなたがRubyプログラマーなら、Rake(故Jim Weirichによって作られたビルドユーティリティー)をほぼ確実に使ったことがあるだろう。しかし、Rakeがいかにパワフルでフレキシブルなツールになりうるかということは理解していないかもしれない。実際、私は、Quarto(自作のe-book製作ツールチェーン)の土台としてRakeを使うと決めるまで理解していなかった。

この投稿はRakeについてのシリーズの一部で、基本から始めて高度な使用法に進む。2013年の8月から9月に購読者に向け公開されたRubyTapasビデオのシリーズのひとつをもとにしている。各投稿はビデオから始まり、ビデオより文字を好む人のためにスクリプトが続く。

これらのエピソードを無料で公開するにあたり、より多くの人々が、この広く行き渡っているが過小評価されているツールのすべての能力を知り愛するようになることを願う。もしあなたがRakeに感謝するのであれば、Jimへの追悼としてthe Weirich Fundへの寄付を検討して欲しい。





今日は、Rakeツールの探求を続けるべく、Rakeのもっともパワフルな機能のひとつである#pathmapメソッドをお見せしたいと思います。
これは数エピソード前に考えたFileListです。プロジェクトディレクトリ内にあるHTMLファイルにビルドされるMarkdownファイルのリストを出力します。

 
require "rake"
Dir.chdir "project"
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

SOURCE_FILES
# => ["ch1.md", "ch3.md", "ch2.md", "subdir/appendix.md", "ch4.markdown"]

Markdown拡張子を.htmlに変換することによって入力ファイルのリストをターゲットファイルのリストに変換するために#extメソッドを使用できることはすでに見てきました。

 
require './source_files'
SOURCE_FILES.ext('html')
# => ["ch1.html", "ch3.html", "ch2.html", "subdir/appendix.html", "ch4.html"]

ソフトウェアのビルド、ソフトウェアのパッケージング、あるいはシステム管理タスクを扱っている時、ファイルのリストを受け取り、そこから2番目の、修正されたリストを生成したいことはよくあります。あるファイル拡張子のセットを別のセットに変換することは、このような問題の一例すぎません。ファイル拡張子を置換する以外のことを行いたい場合には、Rakeの工具を、ファイル名を改変するための#pathmapに取り換える必要があります。


#pathmapはその引数として、1つの書式指定文字列をとります。書式指定文字列は、もとのファイル名と異なる部分に対応する文字列を含むコードです。いくつかのコードを試してみましょう。


まず最初に、%pはもとのパスをすべて表示します。あまり面白いとは言えませんが。


%fは、ディレクトリ部分を除いたファイル名を表示します。このコードを使うと、subdir/appendix.mdが単にappendix.mdになることに注意してください。


%nは拡張子とディレクトリ部分のどちらも除いたファイル名を表示します。


%dはディレクトリを表示し、ファイル名は表示しません。


%xはファイル拡張子のみを表示します。


そして%Xは拡張子以外のすべてを表示します。

 
require './source_files'
SOURCE_FILES.ext('html')
SOURCE_FILES.pathmap("%p")
# => ["ch1.md", "ch3.md", "ch2.md", "subdir/appendix.md", "ch4.markdown"]
SOURCE_FILES.pathmap("%f") 
# => ["ch1.md", "ch3.md", "ch2.md", "appendix.md", "ch4.markdown"]
SOURCE_FILES.pathmap("%n")
# => ["ch1", "ch3", "ch2", "appendix", "ch4"]
SOURCE_FILES.pathmap("%d")
# => [".", ".", ".", "subdir", "."]
SOURCE_FILES.pathmap("%x")
# => [".md", ".md", ".md", ".md", ".markdown"]
SOURCE_FILES.pathmap("%X")
# => ["ch1", "ch3", "ch2", "subdir/appendix", "ch4"]

#pathmapに渡す文字列には、プレースホルダーのコードだけでなく、任意のテキストも含めることができます。特別に構成したライブライのロードパスでRubyの子プロセスを開始したいとしましょう。ディレクトリのリストがあり、FileListにそのロードパスを含めたいとします。


ディレクトリのリストをコマンドラインの引数(この引数が各ディレクトリをロードパスに追加するようRubyに告げる)に変換するために、各ディレクトリ名の前に-Iを追加する目的で#pathmapを使用できます。(-Iは、ロードパスとして特別なディレクトリを指定するRubyのコマンドラインフラグです。)


このリストをコマンド文字列に展開すると、リスト内の各ディレクトリに対して-Iが付与された引数を得ます。

 
require "rake"
load_paths = FileList["mylibs", "yourlibs", "sharedlibs"]
ruby_args  = load_paths.pathmap("-I%p")
command    = "ruby #{ruby_args} myscript.rb"
# => "ruby -Imylibs -Iyourlibs -Isharedlibs myscript.rb"

これは、FileListの別の機能も示しています。すなわち、配列とは異なり、文字列に変換した際に、自身を要素のスペース区切りのリストとしてフォーマットします。

 
load_paths.to_s                 # => "mylibs yourlibs sharedlibs"
load_paths.to_a.to_s            # => "[\"mylibs\", \"yourlibs\", \"sharedlibs\"]"

#pathmapにできる他のこととしては、テキスト置換があります。Markdownファイルのリストに戻ってみましょう。ただし、すべてのソースとなるmarkdownを、sourcesと呼ばれるプロジェクトのサブディレクトリに移動したと仮定します。また、生成したHTMLファイルをソースファイルと同じディレクトリに配置するのではなく、ソースディレクトリツリーの構造を反映したoutputディレクトリに分けて配置したいとします。


ビルドするHTMLファイルのリストを得るために、いつものように%記号を持つ#pathmapパターンから始めますが、今回は文字のコードの代わりに開きの中括弧を挿入します。その括弧の中で、文字列の最初にsources/ディレクトリを探す単純な正規表現を指定します。そしてコンマをつけ、それに続いて置換文字列(outputs/ディレクトリの名前)を付け加えます。そして閉じの中括弧を追加します。次に、パスのどの部分に対してこの置換を適用するかを#pathmapに告げる必要があります。大文字のXを使用しますが、これは前に見たように“ファイル拡張子以外のすべて”を表します。最後に、新しいファイル拡張子として.htmlを追加します。

 
require "rake"
Dir.chdir "project2"
SOURCE_FILES = Rake::FileList.new("sources/**/*.md", "sources/**/*.markdown")

SOURCE_FILES
# => ["sources/ch1.md", "sources/ch3.md", "sources/ch2.md", "sources/subdir/appendix.md", "sources/ch4.markdown"]

OUTPUT_FILES = SOURCE_FILES.pathmap("%{^sources/,outputs/}X.html")
OUTPUT_FILES
# => ["outputs/ch1.html", "outputs/ch3.html", "outputs/ch2.html", "outputs/subdir/appendix.html", "outputs/ch4.html"]

このコードを評価すると、その結果が、outputsサブディレクトリ内に作成されるべきHTMLファイルのリストであることがわかります。ソースファイルツリー内の位置を反映して、outputsのサブディレクトリ内に付録ファイルがあることに注意して下さい。


実際に、そのファイルを詳しく見ていきましょう。このファイルのビルドを試みると、すでにoutputsディレクトリが作成されているにもかかわらず、おそらくsubdirが作成できないという問題に突き当たることになります。pandocにファイルを生成するよう命令すると、ディレクトリが存在しないので出力ファイルを開けないというエラーになります。

mkdir -pコマンドでディレクトリを作成できることは知っています。しかし、mkdirにはファイル名ではなくディレクトリだけを与える必要があります。このために、ファイル名のディレクトリ部分だけを返すことを命令する書式指定文字列として%dをつけて、#pathmapを別に呼び出します。


ここでは、FileListではなく文字列に対して#pathmapを呼び出していることに注意してください。前回のエピソードで見た#extメソッドと同様、Rakeは#pathmapStringクラスに追加するので、file listと個々のファイル名文字列を同じように使用できます。

 
require "rake"
Dir.chdir "project2"
SOURCE_FILES = Rake::FileList.new("sources/**/*.md", "sources/**/*.markdown")
OUTPUT_FILES = SOURCE_FILES.pathmap("%{^sources/,outputs/}X.html")

f = OUTPUT_FILES[3]
f                               # => "outputs/subdir/appendix.html"
cmd = "mkdir -p #{f.pathmap('%d')}"
cmd                             # => "mkdir -p outputs/subdir"

信じられないかもしれませんが、このエピソードでは#pathmapの全機能のほんの一部を確認したに過ぎません。もし何ができるのかもっと見たいのであれば、Rakeのドキュメントを確認して下さい。複数の置換を一気に行う方法や、テキストを置換するために任意の演算を実行するブロックを使う方法やさらに多くのことを発見するでしょう。


しかし、今日取り上げた内容でも、さまざまなRakeシナリオで#pathmapを効果的に使用するのに十分な内容を知ったはずです。ハッピーハッキング!




Rakeについてのエピソード・文章を楽しんでいただけたことを願っている。もし今日、何かしら学んだのであれば、Jimの遺産である教育プログラムの継続を支援するため、the Weirich Fundに寄付することにより「恩送りすること(訳注:原文は"paying it forward")」を検討して欲しい。 もし、今回のようなビデオをもっと見たければ、RubyTapas をじっくり見てほしい。シリーズが完結するまで、ほぼ毎日公開していくつもりなので、ほどなくご確認を!

P.S. 特に興味深い方法でRakeを使っているなら、連絡して欲しい

関連する投稿:
  1. Rake Part 2: File Lists
  2. Rake Part 3: Rules
  3. Rake Part 1: Files and Rules

0 件のコメント:

コメントを投稿