Skip to content

Instantly share code, notes, and snippets.

@arika
Last active December 18, 2020 10:16
Show Gist options
  • Star 3 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save arika/11243276 to your computer and use it in GitHub Desktop.
Save arika/11243276 to your computer and use it in GitHub Desktop.
Rails(Action View) ERB template syntax checker
require 'action_view'
require 'ruby-beautify'
require 'ripper'
require 'tmpdir'
require 'fileutils'
require 'optparse'
def check_syntax(path, options = {})
erb = content(path)
code = ruby_code(erb)
eval("BEGIN { return true }; #{code}", nil, path, 0)
rescue SyntaxError
puts "#{path}: syntax error"
puts $! if options[:verbose]
git_diff(path, options) if options[:show_diff]
end
def git_diff(path, options = {})
code = ruby_code(content(path), true)
code = beautify_code(code) if options[:beautify]
gcode = ruby_code(git_content(path), true)
gcode = beautify_code(gcode) if options[:beautify]
gpath = path + '~'
diff_cmd = ['diff', *options[:diff_opts].uniq.flatten]
Dir.mktmpdir do |td|
dir, file = File.split(path)
Dir.chdir(td) do
FileUtils.mkdir_p(dir)
open(path, 'w') {|o| o.print code }
open(gpath, 'w') {|o| o.print gcode }
system(*(diff_cmd << gpath << path))
end
end
end
def show_code(path, options = {})
code = ruby_code(content(path), true)
code = beautify_code(code) if options[:beautify]
print code
end
def content(path)
open(path, 'r') {|i| i.read }
end
def git_content(path)
`git show HEAD:#{path}`
end
def ruby_code(erb_text, notext = false)
erb = ActionView::Template::Handlers::Erubis.new
erb.extend(Erubis::NoTextEnhancer) if notext
erb.convert!(erb_text)
erb.src
end
def beautify_code(code)
code = Ripper.tokenize(code).map do |t|
case t
when 'do'
"do\n"
when ';'
"\n"
else
t
end
end.join
RBeautify.beautify_string(:ruby, code).gsub(/\n\n\n+/, "\n\n")
end
options = {mode: :check, beautify: true, diff_opts: %w(-u)}
OptionParser.new do |o|
o.banner = "usage: #{$0} [options] path [path ...]"
o.separator ''
o.on('-c', 'syntax check') { options[:mode] = :check }
o.on('-s', 'show code') { options[:mode] = :code }
o.on('-d', 'show code diff') { options[:mode] = :diff }
o.on('-v', 'verbose') { options[:verbose] = true }
o.separator ''
o.on('-U NUM', Integer, 'number of context lines') do |num|
options[:show_diff] = true
options[:diff_opts] << ['-U', num.to_s]
end
o.on('-b', 'ignore changes in the amount of white spaces') do
options[:diff_opts] << ['-b']
end
o.on('-w', 'ignore all spaces') do
options[:diff_opts] << ['-w']
end
o.separator ''
o.on_tail('-h', 'print usage') do
puts o
exit
end
end.parse!
if ARGV.empty?
abort 'no files specified'
end
ARGV.each do |path|
case options[:mode]
when :check
check_syntax(path, options)
when :code
show_code(path, options)
when :diff
git_diff(path, options)
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment