概要
- Ruby の tilt-mustache パッケージを使って Mustache テンプレートを処理する
今回の環境
- macOS Catalina
- Ruby 2.7.0
- tilt-mustache 0.0.2
- Mustache 1.1.1
- Tilt 2.0.10
- Sinatra 2.0.8.1
- Puma 4.3.1
Tilt とは
様々なテンプレートエンジンを統一して扱えるインターフェースを持ったライブラリ。
tilt | RubyGems.org | コミュニティのGemホスティングサービス
Generic interface to multiple Ruby template engines
tilt を導入することで、複数のテンプレートエンジンに対応したライブラリ (Web アプリケーションフレームワークや静的サイトジェネレーターなど) が作りやすくなる。
rtomayko/tilt: Generic interface to multiple Ruby template engines
Tilt is a thin interface over a bunch of different Ruby template engines in an attempt to make their usage as generic as possible. This is useful for web frameworks, static site generators, and other systems that support multiple template engines but don't want to code for each of them individually.
Tilt は一時期 Mustache をサポートしていたが今はしていない
Tilt と Mustache のコンセプト的なところで合わないということなのだろうか。
Tilt integration · Issue #72 · mustache/mustache
Tilt had a MustacheTemplate at one point but we ripped it out because of Mustache's inverted view/template relationship made it feel a little wonky. I actually prefer using Mustache's normal view-oriented interface in Sinatra as opposed to the render support. I think you miss out on a lot of the benefits of mustache when you try to shoehorn it into the single template file approach used by most other template systems.
Any Reason For Using Only Symbols in Locals Hash? · Issue #72 · rtomayko/tilt
That would require some more render-time code. I'm not sure if it's worth adding edge cases like this when they will both decrease performance and make the code more complex. Tilt doesn't have to be flexible when it comes to the locals-hash.
Anyway, we should still raise some exceptions if any of the keys are not a symbol.
Mustache support by Becojo · Pull Request #51 · rtomayko/tilt
Mustache isn't a regular template engine in the way that it requires two parts: a template and an implementation (in Ruby). Chris (of Mustache) closed the equivalent issue at Mustache's issue tracker because a Tilt implementation (at least the one provided here) wouldn't be able to take advantage of Mustache's core concept.
This patch also uses an interpreted mode even when Mustache is actually compiled behind the scenes.
tilt-mustache パッケージとは
Tilt に採用されなかった Pull Request のコードを元に、別にパッケージ化したものが tilt-mustache パッケージらしい。
tilt-mustache | RubyGems.org | コミュニティのGemホスティングサービス
Add Mustache to Tilt
DAddYE/tilt-mustache: Add Mustache to Tilt
This gem is a verbatim copy of this tilt pull request done by @Becojo
tilt-mustache パッケージのインストール
依存関係で mustache と tilt の現時点(2020年2月10日現在)での最新版もインストールされる。
$ gem install tilt-mustache
Fetching mustache-1.1.1.gem
Fetching tilt-mustache-0.0.2.gem
Fetching tilt-2.0.10.gem
(以下略)
Mustache テンプレートの拡張子
tilt-mustache 0.0.2 では Mustache テンプレートは拡張子 mustache と ms に関連付けられている。
tilt-mustache/tilt-mustache.rb at v0.0.2 · DAddYE/tilt-mustache
register 'mustache', MustacheTemplate
register 'ms', MustacheTemplate
Hello World
HTML を記述した Mustache テンプレートファイルを用意。
今回は hello.ms というファイル名で保存する。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- title を出力-->
<title>{{title}}</title>
</head>
<body>
<!-- message を出力-->
<p>{{message}}</p>
</body>
</html>
ソースコード。
require 'tilt-mustache'
# 変数を定義
title = 'Hello, world.<&>'
message = 'こんにちは、世界。'
# Tile に包まれた Mustache テンプレートエンジン
template = Tilt.new('hello.ms')
# self スコープでレンダリング
output = template.render(self, :title => title, 'message' => message)
# 結果を出力
puts output
実行結果。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- title を出力-->
<title>Hello, world.<&></title>
</head>
<body>
<!-- message を出力-->
<p>こんにちは、世界。</p>
</body>
</html>
ループや条件分岐など
HTML を記述した Mustache テンプレートファイルを用意。
今回は my-template.mustache というファイル名で保存する。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- title を出力-->
<title>{{title}}</title>
</head>
<body>
<!-- mydata オブジェクトの message を出力-->
<p>{{mydata.message}}</p>
<p>
<!-- ループ -->
{{#mydata}}
{{#list}}
{{.}}<br>
{{/list}}
{{/mydata}}
</p>
<p>
<!-- hoge が存在する場合に出力-->
{{#mydata.hoge}}
Hoge exists.
{{/mydata.hoge}}
</p>
<p>
<!-- fuga が存在しない場合に出力-->
{{^mydata.fuga}}
Fuga does not exists.
{{/mydata.fuga}}
</p>
</body>
</html>
ソースコード。
require 'tilt-mustache'
# Tile に包まれた Mustache テンプレートオブジェクト
template = Tilt.new('my-template.mustache')
# 第一引数に他に影響されないスコープとして Object.new を指定
# 第二引数以降にHashオブジェクト
output = template.render(
Object.new,
:title => 'タイトル',
:mydata => {
:message => 'メッセージ',
'list' => ['foo', 'bar', 'baz'],
'hoge' => 'ほげ'
})
# 結果を出力
puts output
実行結果。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!-- title を出力-->
<title>タイトル</title>
</head>
<body>
<!-- mydata オブジェクトの message を出力-->
<p>メッセージ</p>
<p>
<!-- ループ -->
foo<br>
bar<br>
baz<br>
</p>
<p>
<!-- hoge が存在する場合に出力-->
Hoge exists.
</p>
<p>
<!-- fuga が存在しない場合に出力-->
Fuga does not exists.
</p>
</body>
</html>
Sinatra で Mustache を使う
ソースコード一覧
├── app.rb
└── views
└── hello.mustache
app.rb
Mustache は Sinatra でサポートされていない。
Sinatra にテンプレートエンジンを追加するには、Tilt でテンプレートエンジンを登録するのと、レンダリングメソッドを作る必要がある。
Tilt でテンプレートエンジンを登録するのは tilt-mustache パッケージがやってくれている。
レンダリングメソッドを作るのは自分でやる必要がある。
require 'sinatra'
require 'tilt-mustache'
# レンダリングメソッドを作る
helpers do
def mustache(*args) render(:mustache, *args) end
end
get '/hello/:message' do
# HTML に埋め込む値
values = {
:message => params['message'],
'ruby_desc' => RUBY_DESCRIPTION
}
# HTML を表示
mustache :hello, :locals => values
end
views/hello.mustache
HTML を記述した Mustache テンプレートファイル。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello, World!</title>
</head>
<body>
<p>{{ message }}</p>
<p>{{ ruby_desc }}</p>
</body>
</html>
サーバを起動
$ ruby app.rb
== Sinatra (v2.0.8.1) has taken the stage on 4567 for development with backup from Puma
Puma starting in single mode...
* Version 4.3.1 (ruby 2.7.0-p0), codename: Mysterious Traveller
* Min threads: 0, max threads: 16
* Environment: development
* Listening on tcp://127.0.0.1:4567
* Listening on tcp://[::1]:4567
Use Ctrl-C to stop
curl でアクセス
必要な値が HTML に埋め込まれている。
$ curl -i "http://localhost:4567/hello/こんにちは&世界"
HTTP/1.1 200 OK
Content-Type: text/html;charset=utf-8
X-XSS-Protection: 1; mode=block
X-Content-Type-Options: nosniff
X-Frame-Options: SAMEORIGIN
Content-Length: 219
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello, World!</title>
</head>
<body>
<p>こんにちは&世界</p>
<p>ruby 2.7.0p0 (2019-12-25 revision 647ee6f091) [x86_64-darwin19]</p>
</body>
</html>