ohboyohboyohboy-monocle-print 1.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,132 @@
1
+ #!/usr/bin/ruby
2
+ # encoding: utf-8
3
+
4
+ module MonoclePrint
5
+ module Presentation
6
+ include MonoclePrint
7
+ ALIGNMENTS = [ :left, :right, :center ]
8
+
9
+ def self.included( klass )
10
+ super
11
+ klass.extend( ClassMethods )
12
+ end
13
+
14
+ attr_accessor :owner
15
+ protected :owner=
16
+
17
+ for attr in %w( margin padding border )
18
+ class_eval( <<-END, __FILE__, __LINE__ + 1 )
19
+ def #{ attr }( value = nil )
20
+ value and self.#{ attr } = value
21
+ @#{ attr } ||= default_#{ attr }
22
+ block_given? ? yield( @#{ attr } ) : @#{ attr }
23
+ end
24
+
25
+ def #{ attr }= value
26
+ @#{ attr } = Rectangle( value )
27
+ end
28
+ END
29
+ end
30
+
31
+ def alignment( value = nil )
32
+ value and self.alignment = value
33
+ @alignment or @owner ? @owner.alignment : :left
34
+ end
35
+
36
+ def alignment= value
37
+ ALIGNMENTS.member?( value = value.to_sym ) or
38
+ raise( ArgumentError, "unkown alignment: %p" % value )
39
+ @alignment = value
40
+ end
41
+
42
+ def style( value = nil )
43
+ value and self.style = value
44
+ @style or @owner ? @owner.style : Graphics.default
45
+ end
46
+
47
+ def style= value
48
+ @style = Style( value )
49
+ end
50
+
51
+ def render( output = @output )
52
+ if output
53
+ render_content( output )
54
+ return output
55
+ else
56
+ OutputDevice.buffer do | out |
57
+ render_content( out )
58
+ end
59
+ end
60
+ end
61
+
62
+ def to_s
63
+ OutputDevice.buffer do | out |
64
+ render_content( out )
65
+ end
66
+ end
67
+
68
+ def height
69
+ @height or calculate_height
70
+ end
71
+
72
+ def width
73
+ @width or calculate_width
74
+ end
75
+
76
+ attr_writer :max_width
77
+
78
+ def max_width
79
+ @max_width or @owner && @owner.max_width or output.width
80
+ end
81
+
82
+ def output
83
+ @output ||= ( @owner and @owner.output or OutputDevice.stdout )
84
+ end
85
+
86
+ def output=( io )
87
+ @output = io.nil? ? io : Output( io )
88
+ end
89
+
90
+ private
91
+
92
+ def initialize_view( options = nil, owner = nil )
93
+ @max_width = @width = @height = nil
94
+ @margin = @padding = @alignment = @style = nil
95
+ @output = @foreground = @background = nil
96
+
97
+ if options
98
+ val = options[ :width ] and self.width = val
99
+ val = options[ :align ] and self.alignment = val
100
+ val = options[ :style ] and self.style = val
101
+ val = options[ :padding ] and self.padding = val
102
+ val = options[ :margin ] and self.margin = val
103
+ val = options[ :output ] and self.output = val
104
+ end
105
+
106
+ @owner = owner
107
+ end
108
+
109
+ def default_margin
110
+ Rectangle.new( 0, 0, 0, 0 )
111
+ end
112
+
113
+ def default_padding
114
+ Rectangle.new( 0, 0, 0, 0 )
115
+ end
116
+
117
+ def default_border
118
+ Rectangle.new( false, false, false, false )
119
+ end
120
+
121
+ end
122
+
123
+ module Presentation::ClassMethods
124
+ def default( property, value = nil, &dynamic )
125
+ if dynamic
126
+ define_method( :"default_#{property}", &dynamic )
127
+ else
128
+ define_method( :"default_#{property}" ) { value }
129
+ end
130
+ end
131
+ end
132
+ end
@@ -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,229 @@
1
+ #!/usr/bin/ruby
2
+
3
+ require 'monocle-print/table/segments'
4
+ require 'monocle-print/table/members'
5
+ require 'monocle-print/table/column'
6
+
7
+ module MonoclePrint
8
+ class Table
9
+ include MonoclePrint
10
+ include Presentation
11
+ include Enumerable
12
+
13
+ def self.build( *args )
14
+ table = new( *args ) do | table |
15
+ block_given? and yield( table )
16
+ end
17
+ return( table.render )
18
+ end
19
+
20
+
21
+ def initialize( columns, options = {} )
22
+ initialize_view( options )
23
+
24
+ @titles = nil
25
+ @item = @head = Divider.new( self, :head )
26
+ @foot = Divider.new( self, :foot )
27
+ @columns = []
28
+ @body = []
29
+
30
+ case columns
31
+ when Fixnum
32
+ expand_columns( columns )
33
+ when Array
34
+ title_row( *columns )
35
+ end
36
+
37
+ block_given? and yield( self )
38
+ end
39
+
40
+ def render_content( out )
41
+ style = @style || out.style
42
+ lock do
43
+ width > out.width and resize( out.width )
44
+ each { | member | member.render( out, style ) }
45
+ end
46
+ end
47
+
48
+ def each
49
+ block_given? or return( enum_for( :each ) )
50
+ for item in @head
51
+ yield( item )
52
+ end
53
+ end
54
+
55
+ attr_reader :columns, :titles
56
+
57
+ def row( *members )
58
+ @item = @item.row!( *members )
59
+ end
60
+
61
+ def title_row( *members )
62
+ @titles = @item = @item.title_row!( *members )
63
+ while @titles
64
+ if TitleRow === @titles
65
+ @titles = @titles.cells.map { | c | c.to_s }
66
+ break
67
+ end
68
+ @titles = @titles.before
69
+ end
70
+ self
71
+ end
72
+
73
+ def rows( *list_of_rows )
74
+ for row in list_of_rows do row( *row ) end
75
+ self
76
+ end
77
+
78
+ def section( title, options = {} )
79
+ @item = @item.section!( title, options )
80
+ end
81
+
82
+ def divider( type = :row_divider )
83
+ @item = @item.divider!( type )
84
+ end
85
+
86
+ def fixed_columns( *column_indicies )
87
+ for column_index in column_indicies
88
+ if c = column( column_indicies )
89
+ if Array === c then c.each { | i | i.fixed = true }
90
+ else c.fixed = true
91
+ end
92
+ end
93
+ end
94
+ end
95
+
96
+ def malleable_columns( *column_indicies )
97
+ for column_index in column_indicies
98
+ if c = column( column_index )
99
+ if Array === c then c.each { | i | i.malleable = true }
100
+ else c.malleable = true
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ def column( name_or_index )
107
+ case name_or_index
108
+ when Integer, Range then @columns[ name_or_index ]
109
+ else
110
+ @columns.find do | col |
111
+ name_or_index === col.title
112
+ end
113
+ end
114
+ end
115
+
116
+ def resize( new_size )
117
+ resizable = @columns.select { | c | c.malleable? }
118
+ if resizable.empty?
119
+ warn( "cannot resize #{ self.inspect } as all columns are fixed" )
120
+ return( self )
121
+ end
122
+
123
+ lock do
124
+ difference = new_size - @width
125
+ resize_columns( difference, resizable )
126
+ end
127
+
128
+ return( self )
129
+ end
130
+
131
+
132
+
133
+ def inner_width
134
+ @inner_width or calculate_inner_width
135
+ end
136
+
137
+ def width
138
+ @width or calculate_width
139
+ end
140
+
141
+ def expand_columns(new_size)
142
+ new_size.zero? and return
143
+
144
+ until @columns.length >= new_size
145
+ @columns << Column.new( self, @columns.length )
146
+ end
147
+ end
148
+
149
+ private
150
+
151
+ def resize_columns( amount, columns )
152
+ leftover = amount
153
+ resizable_area = columns.inject( 0 ) do | sz, c |
154
+ sz + c.width
155
+ end
156
+
157
+ for column in columns
158
+ proportion = ( column.width.to_f / resizable_area )
159
+ delta = ( proportion * amount ).round
160
+ column.width += delta
161
+ leftover -= delta
162
+ end
163
+
164
+ columns.last.width += leftover
165
+ end
166
+
167
+ def expand( amount, columns )
168
+ leftover = amount
169
+ resizable_area = columns.inject( 0 ) do | sz, c |
170
+ sz + c.width
171
+ end
172
+
173
+ for column in columns
174
+ proportion = ( column.width.to_f / resizable_area )
175
+ delta = ( proportion * amount ).round
176
+ column.width += delta
177
+ leftover -= delta
178
+ end
179
+
180
+ columns.last.width += leftover
181
+ end
182
+
183
+
184
+
185
+ def lock
186
+ calculate_metrics
187
+ @item.link( @foot )
188
+ yield
189
+ ensure
190
+ @foot.unlink
191
+ clear_metrics
192
+ end
193
+
194
+ def calculate_metrics
195
+ @columns.each { | c | c.calculate_metrics }
196
+ @inner_width = calculate_inner_width
197
+ @width = calculate_width
198
+ end
199
+
200
+ def clear_metrics
201
+ @columns.each { | c | c.clear_metrics }
202
+ @inner_width = nil
203
+ @width = nil
204
+ end
205
+
206
+ def calculate_inner_width
207
+ w = @columns.inject( 0 ) { | w, c | w + c.width }
208
+ w + ( @columns.length - 1 ) * 3
209
+ end
210
+
211
+ def calculate_width
212
+ calculate_inner_width + 4
213
+ end
214
+
215
+ end
216
+
217
+ class ColumnLayout < Table
218
+ def initialize( columns, options = {} )
219
+ super( columns, options ) do
220
+ @item = @head = Blank.new( self )
221
+ @foot = Blank.new( self )
222
+ @style = Style( :blank )
223
+ block_given? and yield( self )
224
+ end
225
+ end
226
+ end
227
+
228
+ end
229
+