2014年6月27日金曜日

Learn advanced Rake in 7 episodes 翻訳

Avdi GrimmLearn Advanced Rake in 7 Episodesに含まれる7つのエピソードをすべて翻訳しました。そのまとめとして、Learn Advanced Rake in 7 Episodesを、本人の許可を得て翻訳しました。各パートへのリンクは翻訳したものを指しています(原文へのリンクも併記しています)。以下、翻訳です。



RakeはRuby界で普及しているが、そのパワーはたびたび低く見積もられ十分に活用されていない。この2週間にわたり、Rakeで行ったことをRubyTapasのビデオシリーズとして、連続して投稿してきた。そこでは、共通のタスクを優雅に自動化するのを助けるRakeの高度な機能を詳しく説明している。便利なよう、以下にすべてのエピソードのリストをまとめておく。もとの投稿(気軽に順を追って理解したい人のためのスクリプトとソースコードを含む)へのリンクも含まれている。

Part 1: Files and Rules

(原文:Part 1: Files and Rules)

Part 2: File Lists

(原文:Part 2: File Lists)


Part 3: Rules

(原文:Part 3: Rules)


Part 4: Pathmap

(原文:Part 4: Pathmap)


Part 5: File Operations

(原文:Part 5: File Operations)


Part 6: Clean and Clobber

(原文:Part 6: Clean and Clobber)


Part 7: MultiTask

(原文:Part 7: MultiTask)


これは、Rakeの全機能の完全なガイドではないが、より効率的に作業する助けになる何かを、ここで見つけてくれればと思う。


もし、すばらいいツールであるRakeに感謝しているのなら、Weirich Fundへの寄付を考えてほしい。また、このようなスタイルのビデオがお好みなら、RubyTapas.comをさらにチェックして欲しい。


おっと、特におもしろい何かのためにRakeをつかっているのなら、教えてほしい


ハッピーハッキング!

関連する投稿:
  1. Rake Part 1: Files and Rules
  2. Rake Part 7: MultiTask
  3. Rake Part 6: Clean and Clobber

Rake Part 7: MultiTask 翻訳

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



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

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

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






このRakeの連続シリーズは終わりに向かって進んでいます。前回までのいくつかのビデオを通して、私がそうであったように、Rakeのパワーのすごさが分かってくるようになってきたと思います。しかしおそらく、素のRubyやシェルスクリプトを利用することに対するRakeを使用することの利点については、まだ懐疑的でしょう。もしそうであれば、今日がその印象を変える日だと思います。Rakeが、ただ同然で与えてくれる驚くほどパワフルな能力をお見せしたいと思います。


電子書籍を作るとします。pygmentizeユーティリティを使ってシンタックスハイライトされたHTMLに変換するために準備した、テキストを取り除いた数百のコード目録が含まれたディレクトリがあります。


これは、このタスクを行う小さなRakefileです。目録のリスト、“highlights”のリスト(これは、HTMLの最終成果物です)、そしてhighlightと呼ばれるすべてを生成するタスクを定義しています。最後に、pygmentizeを実行して目録ファイルから.htmlファイルを生成するルールを定義しています。また、highlightタスクに依存するデフォルトタスクも定義しています。

 
require "rake/clean"

task :default => :highlight

LISTINGS   = FileList["listings/*"]
HIGHLIGHTS = LISTINGS.ext(".html")
CLEAN.include(HIGHLIGHTS)

task :highlight => HIGHLIGHTS

rule ".html" => ->(f){ FileList[f.ext(".*")].first } do |t|
  sh "pygmentize -o #{t.name} #{t.source}"
end

pygmentizeタスクを使ったソースコードのハイライトは時間が掛かります。たくさんのソースファイルがある場合、多くの時間が掛かります。もしrakeをtimeコマンドの下で実行すると、そのプロセスに48秒かかることがわかります。

 
$ time rake
...
pygmentize -o listings/fd673484d50a66ea67fcd20e0c55f038a729e4d7.html listings/fd673484d50a66ea67fcd20e0c55f038a729e4d7.rb
pygmentize -o listings/ff6e24090e794c4db847b10ca993c872ca804101.html listings/ff6e24090e794c4db847b10ca993c872ca804101.rb

real    0m47.961s
user    0m41.912s
sys     0m4.852s

今のところ、これらのハイライトされたファイルは1つずつビルドされています。しかし、今は2013年で、2つの物理コアが載ったコンピュータを私は持っていて、ハイパースレッディングにより4つの仮想コアがあることになります。どうして2つ以上のファイルを同時にビルドできないのでしょうか?


それは実際に可能です。我々がすべきことは、rakefileの1行をtaskからmultitaskに変えるだけです。

 
multitask :highlight => HIGHLIGHTS

これは、:highlightタスクの事前条件を並行に処理できることをRakeに告げます。並列化したいタスクそのものではなく、並列化したいタスクに依存するタスクにこの変更を行っている点に注意してください。


Rakeをもう一度実行します。Rakeが数百のRakeサブプロセスを同時に並列で起動し、それらが同じSTDOUTに出力を行うため、かなりみだれた出力になります。


25秒を少し超えてたところで、ビルドが完了します。この1つの変更により、処理時間をおよそ半分にカットしました!

 
$ time rake
...

real    0m25.701s
user    1m13.492s
sys     0m8.272s

何個のタスクを並列で実行するかを微調整したい場合は、同時に実行できるプロセスの最大数をRakeに告げるために-jオプションを使用できます。各仮想コアにたいして1つ、すなわち4を指定してみます。


