lib/klipp.rb in klipp-0.0.1 vs lib/klipp.rb in klipp-0.2.0

- old
+ new

@@ -1,135 +1,191 @@ -require 'klipp/buffered_output' -require 'klipp/version' +require 'ptools' +require 'formatador' +require 'colorize' +require 'fileutils' +require 'grit' +require 'highline/import' + +require 'template' require 'klipp/configuration' -require 'klipp/token' -require 'klipp/template' +require 'klipp/version' require 'klipp/parameter_list' -require 'klipp/project' -require 'colorize' +require 'klipp/creator' module Klipp - extend BufferedOutput::ClassMethods - def self.display_exception exception - if exception.is_a? HelpRequest - help = exception - else - help = HelpRequest.new exception.message, true - help.set_backtrace(exception.backtrace) - end + class Hint < StandardError + end - buffer_puts help.message - exit help.exit_status + def self.env + @@env ||= StringInquirer.new('prod') end + def self.env=(env) + @@env = env + end + def self.route(*argv) - @params = Klipp::ParameterList.new(argv) - command = @params.shift_argument + params = Klipp::ParameterList.new(argv) + command = params.shift_argument; + commands = { + prepare: lambda { cli_prepare(params) }, + create: lambda { cli_create(params) }, + template: lambda { Template.route(*params) } + } case command - when 'version' - version - when 'list' - list - when 'prepare' - prepare @params.first - when 'create' - create @params.first when nil - raise HelpRequest.new('Use one of the commands below to start with klipp.', false, true) + raise Klipp::Hint.new "Add a command to `klipp [#{commands.keys.join('|')}]`" else - raise "Unknown command: #{command}" + if commands[command.to_sym] + commands[command.to_sym].call + else + raise "Unknown command `klipp #{command}`" + end end - + 0 # exit code rescue Exception => e - display_exception e + case e + when Klipp::Hint + Formatador.display_line("[yellow][?] #{e.message}[/]") + else + Formatador.display_line("[red][!] #{e.message}[/]") + Formatador.display_line(e.backtrace[0..10].join("\n")) + end + 1 # exit code end - def self.version - buffer_puts Klipp::VERSION - end + def self.cli_prepare(params=[]) + params = Klipp::ParameterList.new(params) + template = params.shift_argument + raise Klipp::Hint.new("Add a template name to `klipp prepare [template]`. Use `klipp template list` to see your options.") unless template - def self.list - files = template_files + spec = Template::Spec.from_file Template::Spec.spec_path_for_identifier(template) + filename = 'Klippfile' - raise "No templates found. Create a template directory and .yml file in #{Klipp::Configuration.templates_dir}" unless files.length > 0 + force = params.splice_option('-f') + will_overwrite = File.exists?(filename) && force - buffer_puts("Available templates for use with #{'klipp prepare'.yellow} or #{'klipp create'.yellow}:\n\n") - files.each do |file| - buffer_puts(" * #{File.basename(file, '.*').green}") - end - end + raise "#{filename} already exists, not overwriting. Use -f to force overwriting." if File.exists?(filename) && !force - def self.prepare(template_name) - raise HelpRequest.new 'Add a template name to the `prepare` command.' unless template_name + File.write('Klippfile', spec.klippfile) - template = Klipp::Template.new(Klipp::Configuration.templates_dir, template_name) - raise "#{template.klippfile} already exists. Delete it if you want to prepare a new template." if File.exists? template.klippfile - IO.write(template.klippfile, template.generated_klippfile) + Formatador.display_line("[green][√] Prepared #{filename} #{'again' if will_overwrite}.[/]") + + capture_stdout { + `open -a TextMate #{filename} 2>&1` if File.exists?(filename) + } + + if $? && $?.exitstatus > 0 + `open -t #{filename}` if File.exists?(filename) + end end - def self.create(template_name) - if template_name - klippfile = File.join(Dir.pwd, "#{template_name}.klippfile") + def self.cli_create(params, highline = nil) + params = Klipp::ParameterList.new(params) + if (interactive_identifier = params.shift_argument) + creator = Klipp::Creator.from_user_input(interactive_identifier, highline) + puts() else - klippfile = Dir.glob(File.join(Dir.pwd, '*.klippfile')).first - template_name = File.basename(klippfile, File.extname(klippfile)) if klippfile + creator = Klipp::Creator.from_file File.join(Dir.pwd, 'Klippfile') end + spec_path = Template::Spec.spec_path_for_identifier creator.identifier + spec = Template::Spec.from_file spec_path + spec.set_token_values(creator.tokens, params.splice_option('-v')) - template = Klipp::Template.new(Klipp::Configuration.templates_dir, template_name) + block_actions = spec.block_actions_under_git && git_repository? + if spec.pre_actions.count > 0 + if block_actions + Formatador.display_line("[yellow][i][/] Git repository found, not running pre-actions (see .klippspec).") + puts() + else + run_actions(spec.pre_actions) if Klipp.env.prod? + Formatador.display_line("[green][√] Pre-actions complete.[/]") + puts() + end + end - if klippfile - # load token values from klippfile - template.load_klippfile klippfile - elsif template_name - # ask for token values with highline - raise "Direct user input not yet supported. Use #{'klipp prepare'.yellow} to prepare a .klippfile" - else - raise "Add a template name to the `create` command, or use #{'klipp prepare'.yellow} to prepare a .klippfile" + force = params.splice_option('-f') + + source_dir = File.dirname(Template::Spec.spec_path_for_identifier creator.identifier) + target_dir = Dir.pwd + + source_files = Dir.glob(File.join(source_dir, '**', '*'), File::FNM_DOTMATCH).reject { |f| f == spec_path } + + result = source_files.map do |source_file| + spec.transfer_file source_file, spec.target_file(source_dir, source_file, target_dir), force end - project = Klipp::Project.new(template) - project.create - end + verbose = params.splice_option '-v' - private + Formatador.display_line("[green][√] Creation completed using template #{Template::Spec.expand_identifier creator.identifier}. #{'Run `klipp create -v` to see what files were created.' unless verbose}[/]") + puts() - def self.template_files - Dir.glob File.join(Klipp::Configuration.templates_dir, '*.yml') - end + if (verbose) + strip = File.dirname(Dir.pwd)+File::SEPARATOR + result.each { |r| Formatador.display_line(r.gsub(strip, '')) unless File.directory? r } + puts() + end -end + if spec.post_actions.count > 0 + if block_actions + Formatador.display_line("[yellow][i][/] Git repository found, not running post-actions (see .klippspec).") + puts() + else + run_actions(spec.post_actions) if Klipp.env.prod? + Formatador.display_line("[green][√] Post-actions complete.[/]") + puts() + end + end -class HelpRequest < StandardError - def initialize(msg, unknown=false, show_title=false) - @unknown = unknown - @show_title = show_title - super(msg) + Formatador.display_line("[green][√] Done.[/]") end - def message - if @unknown - "[!] #{super.to_s}".red+"\n\n#{commands}\n\n#{self.backtrace.join("\n")}" - else - "#{@show_title ? title+"\n\n" : ''}"+"[?] #{super.to_s}".yellow+"\n\n#{commands}" - end + def self.git_repository? + `git rev-parse --is-inside-work-tree 2>&1`.match /true/ end - def title - "\033[1mKlipp\033[22m, Xcode templates for the rest of us. Version: #{Klipp::VERSION}" + def self.run_actions(actions) + count = actions.count() + puts() + actions.each do |action| + Formatador.display_line("[yellow][i][/] Running `#{action}`...") + puts() + system(action) if Klipp.env.prod? + puts() + #IO.popen(action, :err=>[:child, :out]) { |f| puts ' '+f.read.gsub("\n", "\n ") } + raise "Error running action `#{action}`." if $? && $?.exitstatus > 0 + end end +end - def commands - commands = [ - version: 'Display the Klipp version number.', - list: "List all available klipp templates in #{Klipp::Configuration.templates_dir}", - prepare: 'Prepare a .klippfile to edit in your favorite text editor.', - create: 'Create a project based on the template name or .klippfile in the current directory' - ] - command_list = commands.map { |cmd| cmd.map { |key, summary| " * klipp #{key.to_s.ljust(10).green} #{summary}" } }.join("\n") - "Commands:\n\n#{command_list}" +class StringInquirer < String + def method_missing(method_name, *arguments) + if method_name.to_s[-1,1] == '?' + self == method_name.to_s[0..-2] + else + super + end end +end - def exit_status - @unknown ? 2 : 1 +def capture_stdout + old_stdout = STDOUT.clone + pipe_r, pipe_w = IO.pipe + pipe_r.sync = true + output = '' + reader = Thread.new do + begin + loop do + output << pipe_r.readpartial(1024) + end + rescue EOFError + end end + STDOUT.reopen pipe_w + yield +ensure + STDOUT.reopen old_stdout + pipe_w.close + reader.join + return output end \ No newline at end of file