ztk 1.6.2 → 1.6.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +1 -0
 - data/Rakefile +1 -1
 - data/lib/ztk/command.rb +14 -160
 - data/lib/ztk/command/download.rb +16 -0
 - data/lib/ztk/command/exec.rb +139 -0
 - data/lib/ztk/command/private.rb +26 -0
 - data/lib/ztk/command/upload.rb +16 -0
 - data/lib/ztk/locator.rb +13 -0
 - data/lib/ztk/report.rb +11 -155
 - data/lib/ztk/report/list.rb +67 -0
 - data/lib/ztk/report/private.rb +44 -0
 - data/lib/ztk/report/spreadsheet.rb +72 -0
 - data/lib/ztk/ssh.rb +20 -114
 - data/lib/ztk/ssh/bootstrap.rb +1 -1
 - data/lib/ztk/ssh/console.rb +28 -0
 - data/lib/ztk/ssh/core.rb +37 -0
 - data/lib/ztk/ssh/download.rb +2 -2
 - data/lib/ztk/ssh/file.rb +1 -1
 - data/lib/ztk/ssh/private.rb +67 -0
 - data/lib/ztk/ssh/upload.rb +2 -2
 - data/lib/ztk/ui.rb +14 -0
 - data/lib/ztk/version.rb +1 -1
 - data/spec/ztk/logger_spec.rb +1 -1
 - data/spec/ztk/ssh_spec.rb +8 -8
 - metadata +18 -2
 
    
        data/Gemfile
    CHANGED
    
    
    
        data/Rakefile
    CHANGED
    
    | 
         @@ -39,7 +39,7 @@ require 'yard' 
     | 
|
| 
       39 
39 
     | 
    
         
             
            require 'yard/rake/yardoc_task'
         
     | 
| 
       40 
40 
     | 
    
         | 
| 
       41 
41 
     | 
    
         
             
            GEM_NAME = File.basename(Dir.pwd)
         
     | 
| 
       42 
     | 
    
         
            -
            DOC_PATH = File.expand_path(File.join("..", " 
     | 
| 
      
 42 
     | 
    
         
            +
            DOC_PATH = File.expand_path(File.join("..", "", "#{GEM_NAME}.doc"))
         
     | 
| 
       43 
43 
     | 
    
         | 
| 
       44 
44 
     | 
    
         
             
            namespace :doc do
         
     | 
| 
       45 
45 
     | 
    
         
             
              YARD::Rake::YardocTask.new(:pages) do |t|
         
     | 
    
        data/lib/ztk/command.rb
    CHANGED
    
    | 
         @@ -1,7 +1,3 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'ostruct'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'timeout'
         
     | 
| 
       3 
     | 
    
         
            -
            require 'socket'
         
     | 
| 
       4 
     | 
    
         
            -
             
     | 
| 
       5 
1 
     | 
    
         
             
            module ZTK
         
     | 
| 
       6 
2 
     | 
    
         | 
| 
       7 
3 
     | 
    
         
             
              # Command Error Class
         
     | 
| 
         @@ -22,6 +18,18 @@ module ZTK 
     | 
|
| 
       22 
18 
     | 
    
         
             
              #
         
     | 
| 
       23 
19 
     | 
    
         
             
              # @author Zachary Patten <zachary AT jovelabs DOT com>
         
     | 
| 
       24 
20 
     | 
    
         
             
              class Command < ZTK::Base
         
     | 
| 
      
 21 
     | 
    
         
            +
                require 'ostruct'
         
     | 
| 
      
 22 
     | 
    
         
            +
                require 'timeout'
         
     | 
| 
      
 23 
     | 
    
         
            +
                require 'socket'
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
                require 'ztk/command/download'
         
     | 
| 
      
 26 
     | 
    
         
            +
                require 'ztk/command/exec'
         
     | 
| 
      
 27 
     | 
    
         
            +
                require 'ztk/command/private'
         
     | 
| 
      
 28 
     | 
    
         
            +
                require 'ztk/command/upload'
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                include ZTK::Command::Download
         
     | 
| 
      
 31 
     | 
    
         
            +
                include ZTK::Command::Exec
         
     | 
| 
      
 32 
     | 
    
         
            +
                include ZTK::Command::Upload
         
     | 
| 
       25 
33 
     | 
    
         | 
| 
       26 
34 
     | 
    
         
             
                # @param [Hash] configuration Sets the overall default configuration for the
         
     | 
| 
       27 
35 
     | 
    
         
             
                #   class.  For example, all calls to *exec* against this instance will use
         
     | 
| 
         @@ -46,168 +54,14 @@ module ZTK 
     | 
|
| 
       46 
54 
     | 
    
         
             
                    :exit_code => 0,
         
     | 
| 
       47 
55 
     | 
    
         
             
                    :silence => false
         
     | 
| 
       48 
56 
     | 
    
         
             
                  }.merge(configuration))
         
     | 
| 
       49 
     | 
    
         
            -
                  config.ui.logger.debug { "config=#{config.send(:table).inspect}" }
         
     | 
| 
       50 
     | 
    
         
            -
                end
         
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
                # Execute Command
         
     | 
| 
       53 
     | 
    
         
            -
                #
         
     | 
| 
       54 
     | 
    
         
            -
                # @example Execute a command:
         
     | 
| 
       55 
     | 
    
         
            -
                #   cmd = ZTK::Command.new(:silence => true)
         
     | 
| 
       56 
     | 
    
         
            -
                #   puts cmd.exec("hostname", :silence => false).inspect
         
     | 
| 
       57 
     | 
    
         
            -
                #
         
     | 
| 
       58 
     | 
    
         
            -
                # @param [String] command The command to execute.
         
     | 
| 
       59 
     | 
    
         
            -
                # @param [Hash] options The options hash for executing the command.
         
     | 
| 
       60 
     | 
    
         
            -
                # @option options [Integer] :timeout (600) How long in seconds before
         
     | 
| 
       61 
     | 
    
         
            -
                #   the command will timeout.
         
     | 
| 
       62 
     | 
    
         
            -
                # @option options [Boolean] :ignore_exit_status (false) Whether or not
         
     | 
| 
       63 
     | 
    
         
            -
                #   we should ignore the exit status of the the process we spawn.  By
         
     | 
| 
       64 
     | 
    
         
            -
                #   default we do not ignore the exit status and throw an exception if it is
         
     | 
