liquid-rails
liquid-rails copied to clipboard
Layout Tag Implementation
Hi Chamnap,
Thank you for making this library. I have been working with the code and have a working prototype for implementing the layout tag similar to what shopify has: https://help.shopify.com/en/themes/liquid/tags/theme-tags#layout (I doubt my implementation is the same)
My method was this:
- Created a simple layout tag:
class Liquid::Tags::LayoutTag < Liquid::Tag
def initialize(tag_name, markup, tokens)
super
@template = markup.delete("\"").strip
end
def render(context)
context.environments.first["layout_override_filename"] = @template
""
end
end
- Then extended the view class:
class TemplateHandler
def self.call(template)
"Liquid::Handlers::TemplateHandler.new(self).render(#{template.source.inspect}, local_assigns)"
end
def initialize(view)
@view = view
@controller = @view.controller
@helper = ActionController::Base.helpers
end
def render(template, local_assigns={})
assigns = if @controller.respond_to?(:liquid_assigns, true)
@controller.send(:liquid_assigns)
else
@view.assigns
end
# check if there is a layout override
layout_override_filename = assigns["layout_override_filename"]
# if we are processing the layout and there is an override, replace the template with the override content.
if @view.content_for?(:layout) && !layout_override_filename.nil?
Rails.logger.info "Rendering layout override from #{layout_override_filename}"
template = read_template_from_file_system(registers, layout_override_filename)
end
assigns['content_for_layout'] = @view.content_for(:layout) if @view.content_for?(:layout)
assigns.merge!(local_assigns.stringify_keys)
liquid = Liquid::Template.parse(template)
liquid.send(render_method, assigns, filters: filters, registers: registers).html_safe
end
def filters
if @controller.respond_to?(:liquid_filters, true)
@controller.send(:liquid_filters)
else
[@controller._helpers]
end
end
def registers
{
view: @view,
controller: @controller,
helper: @helper,
file_system: Liquid::Handlers::FileSystem.new(@view)
}
end
def compilable?
false
end
def render_method
(::Rails.env.development? || ::Rails.env.test?) ? :render! : :render
end
def read_template_from_file_system(registers, name)
file_system = registers[:file_system] || Liquid::Template.file_system
file_system.read_template_file(name)
end
end
The template handler then replaces the view with the select template when rendering occurs. This was really just a proof of concept. If I clean this up and submit it as a pull request would that be useful to you or others do you think?
Any suggestions on improvements? Liquid is very new to me and I had troubles finding a good way to pass the template override through and ended up using the environment.