Kesinの知見置き場

知見を共有していきたいじゃないですか

DroidKaigi 2018最高でした!!

スタッフの皆様や登壇された皆様お疲れ様でした!!
ここ最近参加したカンファレンスの中では最高に楽しかったので珍しく感想エントリを書いてみました。

droidkaigi

会場・スポンサーブースの様子

自分ではほとんど写真を撮っていなかったので雰囲気はこちらからどうぞ。 https://twitter.com/droidkaigi/status/963254146013872128

個人的には最近参加した他のカンファレンスよりもスポンサーブースで自分が普段使っているサービスをお見かけして驚きました。Androidだからか。
コーヒーとお菓子片手に、いつもサービス使ってます!と感謝を伝えまくってきました。

あと印象的だったのはQRコード。ブースでは主催している勉強会のURLや技術同人誌の配布のためのQRコードなど、いたるところでQRコードが使われていました。 やっぱり便利ですよね。

セッション

発表を聞いて印象深かったセッションの感想をいくつか紹介します。
ちなみに自分のバックグラウンドとしてはここ1-2年はjsを追っかけていて、業務の内容が変わったのでiOSAndroidを触り始めてそろそろ半年という感じです。
なのでjsから見たAndroidという感じで読んでもらえればと思います。

アーキテクチャ

Flux for Android by Shohei Kawano

ここ2-3年ぐらいjs界隈を賑やかしていたFluxをAndroidに適用したという発表。とても素直にFluxをAndroidに適用している感じでした。
jsとの違いはDispatcherやStoreといったFluxの構造上各所に引き回されるオブジェクトをDIで渡しているところと、 ReactのVirtualDOMの代わりにStateの更新をデータバインディングに接続しているところぐらいでした。

Android における Model-View-Intent アーキテクチャ by Benoît Quenaudon

Model-View-Intentという単語は初めて聞きましたが、内容としてはFlux(より正確にはRedux)をRxJavaで実装してActionとStore(Reducer)間にビジネスロジックを挟むというアーキテクチャだと理解しました。
ボタンなどをクリックしたときにIntentというものを発行するところが、Actionを発行するFluxとそっくりですね。
RxJavaもガッツリ使ってるので、FluxとRxJavaの両方を知らないと難易度が高かったのではないかと思います。

Kotlin版CleanArchitectureのテンプレート作ったら爆速開発になった話+α by Keisuke kiuchi

こちらもFlux同様、ここ最近話題となっているClean ArchitectureをAndroidに適用する + クラス数が多くなる問題を解決しましたという発表。
これも先程のFluxと同じくAndroidに素直に適用したという感じで、jsとの差異はあまりないかなという感じでした。

Clean Architectureみたいな設計にするとどうしてもクラス数は増えてしまうのですが、だいたい前に作ったクラスをコピペして必要なところだけ書き換えるということを繰り返しがちです。
テンプレを作ってIDEから呼び出せるようにしておくのは地味に生産性に効いてくると思いました。

アーキテクチャの話まとめ

自分はjsで既にFluxやClean Architectureを使ったことがあったので、Androidにもその波が来ているのだなーというのが全体的な感想です。

個人的にはFlux(Redux)のアーキテクチャは非常に好みでして、良い点が2つあります。
1つ目は、例えばボタンを押したときにイベントリスナーから状態を直接更新するのではなくて一度Actionというものを発行する仕組み。これにより状態の更新フローがView層から疎結合になります。
2つ目は、更新フローがAction -> Store(ReduxだとReducer) -> Viewという必ずこの単方向のフローを通るように強制できる(これがFluxにおけるDispatcherの役割)ためデバッグがしやすいという点です。実際、jsの方ではFluxが流行った初期からActionが発行されるごとにログ出力されるプラグインミドルウェアとも呼ばれてます)が存在していたと思います。

Clean ArchitectureのメリットもFlux同様にView層と状態の更新ロジックが疎結合になるところで、こちらはFluxよりもさらに各層を疎結合にしていてDDD的な思想なのでビジネスロジックをどこに入れるかがハッキリしています。
ビジネスロジックに関してFluxの場合は特に非同期処理をActionでやるかStoreでやるか、はたまたActionとStoreの間にミドルウェアという層をおいてそこでやるか(redux-sageとか)でずっと議論しているのでみんなの悩みどころだと思ってます。

FluxでもClean Architectureにおいても個人的に重要だと思っているメリットは、更新フローがView層から疎結合になるという点です。これはAndroidにおいては状態の更新ロジックとUIコンポーネントが完全に切り離されるということを意味します。
これにより、アプリのロジックのユニットテストJVM上で行うためにUIパーツをモックしなくて済む点が大きいです。 もはやボタンというパーツをモックする必要はなくて、ボタンを押したという動作の代わりに本来発行されるActionを発行するだけで更新ロジックのテストを書くことが可能となります。
想定しうる更新フローと最終的な状態(リストに表示するためのオブジェクトなど)を先に決めてしまえばテストをしっかり書きながらロジックを実装することで、ボタンをぽちぽち押して様々なケースの動作確認をするという必要も減らせるはずです。なにせView層はただActionを発行するだけなので。

というわけで、FluxやClean Architectureはテストが書きやすくなるアーキテクチャなので来年はこれらに対してテストをどうやって書くかというセッションが増えるのではないかな(増えて欲しい)と思ってます。

ちなみにFluxにしてもClean Architectureにしてもクラス数が多くなるというデメリットがよく言われるのですが、それに対する自分の見解はこちらです。

ReactNative系

React Native Androidはなぜ動くのか

最近流行り始めているReactNativeのAndroid側から見たディープな話。ReactNativeは趣味で最近触り始めたので深い話が聞けてよかったです。

ReactNativeは今年ぐらいから新しいプロダクトを作るときに選択肢の候補に上がってくるのではないかなーと個人的には予想しています。
自分みたいなjsを書いてきたエンジニアもネイティブアプリの戦力としてカウントできるようになるものの、ReactNativeコンポーネントを自作したりだとかそもそもビルド環境を整えることは今までネイティブを触り続けてきた人達の方が圧倒的に強いと感じています。

ReactNativeで作るにしてもネイティブ未経験者オンリーなチームでは大変だろうなという気がしています。特にiOSの証明書とか・・・。

コードで見るFlutterアプリの実装 by konifar

Flutterって全く聞いたことなかったのですが、発表を聞いた限りではDartで書くGoogle公式のReactNativeに近いフレームワークという印象でした。

公式でUIパーツが一通りそろってるのはよさそうですね。ReactNativeはググるサードパーティコンポーネントの記事がたくさん出てくるので・・・
Dartって表舞台には出てきていないと思っているのですが、Googleの一部でずっと生き続けているっぽいのでもしかすると今後盛り返してくる可能性も?

ReactNativeとFlutterの話まとめ

ReactNativeってネイティブ開発者からすると「昔から存在するjsでネイティブが書けるって吹聴してる一派でしょ?」と思われている気がします。
個人的にはjsで書けるという点はSwiftやKotlinの登場によってそれほどメリットではなくなってきていると思いますが、1つ大きなメリットだと感じているのはコードを編集すると再コンパイル無しでUIを更新できるところです。実際にkonifarさんも発表中にDartのコードを書き換えると自動的にUIが変わっていく様子をデモしていました。
これってWeb界隈でlive reloadやhot reloadができるようになったことでUIのコードを編集するたびにブラウザの更新ボタンをぽちぽち押していた時代から開放されたのと同じだなと思いました。

このデザインをWeb開発のようにガンガン変えていけるというスピード感は普通のネイティブ開発では難しいはず。SwiftやKotlinによるネイティブ開発ではちょっとUIを変更するだけでもコンパイルが必要なので自分は非常にめんどくさいと感じてます。
ちなみにTitaniumとかは知らないので、もしかしてFlutterみたいなことは昔から可能だったのかもしれませんがもしそうだったらすみません。

DroidKaigi 2018のアプリについて

DroidKaigiでは毎年カンファレンスのタイムテーブルを閲覧したり、アンケートを送るためのアプリをOSSで開発していて毎年ナウい構成で作り直されているので名物となっているようです。
https://github.com/DroidKaigi/conference-app-2018

自分もKotlinの勉強のために小さいバグの修正でしたがPRを送ってContributorsに名を連ねさせていただきました。:pray:
多分これぐらいの規模でちゃんと作られてるプロダクションのソースコードを見ることができるのは珍しいと思うので、AndroidやKotlinに興味がある人は良い勉強材料になるのではないかなと思います。

