monocle-print 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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