vendor/plugins/haml/lib/haml/exec.rb in radiant-0.8.2 vs vendor/plugins/haml/lib/haml/exec.rb in radiant-0.9.0.rc2

- old
+ new

@@ -1,22 +1,22 @@ require 'optparse' require 'fileutils' +require 'rbconfig' 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: + # This module handles the various Haml executables (`haml`, `sass`, `css2sass`, etc). + module Exec + # An abstract class that encapsulates the executable code for all three executables. + class Generic + # @param args [Array<String>] The command-line arguments def initialize(args) @args = args @options = {} end + # Parses the command-line arguments and runs the executable. + # Calls `Kernel#exit` at the end, so it never returns. def parse! begin @opts = OptionParser.new(&method(:set_opts)) @opts.parse!(@args) @@ -30,78 +30,113 @@ exit 1 end exit 0 end + # @return [String] A description of the executable def to_s @opts.to_s end protected + # Finds the line of the source template + # on which an exception was raised. + # + # @param exception [Exception] The exception + # @return [String] The line number 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+)/).first.first if exception.is_a?(::SyntaxError) exception.backtrace[0].scan(/:(\d+)/).first.first end - private - + # Tells optparse how to parse the arguments + # available for all executables. + # + # This is meant to be overridden by subclasses + # so they can add their own options. + # + # @param opts [OptionParser] 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 + if RbConfig::CONFIG['host_os'] =~ /mswin|windows/i + opts.on('--unix-newlines', 'Use Unix-style newlines in written files.') do + @options[:unix_newlines] = true + end + 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]}") + puts("Haml/Sass #{::Haml.version[:string]}") exit end end + # Processes the options set by the command-line arguments. + # In particular, sets `@options[:input]` and `@options[:output]` + # to appropriate IO streams. + # + # This is meant to be overridden by subclasses + # so they can run their respective programs. def process_result input, output = @options[:input], @options[:output] input_file, output_file = if input - [nil, open_file(ARGV[0], 'w')] + [nil, open_file(@args[0], 'w')] else - [open_file(ARGV[0]), open_file(ARGV[1], 'w')] + @options[:filename] = @args[0] + [open_file(@args[0]), open_file(@args[1], 'w')] end input ||= input_file output ||= output_file input ||= $stdin output ||= $stdout @options[:input], @options[:output] = input, output end + private + def open_file(filename, flag = 'r') return if filename.nil? + flag = 'wb' if @options[:unix_newlines] && flag == 'w' File.open(filename, flag) end end - # A class encapsulating the executable functionality - # specific to Haml and Sass. - class HamlSass < Generic # :nodoc: + # An abstrac class that encapsulates the code + # specific to the `haml` and `sass` executables. + class HamlSass < Generic + # @param args [Array<String>] The command-line arguments def initialize(args) super @options[:for_engine] = {} end - private + protected + # Tells optparse how to parse the arguments + # available for the `haml` and `sass` executables. + # + # This is meant to be overridden by subclasses + # so they can add their own options. + # + # @param opts [OptionParser] def set_opts(opts) opts.banner = <<END Usage: #{@name.downcase} [options] [INPUT] [OUTPUT] Description: @@ -135,11 +170,10 @@ puts "Cannot create #{dir}" exit end File.open(File.join(dir, 'init.rb'), 'w') do |file| - file.puts "require 'rubygems'" file << File.read(File.dirname(__FILE__) + "/../../init.rb") end puts "Haml plugin added to #{original_dir}" exit @@ -152,66 +186,113 @@ end super end + # Processes the options set by the command-line arguments. + # In particular, sets `@options[:for_engine][:filename]` to the input filename + # and requires the appropriate file. + # + # This is meant to be overridden by subclasses + # so they can run their respective programs. def process_result super + @options[:for_engine][:filename] = @options[:filename] if @options[:filename] require File.dirname(__FILE__) + "/../#{@name.downcase}" end end - # A class encapsulating executable functionality - # specific to Sass. - class Sass < HamlSass # :nodoc: + # The `sass` executable. + class Sass < HamlSass + # @param args [Array<String>] The command-line arguments def initialize(args) super @name = "Sass" + @options[:for_engine][:load_paths] = ['.'] + (ENV['SASSPATH'] || '').split(File::PATH_SEPARATOR) end + protected + + # Tells optparse how to parse the arguments. + # + # @param opts [OptionParser] def set_opts(opts) super opts.on('-t', '--style NAME', 'Output style. Can be nested (default), compact, compressed, or expanded.') do |name| @options[:for_engine][:style] = name.to_sym end + opts.on('-l', '--line-numbers', '--line-comments', + 'Emit comments in the generated CSS indicating the corresponding sass line.') do + @options[:for_engine][:line_numbers] = true + end + opts.on('-i', '--interactive', + 'Run an interactive SassScript shell.') do + @options[:interactive] = true + end + opts.on('-I', '--load-path PATH', 'Add a sass import path.') do |path| + @options[:for_engine][:load_paths] << path + end + opts.on('--cache-location PATH', 'The path to put cached Sass files. Defaults to .sass-cache.') do |loc| + @options[:for_engine][:cache_location] = loc + end + opts.on('-C', '--no-cache', "Don't cache to sassc files.") do + @options[:for_engine][:cache] = false + end end + # Processes the options set by the command-line arguments, + # and runs the Sass compiler appropriately. def process_result + if @options[:interactive] + require 'sass' + require 'sass/repl' + ::Sass::Repl.new(@options).run + return + end + super - input = @options[:input] - output = @options[:output] - template = input.read() - input.close() if input.is_a? File - begin - # We don't need to do any special handling of @options[:check_syntax] here, - # because the Sass syntax checking happens alongside evaluation - # and evaluation doesn't actually evaluate any code anyway. - result = ::Sass::Engine.new(template, @options[:for_engine]).render + input = @options[:input] + output = @options[:output] + + tree = + if input.is_a?(File) && !@options[:check_syntax] + ::Sass::Files.tree_for(input.path, @options[:for_engine]) + else + # We don't need to do any special handling of @options[:check_syntax] here, + # because the Sass syntax checking happens alongside evaluation + # and evaluation doesn't actually evaluate any code anyway. + ::Sass::Engine.new(input.read(), @options[:for_engine]).to_tree + end + + input.close() if input.is_a?(File) + + output.write(tree.render) + output.close() if output.is_a? File rescue ::Sass::SyntaxError => 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: + # The `haml` executable. + class Haml < HamlSass + # @param args [Array<String>] The command-line arguments def initialize(args) super @name = "Haml" @options[:requires] = [] @options[:load_paths] = [] end + # Tells optparse how to parse the arguments. + # + # @param opts [OptionParser] def set_opts(opts) super opts.on('-t', '--style NAME', 'Output style. Can be indented (default) or ugly.') do |name| @@ -239,10 +320,12 @@ opts.on('--debug', "Print out the precompiled Ruby source.") do @options[:debug] = true end end + # Processes the options set by the command-line arguments, + # and runs the Haml compiler appropriately. def process_result super input = @options[:input] output = @options[:output] @@ -278,27 +361,31 @@ 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: + # The `html2haml` executable. + class HTML2Haml < Generic + # @param args [Array<String>] The command-line arguments 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!" + raise err if @options[:trace] || dep.nil? || dep.empty? + $stderr.puts "Required dependency #{dep} not found!\n Use --trace for backtrace." exit 1 end end + # Tells optparse how to parse the arguments. + # + # @param opts [OptionParser] def set_opts(opts) opts.banner = <<END Usage: html2haml [options] [INPUT] [OUTPUT] Description: Transforms an HTML file into corresponding Haml code. @@ -319,10 +406,12 @@ end super end + # Processes the options set by the command-line arguments, + # and runs the HTML compiler appropriately. def process_result super input = @options[:input] output = @options[:output] @@ -332,36 +421,43 @@ output.write(::Haml::HTML.new(input, @module_opts).render) end end - # A class encapsulating executable functionality - # specific to the css2sass executable. - class CSS2Sass < Generic # :nodoc: + # The `css2sass` executable. + class CSS2Sass < Generic + # @param args [Array<String>] The command-line arguments def initialize(args) super @module_opts = {} require 'sass/css' end + # Tells optparse how to parse the arguments. + # + # @param opts [OptionParser] def set_opts(opts) opts.banner = <<END Usage: css2sass [options] [INPUT] [OUTPUT] Description: Transforms a CSS file into corresponding Sass code. Options: END - opts.on('-a', '--alternate', 'Output using alternative Sass syntax (margin: 1px)') do - @module_opts[:alternate] = true + opts.on('--old', 'Output the old-style ":prop val" property syntax') do + @module_opts[:old] = true end + opts.on_tail('-a', '--alternate', 'Ignored') {} + super end + # Processes the options set by the command-line arguments, + # and runs the CSS compiler appropriately. def process_result super input = @options[:input] output = @options[:output]