SymfonyとLaravelを比べてみての考察
SymfonyとLaravelはPHPのWebアプリケーションフレームワークで 2014年現在、モダンなPHPフレームワークとして人気があるようです。 両方ともComposerによるパッケージ管理、MVCアーキテクチャ、 開発時のPHP組み込みサーバのサポート、 バンドル(Laravelではパッケージ)などでの機能拡張性を持っています。
ここ最近、個人的な興味でこのふたつのフレームワークを触ってみていたので、 感想と比較を述べてみます。
なお、実際に触ったバージョンはSymfony2.4とLaravel4.1です。 また実際のプロダクトの開発に使用したわけではないのと、 パフォーマンス比較などは行っていないことを断っておきます。
ざっくりとした比較イメージ
細かい感想と比較を書く前に、ざっくりとイメージを述べておきます。
- Symfonyは構造がしっかりしていて重厚
- Laravelは構造がフラットで軽量
ここでの重厚とか軽量とかは、プログラミングをしているときの感触であって、 アプリケーションの動作のことではありません。 また、開発するアプリケーションの規模にそれを合わせて、 サービスが小規模だからSymfonyは適していないとか、 大規模だからLaravelはよくないという話でもありません。
基本的にアプリケーションの規模が小規模でも大規模でもどっちのフレームワークを使ってもよいと思いました。
アプリケーションの規模にフレームワークの向き不向きが全くないとは思いませんが、 それよりも開発するプログラマのやり方、好みにマッチしているかどうか が一番のポイントなんじゃないかと思っています。以下、個別にピックアップします。
ネームスペース
PHPでは5.3からネームスペースによるコードの構造化がサポートされました。
Symfonyで開発するコードはこのネームスペースで厳格に構造化する必要があります。
たとえば、Pakagistのトップページ
のコントローラはPackagist\WebBundle\Controller\WebController
というネームスペースに置かれています。
Laravelではコントローラをはじめ多くのクラスがグローバルなネームスペースを使用します。Laravelの典型的なコントローラは以下のようにnamespaceなしで定義されます。
<?php
class HomeController extends BaseController {
public function index()
{
return View::make('home.index');
}
}
ネームスペースによる構造化はクラス名の衝突を避けられ、構造の見通しをよくします。 一方でそれに合わせてディレクトリ構造が深く複雑になりがちです。
Symfonyは構造が健全であることに重きをおいて設計され、Laravelは記述のシンプルさに重きをおいて設計されている感じです。
デフォルトのテンプレートエンジン
SymfonyはTwig、LaravelはBladeというテンプレートエンジンを標準で使います。
ところで、一世代まえのPHPフレームワーク(CakePHP,Symfony1,ZF1)だと、素のPHPをテンプレートエンジンに使用するのがスタンダードでした。 でも最近はそうでもなく、Symfonyの開発者であるFabien Potencierの ブログなどで言及 されているように、素のPHPでは「最適」なテンプレートの記述が行えないということから、 専用のテンプレートエンジンを利用するようになってきています。
素のPHPがスマートに対応できない領域は、テンプレート継承や、エスケープ処理、イテレータ処理などです。 もちろん、全くできないわけではないが、インラインのphpタグでテンプレートがごちゃごちゃしてしまうのが避けられない。 このへんの問題は、前述のFabienさんのブログでも参照してください。
で、SymfonyとLaravelのテンプレートエンジンのはなし。
SymfonyのTiwgは前述のPHPテンプレートのダメな部分を完全に取り除いて 理想的なテンプレートエンジンを目指して設計されている感じです。 テンプレート継承もできるし、エスケープも自動。またfor文は以下のように書ける
{% for user in users %}
* {{ user.name }}
{% else %}
No users have been found.
{% endfor %}
このテンプレートはPHPとは基本的に別ものであり、テンプレートにPHPを書くことはできません。
LaravelのBladeも同様にテンプレート継承やエスケープ機能を提供してくれますが、 設計としてPHPと別ものではなく素のPHPテンプレートに正規表現による薄い変換処理をかぶせて、ちょっと文法を拡張した感じのものです。 なので、bladeのテンプレートにはPHPのコードも書けるのですが、Twigのfor文みたいなPHPの書式から大きく異るような機能は用意されていません。
テンプレートに素のPHPを記述できることを、害悪ととるか柔軟性ととるかは、プログラマの好みによる話だと思います。 私はPHPを記述できたほうがいいと感じるタイプです。
Symfonyはその領域において最適なコンポーネントを提供するのに対して、 Laravelは最適ではないが、PHPプログラマが慣れ親しみやすいコンポーネントを提供する。 そういう設計になっています。
設定ファイル
設定ファイルの記述に関しても設計思想の違いが感じられます。 たとえば、URLとアクションをひもづけるルーティングの定義をSymfonyはyamlかアノテーションを使います。 (PHPでも記述できるが、かなり冗長なのでこれを使うひとは多分いない)
一方、Laravelは素のPHPを使います。ただしSymfonyのPHP設定ファイルのような冗長さはなく、 グローバルネームスペースとスタティッククラスでRubyのDSLっぽい簡潔な記述を提供します。
テンプレートエンジンの話と同様なのですが、 Symfonyは設定ファイルの記述においても最適な方法(PHPは最適ではないのでyamlなどの別の手段)を提供するのに対して、 LaravelはあくまでPHPを使い(PHPプログラマのためのフレームワークだからか)、その上で記述しやすい施策をとっている印象です。
まとめ
最後に抽象的にまとめると
といったところでしょうか。
LaravelのRouteクラスがグローバル空間で呼び出せる仕組み
最近Laravelを触っています。
で、LaravelだとURLと実行するアクションを関連付けるルーティング部分を
Route::get('/', function()
{
return 'Hello World';
});
こんなふうに、RubyのSinatraっぽくかけるんですよね。ところがこのRoute
というクラス、事前にuse
で使う名前空間の指定もしていないし、実際にどのPHPクラスが使われているかよくわからない。そこでソースを追ってみました。
エイリアス
フロントコントローラのpubic/index.php
を起点に読み始めていくと、
pubic/index.php
->bootstrap/start.php
->vendor/laravel/framework/src/Illuminate/Foundation/start.php
とファイルが読み込まれて、この中で、
$aliases = $config['aliases'];
AliasLoader::getInstance($aliases)->register();
ということをやっている。でregister
メソッドで$aliasesのデータつかってspl_autoload_register
関数を呼び出してオートローディングを設定している。$aliases
の内容はメインのコンフィグファイルconfig/app.php
にかかれていて、デフォルトは以下のようになっている。
'aliases' => array(
'App' => 'Illuminate\Support\Facades\App',
'Artisan' => 'Illuminate\Support\Facades\Artisan',
'Auth' => 'Illuminate\Support\Facades\Auth',
'Blade' => 'Illuminate\Support\Facades\Blade',
'Cache' => 'Illuminate\Support\Facades\Cache',
'ClassLoader' => 'Illuminate\Support\ClassLoader',
'Config' => 'Illuminate\Support\Facades\Config',
'Controller' => 'Illuminate\Routing\Controller',
'Cookie' => 'Illuminate\Support\Facades\Cookie',
'Crypt' => 'Illuminate\Support\Facades\Crypt',
'DB' => 'Illuminate\Support\Facades\DB',
'Eloquent' => 'Illuminate\Database\Eloquent\Model',
'Event' => 'Illuminate\Support\Facades\Event',
'File' => 'Illuminate\Support\Facades\File',
'Form' => 'Illuminate\Support\Facades\Form',
'Hash' => 'Illuminate\Support\Facades\Hash',
'HTML' => 'Illuminate\Support\Facades\HTML',
'Input' => 'Illuminate\Support\Facades\Input',
'Lang' => 'Illuminate\Support\Facades\Lang',
'Log' => 'Illuminate\Support\Facades\Log',
'Mail' => 'Illuminate\Support\Facades\Mail',
'Paginator' => 'Illuminate\Support\Facades\Paginator',
'Password' => 'Illuminate\Support\Facades\Password',
'Queue' => 'Illuminate\Support\Facades\Queue',
'Redirect' => 'Illuminate\Support\Facades\Redirect',
'Redis' => 'Illuminate\Support\Facades\Redis',
'Request' => 'Illuminate\Support\Facades\Request',
'Response' => 'Illuminate\Support\Facades\Response',
'Route' => 'Illuminate\Support\Facades\Route',
'Schema' => 'Illuminate\Support\Facades\Schema',
'Seeder' => 'Illuminate\Database\Seeder',
'Session' => 'Illuminate\Support\Facades\Session',
'SSH' => 'Illuminate\Support\Facades\SSH',
'Str' => 'Illuminate\Support\Str',
'URL' => 'Illuminate\Support\Facades\URL',
'Validator' => 'Illuminate\Support\Facades\Validator',
'View' => 'Illuminate\Support\Facades\View',
),
つまりRoute
クラスの実体はIlluminate\Support\Facades\Route
ということがわかる。
Laravelは自前で設定ファイルからオートローディングの登録を行うロジックをもっていて、それをつかってグローバル空間にクラスのエイリアスを作るという機能を実装していたわけだった。
※じつはIlluminate\Support\Facades\Route
にはルーティングに使用していた上記のget
メソッドは直接定義されておらず、ここからさらにFacade
という仕組みをつかって、別のクラスに実装を移しているのだけど割愛。
PHPでDSLっぽいこと
調べてみると、どうってことはない作りでした。でもこの仕組みは、クラスの実装はネームスペースで構造化された空間に定義でき、インターフェースのみグローバル空間に公開するというのが、うまく出来てると思う。これに無名関数を組み合わせることで、LaravelのRoute定義のような、Rubyの内部DSL的な記述をPHP上に表現しているのだな、とちょっと感心したのでした。
おしまい。
TravisCIでSSHクライアントのテストをする。
AltaxというPHPで並列SSHするデプロイツールを作っているのですが、 これのSSH接続部分のテストは当然SSHサーバが必要で、そのへんをTravisでどーやるのかわからず、ずっと悩んでいました。
で、調べていたら、
https://github.com/libgit2/libgit2/pull/1774
が検索に引っかかって、これを見ながらやってみたらうまくできたので、メモしておきます。
やり方
以下のようなシェルスクリプトを用意します。
setup_ssh_server.sh
#!/bin/sh
sudo apt-get update -qq
sudo apt-get install -qq libssh2-1-dev openssh-client openssh-server
sudo start ssh
ssh-keygen -t rsa -f ~/.ssh/id_rsa -N "" -q
cat ~/.ssh/id_rsa.pub >>~/.ssh/authorized_keys
ssh-keyscan -t rsa localhost >>~/.ssh/known_hosts
export SSH_PRIVATE_KEY="$HOME/.ssh/id_rsa"
export SSH_PUBLIC_KEY="$HOME/.ssh/id_rsa.pub"
apt-getでSSHサーバ入れて起動して、ssh-keygenで鍵を作っているわけです。
あとはこれを.travis.yml
でbefore_scriptとかに設定してテスト前に実行するようにすれば、
SSHサーバがローカルホストで起動するので、SSH接続のテストができるようになります。
.travis.yml
language: php
php:
- 5.3
- 5.4
- 5.5
before_script:
- sh setup_ssh_server.sh
script:
- php vendor/bin/phpunit
ヽ(=´▽`=)ノ
JenkinsをCentOSにインストールしたときのメモ
Githubで公開しているプロジェクトのCIにはTravisが使えて便利なのですが、 仕事で開発しているWebアプリとかは公開するわけには行かなかったりするので、Travisは使えません。
そんなわけで、仕事でもCIの環境が欲しくなったので、今更ながらjenkinsをインストールしてみましたので、メモを残しておきます。
目的
環境
ローカルのVirtualBox上に構成したCentOS6。
Jenkinsのインストール
Javaが必要なので先にyumとかで入れておきます
$ yum install java-1.7.0-openjdk
http://pkg.jenkins-ci.org/redhat/
にredhat,centos用のRPMがあるのでそれをダウンロードしてインストールします。
$ rpm -ivh jenkins-1.540-1.1.noarch.rpm
実際はchefのレシピを用意してインストールしているので以下のようなrecipeを書いて実行しています。
jenkins/attributes/default.rb
default['jenkins']['rpm'] = "jenkins-1.540-1.1.noarch.rpm"
default['jenkins']['rpm_url'] = "http://pkg.jenkins-ci.org/redhat/jenkins-1.540-1.1.noarch.rpm"
jenkins/recipes/default.rb
remote_file '/tmp/' + node['jenkins']['rpm'] do
source node['jenkins']['rpm_url']
owner "root"
group "root"
mode "0755"
not_if "test -e " + '/tmp/' + node['jenkins']['rpm']
end
package "jenkins" do
action :install
source '/tmp/' + node['jenkins']['rpm']
provider Chef::Provider::Package::Rpm
not_if "rpm -q jenkins"
end
service "jenkins" do
action [:enable, :start]
end
インストールすると
/usr/lib/jenkins/jenkins.war
にjenkins本体のwarが
/var/lib/jenkins/
にデータディレクトリが作成されます。
WebUIにアクセス
仮想サーバの8080ポートにブラウザでアクセスするとjenkinsの画面が表示されます。
設定
ローカル環境なので、セキュリティとか気にしない。というわけで特に環境設定なし。デフォルトのまま。
ジョブ
ジョブはjenkinsに実行させたい処理を定義します。今回はリポジトリからPHPアプリのソースをとってきて、PHPUnitでテストを実行させたいと思います。まず、
- 左メニューの[新規ジョブ作成]→[ジョブ名]に適当な名前をつけて[フリースタイル・プロジェクトのビルド]を選択して[OK]。
その後、最低限の設定だけします。
[ソースコード管理]は使っているVCSを選択します。お仕事ではsvnを使っているので[Subversion]。[リポジトリURL]を入力します。
[ビルド・トリガ]は[SCMをポーリング]を選択。スケジュールには1分ごとにチェックさせたいので以下のようにしました。
*/1 * * * *
[ビルド]の箇所では[ビルド手順の追加]→[シェルの実行]を選んで、svnからソースを落としてきたあとに行う実際のタスクを書きます。私の場合は以下のようになりました。
/usr/local/bin/composer install --no-interaction php vendor/bin/phpunit -c phpunit-ci.xml.dist
なんでかJenkinsからの実行だと/usr/local/bin/
にパスが通っていないので、グローバルインストールしてあるcomposerをフルパスで実行してます。
composer installのあとにphpunitを実行していますが、jenkins用のテストの設定(カバレッジをとるなど、後述します)を使いたいので-c
オプションで専用のphpunitの設定ファイルを指定しています。
これで[保存]をクリックして完了。トップに戻って、時計アイコンをクリックして、試しに実行してみます。
カバレッジ
テストがうまくできたら、カバレッジもとりたくなりました。PHPでカバレッジをとるには、Clover PHP pluginを使います。 左メニューの[Jenkinsの管理]→[プラグインの管理]から
- Jenkins Clover PHP plugin
をインストール。Jenkinsを再起動。
ジョブの設定画面に行くと[ビルド後の処理の追加]に[Clover PHP カバレッジレポートを集計]という項目が増えているので、これをクリックして処理を追加します。
- Clover XMLパス:
reports/coverage/coverage.xml
- Clover HTMLレポート ディレクトリ:
reports/coverage
のように設定します。その後phpunit実行時に指定する設定ファイルphpunit-ci.xml.dist
に、以下のようなカバレッジを出力する設定を追加します。
<?xml version="1.0" encoding="UTF-8"?>
<phpunit colors="true" berbose="true" bootstrap="test/bootstrap.php">
...いろいろ設定
<logging>
<log type="coverage-clover" target="reports/coverage/coverage.xml"/>
<log type="coverage-html" target="reports/coverage" charset="UTF-8" yui="true" highlight="true" lowUpperBound="35" highLowerBound="70"/>
</logging>
</phpunit>
以上でOK。テストとともにカバレッジを出力してくれるようになります。
終わりに
というわけで開発作業中、ローカルPCに開発環境用とCI用の2つのVMを常時起動させて作業するようになりました。 なんでもできることはローカルで、というのが最近の作業スタイルになっています。 基本一人で作業している身なので、サーバが外にある必要がないんですよね、あんまし。
あと、Jenkinsのジョブの設定とかもWebからやらないでchefでやりたいのですが、このへんは今後調べようかと思っています。
Composerのドキュメントを日本語訳してみてる話
先週末からComposerのドキュメントを個人的に日本語訳してみてます。
英語むずいいぃぃぃ~~~~~!
はい。英語が苦手なんです。キライなんです。使えるようになりたくはあるのだけど、本腰入れる気合もなくて、なんというかだらだら勉強してる。 とりあえず、通勤時間に電車で単語帳見たり、iPhoneに英語教材のオーディオブック入れて流し聞いたりしてます。 で、そんな英語学習のひとつとして、ちょっとプログラミングに関連させてドキュメントの和訳をやってみてます。
正直、続くかわからんけど。
ひとまず、IntroductionとBasic Usageのとこだけ和訳してみたので、そのことをブログに書いてみました。
なお、一応補足しておくと、ComposerはPHPの依存管理ツールです。RubyのBundlerみたいなやつですね。
Grunt
ところで英語の話とは別に、最近買ったWebDBPressにGruntの話があって、 これをちょっと使ってみたいのもあって、HTMLのビルド周りをGruntで自動化してみました。
GruntはJavaScriptのタスクランナーとのこと。要はmakeのJavaScript版みたいな感じだと思う。 特徴的なのがプラグインでタスクを簡単に追加、拡張できて、Webフロントエンド開発に便利なタスクが最初からいっぱい用意されていること。 今回使ったのは以下のプラグイン
grunt-contrib-watch
: 作業中のファイル更新を監視して、タスクを自動実行させる。grunt-contrib-connect
: 作業中の確認用HTTPサーバ。livereloadサーバも立ててブラウザの自動再読み込みもできる。grunt-contrib-cssmin
: CSSのミニファイ。grunt-markdown
: MarkdownをHTMLに変換。grunt-contrib-copy
: ファイルのコピー。grunt-contrib-clean
: ファイルの削除。grunt-contrib-uglify
: JavaScriptのミニファイ。
ちょっとした静的サイトジェネレータのようなものが構築できてしまって、大変便利なのでした。( ´∀`)
TravisCIでGithubPagesを自動更新
ついでにGithubのリポジトリにソース一式Pushしたら、自動でGithubPagesをジェネレートしたサイトで更新するようにしてみました。以下が参考サイト。
- http://webtech-walker.com/archive/2013/08/jekyll_to_middleman.html
- http://tricknotes.hateblo.jp/entry/2013/06/17/020229
参考サイトでやってることほとんどそのまんまです。 travisでgruntを実行しサイトをジェネレートして、生成物をgh-pagesブランチにコミット、Pushする。
こりゃ便利だわ~( ´∀`)。
後になって気づいたこと
getcomposer.org_doc_jpというリポジトリ名だけどgetcomposer.org_doc_jaのほうが良かったんじゃないかと。 でもtravisの設定とかやり直したくないので、このままにします。。。
あと、えいごガンバル。
PHPのArrayを少し便利に扱えるクラスを書いた。
大したコード量じゃないので毎回コピペして使ってたコードなのですが、せっかくなのでテスト書いて、TravisCIとCoverallsでテストして、Packagistに登録してみました。
https://github.com/kohkimakimoto/EArray
EArray
PHPで配列にアクセスするとき存在しないキーで値を取得しようとすると、警告がでます。
<?php $arr = array("key_a" => "aaa"); echo $arr["key_b"]; // PHP Notice: Undefined index: key_b ...
で、これを避けるために事前にキーの存在をチェックする処理をいれたりするんですが、いちいちこのコードを書くのが地味に面倒なのですよね。
$arr = array("key_a" => "aaa"); echo isset($arr["key_b"]) ? $arr["key_b"] : null;
これ、多次元配列だと更に面倒。
@(アットマーク)を配列の前につけてエラー出力しないように制御する方法もありますが、単に警告を無視してることになって、どうにも「その対応は違うだろ」って気持ちになってやりたくないですし。
そんなわけで、前述のEArrayというクラスを書きました。使い方は対象の配列をコンストラクタで登録してgetメソッドを使ってアクセスする。キーが存在しない場合、デフォルトでnullを戻します。
use Kohkimakimoto\EArray\EArray; $earray = new EArray(array("foo" => "bar")); $earray->get("foo"); $earray->get("foo2", "default"); // 第二引数でキーが存在しない時に戻す値を指定できる。
多次元配列の場合は、スラッシュ区切りの記法でスマートにアクセスできます。
$earray = new EArray( array( "foo" => array( "foo2" => array( "foo3", "foo4", ), "foo2-1" => "foo5", ), "bar", "hoge", ) ); $earray->get("foo/foo2-1"); # "foo5"を戻す。 $earray->get("foo.foo2-1", null, "."); # キーにスラッシュが含まれている場合は第三引数で区切り文字を指定すればいい。
なお、ArrayAccessやIteratorインターフェースを実装しているので、普通の配列としてもオペレーションできます。 他にもREADMEにサンプルコードをいくつか書いておいたので参考にしてください。
https://github.com/kohkimakimoto/EArray/blob/master/README.md
インストール
composerを使います。composer.jsonに以下の行を追加してcomposer install
すればいいと思います。
"kohkimakimoto/earray": "1.1.*"
まあ、クラスファイル一個だけなので、ちょっと使いたい人は以下のコードをコピペしてネームスペース定義を変えるとかしてもいいかもしれません。
https://raw.github.com/kohkimakimoto/EArray/master/src/Kohkimakimoto/EArray/EArray.php
ちなみにEArrayのEはExtendedの意味です。
デプロイツールAltaxをバージョン2に更新しました
Capistrano的なPHP製デプロイツールAltaxをバージョン1→2としてアップデートしました。
https://github.com/kohkimakimoto/altax
このソフトウェアはPHPで記述したタスクを、複数のリモートサーバに対して実行します。 サーバ接続には鍵認証のSSHを使用し、並列に実行します。 動作に必要なファイルは1つだけで、PHPの実行環境があれば簡単に導入できます。
Version1からの変更点
- 実装をSymfony Componentsを使って大幅に変更、再構成。
- pharによる実行ファイル形式の配布をサポート。
- デフォルトの読み込み設定ファイルのパスを変更。
- クラスベースのタスク定義をサポート。
- ログ出力機能を追加。
- SSH接続の実装部分をsshコマンドからphpseclibに変更。
インストール
ドキュメントはまだ更新してないので、ざっくり最初の使い方だけ記述しておきます。実行にはPHP5.3以上が必要です。またWindowsでは多分動きません。
インストールは以下のコマンドを実行するだけです。
$ curl https://raw.github.com/kohkimakimoto/altax/master/installer.sh | sudo bash -s system v2
/usr/local/bin/altax
に実行ファイルがインストールされます。
手動で好きな場所にインストールしたい場合は、以下のように直接altax.phar
を持ってきて配置すればいいでしょう。
$ wget https://github.com/kohkimakimoto/altax/raw/master/altax.phar
$ chmod 755 altax.phar
$ mv altax.phar /path/to/install_dir/altax
とりあえず実行する
なにも付けずにコマンドを叩くと、バージョン情報やサブコマンド一覧が表示されます
$ altax
設定ファイルを作成する
$ altax init
initコマンドを実行するとカレントディレクトリに以下のような構造の.altax
ディレクトリを作成します。
.altax/
config.php # メインの設定ファイル。
hosts.php # 接続先ホストを記述する用のファイル
tasks/ # タスク定義を置くディレクトリ
hellow.php.sample # サンプルのタスク定義。
必須なのはconfig.php
だけで、その他のファイルは必要に応じてconfig.php
から読み込んで使用します(今回は使いません)。
config.phpはコメントアウトされているところを除くと以下のようになっています。
host('127.0.0.1', array('web', 'localhost'));
desc('This is a sample task.');
task('sample',array('roles' => 'web'), function($host, $args){
run('echo Hellow World!');
});
host関数で接続先ホストの設定、taskで実行タスクを定義しています。このサンプルではローカルホストに接続しにいきます。 接続先ホストはパスワードなしの鍵認証でSSHできるようにしておく必要があります。
再びaltax
コマンドをサブコマンドなしで実行します。
$ altax
Available commands:
config Show configurations
help Displays help for a command
init Create default configuration directory in the current directory
list Lists commands
sample This is a sample task.
Available commandsにsampleタスクが表示されます。実行してみましょう。
$ altax sample
Altax version 2.1.0
Starting altax process
- Starting task sample
Found 1 target hosts: 127.0.0.1
- Running sample at 127.0.0.1
Hellow World!
Completed task sample
以上のようにHellow World!がechoコマンドで実行されます。
詳細な使い方について
今回はここまでです。詳細は後日、ドキュメントかブログにまとめようと思っています。
メモ) Symfony Componentsが激しく便利な件
今回コマンドラインのプログラムを書き直したわけですが、その際に利用したSymfony Componentsが激しく便利でした。
あたりを使ったわけですが、 今までコマンドラインオプションをパースする処理をgetopt関数使ってシコシコ書いていたりしたのが、すんごく簡単に書けるようになります。 出力メッセージに色つけたり、ディレクトリからファイル一覧を持ってきたりとかもすんごく楽。
みんなも使ったほうがいいですヽ(^o^)丿。
メモ) だれか知ってたら教えて
SSHでコマンド実行が正常にできることのテストをtravis-ciでやるのってどうすんだと。 わからん。とりあえず、このあとCapistranoのテストコード見に行ってきますよ、と。