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.
@@ -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