Skip to content

Instantly share code, notes, and snippets.

@DanielHeath
Created January 30, 2024 03:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DanielHeath/43116cd40b970af63695806326a87a7a to your computer and use it in GitHub Desktop.
Save DanielHeath/43116cd40b970af63695806326a87a7a to your computer and use it in GitHub Desktop.
Had enough of Slim in your rails app? Slim2ERB does a reasonable job of auto-converting any files that don't generate eyebleeding source.
require 'slim/erb_converter'
def reject_newlines_in(content)
result = []
content.each do |item|
next if item == [:newline]
if item.is_a? Array
result.push(reject_newlines_in(item))
else
result.push(item)
end
end
result
end
Temple::HTML::Pretty
module Temple
module HTML
# @api public
class Pretty < Fast
def on_dynamic(code)
[:multi, [:static, indent], [:dynamic, code]]
end
def on_code(code)
[:multi, [:static, indent], [:dynamic, code]]
end
def on_html_tag(name, attrs, content = nil)
name = name.to_s
closed = !content || (empty_exp?(content) && options[:autoclose].include?(name))
indent = tag_indent(name)
@pretty = false
result = [:multi, [:static, "#{indent}<#{name}"], compile(attrs)]
result << [:static, (closed && @format != :html ? ' /' : '') + '>']
@pretty = true
if content
content = reject_newlines_in(content)
@indent += 1
result << compile(content)
@indent -= 1
end
unless closed
result << [:static, "#{content && !empty_exp?(content) ? indent : ''}</#{name}>"]
end
@pretty = true
result
end
# Return indentation before tag
def tag_indent(name)
flag = @indent_next != nil
@indent_next = true
flag ? indent : ''
end
end
end
end
Temple::Generators::ERB
module Temple
module Generators
# Implements an ERB generator.
#
# @api public
class ERB < Generator
def on_dynamic(code)
"<%= #{code.strip} %>"
end
def on_code(code)
"<% #{code.strip} %>"
end
end
end
end
Slim::Controls
module Slim
class Controls < Filter
# Handle output expression `[:slim, :output, escape, code, content]`
#
# @param [Boolean] escape Escape html
# @param [String] code Ruby code
# @param [Array] content Temple expression
# @return [Array] Compiled temple expression
def on_slim_output(escape, code, content)
[:multi, [:escape, escape, [:dynamic, code]], compile(content)]
end
end
end
Slim::CodeAttributes
module Slim
# @api private
class CodeAttributes < Filter
# Handle attribute expression `[:html, :attr, name, value]`
#
# @param [String] name Attribute name
# @param [Array] value Value expression
# @return [Array] Compiled temple expression
def on_html_attr(name, value)
if value[0] == :slim && value[1] == :attrvalue && !options[:merge_attrs][name]
# We handle the attribute as a boolean attribute
escape, code = value[2], value[3]
case code
when 'true'
[:html, :attr, name, [:multi]]
when 'false', 'nil'
[:multi]
else
begin
return [:html, :attr, name, [:escape, escape, [:static, eval(code)]]]
rescue
raise DoNotConvert, 'not supported'
end
tmp = unique_name
[:multi,
[:code, "#{tmp} = #{code}"],
[:if, tmp,
[:if, "#{tmp} == true",
[:html, :attr, name, [:multi]],
[:html, :attr, name, [:escape, escape, [:dynamic, tmp]]]]]]
end
else
# Attribute with merging
@attr = name
super
end
end
# Handle attribute expression `[:slim, :attrvalue, escape, code]`
#
# @param [Boolean] escape Escape html
# @param [String] code Ruby code
# @return [Array] Compiled temple expression
def on_slim_attrvalue(escape, code)
# We perform attribute merging on Array values
if delimiter = options[:merge_attrs][@attr]
raise DoNotConvert, 'not supported'
tmp = unique_name
[:multi,
[:code, "#{tmp} = #{code}"],
[:if, "Array === #{tmp}",
[:multi,
[:code, "#{tmp} = #{tmp}.flatten"],
[:code, "#{tmp}.map!(&:to_s)"],
[:code, "#{tmp}.reject!(&:empty?)"],
[:escape, escape, [:dynamic, "#{tmp}.join(#{delimiter.inspect})"]]],
[:escape, escape, [:dynamic, tmp]]]]
else
[:escape, escape, [:dynamic, code]]
end
end
end
end
class DoNotConvert < StandardError
end
Slim::Splat::Filter
module Slim
module Splat
class Filter < ::Slim::Filter
def on_html_attrs(*attrs)
if attrs.any? {|attr| splat?(attr) }
raise DoNotConvert, 'unsupported splat'
builder, block = make_builder(attrs)
[:multi,
block,
[:dynamic, "#{builder}.build_attrs"]]
else
super
end
end
end
end
end
converter = Slim::ERBConverter.new(disable_capture: true, pre_tags: [], pretty: true, format: :html, escape_code: '%s')
def strip_converted(str)
str.sub(/<% _temple_html_pretty\d+ = \/\(\?\!\)\/ %>/, '').sub(/_temple_html_pretty\d+ = \/\(\?\!\)\/;/, '').lines.map(&:rstrip).reject(&:blank?).join("\n")
end
Dir.glob('app/views/**/*.slim').each do |path|
begin
File.write(path.sub(/slim$/, 'erb'), strip_converted(converter.call(File.read(path))) + "\n")
File.delete(path)
rescue DoNotConvert
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment