require 'optparse' require 'fileutils' module Haml # This module contains code for working with the # haml, sass, and haml2html executables, # such as command-line parsing stuff. # It shouldn't need to be invoked by client code. module Exec # :nodoc: # A class that encapsulates the executable code # for all three executables. class Generic # :nodoc: def initialize(args) @args = args @options = {} end def parse! begin @opts = OptionParser.new(&method(:set_opts)) @opts.parse!(@args) process_result @options rescue Exception => e raise e if @options[:trace] || e.is_a?(SystemExit) $stderr.puts e.message exit 1 end exit 0 end def to_s @opts.to_s end protected def get_line(exception) # SyntaxErrors have weird line reporting # when there's trailing whitespace, # which there is for Haml documents. return exception.message.scan(/:(\d+)/)[0] if exception.is_a?(::SyntaxError) exception.backtrace[0].scan(/:(\d+)/)[0] end private def set_opts(opts) opts.on('-s', '--stdin', :NONE, 'Read input from standard input instead of an input file') do @options[:input] = $stdin end opts.on('--trace', :NONE, 'Show a full traceback on error') do @options[:trace] = true end opts.on_tail("-?", "-h", "--help", "Show this message") do puts opts exit end opts.on_tail("-v", "--version", "Print version") do puts("Haml #{::Haml.version[:string]}") exit end end def process_result input, output = @options[:input], @options[:output] input_file, output_file = if input [nil, open_file(ARGV[0], 'w')] else @options[:filename] = ARGV[0] [open_file(ARGV[0]), open_file(ARGV[1], 'w')] end input ||= input_file output ||= output_file input ||= $stdin output ||= $stdout @options[:input], @options[:output] = input, output end def open_file(filename, flag = 'r') return if filename.nil? File.open(filename, flag) end end # A class encapsulating the executable functionality # specific to Haml and Sass. class HamlSass < Generic # :nodoc: def initialize(args) super @options[:for_engine] = {} end private def set_opts(opts) opts.banner = < e raise e if @options[:trace] raise "Syntax error on line #{get_line e}: #{e.message}" end output.write(result) output.close() if output.is_a? File end end # A class encapsulating executable functionality # specific to Haml. class Haml < HamlSass # :nodoc: def initialize(args) super @name = "Haml" @options[:requires] = [] @options[:load_paths] = [] end def set_opts(opts) super opts.on('-t', '--style NAME', 'Output style. Can be indented (default) or ugly.') do |name| @options[:for_engine][:ugly] = true if name.to_sym == :ugly end opts.on('-f', '--format NAME', 'Output format. Can be xhtml (default), html4, or html5.') do |name| @options[:for_engine][:format] = name.to_sym end opts.on('-e', '--escape-html', 'Escape HTML characters (like ampersands and angle brackets) by default.') do @options[:for_engine][:escape_html] = true end opts.on('-r', '--require FILE', "Same as 'ruby -r'.") do |file| @options[:requires] << file end opts.on('-I', '--load-path PATH', "Same as 'ruby -I'.") do |path| @options[:load_paths] << path end opts.on('--debug', "Print out the precompiled Ruby source.") do @options[:debug] = true end end def process_result super input = @options[:input] output = @options[:output] template = input.read() input.close() if input.is_a? File begin engine = ::Haml::Engine.new(template, @options[:for_engine]) if @options[:check_syntax] puts "Syntax OK" return end @options[:load_paths].each {|p| $LOAD_PATH << p} @options[:requires].each {|f| require f} if @options[:debug] puts engine.precompiled puts '=' * 100 end result = engine.to_html rescue Exception => e raise e if @options[:trace] case e when ::Haml::SyntaxError; raise "Syntax error on line #{get_line e}: #{e.message}" when ::Haml::Error; raise "Haml error on line #{get_line e}: #{e.message}" else raise "Exception on line #{get_line e}: #{e.message}\n Use --trace for backtrace." end end output.write(result) output.close() if output.is_a? File end end # A class encapsulating executable functionality # specific to the html2haml executable. class HTML2Haml < Generic # :nodoc: def initialize(args) super @module_opts = {} begin require 'haml/html' rescue LoadError => err dep = err.message.scan(/^no such file to load -- (.*)/)[0] puts "Required dependency #{dep} not found!" exit 1 end end def set_opts(opts) opts.banner = <