ActionPack Variantsでは、request.variantに値を入れると入れた値を元にしてviewファイルを探してくれます。
今回、スマホ対応にこの機能を使おうとしたのですが、スマホレイアウトを用意して、一部のviewにスマホ用を用意しても、対応していない方もスマホ用のレイアウトが適応されてしまうので、全部用意する必要がでてきました。
app/views/layouts/articles.html+mobile.erb
app/views/layouts/articles.html.erb
app/views/articles/index.html+mobile.erb
app/views/articles/index.html.erb
app/views/articles/show.html.erb
こんなファイル構成だと/articles/index でlayouts/articles.html+mobile.erbが適応されるのは嬉しいのですが、articles/showでlayouts/articles.html+mobile.erbを適応すると崩れてしまうので、PG的に回避したかったのです。
というわけで、以下の様なmodlueを作りました
# Rails 4.2 使用
module ActionView class LookupContext
register_detail(:variants_use) { []}
end
class TemplateRenderer
def render_template(template, layout_name = nil, locals = nil) #:nodoc:
view, locals = @view, locals || {}
if defined?(template.variants) && template.variants
@lookup_context.variants_use << template.variants
end
render_with_layout(layout_name, locals) do |layout|
instrument(:template, :identifier => template.identifier, :layout => layout.try(:virtual_path)) do
template.render(view, locals) { |*name| view._layout_for(*name) }
end
end
end
end
end
module MobileRender
def _default_layout(require_layout = false)
lookup_context.variants_use = lookup_context.variants_use.flatten.uniq
variants = []
lookup_context.variants.delete_if{ | variant | ! lookup_context.variants_use.include?(variant.to_s) }
super(require_layout)
end
end
render時に、templateを呼び出した時、templateで使ったvariantsを取得して、variants_useに格納し、レイアウトを呼び出す際にvariants_useにないものをvariantsから削除するという方法を取っています。
もし、別アプローチでの実現方法や、コード上の問題がありましたらご指摘頂けますと助かります。
[20170831追記]
@yasaichi さんのコメントより
以下の方法の方がスッキリしました。
module ActionViewTemplateRendererPatch
def render_template(template, layout_name = nil, locals = nil)
@lookup_context.variants = nil if template.variants == [nil]
super
end
end
ActionView::TemplateRenderer.prepend(ActionViewTemplateRendererPatch)