というわけでDroidKaigi 2018の感想エントリでした!来年も絶対行きます!

Jenkins2のPipeline入門のメモ

Jenkins2のPipelineに入門した際にメモした内容をせっかくなので公開しておきます。

基本

step, node, stage

パイプラインのチュートリアルでいきなり図と共に登場するので最初はどう違うのか分かりにくいが、Jenkinsのビルドの流れにそれぞれ対応しているので実は難しくない。

step

今までのbuild stepと同じ。普通に1ジョブ内でやることを分割する。
主にプラグインの機能を呼び出すときにスニペットビルダー(後述)から生成する。

node

キューに独立して乗せられ、毎回削除されるWorkspaceが割り当てられる。
ということがチュートリアルでは書かれているが、要はnodeのブロック単位でJenkinsのキューに積まれるということ。

stage

stepやnodeと違って明確な役割はないが、パイプライン中の処理に名前を付けることができる。
GUI上でどのstageまで通過したか、どこで失敗したか分かりやすくなるのである程度の処理の固まりをstageに分割すると分かりやすい。
一般的にはinstall, build, test, deployみたいな単位でstageを分けると良さそう。

実際にパイプラインを書くときにはnode, stage, stepという順にブロックを作っていくことになる。

node {
    stage('build') {
        echo 'hoge'
    }
}

変数

パイプライン中でのローカル変数は以下のように作れる。

def hoge = 1

Jenkins1系では1ビルド内で新たに変数を作ることができなかったので何気にすごい嬉しい。
(自分が知らなかったでそのようなプラグインはあったかもしれませんが)

関数

パイプライン中での関数も定義することができる。

def hoge(arg) {
  echo 'hoge'
}

これも変数と同じく今までできなかったのでとてもありがたい。

条件分岐・例外処理

ifやtryなど基本的な文法はgroovyのものが使える。 buildで呼び出したジョブがfailしたかはtry-catch-finallyでハンドリングできる。

具体例は後述

別のジョブ呼び出し

build jobで既に作られているジョブやパイプラインを呼び出すことができる。
自分で書いてもいいが、スニペットビルダーを使うとジョブ名の補完が効いたり、必要なパラメータをフォームに入力するとコードを出力してくれるのでこれを使うべし。

f:id:Kesin:20161013013452p:plain

イディオム集

ビルド中に下流ビルドを実行する

今までもParameterized Trigger Pluginを使うことで実現できていましたが、ビルド途中でパラメータの中身を改変したりパラメータの名前を変えることが大変でした。

パイプラインでは変数を新たに定義したり、パラメータを途中で書き換えることも容易です。

node {
    stage ('build start'){
        def text = 'hoge'
        build job: 'my_echo', parameters: [text(name: 'text', value: text)]
    }
}

条件付きで別ジョブを走らせたい

パラメータの内容によって次に走らせるジョブを変更したり、そもそも実行させなかったりするような条件分岐は今まではFlexible Publishを使っていましたが、GUIで条件を設定する必要があったのでめんどくさかった奴です。

パイプラインではif-elseやtry-catch-finallyで制御できるので、普段からコーディングで使ってる我々からすると直感的に書けるようになりました。

前のジョブが失敗したときに次のジョブをスキップして通知を出すというサンプル

