1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Slim の早期リターン

1
Posted at

Ruby のテンプレートシステム Slim における早期リターンについて書く。

早期リターンとは

まずメソッド定義における早期リターンについて。

以下のようなメソッドがあったとする。

def write_items(items)
  if items.empty?
    puts "無い"
  else
    puts "全部で #{ items.length } 個あった"
    items.each do |item|
      puts "* #{ item }"
    end
  end
end

以下のように書き換えたほうが把握しやすい(個人の感想です)。

def write_items(items)
  if items.empty?
    puts "無い"
    return
  end
  
  puts "全部で #{ items.length } 個あった"
  items.each do |item|
    puts "* #{ item }"
  end
end

いや,こんな短いコードだと大して変わらないけど,else 節が長く複雑になると脳の負担が大きいからね。

こんなふうに,例外的な場合にとっとと話を終わらせちゃうのを早期リターン(early return)という。

これと同じように,Slim テンプレートでも,ある条件を満たす場合にとっとと話を終わらせたいことがある。
それをどう書くか。

お題

以下のようなコードを何とかしたい。

- if items.empty?
  p 無い
- else
  p 全部で #{ items.length } 個あった
  ul
    - items.each do |item|
      li = item

いや,こんな単純なテンプレートだったら,このままでいい。
しかし,items が empty でなかったときのコードがもっと長く複雑だったなら,早期リターンしたいじゃない。

打ち切るだけなら,Slim テンプレート中でも return を使えばよいだけなのだが,<p>無い</p> をちゃんと表示してほしいんである。

「そこまでの内容をレンダリング結果とする」という仕組みが,どうも Slim には備わっていないようなのだ。

方案

Rails の場合

Rails では,(Slim に限らず)テンプレート中で output_buffer というメソッドで「そこまでの内容」が得られる。

そこで,以下のように書ける。

- if items.empty?
  p 無い
  - return output_buffer()

p 全部で #{ items.length } 個あった
ul
  - items.each do |item|
    li = item

ええと,こういうことやっていいのかな。
Rails API に output_buffer メソッドが見当たらないってことは,使っちゃいけないのかな(探し方が悪いだけかもしれないが)。

ちな,output_buffer()() が付いているのは,同名のローカル変数があるため(次節参照)。() 無しだとローカル変数の参照になり,ありだとメソッド呼び出しになる。

バッファー変数を参照

Slim テンプレートの処理で,「そこまでの内容」はローカル変数に蓄えられている。

デフォルトではこの変数は _buf であるらしい。
よって以下のように書ける。

- if items.empty?
  p 無い
  - return _buf

p 全部で #{ items.length } 個あった
ul
  - items.each do |item|
    li = item

うーむ,しかしお行儀のよいコードとは言えまい。
バッファー変数はテンプレート中で使うことを想定していないと思う(知らんけど)。

また,この変数名は Slim の設定で変えることができる。
実際,いま Rails 7.2 で試したところ,output_buffer という名前の変数になっているようだった(先程見たメソッドと同名)。

だから,このテンプレートは普遍性にやや欠ける。何かのバージョンアップによって動かなくなるかもしれない。

lambda

以下はもう奇策といってよいかもしれない。

- if items.empty?
  - return lambda{ |&b| b.call }.call
    p 無い

p 全部で #{ items.length } 個あった
ul
  - items.each do |item|
    li = item

何をやっているのか分かりにくい。
書いた私にもよく分からん(笑)

キャプチャー

前節のやり方は,キャプチャーのメソッドがあればもっと素直に書ける。

capture ヘルパーを

def capture = yield

と定義すれば

- if items.empty?
  - return capture
    p 無い

p 全部で #{ items.length } 個あった
ul
  - items.each do |item|
    li = item

と書ける。

Rails というか ActionView には capture というメソッドがあるが,こちらはもっと複雑な定義になっている。

結局どれを

結局どれを使えばいいのだ。

Rails の場合は output_buffer() かなという気もするが,そもそもこれをテンプレート中に書いてよいのか分からん。
Rails で ActionView 備え付けの capture を使ってもできそうだが,試していない。

Rails じゃない場合は自前 capture だろうか(メソッド名はともかく)。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?