Ari-Press

エンジニアAriのブログ.

Play Framework(Scala)にTwitter Bootstrap(LESS)を組み込む

前回でPlay Framework2.0.2(Scala)を動かしたわけですが,今回はTwitter Bootstrapを組み込んでみました.

どうやらPlay Framework用のプラグインがあるようなのですが,githubを見る限り最終更新が8ヶ月前・・・?ということで,Playアプリケーションにgithubから入手した新しいTwitter Bootstrapを組み込みました.

導入に向けて

Twitter Bootstrapは単純にCSSファイル・JSファイル・画像ファイルの一式をダウンロードしてHTMLから読み込むだけで簡単に導入できるものですが,このCSSは変数・関数・ネスト構造などを使える便利なCSS拡張であるLESSのファイルをコンパイルしたものです.

このLESSファイルはTwitterのgithubから簡単に入手でき,また変数をいじるだけで色々とカスタマイズが効きます.LESSの動的コンパイルが可能なPlay Frameworkではこっちを使ったほうがお得!ということでCSSではなくLESSの状態での導入を目指します.

また,PlayではスタイルシートやJavascriptについて,動的なもの(Lessやcoffeescript)をapp/assetsフォルダ以下に,静的なものをpublicフォルダ以下に置きます.

それぞれに置いたファイルはビューから@routes.Assets.at("filepath")で区別なく呼び出せるので,非常に便利!

なので,以下の様なフォルダ構成でlessを動的にコンパイルしながらTwitter Bootstrapを呼び出せるようにしてみることにします.

public/bootstrap

js:JSファイルを入れるフォルダ

img:画像ファイルを入れるフォルダ

app/assets/bootstrap

css:スタイルシート(less)ファイルを入れるフォルダ

このようにすると,@routes.Assets.at("bootstrap/css/bootstrap.min.css")ではapp/assets/bootstrap/css/bootstrap.min.cssを,@routes.Assets.at("bootstrap/js/bootstrap.min.js")ではpublic/bootstrap/js/bootstrap.min.jsをそれぞれ呼び出せる便利仕様になります.

Twitter Bootstrapの用意

まず適当なディレクトリでTwitter Bootstrapのソースコードを入手し,gitコマンドでバージョンを2.0.1に切り替えます.

1
2
3
git clone https://github.com/twitter/bootstrap.git
cd bootstrap
git checkout refs/tags/v2.0.4 -b v2.0.4-compiled

以上でv2.0.4-compiledブランチが出来上がるので,これを使っていきます.

ちなみにブランチに「-compiled」という名前を付けたのは下でコンパイルをするためです.

Twitter Bootstrapのコンパイル

ソースコードは用意しましたが,動的コンパイルをする予定のLESS以外(Javascript)はここでコンパイルをする必要があります.

ここでrecess(v2.0.3より以前はlessc)・uglifyjsコマンドが必要になりますので,両方共インストールしましょう(このコマンドのインストールのためにnpmも必要です).

1
2
sudo npm --global install recess
sudo npm --global install uglify-js

以上でrecessとuglifyjsコマンドが使えるようになったことを確認したら,bootstrapフォルダで以下のコマンドを実行します.

1
make bootstrap

これでカレントフォルダ以下にbootstrapフォルダが作成され,その中にcss・img・jsの三つのフォルダとその中身が作成されます.

Playアプリケーションへの組み込み

まずは上の「導入に向けて」で示したフォルダ構造の通りにTwitter Bootstrapのファイルを格納します.

ファイルの対応関係は以下の通り(以下で示すのはPlayアプリケーションのフォルダ構成です).

public/bootstrap

js:bootstrap/bootstrap/jsをコピーして作成

img:bootstrap/bootstrap/imgをコピーして作成

app/assets/bootstrap

css:bootstrap/lessをコピー・リネームして作成

以上で準備が完了,scala.htmlファイルにCSS読み込みを追記します.

1
2
<link rel='stylesheet' media='screen' href='@routes.Assets.at('bootstrap/css/bootstrap.min.css')'>
<link rel='stylesheet' media='screen' href='@routes.Assets.at('bootstrap/css/responsive.min.css')'>

スクリプトも以下のように読み込みます.

1
<script src='@routes.Assets.at('bootstrap/js/bootstrap.min.js')' type='text/javascript'></script>

これでTwitter Bootstrapが読み込まれる!