node {
    stage ('build'){
        num = 1
        try {
           // failになるジョブ
           build job: 'odd_error', parameters: [string(name: 'num', value: num)]
        } catch (err) {
            currentBuild.result = 'FAILURE'
        } finally {
            echo 'end try block'
        }

        if(currentBuild.result == 'FAILURE') {
            // 実際にはechoの代わりにSlack通知など
            echo 'job failed'
        }
    }

並列に下流ジョブを走らせたい

今までのParameterized Trigger Pluginでは下流ジョブの名前をカンマ区切りで並べていけば実現できていましたが、パイプラインではちょっと複雑な書き方になりました。

job = [:]
job['job1'] = { build job: 'my_echo', parameters: [text(name: 'text', value: 'job1')] }
job['job2'] = { build job: 'my_echo', parameters: [text(name: 'text', value: 'job2')] }
parallel job

この記述でjob1, job2は並列にキューに乗って実行されます。

参考

ベターVimとしてAtomを使う

この記事はATOM-editor Advent Carendar 2015の15日目の記事です。

f:id:Kesin:20151214234544p:plain

2016/03/01 追記

vim-mode-plus, vim風キーマッピングの発見により色々追記しました。

はじめに

どうも、Vimの方からやってきました。
様々なエディタやIDEVimプラグインがあることから分かるように、どんな環境に移ろうともVimと共に生きるVim愛好者は世界中にたくさんいるようです。
自分もそのうちの一人でメインエディタはVimを使い、IDEを使うときもVimプラグインが手放せなかったのですが、さすがに2015年にもなったし流行りのエディタも試してみたいということで今年からAtomに入門してみました。
Atomにも例に漏れずVimプラグインがあるので初日からVim化に挑戦するも、色々と足りない機能に納得できずSublimeのときのように諦めかけていたのですが、パッケージを色々探したところ大体自分が満足するところまでVim化することができました。今回はそのときに見つけたパッケージを紹介したいと思います。

*ほとんどのパッケージのページにgifアニメがありますので、ぜひリンクを覗いてみてください。

自分の紹介より見たほうが分かりやすいかと思います。

Vim標準機能を実現するパッケージ

vim-mode

2016/03/01 追記
vim-modeから色々機能が拡張されているvim-mode-plusに乗り換えました

まずはこれを入れないと始まらないです。

機能 キー
カーソル移動 h, j, k, l, gg, G, ctrl+j, ctrl+k
インサートモード i, I, o, O
ビジュアルモード v, V
ヤンク y, yy, yaw, yiw
削除 d, dd, daw, diw(削除しつつinsertモードにする'c'も使用可能)
ペースト p, P
undo/redo u, R
検索 /, * (検索後のn, Nも可能)
Paneの移動 ctrl+h,j,k,l
数値のインクリメント/デクリメント ctrl+a, ctrl+x
繰り返し .(コロン)

実現できている機能一覧ページが見つからなかったので、どこまで本家Vimの挙動をカバーできているのか正確には分かりませんがこのあたりの基本操作は抑えているようです。
vimで検索結果をハイライトさせるhlsearch機能が無いことが個人的には非常に惜しいのですが、Pull requestを見る限り精力的に開発が続けられているのでそのうち実現されるでしょう。

ex-mode

2016/03/01 追記
vim-modeからvim-mode-plusに乗り換え、ex-modeが使えなくなったので代わりにキーマッピングを使う方法を追記しました

vimという名前が付いていないので最初は気が付きませんでした。vimのexモードを実現するパッケージです。

‘:'を入力することで本家Vimと同じように:w, :wq, :q, :sp, :vspなどが使えます。
vimで作業(特にコードリーディング)するときはガンガン画面分割をしてカーソル移動させまくっているので、ex-modeを入れることでatomでも同じことができるのが感動的でした。

ちなみに:s, :%sによる置換も一応可能なのですが、自分で試したところ以下の機能が実装されていないため残念ながら実用的とは言い難いです。

  • 検索結果を使った省略記法(fooを検索後にbarに置換したい場合、本家vimであれば:s//bar/で置換できる)
  • カーソル上の単語をテキストボックスにペースト(本家vimでのctrl+r, w)
  • 置換オプションのg, c
  • ビジュアルモードでの選択範囲限定での置換

vim-mode-visual-block

2016/03/01 追記
vim-mode-plusには元々ビジュアルモードが組み込まれているので自分は不要になりました。

本家vimでの矩形ビジュアルモードを実現するパッケージ。神。

矩形モード選択からの'I'や'A'でinsertモードに移行した一括編集も実現してます

cursor-history

vim-mode-visual-blockと同じt9mdさんによる本家vimのctrl-oを実現するパッケージ。神。

Highlight Selected

元々はSublimeTextで単語をダブルクリックすると同じ単語を全てハイライトしてくれるという機能のコピーのようですが、 vim-modeでは現状検索結果のハイライトができないのでその代わりとして使っています。

sort-lines

選択した範囲の行をソートしてくれます。
たまにお世話になるvimの:sortと同じですね。

有名なVimプラグインの代替

linter系

Vimだと: syntastic

lintでパッケージ検索すると各言語用のlinter-**がたくさん見つかります。
syntasticは非同期にチェックできなかったので巨大なファイルだと保存時に固まるという問題がありましたが、atomのlinterは非同期にチェックが走っているので非常に快適です。

autocomplete系

Vimだと: neocomplcache, neocomplete

linter同様、autocompleteで検索すると各言語用のパッケージが色々見つかります。
autocomplete-pathと自分が使う言語用のものをインストールしてしまえば入力補完で困ることはほぼ無いと思います。

status-bar系

Vimだと: powerlineあたり
(ちなみに自分はlightline.vimを愛用してました)

vimにはステータスバーにgitブランチ情報や文字コードを表示させるpowerline系のプラグインが色々ありましたが、atomではそのあたりの情報はデフォルトでステータスバーに表示されてます。

clip-history

vimだと: yankringあたり (ちなみに自分はyankround.vimを愛用してました)

いわゆるクリップボード拡張系のパッケージ。
vimモードに関係なくatomのパッケージに色々ありますが貼り付けるときにいちいち候補を選ぶUIが余計で、yankround.vimでctrl+pだけで済んでいた操作性に比べるとイマイチっぽかったです。
clip-historyはyankround.vimをそのままatomに移植してきたという感じで非常に使いやすいです。

ちなみに作者はvim-mode-visual-block, cursol-historyと同じt9mdさんです。いつもお世話になっております。

script

vimだと: vim-quickrun

開いているスクリプトを実行して、実行結果をatomに表示してくれます。 試行錯誤しながら書いているときに非常に便利。

vim-modeでのキーマッピング

vim-modeを入れるとatomのキーマッピング設定にvimでのnormalモードや、insertモード中かの条件分けができます。

'atom-text-editor.vim-mode.normal-mode':
  'ctrl-i': 'cursor-history:next'
  'ctrl-o': 'cursor-history:prev'
'atom-text-editor.vim-mode:not(.insert-mode)':
  'ctrl-p': 'clip-history:paste'
  'ctrl-P': 'clip-history:paste-last'
  'ctrl-n': 'clip-history:paste-newer'

上は自分のkeymap.csonの一部です。
カーソル移動系のcursor-historyはnormalモード、ペースト拡張系のclip-historyはinsertモード以外、というように各モードごとにキーマッピングが可能になっています。
今後別のパッケージをマッピングしたいということになってもコンフリクトを減らせそうですね。

おわりに

ここまで紹介したパッケージを導入したことでVimで実現できていたことをAtomでも実現できるようになり、今では快適なVim生活をAtom上でおくれています。

じゃあVimでよくない?という意見をもらうことが多いのですが、何度かvimscriptにもチャレンジしたもののvimscriptの基本的な挙動について自分が知りたい情報を見つけるのがしんどすぎるのと、業務用に書いた簡単なプラグインを数ヶ月後に自分で見返した際にvimscriptで何が書いてあるのかさっぱり分からなかった(自分が書いたにも関わらず)のがキッカケで本格的にAtomに移行を始めようと思いました。

Atomでのパッケージ作成は勉強し始めたばかりですが、それなりに慣れ親しんだCoffeeScriptで書けるのと、そもそもAtomのほとんどの基本機能がパッケージで提供されていて参考にできるサンプルが多いのでVimを本格的にカスタマイズするよりは障壁がだいぶ低い感じです。

2016年もベターVimとしてAtomを使っていこうと思います。

2016/03/01 追記

記事を書いてからさらにベターVIm化を進めることができたので紹介します

vim-mode-plus

t9mdさんによるvim-modeの拡張版。 詳しい説明は22日目のAdventCalendarにご本人による紹介記事があります。 どのような機能が追加されているのかは、公式のgifアニメを見るのが一番理解が早いと思います。 vim-modeとはパッケージ名が変わっているのでキーマップを変える必要がありますが、これも公式でサンプルが用意されているので必要なところを自分好みに書き換えるだけで乗り換えられると思います。

個人的には/検索の強化が一番感動しました。vim-modeでも/による検索はできましたが、インクリメントサーチとハイライトが効かなかったので、本家Vimに比べると片手落ち感が否めませんでした。 vim-mode-plusはこの点が改善されていて、この機能だけでも乗り換える価値がありました。

キーマッピングによるex-modeの置き換え

自分はこのキーマッピングを追加することでex-modeが不要になりました。

# emulate vim ex-mode
# https://github.com/atom/vim-mode/issues/50
'.editor.vim-mode-plus:not(.insert-mode)':
  ': w enter': 'core:save'
  ': q enter': 'core:close'
  ': s p enter':  'pane:split-down'
  ': v s p enter':  'pane:split-right'
  'g c c': 'editor:toggle-line-comments' # emulate tomtom/tcooment_vim

vim-mode-plusに乗り換えたことでex-modeと連携できなくなり、色々探していたらvim-modeのissueからこのコメントを発見しました。 キーマッピング:wを再現するというもので、見つけた時に「その発想は無かった!」と感激しました。

置換だけはやはり本家Vimの再現はできなかったのですが、もうAtomの元々の置換機能でいいかなという感じになってきました。

2016/02時点の自分の設定

自分のdotfilesの.atomディレクトリにキーマップもパッケージ一覧も置いてあるので、よければ参考にしてみてください。

Python3.5の型ヒントをとりあえず試す

Python3.5がリリースされましたね。
Python書くのはほぼ1年ぶりでしたが、噂の型ヒントがついに3.5で導入されたようなので早速試してみました。

型ヒントについてこちらに素晴らしいPEPの翻訳記事があり、見て頂けると分かりますがPython3.5単独ではまだ型チェックはできません。 標準モジュールに入ったのは型ヒントを与えるためのtypingモジュールであり、そのヒントを基にチェック型チェックを行えるのは(現状では)mypyというサードパーティのモジュールになります。

mypy

Pythonの型ヒントは今のところ実行時に型チェックをしたりJITのためにするためのものではなく、mypyも静的に型チェックを行うLintツールです。

ちなみにmypyをインストールするにはmypy-langという名前でpip installする必要があるので注意。pip install mypyでインストールされるモジュールは全然別物のようです(紛らわしい・・・)

最近エディタとして使っているatomにはまだプラグインが存在していないのか、mypyを使うLinterは見つけられませんでした。
vimプラグインのsyntasticを見るとmypy用の設定があるように見えるのですが、なぜか自分の環境では動かなかったので一旦エディタから使うことを諦めました。

とりあえずgit hooksでmypyを試す

エディタのプラグインは諦めたので、代わりにgit hookに仕込んで最低限の自動チェックを入れてみました。
CIツールを用意しなくても普通にgitでコードを管理していれば絶対に実行されるので、mypyに限らずcommitフックでチェックするのはオススメです。

# .git/hooks/pre-commit

STATUS=0
for file in `git diff HEAD --name-only | grep -E '.py$'`; do
    mypy $file || STATUS=255
done

exit $STATUS

所感

個人の小さなプロジェクト(クラスが1-2個程度)で試して気になった点

  • Atomシンタックスハイライトが関数アノテーションを入れると壊れるのが辛い
    • Vimはちゃんとハイライトしてくれた
  • 関数アノテーションは見た目的にはさほど問題無いが、変数宣言の行末に# type: intとかが並ぶと見た目がうっとおしい
  • mypyがimport文でerror: No module named **とめちゃ怒るのがウザい
    • とりあえず# type: ignoreを付けることでエラーを抑制できる
    • 標準モジュールはスタブを用意すれば問題ないらしいが、サードパーティモジュールは今後もignore書く必要があるのだろうか・・・

個人プロジェクト程度のコード量だと全体を把握できているので、自作クラスにわざわざ型を付けるメリットはあまりなさそうかなという感じでした。

個人規模のものはサードパーティモジュールに依存する部分が広くなるため、有名どころのモジュールがいかに対応するかが普及には重要そう。
逆に企業のコードは自作モジュールに依存する部分が広いと思いますので、自前で型情報を付ける必要があるコード量が多くなる。 という事情を考えると、型ヒントを書くのが一般的になるとしても結構時間がかかりそうですね。
(そういう未来が来ない可能性の方が高そう)

参考

herokuにデプロイするときでもgruntしたい!(+rubyサーバー)

JSの勉強のためにタイトルのような構成のwebアプリをherokuにデプロイしようとしたところ、主にjsのライブラリ管理やcoffeeのコンパイルなどで苦労したのでメモ。

rubyだとjs周りも面倒見てくれるRails関係のgemが色々あるみたいですが、jsは独立して管理した方がいいという風潮を最近感じているのでgemには一切依存しない形にしてみました。
(試していませんがサーバー側をPythonPerlに置き換えても問題ないはず)

構成

サーバー: rubysinatra
クライアント: 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

参考