興味深いことに、今度はほんの少し時間がかかります。どうしてかはよく分かりません。

 
$ time rake -j 4

real    0m26.752s
user    1m10.300s
sys     0m7.208s

先程述べたように、並列に実行するにはコードに1行変更を加えればよいといいましたが、それは少し嘘です。本当は、コードにまったく変更を加えずにタスクを並列に実行するよう、Rakeに命令することができます。multitasktaskに戻して、-mオプションつきでrakeを実行します。これは、すべてのタスクをmultitaskとして扱うよう、Rakeに告げます。


再び、歪んだ出力が確認できます。そして一段落すると、合計時間が25秒超であることが再び確認できます。

 
$ time rake -m

real    0m25.606s
user    1m13.060s
sys     0m7.856s

Rakeの並列化は賢くもあります。もし他のタスクが:highlightタスクに依存していれば、次のフェーズに進む前に、すべてのpygmentizeプロセスが完了するまで待機します。


では、Rakeを使ったビルドの自動化から得たものは何でしょう?タスクを達成するための複雑な依存関係やルールを宣言するための簡単な方法だけではありません。ファイル操作のための便利なメソッドのセットだけではありません。扱いやすいコマンドラインのフロントエンドだけではありません。それらすべてに加え、繰り返し行われるタスクの並列化がただで手に入ります。そして、それが私の言うハッピーハッキングです!




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

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

関連する投稿:
  1. Rake Part 1: Files and Rules
  2. Rake Part 6: Clean and Clobber
  3. Rake Part 3: Rules

2014年6月26日木曜日

Rake Part 6: Clean and Clobber 翻訳

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



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

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

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




前回のエピソードでは、ビルドスクリプトの成果物をクリーンアップするRakeタスクを定義しました。再帰的に“outputs”ディレクトリを削除する単純な方法でこれを実現しました。

 
task :clean do
  rm_rf "outputs"
end

時にはクリーンアップはこれほど単純ではありません。今日は、これまで開発してきたRakefileをいくらか修正したバージョンから始めましょう。


以前と同じように、MarkdownファイルをHTMLに変換しています。直前のエピソードと異なるのは、HTMLファイルがソースファイルと同じディレクトリに生成されることです。つまり、ソースディレクトリと出力ディレクトリは分かれていません。


MarkdownファイルをHTMLにビルドするルールに加え、いくつかの新しいルールも追加しました。すべてのHTMLフラグメントファイルを単一のbook.htmlに結合するルールがあります。そして、Calibre電子書籍パッケージのebook-convertコマンドを使ってbook.htmlをEPUBフォーマットの電子書籍に変換するルールがあります。最後に、Amazonのkindlegenを使って、受け取ったEPUBファイルをKindle互換の.mobiファイルに変換するルールがあります。


最後の調整として、:defaultルールを.epubターゲットと.mobiターゲットに依存するよう更新しています。

 
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 => ["book.epub", "book.mobi"]
task :html => SOURCE_FILES.ext(".html")

rule ".html" => ->(f){source_for_html(f)} do |t|
  sh "pandoc -o #{t.name} #{t.source}"
end

file "book.html" => SOURCE_FILES.ext(".html") do |t|
  chapters   = FileList["**/ch*.html"]
  backmatter = FileList["backmatter/*.html"]
  sh "cat #{chapters} #{backmatter} > #{t.name}"
end

file "book.epub" => "book.html" do |t|
  sh "ebook-convert book.html #{t.name}"
end

file "book.mobi" => "book.epub" do |t|
  sh "kindlegen book.epub -o #{t.name}"
end

def source_for_html(html_file)
  SOURCE_FILES.detect{|f| f.ext('') == html_file.ext('')}
end

このビルドスクリプトは、2つの異なるカテゴリのファイルを生成します。

  • 中間ファイル。すべてのHTMLファイルはこのカテゴリに含まれます。これらのファイルは、全ビルドプロセスが終了すると必要がなくなるので、一時ファイルとも呼ばれます。
  • 成果物である電子書籍ファイル。これらのファイルは、全プロセスの最終目的です。

プロジェクトの自動クリーンアップについて言うなら、これらの2つの異なるカテゴリのファイルを別々に取り扱いたいでしょう。電子書籍の成果物はそのまま残し、中間ファイルだけをクリーンアップしたいこともあれば、すべての生成されたファイルを一掃し、白紙の状態から始めたいこともあります。


これらの2つのタイプのクリーンアップを扱うタスクを書くこともできます。あるいは、Rakeのオプションライブラリrake/cleanを使うこともできます。


rake/cleanを使うには、まずrequireします。そうすると、新たなグローバル定数CLEANを使用できるようになります。この定数はFileListであり、最初は空です。

 
require 'rake/clean'

CLEAN                           # => []
CLEAN.class                     # => Rake::FileList

どのファイルが中間ファイルであるかをRakeに告げるためにCLEANリストを使用できます。まず、Markdownファイルから生成されたHTMLファイルのリストを追加しましょう。

CLEAN.include(SOURCE_FILES.ext(".html"))
そして結合されたbook.htmlをリストに追加します。

 
file "book.html" => SOURCE_FILES.ext(".html") do |t|
  chapters   = FileList["**/ch*.html"]
  backmatter = FileList["backmatter/*.html"]
  sh "cat #{chapters} #{backmatter} > #{t.name}"
end
CLEAN.include("book.html")

次に、ファイルをCLOBBERと呼ばれる他のリストに追加します。このリストはどのファイルが最終成果物であるかをRakeに告げます。CLOBBERリストに.epubおよび.mobiの電子書籍ファイルを追加します。

 
file "book.epub" => "book.html" do |t|
  sh "ebook-convert book.html #{t.name}"
end
CLOBBER << "book.epub"

file "book.mobi" => "book.epub" do |t|
  sh "kindlegen book.epub -o #{t.name}"
end
CLOBBER << "book.mobi"

Rakefileの1か所で、これらのファイルをCLEANCLOBBERのリストに追加することもできたかもしれません。しかし、ファイルを生成する各ルールのすぐ隣にそれぞれを追加することにより、これらのルールを完全に削除したり修正したりする時に、CLEANリストまたはCLOBBERリストの関連するエントリーを変更することを覚えている可能性が高くなります。


コマンドラインで、rake -Tコマンド使って使用可能なタスクの一覧を表示するようRakeに告げると、定義していないcleanclobberという2つのタスクが使用できることが確認できます。


電子書籍ファイルを生成するために、まずrakeを引数なしで実行します。プロジェクト内のファイルを一覧表示すると、様々な.html中間ファイルと最終成果物である.epub.mobiファイルが確認できます。

 
$ rake
$ tree
.
├── backmatter
│   ├── appendix.html
│   └── appendix.md
├── book.epub
├── book.html
├── book.mobi
├── ch1.html
├── ~ch1.md
├── ch1.md
├── ch2.html
├── ch2.md
├── ch3.html
├── ch3.md
├── ch4.html
├── ch4.markdown
├── Rakefile
├── scratch
│   └── test.md
└── temp.md

rake cleanを実行しても、何も出力されません。しかし、プロジェクトの中身を再び一覧表示すると、すべての.htmlファイルが消去されていることが確認できます。

 
$ rake clean
avdi@hazel:~/Dropbox/rubytapas/134-rake-clean/project$ tree
.
├── backmatter
│   └── appendix.md
├── book.epub
├── book.mobi
├── ~ch1.md
├── ch1.md
├── ch2.md
├── ch3.md
├── ch4.markdown
├── Rakefile
├── scratch
│   └── test.md
└── temp.md

そしてrake clobberを実行すると、見つからなかったファイルについて、たくさんの警告が表示されます。これは、clobberはまずcleanタスクを実行しますが、そのタスクはすでに実行されているからです。このタスクはすでに存在しないたくさんのファイルを削除しようとしますが、心配ありません。これらの警告は無害であり、安心して無視できます。


clobberの実行後にプロジェクトの中身を見てみると、中間ファイルと共に電子書籍ファイルがなくなっていることが確認できます。


rake/cleanについては以上ですが、ビルドされたファイルを削除するためにproject独自のクリーンアップタスクを書く必要はありませんでした。適切なファイルあるいはファイルパターンをCLEANCLOBBERのリストに追加しただけで、あとはRakeにしてもらいました。ハッピーハッキング!




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

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

関連する投稿:
  1. Rake Part 5: File Operations
  2. Rake Part 2: File Lists
  3. Rake Part 4: Pathmap

2014年6月25日水曜日

Rake Part 5: File Operations 翻訳

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



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

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

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