...かと思いきや,「sbt run」を実行してブラウザからアプリケーションにアクセスすると以下のようなエラーが発生しました.

1
2
3
4
5
6
7
8
9
10
variable @baseLineHeight is undefined
In /Users/hiroara62/Dropbox/scala_private/favtl/app/assets/bootstrap/css/accordion.less at line 7.

// Parent container
.accordion {
margin-bottom: @baseLineHeight;
}

// Group == heading + body
.accordion-group {

どうやら変数が無いとお怒りのようです.

これはなんだろうということで色々調べて発見したのが以下の記事.

Yann Simon - Google+ - Using #lesscss with #playframework 2.0

どうやらプロジェクトのキー「lessEntryPoints」をいじらなければならないようです.

試しにsbtコンソール上で「play-less-entry-points」とタイプして現在の値を確認してみると,LESSのエントリーポイントが以下の通りたくさん出てきます.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
[info] playapp_root/app/assets/bootstrap/css/accordion.less
[info] playapp_root/app/assets/bootstrap/css/alerts.less
[info] playapp_root/app/assets/bootstrap/css/bootstrap.less
[info] playapp_root/app/assets/bootstrap/css/breadcrumbs.less
[info] playapp_root/app/assets/bootstrap/css/button-groups.less
[info] playapp_root/app/assets/bootstrap/css/buttons.less
[info] playapp_root/app/assets/bootstrap/css/carousel.less
[info] playapp_root/app/assets/bootstrap/css/close.less
[info] playapp_root/app/assets/bootstrap/css/code.less
[info] playapp_root/app/assets/bootstrap/css/component-animations.less
[info] playapp_root/app/assets/bootstrap/css/dropdowns.less
[info] playapp_root/app/assets/bootstrap/css/forms.less
[info] playapp_root/app/assets/bootstrap/css/grid.less
[info] playapp_root/app/assets/bootstrap/css/hero-unit.less
[info] playapp_root/app/assets/bootstrap/css/labels-badges.less
[info] playapp_root/app/assets/bootstrap/css/layouts.less
[info] playapp_root/app/assets/bootstrap/css/mixins.less
[info] playapp_root/app/assets/bootstrap/css/modals.less

(多すぎるので以下略...)

どうやら本来インポートされてコンパイルされるはずのソースコードを直接コンパイルしようとしたことでエラーが発生するようです.

これを解消するために,コンパイルのエントリーポイント(直接コンパイル対象とするファイル)をbootstrap.lessとresponsive.lessの二つのみに変更します.

この方法は上記の記事(Yann Simon - Google+ - Using #lesscss with #playframework 2.0)で紹介されているんですが,ここで紹介されている方法だと自分でLESSファイルを独自に作成した時にそれを手動で追記する必要が出てきます(本来ならばフォルダ以下に置けば読み込まれます).

そのため,今回は以下のようなコードをproject/Build.scalaに書きました.

1
2
3
4
5
6
7
8
9
10
11
12
val main = PlayProject(appName, appVersion, appDependencies, mainLang = SCALA).settings(
// Add your own project settings here
// ここから追記
lessEntryPoints ~= {pathFinder => pathFinder ** (new FileFilter {
def accept(file: File) = file match {
case file: File if 'bootstrap'.r.findFirstIn(file.getPath).isDefined =>
'bootstrap.less$|responsive.less$'.r findFirstIn(file.getPath) isDefined
case _ => true
}
})}
// ここまで追記
 )

このコードはapp/assets/bootstrapフォルダ以下についてはbootstrap.lessとresponsive.lessのみをLESSのエントリーポイントとし,app/assets以下のその他のフォルダについては全てのLESSファイルをエントリーポイントとするように変更するものです.

以上の修正が完了した後,sbtコンソールを再起動して「play-less-entry-points」とタイプすると以下のように出力されました.

1
2
[info] playapp_root/app/assets/bootstrap/css/bootstrap.less
[info] playapp_root/app/assets/bootstrap/css/responsive.less

これが確認できれば今度こそ準備完了です.

この状態で「sbt run」を実行してブラウザからアクセスすると・・・CSSが読み込まれました!

これでTwitter Bootstrapが組み込まれました.

案外長い道のりでしたが,一度できてしまえばLESSの変数も書き換えられるし便利なはずなので色々遊んでみようと思います.