| 
       65 
     | 
    
         
            -
                #   non-zero.
         
     | 
| 
       66 
     | 
    
         
            -
                # @option options [Integer] :exit_code (0) The exit code we expect the
         
     | 
| 
       67 
     | 
    
         
            -
                #   process to return.  This is ignore if *ignore_exit_status* is true.
         
     | 
| 
       68 
     | 
    
         
            -
                # @option options [Boolean] :silence (false) Whether or not we should
         
     | 
| 
       69 
     | 
    
         
            -
                #   squelch the output of the process.  The output will always go to the
         
     | 
| 
       70 
     | 
    
         
            -
                #   logging device supplied in the ZTK::UI object.  The output is always
         
     | 
| 
       71 
     | 
    
         
            -
                #   available in the return value from the method additionally.
         
     | 
| 
       72 
     | 
    
         
            -
                #
         
     | 
| 
       73 
     | 
    
         
            -
                # @return [OpenStruct#output] The output of the command, both STDOUT and
         
     | 
| 
       74 
     | 
    
         
            -
                #   STDERR combined.
         
     | 
| 
       75 
     | 
    
         
            -
                # @return [OpenStruct#exit_code] The exit code of the process.
         
     | 
| 
       76 
     | 
    
         
            -
                def exec(command, options={})
         
     | 
| 
       77 
     | 
    
         
            -
                  options = OpenStruct.new(config.send(:table).merge(options))
         
     | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
                  options.ui.logger.debug { "config=#{options.send(:table).inspect}" }
         
     | 
| 
       80 
     | 
    
         
            -
                  options.ui.logger.debug { "options=#{options.send(:table).inspect}" }
         
     | 
| 
       81 
     | 
    
         
            -
                  options.ui.logger.info { "command(#{command.inspect})" }
         
     | 
| 
       82 
     | 
    
         
            -
             
     | 
| 
       83 
     | 
    
         
            -
                  if options.replace_current_process
         
     | 
| 
       84 
     | 
    
         
            -
                    options.ui.logger.fatal { "REPLACING CURRENT PROCESS - GOODBYE!" }
         
     | 
| 
       85 
     | 
    
         
            -
                    Kernel.exec(command)
         
     | 
| 
       86 
     | 
    
         
            -
                  end
         
     | 
| 
       87 
     | 
    
         
            -
             
     | 
| 
       88 
     | 
    
         
            -
                  output = ""
         
     | 
| 
       89 
     | 
    
         
            -
                  exit_code = -1
         
     | 
| 
       90 
     | 
    
         
            -
                  stdout_header = false
         
     | 
| 
       91 
     | 
    
         
            -
                  stderr_header = false
         
     | 
| 
       92 
     | 
    
         
            -
             
     | 
| 
       93 
     | 
    
         
            -
                  parent_stdout_reader, child_stdout_writer = IO.pipe
         
     | 
| 
       94 
     | 
    
         
            -
                  parent_stderr_reader, child_stderr_writer = IO.pipe
         
     | 
| 
       95 
     | 
    
         
            -
             
     | 
| 
       96 
     | 
    
         
            -
                  start_time = Time.now.utc
         
     | 
| 
       97 
     | 
    
         
            -
             
     | 
| 
       98 
     | 
    
         
            -
                  pid = Process.fork do
         
     | 
| 
       99 
     | 
    
         
            -
                    parent_stdout_reader.close
         
     | 
| 
       100 
     | 
    
         
            -
                    parent_stderr_reader.close
         
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
       102 
     | 
    
         
            -
                    STDOUT.reopen(child_stdout_writer)
         
     | 
| 
       103 
     | 
    
         
            -
                    STDERR.reopen(child_stderr_writer)
         
     | 
| 
       104 
     | 
    
         
            -
                    STDIN.reopen("/dev/null")
         
     | 
| 
       105 
     | 
    
         
            -
             
     | 
| 
       106 
     | 
    
         
            -
                    child_stdout_writer.close
         
     | 
| 
       107 
     | 
    
         
            -
                    child_stderr_writer.close
         
     | 
| 
       108 
57 
     | 
    
         | 
| 
       109 
     | 
    
         
            -
             
     | 
| 
       110 
     | 
    
         
            -
                  end
         
     | 
| 
       111 
     | 
    
         
            -
                  child_stdout_writer.close
         
     | 
| 
       112 
     | 
    
         
            -
                  child_stderr_writer.close
         
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
     | 
    
         
            -
                  reader_writer_key = {parent_stdout_reader => :stdout, parent_stderr_reader => :stderr}
         
     | 
| 
       115 
     | 
    
         
            -
                  reader_writer_map = {parent_stdout_reader => options.ui.stdout, parent_stderr_reader => options.ui.stderr}
         
     | 
| 
       116 
     | 
    
         
            -
             
     | 
| 
       117 
     | 
    
         
            -
                  direct_log(:info) { log_header("COMMAND") }
         
     | 
| 
       118 
     | 
    
         
            -
                  direct_log(:info) { "#{command.inspect}\n" }
         
     | 
| 
       119 
     | 
    
         
            -
                  direct_log(:info) { log_header("STARTED") }
         
     | 
| 
       120 
     | 
    
         
            -
             
     | 
| 
       121 
     | 
    
         
            -
                  begin
         
     | 
| 
       122 
     | 
    
         
            -
                    Timeout.timeout(options.timeout) do
         
     | 
| 
       123 
     | 
    
         
            -
                      loop do
         
     | 
| 
       124 
     | 
    
         
            -
                        pipes = IO.select(reader_writer_map.keys, [], reader_writer_map.keys).first
         
     | 
| 
       125 
     | 
    
         
            -
                        pipes.each do |pipe|
         
     | 
| 
       126 
     | 
    
         
            -
                          data = pipe.read
         
     | 
| 
       127 
     | 
    
         
            -
             
     | 
| 
       128 
     | 
    
         
            -
                          if (data.nil? || data.empty?)
         
     | 
| 
       129 
     | 
    
         
            -
                            sleep(0.1)
         
     | 
| 
       130 
     | 
    
         
            -
                            next
         
     | 
| 
       131 
     | 
    
         
            -
                          end
         
     | 
| 
       132 
     | 
    
         
            -
             
     | 
| 
       133 
     | 
    
         
            -
                          case reader_writer_key[pipe]
         
     | 
| 
       134 
     | 
    
         
            -
                          when :stdout then
         
     | 
| 
       135 
     | 
    
         
            -
                            if !stdout_header
         
     | 
| 
       136 
     | 
    
         
            -
                              direct_log(:info) { log_header("STDOUT") }
         
     | 
| 
       137 
     | 
    
         
            -
                              stdout_header = true
         
     | 
| 
       138 
     | 
    
         
            -
                              stderr_header = false
         
     | 
| 
       139 
     | 
    
         
            -
                            end
         
     | 
| 
       140 
     | 
    
         
            -
                            reader_writer_map[pipe].write(data) unless options.silence
         
     | 
| 
       141 
     | 
    
         
            -
                            direct_log(:info) { data }
         
     | 
| 
       142 
     | 
    
         
            -
             
     | 
| 
       143 
     | 
    
         
            -
                          when :stderr then
         
     | 
| 
       144 
     | 
    
         
            -
                            if !stderr_header
         
     | 
| 
       145 
     | 
    
         
            -
                              direct_log(:warn) { log_header("STDERR") }
         
     | 
| 
       146 
     | 
    
         
            -
                              stderr_header = true
         
     | 
| 
       147 
     | 
    
         
            -
                              stdout_header = false
         
     | 
| 
       148 
     | 
    
         
            -
                            end
         
     | 
| 
       149 
     | 
    
         
            -
                            reader_writer_map[pipe].write(data) unless options.silence
         
     | 
| 
       150 
     | 
    
         
            -
                            direct_log(:warn) { data }
         
     | 
| 
       151 
     | 
    
         
            -
                          end
         
     | 
| 
       152 
     | 
    
         
            -
             
     | 
| 
       153 
     | 
    
         
            -
                          output += data
         
     | 
| 
       154 
     | 
    
         
            -
             
     | 
| 
       155 
     | 
    
         
            -
                          options.on_progress.nil? or options.on_progress.call
         
     | 
| 
       156 
     | 
    
         
            -
                        end
         
     | 
| 
       157 
     | 
    
         
            -
             
     | 
| 
       158 
     | 
    
         
            -
                        break if reader_writer_map.keys.all?{ |reader| reader.eof? }
         
     | 
| 
       159 
     | 
    
         
            -
                      end
         
     | 
| 
       160 
     | 
    
         
            -
                    end
         
     | 
| 
       161 
     | 
    
         
            -
                  rescue Timeout::Error => e
         
     | 
| 
       162 
     | 
    
         
            -
                    direct_log(:fatal) { log_header("TIMEOUT") }
         
     | 
| 
       163 
     | 
    
         
            -
                    log_and_raise(CommandError, "Process timed out after #{options.timeout} seconds!")
         
     | 
| 
       164 
     | 
    
         
            -
                  end
         
     | 
| 
       165 
     | 
    
         
            -
             
     | 
| 
       166 
     | 
    
         
            -
                  Process.waitpid(pid)
         
     | 
| 
       167 
     | 
    
         
            -
                  exit_code = $?.exitstatus
         
     | 
| 
       168 
     | 
    
         
            -
                  direct_log(:info) { log_header("STOPPED") }
         
     | 
| 
       169 
     | 
    
         
            -
             
     | 
| 
       170 
     | 
    
         
            -
                  parent_stdout_reader.close
         
     | 
| 
       171 
     | 
    
         
            -
                  parent_stderr_reader.close
         
     | 
| 
       172 
     | 
    
         
            -
             
     | 
| 
       173 
     | 
    
         
            -
                  options.ui.logger.debug { "exit_code(#{exit_code})" }
         
     | 
| 
       174 
     | 
    
         
            -
             
     | 
| 
       175 
     | 
    
         
            -
                  if !options.ignore_exit_status && (exit_code != options.exit_code)
         
     | 
| 
       176 
     | 
    
         
            -
                    log_and_raise(CommandError, "exec(#{command.inspect}, #{options.inspect}) failed! [#{exit_code}]")
         
     | 
| 
       177 
     | 
    
         
            -
                  end
         
     | 
| 
       178 
     | 
    
         
            -
                  OpenStruct.new(:command => command, :output => output, :exit_code => exit_code)
         
     | 
| 
       179 
     | 
    
         
            -
                end
         
     | 
| 
       180 
     | 
    
         
            -
             
     | 
| 
       181 
     | 
    
         
            -
                # Not Supported
         
     | 
| 
       182 
     | 
    
         
            -
                # @raise [CommandError] Not Supported
         
     | 
| 
       183 
     | 
    
         
            -
                def upload(*args)
         
     | 
| 
       184 
     | 
    
         
            -
                  log_and_raise(CommandError, "Not Supported")
         
     | 
| 
       185 
     | 
    
         
            -
                end
         
     | 
| 
       186 
     | 
    
         
            -
             
     | 
| 
       187 
     | 
    
         
            -
                # Not Supported
         
     | 
| 
       188 
     | 
    
         
            -
                # @raise [CommandError] Not Supported
         
     | 
| 
       189 
     | 
    
         
            -
                def download(*args)
         
     | 
| 
       190 
     | 
    
         
            -
                  log_and_raise(CommandError, "Not Supported")
         
     | 
| 
      
 58 
     | 
    
         
            +
                  config.ui.logger.debug { "config=#{config.send(:table).inspect}" }
         
     | 
| 
       191 
59 
     | 
    
         
             
                end
         
     | 
| 
       192 
60 
     | 
    
         | 
| 
       193 
61 
     | 
    
         | 
| 
       194 
62 
     | 
    
         
             
              private
         
     | 
| 
       195 
63 
     | 
    
         | 
| 
       196 
     | 
    
         
            -
                 
     | 
| 
       197 
     | 
    
         
            -
                # shell.
         
     | 
| 
       198 
     | 
    
         
            -
                def tag
         
     | 
| 
       199 
     | 
    
         
            -
                  @@hostname ||= Socket.gethostname.split('.').first.strip
         
     | 
| 
       200 
     | 
    
         
            -
                  "#{ENV['USER']}@#{@@hostname}"
         
     | 
| 
       201 
     | 
    
         
            -
                end
         
     | 
| 
       202 
     | 
    
         
            -
             
     | 
| 
       203 
     | 
    
         
            -
                # Formats a header suitable for writing to the direct logger when logging
         
     | 
| 
       204 
     | 
    
         
            -
                # sessions.
         
     | 
| 
       205 
     | 
    
         
            -
                def log_header(what)
         
     | 
| 
       206 
     | 
    
         
            -
                  count = 8
         
     | 
| 
       207 
     | 
    
         
            -
                  sep = ("=" * count)
         
     | 
| 
       208 
     | 
    
         
            -
                  header = [sep, "[ #{what} ]", sep, "[ #{tag} ]", sep, "[ #{what} ]", sep].join
         
     | 
| 
       209 
     | 
    
         
            -
                  "#{header}\n"
         
     | 
| 
       210 
     | 
    
         
            -
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
                include ZTK::Command::Private
         
     | 
| 
       211 
65 
     | 
    
         | 
| 
       212 
66 
     | 
    
         
             
              end
         
     | 
| 
       213 
67 
     | 
    
         | 
| 
         @@ -0,0 +1,139 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module ZTK
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Command
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
                # Command Exec Functionality
         
     | 
| 
      
 5 
     | 
    
         
            +
                module Exec
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                  # Execute Command
         
     | 
| 
      
 8 
     | 
    
         
            +
                  #
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # @example Execute a command:
         
     | 
| 
      
 10 
     | 
    
         
            +
                  #   cmd = ZTK::Command.new(:silence => true)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  #   puts cmd.exec("hostname", :silence => false).inspect
         
     | 
| 
      
 12 
     | 
    
         
            +
                  #
         
     | 
| 
      
 13 
     | 
    
         
            +
                  # @param [String] command The command to execute.
         
     | 
| 
      
 14 
     | 
    
         
            +
                  # @param [Hash] options The options hash for executing the command.
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # @option options [Integer] :timeout (600) How long in seconds before
         
     | 
| 
      
 16 
     | 
    
         
            +
                  #   the command will timeout.
         
     | 
| 
      
 17 
     | 
    
         
            +
                  # @option options [Boolean] :ignore_exit_status (false) Whether or not
         
     | 
| 
      
 18 
     | 
    
         
            +
                  #   we should ignore the exit status of the the process we spawn.  By
         
     | 
| 
      
 19 
     | 
    
         
            +
                  #   default we do not ignore the exit status and throw an exception if it is
         
     | 
| 
      
 20 
     | 
    
         
            +
                  #   non-zero.
         
     | 
| 
      
 21 
     | 
    
         
            +
                  # @option options [Integer] :exit_code (0) The exit code we expect the
         
     | 
| 
      
 22 
     | 
    
         
            +
                  #   process to return.  This is ignore if *ignore_exit_status* is true.
         
     | 
| 
      
 23 
     | 
    
         
            +
                  # @option options [Boolean] :silence (false) Whether or not we should
         
     | 
| 
      
 24 
     | 
    
         
            +
                  #   squelch the output of the process.  The output will always go to the
         
     | 
| 
      
 25 
     | 
    
         
            +
                  #   logging device supplied in the ZTK::UI object.  The output is always
         
     | 
| 
      
 26 
     | 
    
         
            +
                  #   available in the return value from the method additionally.
         
     | 
| 
      
 27 
     | 
    
         
            +
                  #
         
     | 
| 
      
 28 
     | 
    
         
            +
                  # @return [OpenStruct#output] The output of the command, both STDOUT and
         
     | 
| 
      
 29 
     | 
    
         
            +
                  #   STDERR combined.
         
     | 
| 
      
 30 
     | 
    
         
            +
                  # @return [OpenStruct#exit_code] The exit code of the process.
         
     | 
| 
      
 31 
     | 
    
         
            +
                  def exec(command, options={})
         
     | 
| 
      
 32 
     | 
    
         
            +
                    options = OpenStruct.new(config.send(:table).merge(options))
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                    options.ui.logger.debug { "config=#{options.send(:table).inspect}" }
         
     | 
| 
      
 35 
     | 
    
         
            +
                    options.ui.logger.debug { "options=#{options.send(:table).inspect}" }
         
     | 
| 
      
 36 
     | 
    
         
            +
                    options.ui.logger.info { "command(#{command.inspect})" }
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                    if options.replace_current_process
         
     | 
| 
      
 39 
     | 
    
         
            +
                      options.ui.logger.fatal { "REPLACING CURRENT PROCESS - GOODBYE!" }
         
     | 
| 
      
 40 
     | 
    
         
            +
                      Kernel.exec(command)
         
     | 
| 
      
 41 
     | 
    
         
            +
                    end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                    output = ""
         
     | 
| 
      
 44 
     | 
    
         
            +
                    exit_code = -1
         
     | 
| 
      
 45 
     | 
    
         
            +
                    stdout_header = false
         
     | 
| 
      
 46 
     | 
    
         
            +
                    stderr_header = false
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                    parent_stdout_reader, child_stdout_writer = IO.pipe
         
     | 
| 
      
 49 
     | 
    
         
            +
                    parent_stderr_reader, child_stderr_writer = IO.pipe
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                    start_time = Time.now.utc
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                    pid = Process.fork do
         
     | 
| 
      
 54 
     | 
    
         
            +
                      parent_stdout_reader.close
         
     | 
| 
      
 55 
     | 
    
         
            +
                      parent_stderr_reader.close
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                      STDOUT.reopen(child_stdout_writer)
         
     | 
| 
      
 58 
     | 
    
         
            +
                      STDERR.reopen(child_stderr_writer)
         
     | 
| 
      
 59 
     | 
    
         
            +
                      STDIN.reopen("/dev/null")
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                      child_stdout_writer.close
         
     | 
| 
      
 62 
     | 
    
         
            +
                      child_stderr_writer.close
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                      Kernel.exec(command)
         
     | 
| 
      
 65 
     | 
    
         
            +
                    end
         
     | 
| 
      
 66 
     | 
    
         
            +
                    child_stdout_writer.close
         
     | 
| 
      
 67 
     | 
    
         
            +
                    child_stderr_writer.close
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                    reader_writer_key = {parent_stdout_reader => :stdout, parent_stderr_reader => :stderr}
         
     | 
| 
      
 70 
     | 
    
         
            +
                    reader_writer_map = {parent_stdout_reader => options.ui.stdout, parent_stderr_reader => options.ui.stderr}
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                    direct_log(:info) { log_header("COMMAND") }
         
     | 
| 
      
 73 
     | 
    
         
            +
                    direct_log(:info) { "#{command.inspect}\n" }
         
     | 
| 
      
 74 
     | 
    
         
            +
                    direct_log(:info) { log_header("STARTED") }
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                    begin
         
     | 
| 
      
 77 
     | 
    
         
            +
                      Timeout.timeout(options.timeout) do
         
     | 
| 
      
 78 
     | 
    
         
            +
                        loop do
         
     | 
| 
      
 79 
     | 
    
         
            +
                          pipes = IO.select(reader_writer_map.keys, [], reader_writer_map.keys).first
         
     | 
| 
      
 80 
     | 
    
         
            +
                          pipes.each do |pipe|
         
     | 
| 
      
 81 
     | 
    
         
            +
                            data = pipe.read
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                            if (data.nil? || data.empty?)
         
     | 
| 
      
 84 
     | 
    
         
            +
                              sleep(0.1)
         
     | 
| 
      
 85 
     | 
    
         
            +
                              next
         
     | 
| 
      
 86 
     | 
    
         
            +
                            end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                            case reader_writer_key[pipe]
         
     | 
| 
      
 89 
     | 
    
         
            +
                            when :stdout then
         
     | 
| 
      
 90 
     | 
    
         
            +
                              if !stdout_header
         
     | 
| 
      
 91 
     | 
    
         
            +
                                direct_log(:info) { log_header("STDOUT") }
         
     | 
| 
      
 92 
     | 
    
         
            +
                                stdout_header = true
         
     | 
| 
      
 93 
     | 
    
         
            +
                                stderr_header = false
         
     | 
| 
      
 94 
     | 
    
         
            +
                              end
         
     | 
| 
      
 95 
     | 
    
         
            +
                              reader_writer_map[pipe].write(data) unless options.silence
         
     | 
| 
      
 96 
     | 
    
         
            +
                              direct_log(:info) { data }
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
                            when :stderr then
         
     | 
| 
      
 99 
     | 
    
         
            +
                              if !stderr_header
         
     | 
| 
      
 100 
     | 
    
         
            +
                                direct_log(:warn) { log_header("STDERR") }
         
     | 
| 
      
 101 
     | 
    
         
            +
                                stderr_header = true
         
     | 
| 
      
 102 
     | 
    
         
            +
                                stdout_header = false
         
     | 
| 
      
 103 
     | 
    
         
            +
                              end
         
     | 
| 
      
 104 
     | 
    
         
            +
                              reader_writer_map[pipe].write(data) unless options.silence
         
     | 
| 
      
 105 
     | 
    
         
            +
                              direct_log(:warn) { data }
         
     | 
| 
      
 106 
     | 
    
         
            +
                            end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
                            output += data
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
                            options.on_progress.nil? or options.on_progress.call
         
     | 
| 
      
 111 
     | 
    
         
            +
                          end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
                          break if reader_writer_map.keys.all?{ |reader| reader.eof? }
         
     | 
| 
      
 114 
     | 
    
         
            +
                        end
         
     | 
| 
      
 115 
     | 
    
         
            +
                      end
         
     | 
| 
      
 116 
     | 
    
         
            +
                    rescue Timeout::Error => e
         
     | 
| 
      
 117 
     | 
    
         
            +
                      direct_log(:fatal) { log_header("TIMEOUT") }
         
     | 
| 
      
 118 
     | 
    
         
            +
                      log_and_raise(CommandError, "Process timed out after #{options.timeout} seconds!")
         
     | 
| 
      
 119 
     | 
    
         
            +
                    end
         
     | 
| 
      
 120 
     | 
    
         
            +
             
     | 
| 
      
 121 
     | 
    
         
            +
                    Process.waitpid(pid)
         
     | 
| 
      
 122 
     | 
    
         
            +
                    exit_code = $?.exitstatus
         
     | 
| 
      
 123 
     | 
    
         
            +
                    direct_log(:info) { log_header("STOPPED") }
         
     | 
| 
      
 124 
     | 
    
         
            +
             
     | 
| 
      
 125 
     | 
    
         
            +
                    parent_stdout_reader.close
         
     | 
| 
      
 126 
     | 
    
         
            +
                    parent_stderr_reader.close
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
                    options.ui.logger.debug { "exit_code(#{exit_code})" }
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                    if !options.ignore_exit_status && (exit_code != options.exit_code)
         
     | 
| 
      
 131 
     | 
    
         
            +
                      log_and_raise(CommandError, "exec(#{command.inspect}, #{options.inspect}) failed! [#{exit_code}]")
         
     | 
| 
      
 132 
     | 
    
         
            +
                    end
         
     | 
| 
      
 133 
     | 
    
         
            +
                    OpenStruct.new(:command => command, :output => output, :exit_code => exit_code)
         
     | 
| 
      
 134 
     | 
    
         
            +
                  end
         
     | 
| 
      
 135 
     | 
    
         
            +
             
     | 
| 
      
 136 
     | 
    
         
            +
                end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
              end
         
     | 
| 
      
 139 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,26 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module ZTK
         
     | 
| 
      
 2 
     | 
    
         
            +
              class Command
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
                # Command Private Functionality
         
     | 
| 
      
 5 
     | 
    
         
            +
                module Private
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
                  # Returns a string in the format of "user@hostname" for the current
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # shell.
         
     | 
| 
      
 9 
     | 
    
         
            +
                  def tag
         
     | 
| 
      
 10 
     | 
    
         
            +
                    @@hostname ||= Socket.gethostname.split('.').first.strip
         
     | 
| 
      
 11 
     | 
    
         
            +
                    "#{ENV['USER']}@#{@@hostname}"
         
     | 
| 
      
 12 
     | 
    
         
            +
                  end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                  # Formats a header suitable for writing to the direct logger when logging
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # sessions.
         
     | 
| 
      
 16 
     | 
    
         
            +
                  def log_header(what)
         
     | 
| 
      
 17 
     | 
    
         
            +
                    count = 8
         
     | 
| 
      
 18 
     | 
    
         
            +
                    sep = ("=" * count)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    header = [sep, "[ #{what} ]", sep, "[ #{tag} ]", sep, "[ #{what} ]", sep].join
         
     | 
| 
      
 20 
     | 
    
         
            +
                    "#{header}\n"
         
     | 
| 
      
 21 
     | 
    
         
            +
                  end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              end
         
     | 
| 
      
 26 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/ztk/locator.rb
    CHANGED
    
    | 
         @@ -31,6 +31,19 @@ module ZTK 
     | 
|
| 
       31 
31 
     | 
    
         
             
                    raise LocatorError, "Could not locate '#{File.join(args)}'!"
         
     | 
| 
       32 
32 
     | 
    
         
             
                  end
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
      
 34 
     | 
    
         
            +
                  # Returns the root for the filesystem we are operating on.  Ignores
         
     | 
| 
      
 35 
     | 
    
         
            +
                  # mount boundries on *nix.
         
     | 
| 
      
 36 
     | 
    
         
            +
                  #
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # For all flavors of *nix this should always return "/".
         
     | 
| 
      
 38 
     | 
    
         
            +
                  #
         
     | 
| 
      
 39 
     | 
    
         
            +
                  # Windows should expect something similar to "C:\".
         
     | 
| 
      
 40 
     | 
    
         
            +
                  #
         
     | 
| 
      
 41 
     | 
    
         
            +
                  # @return [String] The root path of the file-system.  For unix this should
         
     | 
| 
      
 42 
     | 
    
         
            +
                  #   always be "/".  For windows this should be something like "C:\".
         
     | 
| 
      
 43 
     | 
    
         
            +
                  def root
         
     | 
| 
      
 44 
     | 
    
         
            +
                    Dir.pwd.split(File::SEPARATOR).first
         
     | 
| 
      
 45 
     | 
    
         
            +
                  end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
       34 
47 
     | 
    
         
             
                end
         
     | 
| 
       35 
48 
     | 
    
         | 
| 
       36 
49 
     | 
    
         
             
              end
         
     | 
    
        data/lib/ztk/report.rb
    CHANGED
    
    | 
         @@ -1,6 +1,3 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            require 'socket'
         
     | 
| 
       2 
     | 
    
         
            -
            require 'timeout'
         
     | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
1 
     | 
    
         
             
            module ZTK
         
     | 
| 
       5 
2 
     | 
    
         | 
| 
       6 
3 
     | 
    
         
             
              # Report Error Class
         
     | 
| 
         @@ -19,169 +16,28 @@ module ZTK 
     | 
|
| 
       19 
16 
     | 
    
         
             
              #
         
     | 
| 
       20 
17 
     | 
    
         
             
              # @author Zachary Patten <zachary AT jovelabs DOT com>
         
     | 
| 
       21 
18 
     | 
    
         
             
              class Report < ZTK::Base
         
     | 
| 
      
 19 
     | 
    
         
            +
                require 'socket'
         
     | 
| 
      
 20 
     | 
    
         
            +
                require 'timeout'
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                require 'ztk/report/list'
         
     | 
| 
      
 23 
     | 
    
         
            +
                require 'ztk/report/private'
         
     | 
| 
      
 24 
     | 
    
         
            +
                require 'ztk/report/spreadsheet'
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
                include ZTK::Report::List
         
     | 
| 
      
 27 
     | 
    
         
            +
                include ZTK::Report::Spreadsheet
         
     | 
| 
       22 
28 
     | 
    
         | 
| 
       23 
29 
     | 
    
         
             
                # @param [Hash] configuration Configuration options hash.
         
     | 
| 
       24 
30 
     | 
    
         
             
                def initialize(configuration={})
         
     | 
| 
       25 
31 
     | 
    
         
             
                  super({
         
     | 
| 
       26 
32 
     | 
    
         
             
                  }.merge(configuration))
         
     | 
| 
       27 
     | 
    
         
            -
                  config.ui.logger.debug { "config=#{config.send(:table).inspect}" }
         
     | 
| 
       28 
     | 
    
         
            -
                end
         
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
                # Displays data in a spreadsheet style.
         
     | 
| 
       31 
     | 
    
         
            -
                #
         
     | 
| 
       32 
     | 
    
         
            -
                #     +-------------+-------+-------+--------+----------------+-------------------+--------------+---------+
         
     | 
| 
       33 
     | 
    
         
            -
                #     | NAME        | ALIVE | ARCH  | DISTRO | IP             | MAC               | CHEF VERSION | PERSIST |
         
     | 
| 
       34 
     | 
    
         
            -
                #     +-------------+-------+-------+--------+----------------+-------------------+--------------+---------+
         
     | 
| 
       35 
     | 
    
         
            -
                #     | sudo        | false | amd64 | ubuntu | 192.168.99.110 | 00:00:5e:34:d6:aa | N/A          | true    |
         
     | 
| 
       36 
     | 
    
         
            -
                #     | timezone    | false | amd64 | ubuntu | 192.168.122.47 | 00:00:5e:92:d7:f6 | N/A          | true    |
         
     | 
| 
       37 
     | 
    
         
            -
                #     | chef-client | false | amd64 | ubuntu | 192.168.159.98 | 00:00:5e:c7:ce:26 | N/A          | true    |
         
     | 
| 
       38 
     | 
    
         
            -
                #     | users       | false | amd64 | ubuntu | 192.168.7.78   | 00:00:5e:89:f9:50 | N/A          | true    |
         
     | 
| 
       39 
     | 
    
         
            -
                #     +-------------+-------+-------+--------+----------------+-------------------+--------------+---------+
         
     | 
| 
       40 
     | 
    
         
            -
                #
         
     | 
| 
       41 
     | 
    
         
            -
                # @param [Array<Object>,Object] dataset A single object or an array of
         
     | 
| 
       42 
     | 
    
         
            -
                #   objects for which we want to generate a report
         
     | 
| 
       43 
     | 
    
         
            -
                # @param [Array] headers An array of headers used for ordering the output.
         
     | 
| 
       44 
     | 
    
         
            -
                # @return [OpenStruct]
         
     | 
| 
       45 
     | 
    
         
            -
                def spreadsheet(dataset, headers, &block)
         
     | 
| 
       46 
     | 
    
         
            -
                  !block_given? and log_and_raise(ReportError, "You must supply a block!")
         
     | 
| 
       47 
     | 
    
         
            -
                  headers.nil? and log_and_raise(ReportError, "Headers can not be nil!")
         
     | 
| 
       48 
     | 
    
         
            -
                  dataset.nil? and log_and_raise(ReportError, "Dataset can not be nil!")
         
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
                  rows = Array.new
         
     | 
| 
       51 
     | 
    
         
            -
                  max_lengths = OpenStruct.new
         
     | 
| 
       52 
     | 
    
         
            -
                  headers = headers.map(&:to_s).map(&:downcase).map(&:to_sym)
         
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
                  if dataset.is_a?(Array)
         
     | 
| 
       55 
     | 
    
         
            -
                    dataset.each do |data|
         
     | 
| 
       56 
     | 
    
         
            -
                      rows << block.call(data)
         
     | 
| 
       57 
     | 
    
         
            -
                    end
         
     | 
| 
       58 
     | 
    
         
            -
                  else
         
     | 
| 
       59 
     | 
    
         
            -
                    rows << block.call(dataset)
         
     | 
| 
       60 
     | 
    
         
            -
                  end
         
     | 
| 
       61 
     | 
    
         
            -
                  rows.compact!
         
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
                  if rows.count > 0
         
     | 
| 
       64 
     | 
    
         
            -
                    max_lengths = max_spreadsheet_lengths(headers, rows)
         
     | 
| 
       65 
     | 
    
         
            -
                    header_line = headers.collect { |header| "%-#{max_lengths.send(:table)[header]}s" % header.to_s.upcase }
         
     | 
| 
       66 
     | 
    
         
            -
                    header_line = format_row(header_line)
         
     | 
| 
       67 
     | 
    
         
            -
             
     | 
| 
       68 
     | 
    
         
            -
                    config.ui.stdout.puts(format_header(headers, max_lengths))
         
     | 
| 
       69 
     | 
    
         
            -
                    config.ui.stdout.puts(header_line)
         
     | 
| 
       70 
     | 
    
         
            -
                    config.ui.stdout.puts(format_header(headers, max_lengths))
         
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
                    rows.each do |row|
         
     | 
| 
       73 
     | 
    
         
            -
                      row_line = headers.collect do |header|
         
     | 
| 
       74 
     | 
    
         
            -
                        header_length = max_lengths.send(:table)[header]
         
     | 
| 
       75 
     | 
    
         
            -
                        content = row.send(:table)[header]
         
     | 
| 
       76 
     | 
    
         
            -
             
     | 
| 
       77 
     | 
    
         
            -
                        "%-#{header_length}s" % content
         
     | 
| 
       78 
     | 
    
         
            -
                      end
         
     | 
| 
       79 
     | 
    
         
            -
             
     | 
| 
       80 
     | 
    
         
            -
                      row_line = format_row(row_line)
         
     | 
| 
       81 
     | 
    
         
            -
                      config.ui.stdout.puts(row_line)
         
     | 
| 
       82 
     | 
    
         
            -
                    end
         
     | 
| 
       83 
     | 
    
         
            -
             
     | 
| 
       84 
     | 
    
         
            -
                    config.ui.stdout.puts(format_header(headers, max_lengths))
         
     | 
| 
       85 
     | 
    
         
            -
                    OpenStruct.new(:rows => rows, :max_lengths => max_lengths, :width => calculate_spreadsheet_width(headers, max_lengths))
         
     | 
| 
       86 
     | 
    
         
            -
                  else
         
     | 
| 
       87 
     | 
    
         
            -
                    OpenStruct.new(:rows => rows, :max_lengths => 0, :width => 0)
         
     | 
| 
       88 
     | 
    
         
            -
                  end
         
     | 
| 
       89 
     | 
    
         
            -
             
     | 
| 
       90 
     | 
    
         
            -
                end
         
     | 
| 
       91 
     | 
    
         
            -
             
     | 
| 
       92 
     | 
    
         
            -
                # Displays data in a key-value list style.
         
     | 
| 
       93 
     | 
    
         
            -
                #
         
     | 
| 
       94 
     | 
    
         
            -
                #     +-------------------------------------------------------------------+
         
     | 
| 
       95 
     | 
    
         
            -
                #     |                      PROVIDER: Cucumber::Chef::Provider::Vagrant  |
         
     | 
| 
       96 
     | 
    
         
            -
                #     |                            ID: default                            |
         
     | 
| 
       97 
     | 
    
         
            -
                #     |                         STATE: aborted                            |
         
     | 
| 
       98 
     | 
    
         
            -
                #     |                      USERNAME: vagrant                            |
         
     | 
| 
       99 
     | 
    
         
            -
                #     |                    IP ADDRESS: 127.0.0.1                          |
         
     | 
| 
       100 
     | 
    
         
            -
                #     |                          PORT: 2222                               |
         
     | 
| 
       101 
     | 
    
         
            -
                #     |               CHEF-SERVER API: http://127.0.0.1:4000              |
         
     | 
| 
       102 
     | 
    
         
            -
                #     |             CHEF-SERVER WEBUI: http://127.0.0.1:4040              |
         
     | 
| 
       103 
     | 
    
         
            -
                #     |      CHEF-SERVER DEFAULT USER: admin                              |
         
     | 
| 
       104 
     | 
    
         
            -
                #     |  CHEF-SERVER DEFAULT PASSWORD: p@ssw0rd1                          |
         
     | 
| 
       105 
     | 
    
         
            -
                #     +-------------------------------------------------------------------+
         
     | 
| 
       106 
     | 
    
         
            -
                #
         
     | 
| 
       107 
     | 
    
         
            -
                # @param [Array<Object>,Object] dataset A single object or an array of
         
     | 
| 
       108 
     | 
    
         
            -
                #   objects for which we want to generate a report
         
     | 
| 
       109 
     | 
    
         
            -
                # @param [Array] headers An array of headers used for ordering the output.
         
     | 
| 
       110 
     | 
    
         
            -
                # @return [OpenStruct]
         
     | 
| 
       111 
     | 
    
         
            -
                def list(dataset, headers, &block)
         
     | 
| 
       112 
     | 
    
         
            -
                  !block_given? and log_and_raise(ReportError, "You must supply a block!")
         
     | 
| 
       113 
     | 
    
         
            -
                  headers.nil? and log_and_raise(ReportError, "Headers can not be nil!")
         
     | 
| 
       114 
     | 
    
         
            -
                  dataset.nil? and log_and_raise(ReportError, "Dataset can not be nil!")
         
     | 
| 
       115 
33 
     | 
    
         | 
| 
       116 
     | 
    
         
            -
                   
     | 
| 
       117 
     | 
    
         
            -
                  max_lengths = OpenStruct.new
         
     | 
| 
       118 
     | 
    
         
            -
                  headers = headers.map(&:to_s).map(&:downcase).map(&:to_sym)
         
     | 
| 
       119 
     | 
    
         
            -
             
     | 
