背景: classnames とは
HTML を書いていると、条件に応じて class 属性の値を変えたくなることがしばしばあります。
例えばある span 要素に baz
のときだけ "bar" をつけたいとします。 slim で愚直に書くとたとえばこんな感じになります:
span[class="foo #{'bar' if baz}"]
条件が一つくらいならまだいいのですが、これが増えてくるとだんだんどういうときにどういう class になるのかわかりにくくなってきます。(しかも上のコードには実はバグがあります)
この問題を解決するのに JavaScript 界隈ではよく classnames という npm パッケージが使われます:
const classNames = require("classnames")
classNames("foo", { bar: true }) //=> "foo bar"
classNames("foo", { bar: false }) //=> "foo"
Rails で classnames 相当のものを実装する
Ruby も探してみると一応 classnames という gem があるのですが、広く使われているわけではなかったり、最終コミットから 2 年近く経過していたりであまり使いたくないと思う人もいるかも知れません。
幸いにして ActiveSupport が有効な環境では classnames のサブセットを実装することは非常に簡単です。
module ApplicationHelper
# Create conditional css classes. Based on github.com/JedWatson/classnames
#
# @example
# classnames('foo', bar: true, baz: false)
# #=> 'foo bar'
def classnames(*args)
options = args.extract_options!
(args + options.select { |_, v| v }.keys).join(' ')
end
end
途中で使っている .extract_options!
は ActiveSupport によって Array
に追加されるメソッドです。これを使うと冒頭の spam 要素の例は
span[class=classnames('foo', bar: baz)]
のように書き直すことができます。