Created
July 10, 2020 17:43
-
-
Save jamesr2323/6fb74966d48d87750f6f7c8bf8c1270d to your computer and use it in GitHub Desktop.
Proof of concept: Nested liquid templates
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
require 'liquid' | |
class Button < Liquid::Tag | |
def initialize(tag_name, args, tokens) | |
super | |
@color = args.split(' ')[0] | |
@url = args.split(' ')[1] | |
@text = args.split(' ')[2..-1].join(' ') | |
end | |
def render(context) | |
%Q{<a href="#{@url}" style="background: #{@color}; color: white; padding: 20px;">#{@text}</div>} | |
end | |
end | |
class FacebookShareUrl < Liquid::Tag | |
def initialize(tag_name, url, tokens) | |
super | |
@url = url | |
end | |
def render(context) | |
"https://www.facebook.com/sharer.php?u=#{CGI.escape(@url)}" | |
end | |
end | |
Liquid::Template.register_tag('button', Button) | |
Liquid::Template.register_tag('facebook_share_url', FacebookShareUrl) | |
# Find every {% that has another {% before the first %}, and store the index | |
def get_mask_indexes(str, forward = true) | |
if forward | |
open_tokens = ['{%', '{{'] | |
open_pattern = /\{%|{{/ | |
close_pattern = /%}|}}/ | |
else | |
open_tokens = ['}%', '}}'] | |
open_pattern = /\}%|}}/ | |
close_pattern = /%{|{{/ | |
end | |
indexes = [] | |
s = StringScanner.new(str) | |
loop do | |
break if s.scan_until(open_pattern).nil? | |
meanwhile_content = s.scan_until(close_pattern) | |
if open_tokens.any? { |tok| meanwhile_content&.include?(tok) } | |
s.unscan | |
indexes << s.pos - 2 | |
end | |
break if s.eos? | |
end | |
indexes.sort | |
end | |
p get_mask_indexes('{% {{ {% {{ }} %} }} %}') | |
def masked_non_leaf_tags(str) | |
replace_forward = get_mask_indexes(str) | |
replace_backward = get_mask_indexes(str.reverse, false).map { |x| str.size - x - 2 } | |
new_str = str | |
offset = 0 | |
tokens_to_replace = ['{%', '{{', '}}', '%}'] | |
# It's important to sort the indexes so we replace them in order. Otherwise offsets won't work | |
(replace_forward + replace_backward).sort.each do |i| | |
token_index = tokens_to_replace.find_index(new_str[i+offset..i+offset+1]) | |
new_str[i+offset..i+offset+1] = "__TOKEN#{token_index}__" | |
offset += 8 # The token moves all subsequent tokens along by 8 characters | |
end | |
new_str | |
end | |
def unmask_tags(str) | |
new_str = str | |
tokens_to_replace = ['{%', '{{', '}}', '%}'] | |
tokens_to_replace.each_with_index do |token, i| | |
new_str.gsub!("__TOKEN#{i}__", token) | |
end | |
new_str | |
end | |
def render_nested(str, context) | |
rendered_str = str | |
loop do | |
masked_template = masked_non_leaf_tags(rendered_str) | |
rendered_str = Liquid::Template.parse(masked_template, :error_mode => :strict).render(context) | |
break if !masked_template.include?('__TOKEN') # If nothing was masked for that render, we're done | |
rendered_str = unmask_tags(rendered_str) | |
end | |
rendered_str | |
end | |
str = <<~HTML | |
Dear {{ first_name }},<br /> | |
<br /> | |
{% button blue {% facebook_share_url https://www.example.com?tracking_param={{ guid }} %} Share Now {{ first_name | upcase }} %}<br /> | |
<br /> | |
{% button #ff00ff {% facebook_share_url https://www.example.com?tracking_param={{ guid }} %} Share Again {{ first_name }} %} | |
HTML | |
puts render_nested(str, { 'first_name' => 'Hiemanshu', 'guid' => 'absdf324rsd' }) | |
puts render_nested(str, { 'first_name' => 'Frances', 'guid' => 'kkk234jsdf09' }) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment