monocle-print 1.0.0
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.
- checksums.yaml +15 -0
- data/.gemtest +0 -0
- data/History.txt +4 -0
- data/Manifest.txt +19 -0
- data/README.txt +40 -0
- data/Rakefile +21 -0
- data/lib/monocle-print/atomic.rb +462 -0
- data/lib/monocle-print/geometry.rb +66 -0
- data/lib/monocle-print/graphics.rb +111 -0
- data/lib/monocle-print/layout.rb +58 -0
- data/lib/monocle-print/list.rb +138 -0
- data/lib/monocle-print/output-device.rb +491 -0
- data/lib/monocle-print/presentation.rb +132 -0
- data/lib/monocle-print/progress.rb +192 -0
- data/lib/monocle-print/table/column.rb +98 -0
- data/lib/monocle-print/table/members.rb +252 -0
- data/lib/monocle-print/table/segments.rb +62 -0
- data/lib/monocle-print/table.rb +229 -0
- data/lib/monocle-print/terminal-escapes.rb +186 -0
- data/lib/monocle-print/utils.rb +23 -0
- data/lib/monocle-print.rb +70 -0
- metadata +97 -0
| @@ -0,0 +1,192 @@ | |
| 1 | 
            +
            #!/usr/bin/ruby
         | 
| 2 | 
            +
            # encoding: utf-8
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module MonoclePrint
         | 
| 5 | 
            +
            class Progress < OutputDevice
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              attr_accessor :position, :total, :bar_color
         | 
| 8 | 
            +
              attr_reader :output, :title
         | 
| 9 | 
            +
              attr_writer :width
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              def self.enumerate( collection, method = :each, *args )
         | 
| 12 | 
            +
                block_given? or return enum_for( :enumerate, collection, method, *args )
         | 
| 13 | 
            +
                #options = Hash === args.last ? args.pop : {}
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                #method = options.fetch( :method, nil )
         | 
| 16 | 
            +
                #if method.nil? and Symbol === args.first
         | 
| 17 | 
            +
                #  method, *args = args
         | 
| 18 | 
            +
                #else
         | 
| 19 | 
            +
                #  method ||= :each
         | 
| 20 | 
            +
                #end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                #size = options[ :size ]
         | 
| 23 | 
            +
                #size ||= options.fetch( :length ) do
         | 
| 24 | 
            +
                #  collection.length rescue collection.size
         | 
| 25 | 
            +
                #end
         | 
| 26 | 
            +
                size = collection.length rescue collection.size
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                enum = collection.enum_for( method, *args )
         | 
| 29 | 
            +
                run( size ) do | bar |
         | 
| 30 | 
            +
                  for item in enum
         | 
| 31 | 
            +
                    v = yield( item, bar )
         | 
| 32 | 
            +
                    bar.step
         | 
| 33 | 
            +
                    v
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
                end
         | 
| 36 | 
            +
              end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
              def self.run( total, options = {} )
         | 
| 39 | 
            +
                bar = new( total, options )
         | 
| 40 | 
            +
                yield( bar )
         | 
| 41 | 
            +
              ensure
         | 
| 42 | 
            +
                bar.clear_line
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              def initialize( total, options = {} )
         | 
| 46 | 
            +
                @total      = Utils.at_least( total.to_i, 1 )
         | 
| 47 | 
            +
                @position   = 0
         | 
| 48 | 
            +
                @title      = Line( options[ :title ].to_s )
         | 
| 49 | 
            +
                @limit      = @width = @time = @next_time = nil
         | 
| 50 | 
            +
                @bar_color  = options.fetch( :bar_color, :red )
         | 
| 51 | 
            +
                @text_color = options.fetch( :text_color, :black )
         | 
| 52 | 
            +
                @progress   = -1
         | 
| 53 | 
            +
                @draw       = true
         | 
| 54 | 
            +
                super( options.fetch( :output, $stderr ), options )
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              def stage( title, limit = @total - @position, absolute = false )
         | 
| 58 | 
            +
                limit += @position unless absolute
         | 
| 59 | 
            +
                self.title = title
         | 
| 60 | 
            +
                limit( limit ) do
         | 
| 61 | 
            +
                  return( yield( self ) )
         | 
| 62 | 
            +
                end
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
              def step( inc = 1 )
         | 
| 66 | 
            +
                limit = @limit || @total
         | 
| 67 | 
            +
                @position = Utils.at_most( @position + inc, limit )
         | 
| 68 | 
            +
                draw? and display
         | 
| 69 | 
            +
              end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
              def draw?
         | 
| 72 | 
            +
                progress = @position * 100 / @total
         | 
| 73 | 
            +
                if @progress != progress
         | 
| 74 | 
            +
                  @progress = progress
         | 
| 75 | 
            +
                  @draw = true
         | 
| 76 | 
            +
                end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                return @draw
         | 
| 79 | 
            +
              end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
              def limit( l = nil )
         | 
| 82 | 
            +
                unless l.nil?
         | 
| 83 | 
            +
                  begin
         | 
| 84 | 
            +
                    before, @limit = @limit, Utils.at_most( l.to_i, @total )
         | 
| 85 | 
            +
                    yield( self )
         | 
| 86 | 
            +
                  ensure
         | 
| 87 | 
            +
                    self.limit = before
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
                end
         | 
| 90 | 
            +
                return( @limit )
         | 
| 91 | 
            +
              end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
              def limit=( l )
         | 
| 94 | 
            +
                @limit = Utils.at_most( l.to_i, @total )
         | 
| 95 | 
            +
              end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
              def start_time
         | 
| 98 | 
            +
                @start_time ||= Time.now
         | 
| 99 | 
            +
              end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
              def duration
         | 
| 102 | 
            +
                Time.now - start_time
         | 
| 103 | 
            +
              end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
              def title= t
         | 
| 106 | 
            +
                title = Line( t.to_s )
         | 
| 107 | 
            +
                if title != @title
         | 
| 108 | 
            +
                  @title = title
         | 
| 109 | 
            +
                  @draw = true
         | 
| 110 | 
            +
                end
         | 
| 111 | 
            +
              end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
              def display
         | 
| 114 | 
            +
                return!
         | 
| 115 | 
            +
             | 
| 116 | 
            +
                hour, r  = time_remaining.divmod( 3600 )
         | 
| 117 | 
            +
                min, sec = r.divmod( 60 )
         | 
| 118 | 
            +
                sec = sec.round
         | 
| 119 | 
            +
                eta = Line( ' %02i:%02i:%02i' % [ hour, min, sec ] )
         | 
| 120 | 
            +
             | 
| 121 | 
            +
                center_width = 6
         | 
| 122 | 
            +
                right_width  = ( width - center_width ) / 2
         | 
| 123 | 
            +
                left_width   = width - center_width - right_width
         | 
| 124 | 
            +
             | 
| 125 | 
            +
                center = ( @progress.to_s << '%' ).center( 6 ) # " ___% "
         | 
| 126 | 
            +
                left   = title.align( :left, left_width ).truncate!( left_width, '...' )
         | 
| 127 | 
            +
                right  = eta.align( :right, right_width )
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                bar = left << center << right
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                color_code = ''
         | 
| 132 | 
            +
                @bar_color  and color_code << ansi_color( ?b, @bar_color )
         | 
| 133 | 
            +
                @text_color and color_code << ansi_color( ?f, @text_color )
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                unless color_code.empty?
         | 
| 136 | 
            +
                  fill_point = bar.char_byte( width * @position / @total )
         | 
| 137 | 
            +
                  bar.insert( fill_point, "\e[0m" )
         | 
| 138 | 
            +
                  bar.insert( 0, color_code )
         | 
| 139 | 
            +
                end
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                print( bar )
         | 
| 142 | 
            +
                @draw = false
         | 
| 143 | 
            +
                self
         | 
| 144 | 
            +
              end
         | 
| 145 | 
            +
             | 
| 146 | 
            +
              def title_width
         | 
| 147 | 
            +
                width * 0.3
         | 
| 148 | 
            +
              end
         | 
| 149 | 
            +
             | 
| 150 | 
            +
              def reset
         | 
| 151 | 
            +
                @position = 0
         | 
| 152 | 
            +
                @limit = nil
         | 
| 153 | 
            +
                @start_time = nil
         | 
| 154 | 
            +
              end
         | 
| 155 | 
            +
             | 
| 156 | 
            +
              def to_s
         | 
| 157 | 
            +
                title_width = ( width * 0.4 ).round
         | 
| 158 | 
            +
                title = @title.align( :center, title_width )[ 0, title_width ]
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                hour, r  = time_remaining.divmod( 3600 )
         | 
| 161 | 
            +
                min, sec = r.divmod( 60 )
         | 
| 162 | 
            +
                sec = sec.round
         | 
| 163 | 
            +
                eta = '%02i:%02i:%02i' % [ hour, min, sec ]
         | 
| 164 | 
            +
             | 
| 165 | 
            +
                fill_width = width - title_width - eta.length - 9
         | 
| 166 | 
            +
                filled = ( fill_width * @position / @total ).round
         | 
| 167 | 
            +
                title << ' |' << ( '*' * filled ).ljust( fill_width ) <<
         | 
| 168 | 
            +
                  '| ' << ( '%3i%% ' % progress ) << eta
         | 
| 169 | 
            +
              end
         | 
| 170 | 
            +
             | 
| 171 | 
            +
              def progress
         | 
| 172 | 
            +
                @position * 100 / @total
         | 
| 173 | 
            +
              end
         | 
| 174 | 
            +
             | 
| 175 | 
            +
              def time_remaining
         | 
| 176 | 
            +
                @position < 2 and return( 0 )
         | 
| 177 | 
            +
                sec_per_step = duration / @position
         | 
| 178 | 
            +
                ( @total - @position ) * sec_per_step
         | 
| 179 | 
            +
              end
         | 
| 180 | 
            +
             | 
| 181 | 
            +
              alias wipe clear_line
         | 
| 182 | 
            +
             | 
| 183 | 
            +
              def hide
         | 
| 184 | 
            +
                wipe
         | 
| 185 | 
            +
                return yield
         | 
| 186 | 
            +
              ensure
         | 
| 187 | 
            +
                display
         | 
| 188 | 
            +
              end
         | 
| 189 | 
            +
             | 
| 190 | 
            +
             | 
| 191 | 
            +
            end
         | 
| 192 | 
            +
            end
         | 
| @@ -0,0 +1,98 @@ | |
| 1 | 
            +
            #!/usr/bin/ruby
         | 
| 2 | 
            +
            # encoding: utf-8
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module MonoclePrint
         | 
| 5 | 
            +
            class Table
         | 
| 6 | 
            +
            class Column
         | 
| 7 | 
            +
              include MonoclePrint
         | 
| 8 | 
            +
             | 
| 9 | 
            +
              def initialize( table, index )
         | 
| 10 | 
            +
                @table        = table
         | 
| 11 | 
            +
                @index        = index
         | 
| 12 | 
            +
                @wrap         = false
         | 
| 13 | 
            +
                @flow         = false
         | 
| 14 | 
            +
                @alignment    = :left
         | 
| 15 | 
            +
                @fixed_width  = nil
         | 
| 16 | 
            +
                @cached_width = nil
         | 
| 17 | 
            +
              end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
              attr_reader   :table, :index
         | 
| 20 | 
            +
              attr_accessor :alignment
         | 
| 21 | 
            +
             | 
| 22 | 
            +
              for m in %w( wrap flow )
         | 
| 23 | 
            +
                attr_accessor( m )
         | 
| 24 | 
            +
                alias_method( "#{m}?", m )
         | 
| 25 | 
            +
                undef_method( m )
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
              def malleable?
         | 
| 29 | 
            +
                @wrap and @flow
         | 
| 30 | 
            +
              end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              def malleable=( bool )
         | 
| 33 | 
            +
                @wrap = @flow = bool
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
              def fixed?
         | 
| 37 | 
            +
                not malleable?
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              def fixed=( bool )
         | 
| 41 | 
            +
                self.malleable = !bool
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
              def title
         | 
| 45 | 
            +
                @table.titles[ @index ]
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              def cells
         | 
| 49 | 
            +
                @table.grep( Row ) { | row | row[ @index ] || Line( '' ) }
         | 
| 50 | 
            +
              end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              def previous_column
         | 
| 53 | 
            +
                @index.zero? ? nil : @table.columns[ @index - 1 ]
         | 
| 54 | 
            +
              end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
              def next_column
         | 
| 57 | 
            +
                @table.columns[ @index + 1 ]
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
              def first?
         | 
| 61 | 
            +
                @index.zero?
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
              def last?
         | 
| 65 | 
            +
                @index == (table.columns.length - 1)
         | 
| 66 | 
            +
              end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
              def prepare( cell_text )
         | 
| 69 | 
            +
                cell_text = cell_text ? cell_text.dup : Text( ' ' )
         | 
| 70 | 
            +
                @flow and cell_text.reflow!( false )
         | 
| 71 | 
            +
                @wrap and cell_text = cell_text.wrap( width - 1 )
         | 
| 72 | 
            +
                cell_text.align!( @alignment, width )
         | 
| 73 | 
            +
              end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
              def width=( w )
         | 
| 76 | 
            +
                @fixed_width = Utils.at_least( w.to_i, 1 )
         | 
| 77 | 
            +
              end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
              def width
         | 
| 80 | 
            +
                @fixed_width or @cached_width or calculate_width
         | 
| 81 | 
            +
              end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
              def calculate_metrics
         | 
| 84 | 
            +
                @cached_width = @fixed_width || calculate_width
         | 
| 85 | 
            +
              end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              def clear_metrics
         | 
| 88 | 
            +
                @cached_width = nil
         | 
| 89 | 
            +
              end
         | 
| 90 | 
            +
             | 
| 91 | 
            +
            protected
         | 
| 92 | 
            +
             | 
| 93 | 
            +
              def calculate_width
         | 
| 94 | 
            +
                @table.grep( Row ) { |r| c = r[ @index ] and c.width or 0 }.max || 0
         | 
| 95 | 
            +
              end
         | 
| 96 | 
            +
            end
         | 
| 97 | 
            +
            end
         | 
| 98 | 
            +
            end
         | 
| @@ -0,0 +1,252 @@ | |
| 1 | 
            +
            #!/usr/bin/ruby
         | 
| 2 | 
            +
            # encoding: utf-8
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module MonoclePrint
         | 
| 5 | 
            +
            class Table
         | 
| 6 | 
            +
            class Member
         | 
| 7 | 
            +
              include MonoclePrint
         | 
| 8 | 
            +
              include Enumerable
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              class << self
         | 
| 11 | 
            +
                attr_reader :member_name
         | 
| 12 | 
            +
                def define( member_name, sup = self, &body )
         | 
| 13 | 
            +
                  klass =
         | 
| 14 | 
            +
                    Class.new( sup ) do
         | 
| 15 | 
            +
                      @member_name = member_name
         | 
| 16 | 
            +
                      class_eval( &body )
         | 
| 17 | 
            +
                    end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                  define_method( "#{ member_name }!" ) do |*args|
         | 
| 20 | 
            +
                    klass.new( @table, *args ) { |m| link( m ) }.tail
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
                  return( klass )
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              attr_reader :table
         | 
| 27 | 
            +
              attr_accessor :before, :after
         | 
| 28 | 
            +
              protected :before=, :after=
         | 
| 29 | 
            +
             | 
| 30 | 
            +
              def initialize( table, *args )
         | 
| 31 | 
            +
                @table    = table
         | 
| 32 | 
            +
                @before   = nil
         | 
| 33 | 
            +
                @after    = nil
         | 
| 34 | 
            +
                @disabled = false
         | 
| 35 | 
            +
                block_given? and yield( self )
         | 
| 36 | 
            +
                initialize!( *args )
         | 
| 37 | 
            +
              end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
              def initialize!( * )
         | 
| 40 | 
            +
                # do nothing
         | 
| 41 | 
            +
              end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              def inspect( *args )
         | 
| 44 | 
            +
                content = args.map! { |a| a.inspect }.join(', ')
         | 
| 45 | 
            +
                "#{self.class.member_name}(#{content})"
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
              def each
         | 
| 49 | 
            +
                block_given? or return( enum_for( __method__ ) )
         | 
| 50 | 
            +
                node = self
         | 
| 51 | 
            +
                begin
         | 
| 52 | 
            +
                  yield( node )
         | 
| 53 | 
            +
                  node = node.after
         | 
| 54 | 
            +
                end while( node )
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              def disable
         | 
| 58 | 
            +
                @disabled = true
         | 
| 59 | 
            +
              end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
              def enable
         | 
| 62 | 
            +
                @disabled = false
         | 
| 63 | 
            +
              end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
              def enabled?
         | 
| 66 | 
            +
                not disabled?
         | 
| 67 | 
            +
              end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
              def disabled?
         | 
| 70 | 
            +
                @disabled
         | 
| 71 | 
            +
              end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
              def first?
         | 
| 74 | 
            +
                @before.nil?
         | 
| 75 | 
            +
              end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
              def last?
         | 
| 78 | 
            +
                @after.nil?
         | 
| 79 | 
            +
              end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
              def link( item )
         | 
| 82 | 
            +
                after, @after, item.before = @after, item, self
         | 
| 83 | 
            +
                after ? item.link( after ) : item
         | 
| 84 | 
            +
              end
         | 
| 85 | 
            +
             | 
| 86 | 
            +
              def unlink
         | 
| 87 | 
            +
                @before and @before.after = nil
         | 
| 88 | 
            +
                @before = nil
         | 
| 89 | 
            +
                return( self )
         | 
| 90 | 
            +
              end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
              def render( out, style )
         | 
| 93 | 
            +
                render!( out, style ) unless disabled?
         | 
| 94 | 
            +
              end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
              def columns
         | 
| 97 | 
            +
                table.columns
         | 
| 98 | 
            +
              end
         | 
| 99 | 
            +
             | 
| 100 | 
            +
              def tail
         | 
| 101 | 
            +
                @after ? @after.tail : self
         | 
| 102 | 
            +
              end
         | 
| 103 | 
            +
            end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
            Blank =
         | 
| 106 | 
            +
              Member.define( 'blank' ) do
         | 
| 107 | 
            +
                def render!( * )
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
              end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
            Row =
         | 
| 112 | 
            +
              Member.define( 'row' ) do
         | 
| 113 | 
            +
                def initialize!( *content )
         | 
| 114 | 
            +
                  @cells = [ content ].flatten!.map! { | c | Text( c ) }
         | 
| 115 | 
            +
                  @table.expand_columns( @cells.length )
         | 
| 116 | 
            +
                end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                def []( index )
         | 
| 119 | 
            +
                  @cells[ index ]
         | 
| 120 | 
            +
                end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                def []=(index, value)
         | 
| 123 | 
            +
                  @cells[ index ] = value
         | 
| 124 | 
            +
                end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                def cells
         | 
| 127 | 
            +
                  @table.columns.zip( @cells ).
         | 
| 128 | 
            +
                    map! { | col, cell | col.prepare( cell ) }
         | 
| 129 | 
            +
                end
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                def height
         | 
| 132 | 
            +
                  cells.map! { | c | c.height }.max
         | 
| 133 | 
            +
                end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                def render!( out, style )
         | 
| 136 | 
            +
                  cells =
         | 
| 137 | 
            +
                    @table.columns.zip( @cells ).map! do | col, cell |
         | 
| 138 | 
            +
                      col.prepare( cell )
         | 
| 139 | 
            +
                    end
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                  height = cells.map { | col, cell | cell ? cell.height : 1 }.max
         | 
| 142 | 
            +
             | 
| 143 | 
            +
                  joint = style.format( ' <v> ' )
         | 
| 144 | 
            +
                  left  = style.format( '<v> ' )
         | 
| 145 | 
            +
                  right = style.format( ' <v>' )
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                  result = cells.inject { | result, cell | result.juxtapose( cell, joint ) }
         | 
| 148 | 
            +
                  result.each do | line |
         | 
| 149 | 
            +
                    out.put!( left + line + right )
         | 
| 150 | 
            +
                  end
         | 
| 151 | 
            +
                  return( out )
         | 
| 152 | 
            +
                end
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                def inspect
         | 
| 155 | 
            +
                  super( *cells )
         | 
| 156 | 
            +
                end
         | 
| 157 | 
            +
             | 
| 158 | 
            +
              private
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                def prepare
         | 
| 161 | 
            +
                  height = cells.map { | c | c.height }.max
         | 
| 162 | 
            +
                  if height > 1
         | 
| 163 | 
            +
                    cell_lines.zip( @table.columns ) do | lines, col |
         | 
| 164 | 
            +
                      if lines.length < height
         | 
| 165 | 
            +
                        blank = col.fill_text( ' ' )
         | 
| 166 | 
            +
                        lines.fill( blank, lines.length, height - lines.length )
         | 
| 167 | 
            +
                      end
         | 
| 168 | 
            +
                    end
         | 
| 169 | 
            +
                  end
         | 
| 170 | 
            +
                  return( cell_lines )
         | 
| 171 | 
            +
                end
         | 
| 172 | 
            +
             | 
| 173 | 
            +
                def pad
         | 
| 174 | 
            +
                  n = @table.columns.length
         | 
| 175 | 
            +
                  m = @cells.length
         | 
| 176 | 
            +
                  @cells.fill( Text(' '), m, n - m ) if n > m
         | 
| 177 | 
            +
                end
         | 
| 178 | 
            +
              end
         | 
| 179 | 
            +
             | 
| 180 | 
            +
            TitleRow =
         | 
| 181 | 
            +
              Member.define( 'title_row', Row ) do
         | 
| 182 | 
            +
                def initialize!( *content )
         | 
| 183 | 
            +
                  super
         | 
| 184 | 
            +
                  divider!( :title )
         | 
| 185 | 
            +
                end
         | 
| 186 | 
            +
              end
         | 
| 187 | 
            +
             | 
| 188 | 
            +
            Divider =
         | 
| 189 | 
            +
              Member.define( 'divider' ) do
         | 
| 190 | 
            +
                attr_accessor :type
         | 
| 191 | 
            +
             | 
| 192 | 
            +
                def initialize!( type )
         | 
| 193 | 
            +
                  @type = type.to_sym
         | 
| 194 | 
            +
                end
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                def render( out, style )
         | 
| 197 | 
            +
                  super( out, style ) unless @after.is_a?( Divider )
         | 
| 198 | 
            +
                end
         | 
| 199 | 
            +
             | 
| 200 | 
            +
                def inspect( *args )
         | 
| 201 | 
            +
                  super( @type, *args )
         | 
| 202 | 
            +
                end
         | 
| 203 | 
            +
             | 
| 204 | 
            +
                def render!( out, style )
         | 
| 205 | 
            +
                  fills = @table.columns.map { | c | "<h:#{ c.width + 2 }>" }
         | 
| 206 | 
            +
                  template =
         | 
| 207 | 
            +
                    case @type
         | 
| 208 | 
            +
                    when :row, :title
         | 
| 209 | 
            +
                      '<nse>' << fills.join( '<hv>' ) << '<nsw>'
         | 
| 210 | 
            +
                    when :section_open
         | 
| 211 | 
            +
                      '<nse>' << fills.join( '<hs>' ) << '<nsw>'
         | 
| 212 | 
            +
                    when :section_close
         | 
| 213 | 
            +
                      '<nse>' << fills.join( '<hn>' ) << '<nsw>'
         | 
| 214 | 
            +
                    when :head
         | 
| 215 | 
            +
                      '<se>' << fills.join( '<hs>' ) << '<sw>'
         | 
| 216 | 
            +
                    when :foot
         | 
| 217 | 
            +
                      '<ne>' << fills.join( '<hn>' ) << '<nw>'
         | 
| 218 | 
            +
                    end
         | 
| 219 | 
            +
                  out.puts( style.format( template ) )
         | 
| 220 | 
            +
                end
         | 
| 221 | 
            +
              end
         | 
| 222 | 
            +
             | 
| 223 | 
            +
            SectionTitle =
         | 
| 224 | 
            +
              Member.define( 'section' ) do
         | 
| 225 | 
            +
                attr_accessor :title, :alignment
         | 
| 226 | 
            +
             | 
| 227 | 
            +
                def initialize!( title, options = {} )
         | 
| 228 | 
            +
                  @title = Text( title )
         | 
| 229 | 
            +
                  @alignment = options.fetch( :align, :left )
         | 
| 230 | 
            +
                  @before.divider!( :section_close )
         | 
| 231 | 
            +
                  divider!( :section_open )
         | 
| 232 | 
            +
                end
         | 
| 233 | 
            +
             | 
| 234 | 
            +
                def inspect
         | 
| 235 | 
            +
                  super( @title, @alignment )
         | 
| 236 | 
            +
                end
         | 
| 237 | 
            +
             | 
| 238 | 
            +
                def render!( out, style )
         | 
| 239 | 
            +
                  w     = @table.inner_width
         | 
| 240 | 
            +
                  title = @title.width > w ? @title.wrap( w ) : @title
         | 
| 241 | 
            +
                  left  = style.format( '<v> ' )
         | 
| 242 | 
            +
                  right = style.format( ' <v>' )
         | 
| 243 | 
            +
             | 
| 244 | 
            +
                  for line in title.align( @alignment, w )
         | 
| 245 | 
            +
                    out.puts(  left + line + right )
         | 
| 246 | 
            +
                  end
         | 
| 247 | 
            +
                end
         | 
| 248 | 
            +
             | 
| 249 | 
            +
              end
         | 
| 250 | 
            +
             | 
| 251 | 
            +
            end
         | 
| 252 | 
            +
            end
         | 
| @@ -0,0 +1,62 @@ | |
| 1 | 
            +
            #!/usr/bin/ruby
         | 
| 2 | 
            +
            # encoding: utf-8
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            module MonoclePrint
         | 
| 5 | 
            +
            class Table
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            Segments = Struct.new(
         | 
| 8 | 
            +
              :head, :title_row, :title_divider, :row, :row_divider,
         | 
| 9 | 
            +
              :section, :section_close, :section_open, :foot
         | 
| 10 | 
            +
            )
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            class Segments
         | 
| 13 | 
            +
              include MonoclePrint
         | 
| 14 | 
            +
              
         | 
| 15 | 
            +
              def self.default_filling( style )
         | 
| 16 | 
            +
                fill = style.new.dup
         | 
| 17 | 
            +
                new( fill, nil, fill, nil, fill, nil, fill, fill, fill )
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
              
         | 
| 20 | 
            +
              def self.default_joints( style )
         | 
| 21 | 
            +
                head = style.format( "<h><hd><h>" )
         | 
| 22 | 
            +
                row  = style.format( " <v> " )
         | 
| 23 | 
            +
                div  = style.format( "<h><hv><h>" )
         | 
| 24 | 
            +
                foot = style.format( "<h><hu><h>" )
         | 
| 25 | 
            +
                
         | 
| 26 | 
            +
                new( head, row, div, row, div, nil, foot, head, foot )
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
              
         | 
| 29 | 
            +
              def self.default_left_edge( style )
         | 
| 30 | 
            +
                head = style.format( "<dr><h>" )
         | 
| 31 | 
            +
                row  = style.format( "<v> " )
         | 
| 32 | 
            +
                div  = style.format( "<vr><h>" )
         | 
| 33 | 
            +
                foot = style.format( "<ur><h>" )
         | 
| 34 | 
            +
                new( head, row, div, row, div, row, div, div, foot )
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
              
         | 
| 37 | 
            +
              def self.default_right_edge( style )
         | 
| 38 | 
            +
                head = style.format( "<h><dl>" )
         | 
| 39 | 
            +
                row  = style.format( " <v>" )
         | 
| 40 | 
            +
                div  = style.format( "<h><vl>" )
         | 
| 41 | 
            +
                foot = style.format( "<h><ul>" )
         | 
| 42 | 
            +
                new( head, row, div, row, div, row, div, div, foot )
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
              
         | 
| 45 | 
            +
              def mask( inclusion_settings )
         | 
| 46 | 
            +
                masked = self.class.new
         | 
| 47 | 
            +
                each_pair do | name, text |
         | 
| 48 | 
            +
                  if text and inclusion_settings[ name ]
         | 
| 49 | 
            +
                    masked[ name ] = text
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
                return( masked )
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
              
         | 
| 55 | 
            +
              def width( inclusion_mask = nil )
         | 
| 56 | 
            +
                inclusion_mask and return( self.mask( inclusion_mask ).width )
         | 
| 57 | 
            +
                return( map { |text| text ? text.width : 0 }.max )
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
            end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
            end
         | 
| 62 | 
            +
            end
         |