これが前回までのいくつかのエピソードで使用したRakefileです。プロジェクトの“sources”サブディレクトリの中にMarkdownソースファイルがあり、“outputs”サブディレクトリの中に、並列の階層構造をなすHTMLファイルを生成します。

 
SOURCE_FILES = Rake::FileList.new("sources/**/*.md", "sources/**/*.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.pathmap("%{^sources/,outputs/}X.html")

rule ".html" => ->(f){source_for_html(f)} do |t|
  sh "pandoc -o #{t.name} #{t.source}"
end

def source_for_html(html_file)
  SOURCE_FILES.detect{|f| f.ext('') == html_file.ext('')}
end

“outputs”ディレクトリの中に入力ファイルの階層構造を再生成しているので、HTMLファイルを生成する前に、目的のディレクトリが存在することを保証する必要があります。Rakeでこれを実現する簡単な方法は、directoryタスクを使用することです。これはディレクトリを対象としていることを除けばfileタスクと似ていますが、fileタスクとは異なり、存在しない場合にディレクトリを作成する方法をコードとして与える必要がありません。単にタスクを定義することにより、必要ならばディレクトリを作成するようRakeに暗黙的に指示します。


“.html”ルールに対して、このディレクトリを依存関係のリストに追加します。

 
rule ".html" => [->(f){source_for_html(f)}, "outputs"] do |t|
  sh "pandoc -o #{t.name} #{t.source}"
end

rakeを実行すると、HTMLファイルを生成する前にディレクトリが作成されることが確認できます。しかし、残念ながら、appendix.htmlファイルをビルドしようとする時に問題に遭遇します。このファイルはsourcesディレクトリのサブディレクトリの中にあるので、HTMLの出力ファイルは、outputsディレクトリの対応するサブディレクトリの中に出力して欲しいのです。しかし、このサブディレクトリはまだ存在しません。

 
t$ rake
mkdir -p outputs
pandoc -o outputs/backmatter/appendix.html sources/backmatter/appendix.md
pandoc: outputs/backmatter/appendix.html: openFile: does not exist (No such file or directory)
rake aborted!
Command failed with status (1): [pandoc -o outputs/backmatter/appendix.html...]
/home/avdi/Dropbox/rubytapas/133-rake-file-operations/project/Rakefile:16:in `block in <top (required)>'
Tasks: TOP => default => html => outputs/backmatter/appendix.html
(See full trace by running task with --trace)

(訳注:1行目の先頭のtは原文のタイポと思われる。)

HTMLファイルの出力前に、このディレクトリや他の中間ディレクトリが存在することを保証するには、対象ファイルのディレクトリ部だけを渡すために#pathmap使って、mkdir -pシェルコマンドを実行することも可能です。

 
sh "mkdir -p #{t.name.pathmap('%d')}"

しかし、Rakeにはこのためのショートカットがあります。シェルコマンドを実行する代わりに、mkdir_pメソッドをタスクの中で使用することができます。

 
SOURCE_FILES = Rake::FileList.new("sources/**/*.md", "sources/**/*.markdown") do |fl|
  fl.exclude("**/~*")
  fl.exclude(/^sources\/scratch\//)
  fl.exclude do |f|
    `git ls-files #{f}`.empty?
  end
end

task :default => :html
task :html => SOURCE_FILES.pathmap("%{^sources/,outputs/}X.html")

directory "outputs"

rule ".html" => [->(f){source_for_html(f)}, "outputs"] do |t|
  mkdir_p t.name.pathmap("%d")
  sh "pandoc -o #{t.name} #{t.source}"
end

def source_for_html(html_file)
  SOURCE_FILES.detect{|f| 
    f.ext('') == html_file.pathmap("%{^outputs/,sources/}X")
  }
end

ここでrakeを実行すると、markdownからHTMLへの変換されるたびに、前もって対象ディレクトリが存在することが保証されます。

 
$ rake
mkdir -p outputs/backmatter
pandoc -o outputs/backmatter/appendix.html sources/backmatter/appendix.md
mkdir -p outputs
pandoc -o outputs/ch1.html sources/ch1.md
mkdir -p outputs
pandoc -o outputs/ch2.html sources/ch2.md
mkdir -p outputs
pandoc -o outputs/ch3.html sources/ch3.md
mkdir -p outputs
pandoc -o outputs/ch4.html sources/ch4.markdown

ビルドスクリプトを書いているときに、素早くすべての生成されたファイルを消す簡単な方法があれば便利なことはよくあります。これに対処するタスクを追加してみましょう。ここでも、シェルコマンドを実行する代わりに、Rakeのヘルパーメソッドを使用します。これはシェルのrm -rfコマンドに対応し、いかなる警告や確認もせずに再帰的にファイルとディレクトリを削除します。

 
task :clean do
  rm_rf "outputs"
end

Rakeにはこのようなフィル操作を行うヘルパーメソッドがたくさんあり、そのすべてがUNIXシェルの同等なコマンドにちなんで名づけられています。これは、いくつかの点で便利です。一例をあげると、それらはRuby自身のメソッドなので、文字列展開などをすることなく、ファイルやfile listを直接渡せます。


また、Rakeの“quiet”フラグも感知します。Rakeコマンドは-qフラグつきで実行すると、必要な動作はすべて行いますが、STDOUTへの出力は行いません。

 
$ rake -q
$

これらのヘルパーほとんどすべては、Rubyの標準ライブライであるFileUtilsから直接受け継がれています。ですから、使用可能なヘルパーすべてのリストを確認したければ、単にthe FileUtils documentationを確認してください。


今日は以上です。ハッピーハッキング!




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

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

関連する投稿:

  1. Rake Part 4: Pathmap
  2. Rake Part 2: File Lists
  3. Rake Part 1: Files and Rules
  4.  


     

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

2014年6月23日月曜日

Rake Part 3: Rules 翻訳

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



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

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

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





前回までの2回のエピソードでは、MarkdownファイルをHTMLにビルドするためにRakefileを作成しました。今のところRakefileは動作していますが、いくつか重複があります。 HTMLファイルをビルドするためのほとんど同じルールが2つあります。1つは.md拡張子を持つソースファイルを探し、 もう1つは.markdown拡張子をもつファイルを探します。 それらを単一の、より一般的なルールにまとめられるといいですね。
 
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

2番目のルールを取り除くことから始めましょう。この状態でrakeを実行すると失敗します。

 
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
$ rake
rake aborted!
Don't know how to build task 'ch4.html'

Tasks: TOP => default => html
(See full trace by running task with --trace)

先に進む前に、このエラーメッセージについて少し話しましょう。 このメッセージは、“タスク‘ch4.html’をビルドする方法がわかりません”と言っています。 これは多くのことを教えてはくれません。ch4.htmlと呼ばれるtaskが話題となっているので、少し紛らわしくもあります。ch4.htmlはビルドしたいfileであり、 タスクではありませんよね?


ここから次のことがわかります。Rakeは、ビルドするよう依頼されているすべてのものをタスクとして考えています。単純なタスクとファイルタスクの唯一の違いは、 ファイルタスクの場合、タスク名に一致するファイルがあるか、そして事前条件となるファイルよりそのファイルのほうが新しい(タスクを実行する必要はない)かを Rakeが知っているということです。


このケースでは、なぜRakeがこのファイルをビルドできないのか、私たちは知っています。 どうやってビルドするかを告げるルールを単に削除したからです。 しかし、もしそれを知らなかっとしたらどうでしょう? このメッセージは、どう対処したらよいかについては多くを教えてはくれません。


Rakeが何をしようとしているかをより理解するために、Rakeに–traceフラグを渡すことができます。今度は、Rakeはたどるべきパンくずリストを残し、何をしようとしたかを教えてくれます。最初に、デフォルトタスクを呼び出しています。 デフォルトタスクは“html”タスクに依存しているので、次にhtmlタスクが呼ばれます。

その次のステップは、ch4.htmlをビルドする方法がわからないためrakeが中断したことをそっけなく通知し、その後にRubyのスタックトレースが続きます。

 
 $ rake --trace
  ** Invoke default (first_time)
  ** Invoke html (first_time)
  rake aborted!
  Don't know how to build task 'ch4.html'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task_manager.rb:49:in `[]'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:53:in `lookup_prerequisite'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:49:in `block in prerequisite_tasks'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:49:in `map'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:49:in `prerequisite_tasks'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:195:in `invoke_prerequisites'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:174:in `block in invoke_with_call_chain'
  /usr/lib/ruby/1.9.1/monitor.rb:211:in `mon_synchronize'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:168:in `invoke_with_call_chain'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:197:in `block in invoke_prerequisites'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:195:in `each'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:195:in `invoke_prerequisites'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:174:in `block in invoke_with_call_chain'
  /usr/lib/ruby/1.9.1/monitor.rb:211:in `mon_synchronize'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:168:in `invoke_with_call_chain'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:161:in `invoke'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/application.rb:149:in `invoke_task'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/application.rb:106:in `block (2 levels) in top_level'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/application.rb:106:in `each'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/application.rb:106:in `block in top_level'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/application.rb:115:in `run_with_threads'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/application.rb:100:in `top_level'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/application.rb:78:in `block in run'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/application.rb:165:in `standard_exception_handling'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/application.rb:75:in `run'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/bin/rake:33:in `<top (required)>'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/bin/rake:23:in `load'
  /home/avdi/.rvm/gems/ruby-1.9.3-p327/bin/rake:23:in `<main>'
  Tasks: TOP => default => html
#+END_HTML

Let's ask Rake why it was trying to build =ch4.html= in the first
place. We can do this by running Rake with the =-P= flag, which tells
it to dump a list of prerequisites.

#+BEGIN_EXAMPLE
$ rake -P
rake default
    html
rake html
    ch1.html
    ch2.html
    ch3.html
    subdir/appendix.html
    ch4.html

この出力により、htmlタスクはch4.htmlを含むファイルのリストに依存していることが明確になります。


このRakefileの何が問題かをまだ知らないふりをしていることを忘れないでください。 Rakeの考え方について多くの知見を集めましたが、今のところ HTMLファイルとMarkdownファイルの関連(この問題を修正するために理解する必要があるなにか)については依然分かっていません。


Rakeの思考プロセスをより深く見るために、Rakefileファイル内で、Rake.application.options.trace_rulesオプションにtrueを設定します。 このオプションを有効にすると、その名前が示す通り、Rakefileに定義されたルールについてトレース情報を表示するようRakeに命じます。


注:このビデオの作成後、Jim Weirichから、このオプションは`–rules`コマンドラインオプションとしても利用可能であるとの指摘があった。

 
Rake.application.options.trace_rules = true

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

再びrake -traceを実行すると、今度はタスク呼び出しのパンくずリストに加えて、新たな情報を確認できます。それぞれのファイルのビルドに対して、Rakeが使おうとしたルールと、 そのルールの中でどの.htmlファイルがどの対応する.mdファイルに依存しているかを教えてくれます。 ch4.htmlにたどり着くと、rakeは失敗します。事前条件ファイルch4.mdが見つからなかったと明示されてはいませんが、我々の目の前にあるこの情報により、何が問題かを合理的に推定することができます。

 
$ rake --trace
** Invoke default (first_time)
** Invoke html (first_time)
Attempting Rule ch1.html => ch1.md
(ch1.html => ch1.md ... EXIST)
Attempting Rule ch2.html => ch2.md
(ch2.html => ch2.md ... EXIST)
Attempting Rule ch3.html => ch3.md
(ch3.html => ch3.md ... EXIST)
Attempting Rule subdir/appendix.html => subdir/appendix.md
(subdir/appendix.html => subdir/appendix.md ... EXIST)
Attempting Rule ch4.html => ch4.md
(ch4.html => ch4.md ... FAIL)
rake aborted!
Don't know how to build task 'ch4.html'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task_manager.rb:49:in `[]'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:53:in `lookup_prerequisite'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:49:in `block in prerequisite_tasks'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:49:in `map'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:49:in `prerequisite_tasks'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:195:in `invoke_prerequisites'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:174:in `block in invoke_with_call_chain'
/usr/lib/ruby/1.9.1/monitor.rb:211:in `mon_synchronize'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:168:in `invoke_with_call_chain'
!/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:197:in `block in invoke_prerequisites'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:195:in `each'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:195:in `invoke_prerequisites'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:174:in `block in invoke_with_call_chain'
/usr/lib/ruby/1.9.1/monitor.rb:211:in `mon_synchronize'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:168:in `invoke_with_call_chain'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/task.rb:161:in `invoke'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/application.rb:149:in `invoke_task'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/application.rb:106:in `block (2 levels) in top_level'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/application.rb:106:in `each'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/application.rb:106:in `block in top_level'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/application.rb:115:in `run_with_threads'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/application.rb:100:in `top_level'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/application.rb:78:in `block in run'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/application.rb:165:in `standard_exception_handling'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/lib/rake/application.rb:75:in `run'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/gems/rake-10.1.0/bin/rake:33:in `<top (required)>'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/bin/rake:23:in `load'
/home/avdi/.rvm/gems/ruby-1.9.3-p327/bin/rake:23:in `<main>'
Tasks: TOP => default => html

さて、ルールを再び動くようする時です。メソッドsource_for_htmlを定義することから始めます。 このメソッドは、HTMLファイルの名前を引数にとり、対応するMarkdownファイルの名前を返します。そのように動作させるには、ソースファイルのリストへのアクセスが必要です。 今のところ、リストはローカル変数で、このメソッド内からはアクセスできないため、定数に変更します。


そして、 拡張子を除いた名前が、与えられたHTMLファイル名に一致する最初のソースファイルをソースファイルのリストから探します。 拡張子を除いた名前だけで比較するために、#extメソッドを再び使用しています。 HTML出力ファイル名のリストを得るためにこのメソッドをソースファイルのリストに使用したことを、あなたは覚えているかもしれません。今回は、ファイル拡張子を完全に取り除くために、空の文字列を#extに渡しています。

 
def source_for_html(html_file)
  SOURCE_FILES.detect{|f| f.ext('') == html_file.ext('')}
end

あなたが、“ちょっと待てよ!”というのが聞こえます。“前は、#ext メッセージをFileList に送ったけど、ここではそれぞれのファイル名文字列に送っているじゃないか!どうしてそれが動くんだ?”


ここから次のことがわかります。Rakeは、FileListがサポートしているのと同じメソッドのいくつかをサポートするようRubyのStringクラスを変更しているので、FileListと個々のファイル名が交換可能なものとして同じ操作ができます。


HTMLファイル名を与えると、それを生成するのに必要なソースのMarkdownファイルを探すことができるメソッドがあるので、このメソッドを使う.htmlルールを作る必要があります。

ルールの中の.md依存関係をラムダに置き換えることにより、これを実現します。ラムダの中で、1つの引数をとり#source_for_htmlメソッドに渡します。 Rakeは、.htmlファイルのビルドを試みる時、ターゲットファイルの名前を、事前条件として与えたラムダに引き渡します。 そして、このラムダの戻り値が、存在するファイルに一致するかどうか確認します。もし一致すれば、ルールが一致したと考え、関連するコードの実行に進みます。

 
rule ".html" => ->(f){source_for_html(f)} do |t|
  sh "pandoc -o #{t.name} #{t.source}"
end

まだルールのトレースを有効にしているので、 変更されたルールを使ってRakeがどのように判断しているか、ウインドウに出力されます。 ch4.htmlターゲットにたどり着くと、依存関係がch4.mdではなく ch4.markdownであると、正しく決定します。そのファイルを見つけて、 ch4.htmlファイルをビルドします。

 
$ rake
Attempting Rule ch1.html => ch1.md
(ch1.html => ch1.md ... EXIST)
Attempting Rule ch2.html => ch2.md
(ch2.html => ch2.md ... EXIST)
Attempting Rule ch3.html => ch3.md
(ch3.html => ch3.md ... EXIST)
Attempting Rule subdir/appendix.html => subdir/appendix.md
(subdir/appendix.html => subdir/appendix.md ... EXIST)
Attempting Rule ch4.html => ch4.markdown
(ch4.html => ch4.markdown ... EXIST)
pandoc -o ch4.html ch4.markdown

これで、長い拡張子・短い拡張子のいずれのMarkdownファイルからもHTMLファイルをビルドする一般的なルールができました。しかし、もっと大切なことは、 何をどのようにビルドするかを決定するルールが、Rakeでどのように動作するかについて、より多くの知見を得たことです。 ハッピーハッキング!


ちなみに、これが最終的なRakefileになります。

 
Rake.application.options.trace_rules = true

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" => ->(f){source_for_html(f)} do |t|
  sh "pandoc -o #{t.name} #{t.source}"
end

def source_for_html(html_file)
  SOURCE_FILES.detect{|f| f.ext('') == html_file.ext('')}
end



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

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

関連する投稿:
  1. Rake Part 1: Files and Rules
  2. Rake Part 2: File Lists
  3. Double-Load Guards in Ruby

2014年6月20日金曜日

Rake Part 2: File Lists 翻訳

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



もしあなたが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を使っているなら、連絡して欲しい

関連する投稿:
  1. Rake Part 1: Files and Rules
  2. Keep local modifications in Git-tracked files
  3. Loading plugins with Rubygems

2014年6月17日火曜日

Rake Part 1: Files and Rules 翻訳

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



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

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

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





これから数回のエピソードにわたりRakeについて見ていきます。 気に入ってもらえるといいのですが。

おそらく、あなたはどこかでRakeを使ったことがあるでしょう。少なくとも、Railsプロジェクトに関連した様々なRakeタスクを実行したことがあるでしょう。自分自身でRakefileを書いたことが あるかもしれません。

しかしおそらく、あなたはRakeの持つ可能性のほんの一面に触れたにすぎません。 それは、数週間前まで私にも当てはまることでした。自分のRakefileやtaskファイルを書いたことは確かにありましたが、Rakeができることすべてについて本当に詳しく調べることは決してありませんでした。Rakeを本当に学習することに時間を費やした今、驚くべきパワーをもつツールだと実感しています。 私が学んだことをみなさんと共有していきます。

まずはRakeの基本を復習することから始めます。

Pandocを使ってHTMLに変換したいMarkdownファイルが含まれるディレクトリがあるとします。 複数のファイルをひとつずつ繰り返し変換する単純なスクリプトを書けるかもしれません。

%W[ch1.md ch2.md ch3.md].each do |md_file|
  html_file = File.basename(md_file, ".md") + ".html"
  system("pandoc -o #{html_file} #{md_file}")
end
 
しかし、このスクリプトを実行すると、ソースファイルに変更がなかった場合でも毎回すべてのHTMLファイルを再生成します。markdownファイルがとても大きい場合、 長時間待つことになってしまいます。

代わりに、Rakefileを作って、HTMLを生成するRakeタスクを書いてみましょう。 スクリプトと同じように、入力ファイルのリストに対して繰り返し処理を行い、 対応するHTMLファイルを決定することから始めます。しかし、ここから違いが出てきます。html_fileがmarkdownファイルに依存関係を持つことを宣言するために、 Rakeのfileメソッドを使用します。そしてブロックの中で、シェルコマンドを使用して markdownファイルからHTMLファイルをどのように取得するかをRakeに伝えます。

ここで書いたのはルール(正確には3つのルール)で、それぞれのルールがMarkdownのソースファイルから特定のHTMLファイルをビルドする方法をRakeに伝えています。

%W[ch1.md ch2.md ch3.md].each do |md_file|
  html_file = File.basename(md_file, ".md") + ".html"
  file html_file => md_file do
    sh "pandoc -o #{html_file} #{md_file}"
  end
end

これ自体、すでにRakefileとして使用できます。コマンドライン上で、HTMLファイルの1つをビルドするようrakeに伝えることができ、rakeはそれを受け入れます。 すでに、スクリプトに対する利点、すなわち実行中のコマンドをRakeが表示しているのを確認できます。

$ rake ch1.html
pandoc -o ch1.html ch1.md

Rakeに同じファイルをもう一度ビルドするよう伝えた場合には、何も起こりません。 これは、HTMLファイルが作成された後にMarkdownファイルが変更されたかを確認するために、Rakeがファイルの変更時刻をチェックしているからです。 Markdownファイルは変更されていないのでHTMLファイルを再ビルドする必要がないということを、Rakeは理解しています。
 
$ rake ch1.html
$

Markdownファイルを修正しRakeをもう一度実行すると、再びHTMLファイルをビルドします。
 
$ rake ch1.html
$

ファイルが再ビルドされる必要があるかをRakeが追跡しているのは素晴らしいことです。 しかし、どのファイルをビルドしたいかを指定する必要があるのは面倒です。 Rakeに古くなったHTMLファイルを単に再ビルドさせるほうが望ましいです。

これを実現させるには、Rakefileにtaskを追加します。“html”という名前をつけ、 3つのHTMLファイルに依存するようにします。
 
task :html => %W[ch1.html ch2.html ch3.html]

%W[ch1.md ch2.md ch3.md].each do |md_file|
  html_file = File.basename(md_file, ".md") + ".html"
  file html_file => md_file do
    sh "pandoc -o #{html_file} #{md_file}"
  end
end

このタスク自身はコードを持ちません。しかし“html”タスクをビルドするとRakeに告げると、 RakeはHTMLファイルへの依存関係に従います。すでに書いたルールにより、 Rakeはこれらのファイルをビルドする方法を知っていので、ビルドを進めます。
 
$ rake html
pandoc -o ch1.html ch1.md
pandoc -o ch2.html ch2.md
pandoc -o ch3.html ch3.md

Markdownファイルのひとつを修正してRakeタスクをもう一度実行すると、 Rakeが更新されたファイルのみを再ビルドすることが確認できます。
 
$ rake html
pandoc -o ch2.html ch2.md

このコマンドを何度も実行するつもりなら、htmlタスクに依存関係を持つ :defaultタスクを宣言することで、より便利にすることができます。
 
task :default => :html
task :html => %W[ch1.html ch2.html ch3.html]

%W[ch1.md ch2.md ch3.md].each do |md_file|
  html_file = File.basename(md_file, ".md") + ".html"
  file html_file => md_file do
    sh "pandoc -o #{html_file} #{md_file}"
  end
end

これにより、rakeを単に引数なしで実行してファイルを再ビルドできます。
 
$ rm *.html
$ rake
pandoc -o ch1.html ch1.md
pandoc -o ch2.html ch2.md
pandoc -o ch3.html ch3.md

ここまでは、ファイルにルールとタスクを宣言する方法を見てきました。

3つのファイルルールにはすべて、“.md”ファイルを“.html”ファイルに変換する、という共通のパターンがあります。実際、このパターンは繰り返し出てくるので、eachループを使ってルールの生成を自動化しました。明示的にループを書く代わりに、Rakeに “.md”ファイルを“.html”ファイルに変換する方法を伝え、あとはRake自身に解決してもらう ようにしてみましょう。

名前がファイル拡張子.htmlruleを宣言することによって、これを実現します。 このルールは、ファイル拡張子 .mdに依存しています。そしてブロックをオープンします。 このブロックは、tという名前のブロック引数を受け入れます。tという名前にしたのは、 RakeのTaskオブジェクトにバインドしているからです。

ブロックの内側では、シェルコマンドを実行するためにshコマンドを使用しています。以前と同様、pandocコマンドで始まっていますが、出力ファイル名としてタスクのname属性を展開しています。そして入力ファイルとして タスクのsource属性を使用しています。
 
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

これで終わりです。 HTMLファイルを削除しRakeをもう一度実行すると、 前と同じようにファイルが生成されることが確認できます。
 
$ rm *.html
$ rake
pandoc -o ch1.html ch1.md
pandoc -o ch2.html ch2.md
pandoc -o ch3.html ch3.md

ところで、ここでは何が起こっているのでしょう?引数を指定していないので、Rakeは :defaultタスクを実行します。:defaultタスクは:htmlタスクに依存し、:htmlタスクは3つの.htmlファイルに依存しています。Rakeは1つ目のch1.htmlから処理を始め、そのファイルが存在するかを確認します。 ファイルは存在しないので、Rakeはそのファイルをビルドする方法を見つけようとします。

まず、明示的にch1.htmlと名付けられたルールを探しますが、それらは すべて削除されています。

Rakeが本当に見つけたのは新しいルールです。対応する.mdファイルから .htmlファイルを生成するというルールを使用します。 そのルールをch1.htmlに適用して、対応するファイル、すなわちch1.mdが 存在することを発見します。 これは、ルールが一致したのでそれに従って実行するということを意味します。 そして、残りの存在しない.htmlファイルに対して、すべてのプロセスを繰り返します。

Rakeについてお見せしたいことはもっとたくさんありますが、RubyTapasでは 1回につき1つのアイデアとしているので、将来のエピソードのために 残しておきます。お見逃しなく、そして、ハッピーハッキング!



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

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

関連する投稿:
  1. Keep local modifications in Git-tracked files
  2. RVM Proxies for Common Commands? (Solved)
  3. Creating Cowsays.com Part 2: Unit Tests and Cow Files

2014年6月11日水曜日

This Moment 翻訳

Authorityの著者であるNathan Barryのnewsletterを購読していますが、最近送られてきたThis Momentという文章がとても印象的だったので、本人の許可を得て、以下に翻訳します。

この瞬間

多くの人が、あなたが今いるところに辿り着きます。半分書き上げた本、顧客が数人のiPhoneアプリ、創りあげる前にうまくいくための環境が必要なだけの完璧なアイデア 。これらの人々(あなたのような)は、偉大なことを成し遂げようとしています。創り出したものによって彼ら自身の人生やキャリアを本当に変えてしまうような。

それはこのプロジェクトによってではないかもしれませんが、行動を起こし、製品を出荷し、彼らから製品を買ってくれる見知らぬ人を獲得することによって前進し、それによって必ずや利益を得て成長するでしょう。

残念なことに、あなたが今いるポジションにいる大部分の人は、何も創り出すことはないでしょう。その可能性(掴まれるのををただ待っている)は失われてしまうでしょう。いかなる教訓も学ばないでしょう。キャリアは変わらないでしょう。

そのかわりに、数年後に配偶者がそれらの大部分の人に言うでしょう。「あの本に取り組んでいた時のこと覚えてる?あれを書き上げるべきよ…時間があるときに。」

高校時代に、いつも偉大なアイデアを持っている友人たちがいました。それについて声高にいつも語っていましたが、決して何も起こりませんでした。数年後、彼らにばったり会ったとしても、これから行くつもりの最高の旅行や、これから始めるつもりの会社について、まだ語っていたでしょう。しかし何年経っても、何も起こりませんでした。

誰も面と向かっては言いませんでしたが、彼らが別のものすごいアイデア言い出す時、彼らの友人は皆、心の中で呆れ顔をしていました。それが形にならないことを誰もが知っていました。口ばかりで行動が伴わないことを。

あなたのポジションにいる大部分の人々は--あなたがもつ可能性の一歩手前で--そんな高校時代の友人になるでしょう。彼らは決して仕事をやめず、本を書き上げず、会社も始めないでしょう。次なる目玉となるアイデアをいつも語ってはいますが、実現させるために時間を費やすことは決してないでしょう。

あなたは違います。

今、この瞬間に、あなたには選択の余地があります。あなたはどちらの人間なりますか?

あなたは必要なものを持っています。私はAuthorityで戦術を、またCommitを使って進捗を追跡する方法をあなたに授けました。これが、次のステップに進むための刺激となることを願っています。そのステップは1歩先ではありません。何百歩も先になります。1日では成し遂げられませんが、ゴールに向かって毎日邁進し続けることによって成し遂げられます。

これが、あなたとまさに同じポジションにいる多くもの人々が失敗する理由です。初めにあった動機は尽き、重い脚を進めるために時間を使おうとはしません。私は彼らを非難するつもりはありません。本を書くこと、あるいは新しいプロジェクトを立ち上げることは本当に大変だからです。簡単なことだと言ったら、私は嘘をついていることになります。

しかしそれは、成功が十分可能である理由でもあります。あなたのポジションにいる大部分の人々--そして何千もの人々があなたと共に今そこにいます--は諦めて、前には進まないでしょう。

私の友人であるJeff Goinsが言うように、「あなたは運のいい者より長く続けることが、怠け者よりいい仕事をすることができます。」

そのようにして、あなたは勝利します。

あなたが成し遂げられることを私は知っています。押し進め、成し遂げることことによって、あなたが偉大な成果を目の当たりにすることを私は知っています。製品を出荷するために、マーケティングで本当に成し遂げるために必要なものをあなたが持っていることを私は知っています。

さあ、私が正しいことを証明してください。

Nathan

 

Nilquebe始めます

Nilquebe(にるきゅーぶ、sounds like nil-cube)は、
  • 資金も場所もないけど
  • 何かを始めてみたい
と思っている人に
  • 明るい
  • すっきりした
  • 落ち着ける
  • 集中できる
空間を提供することを目指します。

なぜか?自分がそのような空間を切実に求めているから。