herokuにデプロイするときでもgruntしたい!(+rubyサーバー)
JSの勉強のためにタイトルのような構成のwebアプリをherokuにデプロイしようとしたところ、主にjsのライブラリ管理やcoffeeのコンパイルなどで苦労したのでメモ。
rubyだとjs周りも面倒見てくれるRails関係のgemが色々あるみたいですが、jsは独立して管理した方がいいという風潮を最近感じているのでgemには一切依存しない形にしてみました。
(試していませんがサーバー側をPythonやPerlに置き換えても問題ないはず)
構成
サーバー: ruby(sinatra)
クライアント: coffee-script(gruntでjsにコンパイル)
jsライブラリ: BoostrapとかjQueryなど(bowerでインストール)
サーバー側
サーバー側でやることは何もありません。
js周りの面倒を見てくれるgemを一切入れないので、sinatraで簡単なrubyサーバーをherokuにプッシュするために最低限必要なGemfileとProcfileだけあればよいです。
Gemfile
ruby "2.1.2" gem "sinatra" gem 'haml' group :development do gem 'foreman' gem 'heroku' end
Procfile
本当はunicornとか使うべきなのだろうけど、sinatraのデフォルトでもとりあえず動く。
web: bundle exec ruby app.rb -p $PORT
クライアント側
今どきのjsはbowerでライブラリを管理するらしいので、BootstrapやjQueryといったライブラリをbower install --save
でインストールします。
--saveを付けて必要なライブラリをbower.jsonで管理することで、外部ライブラリをgitで管理せずともherokuにデプロイしたときに向こう側でインストールできるようにしておきます。
coffeeもコンパイルするたびにgitにコミットするのは大変なので、コンパイルされたjsはgitに追加せず元のcoffeeだけgitで管理します。
問題点
という方針で開発を進めていたのですが、いざherokuへのデプロイという段階で壁にぶつかってしまいました。
herokuのbuildpackは単一言語しか動かない
herokuのbuildpackは基本的に1つのプログラミング言語しか動かないので、この構成ではrubyは動くがnodeが動かずbowerでjsライブラリのインストールができませんでした・・・
この問題を解決するheroku-buildpack-ruby-bowerというサードパーティのbuildpackもあるようですが、今後もメンテされるかが不安なので公式のbuildpackで解決する方法を探してみました。
解決策
mulit_buildpack
で、見つけたのがこのmulti_buildpack。
これを使うと.buildpackに定義した複数のbuildpackを1つのインスタンスに入れられるので、rubyとnode.jsのbuildpackを両方とも入れてしまうことでbowerも使えるようになります。
multi_buildpackを使う場合には、herokuアプリを最初に作成するheroku createで使用するbuildpackの指定をmulti_buildpackにする必要があります。
heroku create -b https://github.com/heroku/heroku-buildpack-multi.git your-app
次に、以下のように公式のrubyとnodejsのbuildpackを定義した.buildpacksというファイルを作ってからherokuにデプロイをすることで、めでたくrubyとnodejsの両方が動くインスタンスになります。
.buildpacks
https://github.com/heroku/heroku-buildpack-nodejs https://github.com/heroku/heroku-buildpack-ruby
ちなみにこの記事を書く前にはこの方法を各所で見みかけたのですが、何気にheroku公式ドキュメントを見たらheroku buildpacks:setとheroku buildpacks:addというコマンドを使うことでも同じことができるみたいです。
お好きな方法でどうぞ。
https://devcenter.heroku.com/articles/using-multiple-buildpacks-for-an-app
JSライブラリのインストールとcoffeeからのコンパイル
gruntやbowerの使い方をググるとだいたいnpm install -g
でインストールしましょうと書かれていることが多い気がしますが、 この方法では手元のPCでgrunt, bowerが使えるようになるのですがheroku側にはインストールされないため、このままherokuにデプロイしても上手くいきません。
解決するにはpackage.jsonをこんな感じに書きます。
{ "name": "MarkdownServer", "version": "0.1.0", "dependencies": { "bower": "^1.4.1", "grunt": "^0.4.5", "grunt-cli": "^0.1.13", "grunt-contrib-coffee": "^0.13.0" }, "devDependencies": { "grunt-contrib-connect": "^0.10.1", "grunt-contrib-watch": "^0.6.1" }, "scripts": { "postinstall": "bower install && grunt build" } }
まずはdependenciesにbowerとgruntを追加してbowerとgruntを使えるようにして、postinstallにbower install && grunt build
を書いておくことでbowerとgruntのインストール後にbowerとgruntを起動させます。
herokuにpushすると自動的にnpm install
を実行してくれるので、
npm install bower install grunt build (Gruntfile.jsにbuildでcoffeeのコンパイルなどが行われるように自分で定義しておく)
の順番にコマンドが実行され、無事に依存jsライブラリののインストールとcoffeeのコンパイルが行われるようになります。
結論
- multibuildbackを使う
- package.jsonにbower, gruntのインストールとpostinstallでの実行を記述しておく。
ローカルのPCで動いている状態からherokuデプロイ用の設定を書くのはなかなか面倒でしたが、herokuへのデプロイは特別なコマンドを使っていないので、
herokuにデプロイできるようにする = どの環境でもコマンド一発叩けばインストールできる
ということになり結果的に非常にポータブルになりました。
いざとなればVPSなどへのお引越しも簡単にできるようになるので、git clone後にコマンド一発でインストールできるように設定しておくことは大事。
ちなみに執筆当時に書いてたWebアプリでここまでの設定を書き終えたのがこちらです。 設定のサンプルにどうぞ https://github.com/Kesin11/MarkdownServer/tree/a389c477f95cabe0624e77d193b56b0237603a1f
参考
今年の自分のvimrcを振り返る(2014)
長らくブログを放置していて、気がつけばAdventCalendarの季節も通りすぎて2015年になってしまいました。
去年は自分にとってVimの進歩があった年だったので、git log .vimrc
の中からTOP10をまとめてみました。
1位 実践Vim
- 作者: DrewNeil,新丈径
- 出版社/メーカー: KADOKAWA / アスキー・メディアワークス
- 発売日: 2014/01/28
- メディア: Kindle版
- この商品を含むブログ (3件) を見る
今まで手に取らずに長いこと損をしていました...っ!
.による繰り返し、検索・置換のテクニック、マクロ機能などの小技が盛り沢山だっただけではなく、知らなかった設定も色々ありました。
中でも便利になった設定が以下の3つ
incsearch
set incsearch
今どきのGUIエディタのように/の検索中にマッチした部分にオートスクロールしてくれます。
Ctrl+pで履歴呼び出しにフィルタリング
cnoremap <C-p> <Up> cnoremap <C-n> <Down>
コマンド履歴の呼び出しを↑↓で行うと入力している文字で自動的にフィルタリングが行われますが、Ctrl+p, Ctrl+nによる履歴の呼び出しではフィルタリングが行われません。
ホームポジションから手を動かしたくないので、良いところどりをするためにCtrl+p, Ctrl+nを↑↓に割り当てます。
wildmenu
Vimのコマンドモードのtab補完はクソすぎると思っていたのですが、ちゃんと設定すれば補完メニューも出せるし、どこまで補完するのかも設定できます。
set wildmenu set wildmode=longest:full "コマンドモードの補完タイプ
自分の設定だと"Neo"まで入力してtabで補完するとこんな感じに表示されます。
続きを読む
PyConJP 2014に参加してきました
今年も9/13,14に行われたPyConJP 2014に参加してきました!去年に引き続き、2回目の参加です。
今年の会場は去年に比べて交通は少し不便になってしまいましたが(特に東京テレポート駅からは遠い)、会場の中はホールや教室とドリンクバーが離れておらず、良い意味でこじんまりとした場所でした。
今年はチケット代に二日間のランチ + 何と懇親会のパーティ付きでした。ありがとうございます!
特に1日目のランチはテーマごとの席が用意されていて、初対面の人とでも会話しやすくてありがたかったです。
実は最近Pythonを使っていないので去年ほどのワクワク感は無くなってしまったのですが、いくつか面白かった講演の感想などを書きたいと思います。
パッケージングの今
去年に引き続き、Pythonのパッケージングライブラリがどうなってきているのか、という話。
実は今までパッケージをPyPIに登録するという経験が無かったので、Pythonのリハビリも兼ねて作った小さなライブラリをPyConJP会場からPyPIに登録したばかりでタイムリーな話題でした*1。
setup.pyにはよく分かっていないままにsetuptoolsを使ったのですが、とりあえず今の本流らしくて安心しました。
wheelは去年聞いたときはピンと来なかったのですが、先日Macでnumpyをpip installしたときにコンパイルが走らずにインストールが完了したときに感動しました。
numpyのようにC言語や他のライブラリとリンクさせる巨大なライブラリではインストールだけで何十分もかかっていたのですが(しかも依存ライブラリとか設定がしっかりしてないとコンパイルエラーで落ちる)、それも過去の話になりそうです。*2
Pythonはおそらく標準ライブラリが充実しているために依存関係が少なくて外部ライブラリのインストールが早い、ということを別言語に移行して再発見したところだったので、wheelの導入でさらにインストールが早くなるのは素晴らしいと思います。
正規表現リテラルは必要なのか
このセッション一番の学びは、re.matchやre.searchは内部でコンパイルした正規表現をキャッシュしているということでした。re.compileが不要かどうかは計測しないと分からないところですが。
個人的には、正規表現リテラルはやはり便利だと思います。文字列操作の関数の方が分かりやすいという意見も分かるのですが、Linuxでsedとかgrepを使いこなすようになると、文字列操作をするときは自然と正規表現を考えるようになるので文字列操作関数はいつの日か使わなくなってしまいました。そうなるとPythonだけre.search()とか使わなきゃいけないのが単純にめんどくさいんですよね。
Perlを使い始めた時はPythonに比べて色々めんどくさいなーと思いながら勉強していましたが、Perlの正規表現に関しては最初に見たときに感動しました。
やっぱりリテラルがあるとカジュアルに正規表現を使っていけるんですよね。まぁ、カジュアルに使いすぎて後々苦しめられることがあるのも事実なのですが・・・。
1日目 Keynote Kenneth Reitzさん
順番的には一番最初の講演なのですが、一番おもしろい話だったので最後に持ってきました。
CH01 Opening~Keynote: Kenneth Reitz - YouTube
RequestsライブラリやHerokuの話しかと予想していましたが、Python2と3の話でした。
自分の記憶ではPython3は3.2ぐらいから安定してきたと言われていて、Djangoやnumpyが3に対応したと聞いていたのでそろそろ3に本格移行が始まっているのかな、と思っていましたが会場のアンケートでは2の使用者がまだまだ多くいらっしゃるようでした。
その後のスライドで2014年1月に2週間にPyPIからPython2と3でどれだけパッケージがダウンロードされたかの統計が示されていたのですが、圧倒的に2が多く(動画の45分あたり) 、コア開発者は3の開発をどんどん続けているのにコミュニティが2に留まり続けているので断絶が起きてしまっているらしいです。
このセッションは質疑応答の方が盛り上がって、Python界隈からするとかなりギリギリの話もあったかと思います。
個人的に印象深かったのは以下の3点
- Kennethさんは個人的な意見としながらも3にメリットは無いと発言
- Pythonは安定した言語だったので、Rubyと違って新バージョンへ移行する文化が無かった
- 現時点でコミュニティが出している回答は2の維持
3にメリットが感じられないというのは個人的に同感で、3に移行するメリットで有名なのは
- Unicode回りが改善
- printとかtry-exceptのようなイマイチだった構文の改善
- 標準ライブラリが整理された
ぐらいだと思います。既に2.7は今後開発されないと宣言されていますが、苦労して今までのコードを書き換えて、正しく動作することを確認するコストと比較すると、現時点で得られるメリットが少ないと言わざるを得ないと思います。
各ライブラリの対応状況を見ると、主要なライブラリは大体3に対応してきているので、Python3に移行しない最大の理由である「ライブラリが対応していないから」というある意味言い訳も通用しなくなっていると思います。それでもユーザーが移行しないとなると、質疑中でPerl5とPerl6の話も出ましたが、Pythonでも同様にPython3の成果をPython2にバックポートし続けて別々の言語として発展し続ける・・・という未来もありえるのかもしれません。
自分としてはPythonを触る機会が減ってきてしまってほとんど新規ユーザーと同じ状態なので、これからはとりあえずPython3を使ってみようと思います。
VimでのCartonを使ったPerlの開発
最近Perlを書くことになりましたKesinです。Pythonが懐かしいです。
言語が変わっても相変わらずVimを愛用していて、文法チェックができるsyntastic *1と、Vimで書いているスクリプトをすぐに実行できるvim-quickrunには相変わらずお世話になっています。
PerlでWebアプリとかを書くときは、CartonというRubyのBundlerのような仕組みを使うのが今どきらしいのですが、上の2つのプラグインがCarton環境以下で上手く動いてくれないので、Cartonに対応させる.vimrcを書いてみました。
" thinca/vim-localrcをインストールし、Cartonで管理しているAmon2プロジェクトなどのlibがあるディレクトリに.local.vimrcとして置く let carton_path = system('carton exec perl -e "print join(q/,/,@INC)"') let lib_path = fnamemodify(finddir("lib", ";"), ":p") let g:syntastic_perl_lib_path = join([carton_path, lib_path], ',') let g:quickrun_config = { \ 'perl' : { \ 'cmdopt': '-Ilib', \ 'exec': 'carton exec perl %o %s', \ }, \}
これを.local.vimrcという名前でCartonで管理しているディレクトリのトップに保存し、vim-localrcプラグインを導入すると、syntasticとquickrunがCarton対応になります。 自分はGithubにアップしてるdotfilesに.local.vimrcという名前で保存しておき、必要なときにプロジェクトのディレクトリにコピーするという使い方をしています。
syntasticの問題
syntasticは文法チェックにシステムのPerlでperl -c
を実行した結果を使うので、CartonでインストールしたモジュールがシステムのPerlにインストールされていないと、下のような感じで先頭のuse文で盛大にエラーを吐いてくれます。
さらに困るのが、以下のようなディレクトリ構成のときに、例えばMyAppにcdしてからControlelr.pmでModelをuseしようとすると、やはりパスが通っていないということでエラーが出力されてしまいます。
libと同じ階層でperl -l
で実行すれば問題は無いので、カレントディレクトリが違うだけで文法チェックで怒られるのは辛い感じです・・・
-- lib |-- MyApp.pm |-- MyApp |-- Controller.pm |-- Model.pm |-- View.pm
# Controller.pm use MyApp::Model; ...
vim-quickrunの問題
quickrunの方も原因は同じで、Cartonでモジュールを管理しているlocalディレクトリにパスが通っていないのでCartonでインストールしたモジュールを使ってくれません。
原理
先に示した設定をすると、syntasticとquickrunがCartonのパスを参照するようになり、問題は解消されます。
syntasticの方は、Cartonを使った時のパス(@INC)と、libディレクトリをsyntasticの設定に渡すことで解決しています。lib_path = fnamemodify(finddir("lib", ";"), ":p")
はカレントディレクトリより上の階層にあるlibディレクトリのパスを返すということをしています。
自分も最初はqiitaのyuku_tさんの記事を参考にしてプロジェクトディレクトリごとに手動でフルパスを設定していたのですが、汎用性が無かったのでパスが自動で設定されるようにしました。
quickrunは見たまんまですが、Carton exec perl
で実行するように設定しています。%oはcmdopt, %sは実行するスクリプトのパスに展開されます。
まとめ
というわけで、プラグインですらない非常に単純な設定ですが、syntasticとquickrunをCartonに対応させたという話でした。
2つのプラグインとCartonの相性の悪さにイライラしていた人は、ストレスから解消されると思います。
参考
Vimとctagsでコードリーディング
というタイトルで会社の同期内でのLTで発表しました。
知ってる人からすれば当たり前のように使ってると思いますが、IDEからエディタへの移行組で知らなかった人は感動する内容だったかもしれません(自分が昔そうでした)。
発表中では実際にデモをしましたが、cpanからモジュールをダウンロードして中身を見るときはこんな感じでできます。
Vimとctagsでコードリーディング1 - YouTube
cpanからモジュールをダウンロードして、ctagsでタグファイル生成、関数ジャンプ
Vimとctagsでコードリーディング2 - YouTube
ctags -aで標準ライブラリの情報をタグファイルに追記、標準ライブラリへのジャンプ、垂直分割
ちなみに、上のスクリーンキャストでも分かるように発表中ではPerlを前提としていました。
最近のPerlのモジュール管理事情は、Cartonというツールでプロジェクトごとにlocalというディレクトリ以下にモジュールをインストールするようになっています。
デモでは標準ライブラリのtagsも生成しましたが、Perlではそこまで必要ではないと思うので、実用上は作業プロジェクトのディレクトリ上でctags -Rで十分かと思います。