| 
       120 
     | 
    
         
            -
                  if dataset.is_a?(Array)
         
     | 
| 
       121 
     | 
    
         
            -
                    dataset.each do |data|
         
     | 
| 
       122 
     | 
    
         
            -
                      rows << block.call(data)
         
     | 
| 
       123 
     | 
    
         
            -
                    end
         
     | 
| 
       124 
     | 
    
         
            -
                  else
         
     | 
| 
       125 
     | 
    
         
            -
                    rows << block.call(dataset)
         
     | 
| 
       126 
     | 
    
         
            -
                  end
         
     | 
| 
       127 
     | 
    
         
            -
                  rows.compact!
         
     | 
| 
       128 
     | 
    
         
            -
             
     | 
| 
       129 
     | 
    
         
            -
                  if rows.count > 0
         
     | 
| 
       130 
     | 
    
         
            -
                    max_key_length = headers.collect{ |header| header.to_s.length }.max
         
     | 
| 
       131 
     | 
    
         
            -
                    max_value_length = rows.collect{ |row| headers.collect{ |header| row.send(:table)[header].to_s.length }.max }.max
         
     | 
| 
       132 
     | 
    
         
            -
             
     | 
| 
       133 
     | 
    
         
            -
                    width = (max_key_length + max_value_length + 2 + 2 + 2)
         
     | 
| 
       134 
     | 
    
         
            -
             
     | 
| 
       135 
     | 
    
         
            -
                    rows.each do |row|
         
     | 
| 
       136 
     | 
    
         
            -
                      config.ui.stdout.puts("+#{"-" * width}+")
         
     | 
| 
       137 
     | 
    
         
            -
                      headers.each do |header|
         
     | 
| 
       138 
     | 
    
         
            -
                        entry_line = format_entry(header, max_key_length, row.send(:table)[header], max_value_length)
         
     | 
| 
       139 
     | 
    
         
            -
                        config.ui.stdout.puts(entry_line)
         
     | 
| 
       140 
     | 
    
         
            -
                      end
         
     | 
| 
       141 
     | 
    
         
            -
                    end
         
     | 
| 
       142 
     | 
    
         
            -
                    config.ui.stdout.puts("+#{"-" * width}+")
         
     | 
| 
       143 
     | 
    
         
            -
                    OpenStruct.new(:rows => rows, :max_key_length => max_key_length, :max_value_length => max_value_length, :width => width)
         
     | 
| 
       144 
     | 
    
         
            -
                  else
         
     | 
| 
       145 
     | 
    
         
            -
                    OpenStruct.new(:rows => rows, :max_key_length => 0, :max_value_length => 0, :width => 0)
         
     | 
| 
       146 
     | 
    
         
            -
                  end
         
     | 
| 
      
 34 
     | 
    
         
            +
                  config.ui.logger.debug { "config=#{config.send(:table).inspect}" }
         
     | 
| 
       147 
35 
     | 
    
         
             
                end
         
     | 
| 
       148 
36 
     | 
    
         | 
| 
       149 
37 
     | 
    
         | 
| 
       150 
38 
     | 
    
         
             
              private
         
     | 
| 
       151 
39 
     | 
    
         | 
| 
       152 
     | 
    
         
            -
                 
     | 
| 
       153 
     | 
    
         
            -
                  max_lengths = OpenStruct.new
         
     | 
| 
       154 
     | 
    
         
            -
                  headers.each do |header|
         
     | 
| 
       155 
     | 
    
         
            -
                    collection = [header, rows.collect{|r| r.send(:table)[header] } ].flatten
         
     | 
| 
       156 
     | 
    
         
            -
                    maximum = collection.map(&:to_s).map(&:length).max
         
     | 
| 
       157 
     | 
    
         
            -
                    max_lengths.send(:table)[header] = maximum
         
     | 
| 
       158 
     | 
    
         
            -
                  end
         
     | 
| 
       159 
     | 
    
         
            -
             
     | 
| 
       160 
     | 
    
         
            -
                  max_lengths
         
     | 
| 
       161 
     | 
    
         
            -
                end
         
     | 
| 
       162 
     | 
    
         
            -
             
     | 
| 
       163 
     | 
    
         
            -
                def calculate_spreadsheet_width(headers, max_lengths)
         
     | 
| 
       164 
     | 
    
         
            -
                  header_lengths = ((headers.count * 3) - 3)
         
     | 
| 
       165 
     | 
    
         
            -
                  max_length = max_lengths.send(:table).values.reduce(:+)
         
     | 
| 
       166 
     | 
    
         
            -
                  (2 + max_length + header_lengths + 2)
         
     | 
| 
       167 
     | 
    
         
            -
                end
         
     | 
| 
       168 
     | 
    
         
            -
             
     | 
| 
       169 
     | 
    
         
            -
                def format_header(headers, lengths)
         
     | 
| 
       170 
     | 
    
         
            -
                  line = headers.collect do |header|
         
     | 
| 
       171 
     | 
    
         
            -
                    "-" * lengths.send(:table)[header]
         
     | 
| 
       172 
     | 
    
         
            -
                  end
         
     | 
| 
       173 
     | 
    
         
            -
             
     | 
| 
       174 
     | 
    
         
            -
                  ["+-", line.join("-+-"), "-+"].join.strip
         
     | 
| 
       175 
     | 
    
         
            -
                end
         
     | 
| 
       176 
     | 
    
         
            -
             
     | 
| 
       177 
     | 
    
         
            -
                def format_row(*args)
         
     | 
| 
       178 
     | 
    
         
            -
                  spacer = " "
         
     | 
| 
       179 
     | 
    
         
            -
                  [spacer, args, spacer].flatten.join(" | ").strip
         
     | 
| 
       180 
     | 
    
         
            -
                end
         
     | 
| 
       181 
     | 
    
         
            -
             
     | 
| 
       182 
     | 
    
         
            -
                def format_entry(key, key_length, value, value_length)
         
     | 
| 
       183 
     | 
    
         
            -
                  "|  %#{key_length}s: %-#{value_length}s  |" % [key.to_s.upcase, value.to_s]
         
     | 
| 
       184 
     | 
    
         
            -
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
                include ZTK::Report::Private
         
     | 
| 
       185 
41 
     | 
    
         | 
| 
       186 
42 
     | 
    
         
             
              end
         
     | 
| 
       187 
43 
     | 
    
         |