hirb 0.2.9 → 0.2.10
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/CHANGELOG.rdoc +13 -0
- data/LICENSE.txt +1 -1
- data/README.rdoc +1 -1
- data/VERSION.yml +3 -2
- data/lib/hirb.rb +22 -9
- data/lib/hirb/formatter.rb +1 -5
- data/lib/hirb/helpers/active_record_table.rb +1 -1
- data/lib/hirb/helpers/object_table.rb +2 -3
- data/lib/hirb/helpers/table.rb +104 -90
- data/lib/hirb/helpers/table/filters.rb +10 -0
- data/lib/hirb/helpers/table/resizer.rb +82 -0
- data/lib/hirb/helpers/vertical_table.rb +12 -6
- data/lib/hirb/menu.rb +204 -36
- data/lib/hirb/util.rb +11 -2
- data/lib/hirb/view.rb +29 -10
- data/test/auto_table_test.rb +8 -23
- data/test/formatter_test.rb +2 -2
- data/test/hirb_test.rb +13 -4
- data/test/menu_test.rb +134 -5
- data/test/object_table_test.rb +23 -4
- data/test/pager_test.rb +1 -1
- data/test/resizer_test.rb +61 -0
- data/test/table_test.rb +182 -75
- data/test/util_test.rb +5 -0
- data/test/view_test.rb +36 -4
- metadata +6 -2
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,16 @@
|
|
1
|
+
== 0.2.10
|
2
|
+
* Added multiple options to Menu, most importantly :two_d and :action.
|
3
|
+
* Improved table resizing algorithm.
|
4
|
+
* Added merging of configs for multiple Hirb.enable calls.
|
5
|
+
* Added :max_fields, :hide_empty, :delete_callbacks, :resize, :header_filter
|
6
|
+
and :return_rows options to Table.
|
7
|
+
* Added escaping for \t and \r in Table.
|
8
|
+
* Renamed Table's :no_newlines option to :escape_special_chars.
|
9
|
+
* Removed Table's :field_lengths option.
|
10
|
+
* Removed Menu's :validate_one option.
|
11
|
+
* Bug fix for table header of a basic array.
|
12
|
+
* Deprecating Hirb.config_file + View.enable in next release.
|
13
|
+
|
1
14
|
== 0.2.9
|
2
15
|
* Added newline filtering and :no_newlines option for table helper.
|
3
16
|
* Added default filters for hashes that have hash values.
|
data/LICENSE.txt
CHANGED
data/README.rdoc
CHANGED
@@ -151,7 +151,7 @@ app's needs}[http://github.com/cldwalker/tag-tree].
|
|
151
151
|
|
152
152
|
== Credits
|
153
153
|
* Chrononaut for vertical table helper.
|
154
|
-
* crafterm, spastorino and joshua for bug fixes.
|
154
|
+
* crafterm, spastorino, xaviershay and joshua for bug fixes.
|
155
155
|
|
156
156
|
== Bugs/Issues
|
157
157
|
Please report them {on github}[http://github.com/cldwalker/hirb/issues].
|
data/VERSION.yml
CHANGED
data/lib/hirb.rb
CHANGED
@@ -17,8 +17,8 @@ require 'hirb/menu'
|
|
17
17
|
|
18
18
|
# Most of Hirb's functionality currently resides in Hirb::View.
|
19
19
|
# For an in-depth tutorial on creating and configuring views see Hirb::Formatter.
|
20
|
-
# Hirb
|
21
|
-
#
|
20
|
+
# Hirb can have multiple config files defined by config_files(). These config files
|
21
|
+
# have the following top level keys:
|
22
22
|
# [:output] This hash is used by the formatter object. See Hirb::Formatter.config for its format.
|
23
23
|
# [:width] Width of the terminal/console. Defaults to DEFAULT_WIDTH or possibly autodetected when Hirb is enabled.
|
24
24
|
# [:height] Height of the terminal/console. Defaults to DEFAULT_HEIGHT or possibly autodetected when Hirb is enabled.
|
@@ -30,6 +30,8 @@ require 'hirb/menu'
|
|
30
30
|
|
31
31
|
module Hirb
|
32
32
|
class <<self
|
33
|
+
attr_accessor :config_files, :config
|
34
|
+
|
33
35
|
# Enables view functionality. See Hirb::View.enable for details.
|
34
36
|
def enable(options={}, &block)
|
35
37
|
View.enable(options, &block)
|
@@ -39,15 +41,21 @@ module Hirb
|
|
39
41
|
def disable
|
40
42
|
View.disable
|
41
43
|
end
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
44
|
+
|
45
|
+
# Array of config files which are merged sequentially to produce config.
|
46
|
+
# Defaults to config/hirb.yml and ~/.hirb_yml
|
47
|
+
def config_files
|
48
|
+
@config_files ||= default_config_files
|
46
49
|
end
|
47
50
|
|
48
51
|
#:stopdoc:
|
49
|
-
def config_file
|
50
|
-
|
52
|
+
def config_file
|
53
|
+
puts "Hirb.config_file is *deprecated*. Use Hirb.config_files"
|
54
|
+
end
|
55
|
+
|
56
|
+
def default_config_files
|
57
|
+
[File.join(Util.find_home, ".hirb.yml")] +
|
58
|
+
(File.exists?('config/hirb.yml') ? ['config/hirb.yml'] : [])
|
51
59
|
end
|
52
60
|
|
53
61
|
def read_config_file(file=config_file)
|
@@ -55,7 +63,12 @@ module Hirb
|
|
55
63
|
end
|
56
64
|
|
57
65
|
def config(reload=false)
|
58
|
-
|
66
|
+
if (@config.nil? || reload)
|
67
|
+
@config = config_files.inject({}) {|acc,e|
|
68
|
+
Util.recursive_hash_merge(acc,read_config_file(e))
|
69
|
+
}
|
70
|
+
end
|
71
|
+
@config
|
59
72
|
end
|
60
73
|
#:startdoc:
|
61
74
|
end
|
data/lib/hirb/formatter.rb
CHANGED
@@ -158,11 +158,7 @@ module Hirb
|
|
158
158
|
end
|
159
159
|
|
160
160
|
def determine_output_class(output)
|
161
|
-
|
162
|
-
output[0].class
|
163
|
-
else
|
164
|
-
output.class
|
165
|
-
end
|
161
|
+
output.is_a?(Array) ? output[0].class : output.class
|
166
162
|
end
|
167
163
|
|
168
164
|
def call_output_method(output_method, output)
|
@@ -5,7 +5,7 @@ class Hirb::Helpers::ActiveRecordTable < Hirb::Helpers::ObjectTable
|
|
5
5
|
# Options:
|
6
6
|
# :fields- Can be any attribute, column or not. If not given, this defaults to the database table's columns.
|
7
7
|
def self.render(rows, options={})
|
8
|
-
rows =
|
8
|
+
rows = Array(rows)
|
9
9
|
options[:fields] ||=
|
10
10
|
begin
|
11
11
|
fields = rows.first.class.column_names
|
@@ -5,9 +5,8 @@ class Hirb::Helpers::ObjectTable < Hirb::Helpers::Table
|
|
5
5
|
# :fields- Methods of the object to represent as columns. Defaults to [:to_s].
|
6
6
|
def self.render(rows, options ={})
|
7
7
|
options[:fields] ||= [:to_s]
|
8
|
-
options[:headers]
|
9
|
-
|
10
|
-
item_hashes = options[:fields].empty? ? [] : rows.inject([]) {|t,item|
|
8
|
+
options[:headers] ||= {:to_s=>'value'} if options[:fields] == [:to_s]
|
9
|
+
item_hashes = options[:fields].empty? ? [] : Array(rows).inject([]) {|t,item|
|
11
10
|
t << options[:fields].inject({}) {|h,f| h[f] = item.send(f); h}
|
12
11
|
}
|
13
12
|
super(item_hashes, options)
|
data/lib/hirb/helpers/table.rb
CHANGED
@@ -1,3 +1,6 @@
|
|
1
|
+
require 'hirb/helpers/table/filters'
|
2
|
+
require 'hirb/helpers/table/resizer'
|
3
|
+
|
1
4
|
module Hirb
|
2
5
|
# Base Table class from which other table classes inherit.
|
3
6
|
# By default, a table is constrained to a default width but this can be adjusted
|
@@ -56,61 +59,78 @@ module Hirb
|
|
56
59
|
# derived from http://gist.github.com/72234
|
57
60
|
class Helpers::Table
|
58
61
|
BORDER_LENGTH = 3 # " | " and "-+-" are the borders
|
62
|
+
MIN_FIELD_LENGTH = 3
|
59
63
|
class TooManyFieldsForWidthError < StandardError; end
|
60
64
|
|
61
65
|
class << self
|
62
66
|
|
63
67
|
# Main method which returns a formatted table.
|
64
68
|
# ==== Options:
|
65
|
-
# [
|
66
|
-
# [
|
67
|
-
#
|
68
|
-
# [
|
69
|
-
#
|
70
|
-
#
|
71
|
-
# [
|
72
|
-
#
|
73
|
-
# [
|
74
|
-
#
|
75
|
-
#
|
76
|
-
# [
|
77
|
-
#
|
78
|
-
# [
|
79
|
-
#
|
80
|
-
# [
|
81
|
-
# [
|
82
|
-
#
|
69
|
+
# [*:fields*] An array which overrides the default fields and can be used to indicate field order.
|
70
|
+
# [*:headers*] A hash of fields and their header names. Fields that aren't specified here default to their name.
|
71
|
+
# This option can also be an array but only for array rows.
|
72
|
+
# [*:max_fields*] A hash of fields and their maximum allowed lengths. Maximum length can also be a percentage of the total width
|
73
|
+
# (decimal less than one). When a field exceeds it's maximum then it's
|
74
|
+
# truncated and has a ... appended to it. Fields that aren't specified have no maximum.
|
75
|
+
# [*:max_width*] The maximum allowed width of all fields put together including field borders. Only valid when :resize is true.
|
76
|
+
# Default is Hirb::View.width.
|
77
|
+
# [*:resize*] Resizes table to display all columns in allowed :max_width. Default is true. Setting this false will display the full
|
78
|
+
# length of each field.
|
79
|
+
# [*:number*] When set to true, numbers rows by adding a :hirb_number column as the first column. Default is false.
|
80
|
+
# [*:change_fields*] A hash to change old field names to new field names. This can also be an array of new names but only for array rows.
|
81
|
+
# This is useful when wanting to change auto-generated keys to more user-friendly names i.e. for array rows.
|
82
|
+
# [*:filters*] A hash of fields and their filters, applied to every row in a field. A filter can be a proc, an instance method
|
83
|
+
# applied to the field value or a Filters method. Also see the filter_classes attribute below.
|
84
|
+
# [*:header_filter*] A filter, like one in :filters, that is applied to all headers after the :headers option.
|
85
|
+
# [*:filter_any*] When set to true, any cell defaults to being filtered by its class in :filter_classes.
|
86
|
+
# Default Hirb::Helpers::Table.filter_any().
|
87
|
+
# [*:filter_classes*] Hash which maps classes to filters. Default is Hirb::Helpers::Table.filter_classes().
|
88
|
+
# [*:vertical*] When set to true, renders a vertical table using Hirb::Helpers::VerticalTable. Default is false.
|
89
|
+
# [*:all_fields*] When set to true, renders fields in all rows. Valid only in rows that are hashes. Default is false.
|
90
|
+
# [*:description*] When set to true, renders row count description at bottom. Default is true.
|
91
|
+
# [*:escape_special_chars*] When set to true, escapes special characters \n,\t,\r so they don't disrupt tables. Default is false for
|
92
|
+
# vertical tables and true for anything else.
|
93
|
+
# [*:return_rows*] When set to true, returns rows that have been initialized but not rendered. Default is false.
|
83
94
|
# Examples:
|
84
95
|
# Hirb::Helpers::Table.render [[1,2], [2,3]]
|
85
|
-
# Hirb::Helpers::Table.render [[1,2], [2,3]], :
|
86
|
-
# Hirb::Helpers::Table.render [['a',1], ['b',2]], :change_fields=>%w{letters numbers}
|
96
|
+
# Hirb::Helpers::Table.render [[1,2], [2,3]], :max_fields=>{0=>10}, :header_filter=>:capitalize
|
97
|
+
# Hirb::Helpers::Table.render [['a',1], ['b',2]], :change_fields=>%w{letters numbers}, :max_fields=>{'numbers'=>0.4}
|
87
98
|
# Hirb::Helpers::Table.render [{:age=>10, :weight=>100}, {:age=>80, :weight=>500}]
|
88
99
|
# Hirb::Helpers::Table.render [{:age=>10, :weight=>100}, {:age=>80, :weight=>500}], :headers=>{:weight=>"Weight(lbs)"}
|
89
100
|
# Hirb::Helpers::Table.render [{:age=>10, :weight=>100}, {:age=>80, :weight=>500}], :filters=>{:age=>[:to_f]}
|
90
101
|
def render(rows, options={})
|
91
|
-
options[:vertical] ? Helpers::VerticalTable.render(rows, options) :
|
102
|
+
options[:vertical] ? Helpers::VerticalTable.render(rows, options) :
|
103
|
+
options[:return_rows] ? new(rows, options).instance_variable_get("@rows") : new(rows, options).render
|
92
104
|
rescue TooManyFieldsForWidthError
|
93
105
|
$stderr.puts "", "** Error: Too many fields for the current width. Configure your width " +
|
94
106
|
"and/or fields to avoid this error. Defaulting to a vertical table. **"
|
95
107
|
Helpers::VerticalTable.render(rows, options)
|
96
108
|
end
|
109
|
+
|
110
|
+
# A hash which maps a cell value's class to a filter. This serves to set a default filter per field if all of its
|
111
|
+
# values are a class in this hash. By default, Array values are comma joined and Hashes are inspected.
|
112
|
+
# See the :filter_any option to apply this filter per value.
|
113
|
+
attr_accessor :filter_classes
|
114
|
+
# Boolean which sets the default for :filter_any option.
|
115
|
+
attr_accessor :filter_any
|
116
|
+
# Holds last table object created
|
117
|
+
attr_accessor :last_table
|
97
118
|
end
|
98
|
-
|
119
|
+
self.filter_classes = { Array=>:comma_join, Hash=>:inspect }
|
120
|
+
|
99
121
|
#:stopdoc:
|
122
|
+
attr_accessor :width, :max_fields, :field_lengths, :fields
|
100
123
|
def initialize(rows, options={})
|
101
|
-
@options = {:description=>true, :filters=>{}, :change_fields=>{}, :
|
102
|
-
|
124
|
+
@options = {:description=>true, :filters=>{}, :change_fields=>{}, :escape_special_chars=>true,
|
125
|
+
:filter_any=>Helpers::Table.filter_any, :resize=>true}.merge(options)
|
103
126
|
@fields = set_fields(rows)
|
104
|
-
@rows =
|
105
|
-
@headers =
|
106
|
-
if @options.has_key?(:headers)
|
107
|
-
@headers = @options[:headers].is_a?(Hash) ? @headers.merge(@options[:headers]) :
|
108
|
-
(@options[:headers].is_a?(Array) ? array_to_indices_hash(@options[:headers]) : @options[:headers])
|
109
|
-
end
|
127
|
+
@rows = set_rows(rows)
|
128
|
+
@headers = set_headers
|
110
129
|
if @options[:number]
|
111
130
|
@headers[:hirb_number] = "number"
|
112
131
|
@fields.unshift :hirb_number
|
113
132
|
end
|
133
|
+
Helpers::Table.last_table = self
|
114
134
|
end
|
115
135
|
|
116
136
|
def set_fields(rows)
|
@@ -124,13 +144,14 @@ module Hirb
|
|
124
144
|
rows[0].is_a?(Array) ? (0..rows[0].length - 1).to_a : []
|
125
145
|
end
|
126
146
|
end
|
147
|
+
@options[:change_fields] = array_to_indices_hash(@options[:change_fields]) if @options[:change_fields].is_a?(Array)
|
127
148
|
@options[:change_fields].each do |oldf, newf|
|
128
149
|
(index = fields.index(oldf)) ? fields[index] = newf : fields << newf
|
129
150
|
end
|
130
151
|
fields
|
131
152
|
end
|
132
153
|
|
133
|
-
def
|
154
|
+
def set_rows(rows)
|
134
155
|
rows = Array(rows)
|
135
156
|
if rows[0].is_a?(Array)
|
136
157
|
rows = rows.inject([]) {|new_rows, row|
|
@@ -142,13 +163,28 @@ module Hirb
|
|
142
163
|
end
|
143
164
|
rows = filter_values(rows)
|
144
165
|
rows.each_with_index {|e,i| e[:hirb_number] = (i + 1).to_s} if @options[:number]
|
145
|
-
|
166
|
+
deleted_callbacks = Array(@options[:delete_callbacks]).map {|e| "#{e}_callback" }
|
167
|
+
(methods.grep(/_callback$/) - deleted_callbacks).sort.each do |meth|
|
146
168
|
rows = send(meth, rows, @options.dup)
|
147
169
|
end
|
148
170
|
validate_values(rows)
|
149
171
|
rows
|
150
172
|
end
|
151
|
-
|
173
|
+
|
174
|
+
def set_headers
|
175
|
+
headers = @fields.inject({}) {|h,e| h[e] = e.to_s; h}
|
176
|
+
if @options.has_key?(:headers)
|
177
|
+
headers = @options[:headers].is_a?(Hash) ? headers.merge(@options[:headers]) :
|
178
|
+
(@options[:headers].is_a?(Array) ? array_to_indices_hash(@options[:headers]) : @options[:headers])
|
179
|
+
end
|
180
|
+
if @options[:header_filter]
|
181
|
+
headers.each {|k,v|
|
182
|
+
headers[k] = call_filter(@options[:header_filter], v)
|
183
|
+
}
|
184
|
+
end
|
185
|
+
headers
|
186
|
+
end
|
187
|
+
|
152
188
|
def render
|
153
189
|
body = []
|
154
190
|
unless @rows.length == 0
|
@@ -203,57 +239,30 @@ module Hirb
|
|
203
239
|
|
204
240
|
def setup_field_lengths
|
205
241
|
@field_lengths = default_field_lengths
|
206
|
-
if @options[:
|
207
|
-
@
|
242
|
+
if @options[:resize]
|
243
|
+
raise TooManyFieldsForWidthError if @fields.size > self.actual_width.to_f / MIN_FIELD_LENGTH
|
244
|
+
Resizer.resize!(self)
|
208
245
|
else
|
209
|
-
|
210
|
-
restrict_field_lengths(@field_lengths, table_max_width) if table_max_width
|
246
|
+
enforce_field_constraints
|
211
247
|
end
|
212
248
|
end
|
213
|
-
|
214
|
-
def
|
215
|
-
|
216
|
-
original_field_lengths = field_lengths.dup
|
217
|
-
@min_field_length = BORDER_LENGTH
|
218
|
-
adjust_long_fields(field_lengths, max_width)
|
219
|
-
rescue TooManyFieldsForWidthError
|
220
|
-
raise
|
221
|
-
rescue
|
222
|
-
default_restrict_field_lengths(field_lengths, original_field_lengths, max_width)
|
249
|
+
|
250
|
+
def enforce_field_constraints
|
251
|
+
max_fields.each {|k,max| @field_lengths[k] = max if @field_lengths[k].to_i > max }
|
223
252
|
end
|
224
253
|
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
while total_length > max_width
|
230
|
-
raise TooManyFieldsForWidthError if @fields.size > max_width.to_f / @min_field_length
|
231
|
-
average_field_length = total_length / @fields.size.to_f
|
232
|
-
long_lengths = field_lengths.values.select {|e| e > average_field_length}
|
233
|
-
if long_lengths.empty?
|
234
|
-
raise "Algorithm didn't work, resort to default"
|
235
|
-
else
|
236
|
-
total_long_field_length = (long_lengths.inject {|t,n| t += n}) * max_width/total_length
|
237
|
-
average_long_field_length = total_long_field_length / long_lengths.size
|
238
|
-
field_lengths.each {|f,length|
|
239
|
-
field_lengths[f] = average_long_field_length if length > average_long_field_length
|
240
|
-
}
|
241
|
-
end
|
242
|
-
total_length = field_lengths.values.inject {|t,n| t += n}
|
243
|
-
end
|
254
|
+
def max_fields
|
255
|
+
@max_fields ||= (@options[:max_fields] ||= {}).each {|k,v|
|
256
|
+
@options[:max_fields][k] = (actual_width * v.to_f.abs).floor if v.to_f.abs < 1
|
257
|
+
}
|
244
258
|
end
|
245
259
|
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
original_field_lengths.each {|k,v| field_lengths[k] = (v / original_total_length.to_f * max_width).to_i }
|
253
|
-
else
|
254
|
-
# set all fields the same if nothing else works
|
255
|
-
field_lengths.each {|k,v| field_lengths[k] = max_width / @fields.size}
|
256
|
-
end
|
260
|
+
def actual_width
|
261
|
+
@actual_width ||= self.width - (@fields.size * BORDER_LENGTH + 1)
|
262
|
+
end
|
263
|
+
|
264
|
+
def width
|
265
|
+
@width ||= @options[:max_width] || View.width
|
257
266
|
end
|
258
267
|
|
259
268
|
# find max length for each field; start with the headers
|
@@ -269,32 +278,37 @@ module Hirb
|
|
269
278
|
end
|
270
279
|
|
271
280
|
def set_filter_defaults(rows)
|
272
|
-
|
273
|
-
|
281
|
+
@filter_classes.each do |klass, filter|
|
282
|
+
@fields.each {|field|
|
283
|
+
if rows.all? {|r| r[field].class == klass }
|
284
|
+
@options[:filters][field] ||= filter
|
285
|
+
end
|
286
|
+
}
|
274
287
|
end
|
275
288
|
end
|
276
289
|
|
277
290
|
def filter_values(rows)
|
278
|
-
|
291
|
+
@filter_classes = Helpers::Table.filter_classes.merge @options[:filter_classes] || {}
|
292
|
+
set_filter_defaults(rows) unless @options[:filter_any]
|
279
293
|
rows.map {|row|
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
row[f].send(*@options[:filters][f])
|
285
|
-
else
|
286
|
-
new_row[f] = row[f]
|
287
|
-
end
|
294
|
+
@fields.inject({}) {|new_row,f|
|
295
|
+
(filter = @options[:filters][f]) || (@options[:filter_any] && (filter = @filter_classes[row[f].class]))
|
296
|
+
new_row[f] = filter ? call_filter(filter, row[f]) : row[f]
|
297
|
+
new_row
|
288
298
|
}
|
289
|
-
new_row
|
290
299
|
}
|
291
300
|
end
|
292
301
|
|
302
|
+
def call_filter(filter, val)
|
303
|
+
filter.is_a?(Proc) ? filter.call(val) :
|
304
|
+
val.respond_to?(Array(filter)[0]) ? val.send(*filter) : Filters.send(filter, val)
|
305
|
+
end
|
306
|
+
|
293
307
|
def validate_values(rows)
|
294
308
|
rows.each {|row|
|
295
309
|
@fields.each {|f|
|
296
310
|
row[f] = row[f].to_s || ''
|
297
|
-
row[f].gsub
|
311
|
+
row[f] = row[f].gsub(/(\t|\r|\n)/) {|e| e.dump.gsub('"','') } if @options[:escape_special_chars]
|
298
312
|
}
|
299
313
|
}
|
300
314
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class Hirb::Helpers::Table
|
2
|
+
# Contains filter methods used by :filters option. To define a custom filter, simply open this module and create a method
|
3
|
+
# that take one argument, the value you will be filtering.
|
4
|
+
module Filters
|
5
|
+
extend self
|
6
|
+
def comma_join(arr) #:nodoc:
|
7
|
+
arr.join(', ')
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
class Hirb::Helpers::Table
|
2
|
+
# Resizes a table's fields to the table's max width.
|
3
|
+
class Resizer
|
4
|
+
# Modifies field_lengths to fit within width. Also enforces a table's max_fields.
|
5
|
+
def self.resize!(table)
|
6
|
+
obj = new(table)
|
7
|
+
obj.resize
|
8
|
+
obj.field_lengths
|
9
|
+
end
|
10
|
+
|
11
|
+
#:stopdoc:
|
12
|
+
attr_reader :field_lengths
|
13
|
+
def initialize(table)
|
14
|
+
@table, @width, @field_size = table, table.actual_width, table.fields.size
|
15
|
+
@field_lengths = table.field_lengths
|
16
|
+
@original_field_lengths = @field_lengths.dup
|
17
|
+
end
|
18
|
+
|
19
|
+
def resize
|
20
|
+
adjust_long_fields || default_restrict_field_lengths
|
21
|
+
@table.enforce_field_constraints
|
22
|
+
add_extra_width
|
23
|
+
end
|
24
|
+
|
25
|
+
# Simple algorithm which allows smaller fields to be displayed while
|
26
|
+
# restricting longer fields to an average_long_field
|
27
|
+
def adjust_long_fields
|
28
|
+
while (total_length = sum(@field_lengths.values)) > @width
|
29
|
+
average_field = total_length / @field_size.to_f
|
30
|
+
long_lengths = @field_lengths.values.select {|e| e > average_field }
|
31
|
+
return false if long_lengths.empty?
|
32
|
+
|
33
|
+
# adjusts average long field by ratio with @width
|
34
|
+
average_long_field = sum(long_lengths)/long_lengths.size * @width/total_length
|
35
|
+
@field_lengths.each {|f,length|
|
36
|
+
@field_lengths[f] = average_long_field if length > average_long_field
|
37
|
+
}
|
38
|
+
end
|
39
|
+
true
|
40
|
+
end
|
41
|
+
|
42
|
+
# Produces a field_lengths which meets the @width requirement
|
43
|
+
def default_restrict_field_lengths
|
44
|
+
original_total_length = sum @original_field_lengths.values
|
45
|
+
# set fields by their relative weight to original length
|
46
|
+
new_lengths = @original_field_lengths.inject({}) {|t,(k,v)|
|
47
|
+
t[k] = (v / original_total_length.to_f * @width).to_i; t }
|
48
|
+
|
49
|
+
# set all fields the same if relative doesn't work
|
50
|
+
unless new_lengths.values.all? {|e| e > MIN_FIELD_LENGTH} && (sum(new_lengths.values) <= @width)
|
51
|
+
new_lengths = @field_lengths.inject({}) {|t,(k,v)| t[k] = @width / @field_size; t }
|
52
|
+
end
|
53
|
+
@field_lengths.each {|k,v| @field_lengths[k] = new_lengths[k] }
|
54
|
+
end
|
55
|
+
|
56
|
+
def add_extra_width
|
57
|
+
added_width = 0
|
58
|
+
extra_width = @width - sum(@field_lengths.values)
|
59
|
+
unmaxed_fields = @field_lengths.keys.select {|f| !remaining_width(f).zero? }
|
60
|
+
# order can affect which one gets the remainder so let's keep it consistent
|
61
|
+
unmaxed_fields = unmaxed_fields.sort_by {|e| e.to_s}
|
62
|
+
|
63
|
+
unmaxed_fields.each_with_index do |f, i|
|
64
|
+
extra_per_field = (extra_width - added_width) / (unmaxed_fields.size - i)
|
65
|
+
add_to_field = remaining_width(f) < extra_per_field ? remaining_width(f) : extra_per_field
|
66
|
+
added_width += add_to_field
|
67
|
+
@field_lengths[f] += add_to_field
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def remaining_width(field)
|
72
|
+
(@remaining_width ||= {})[field] ||= begin
|
73
|
+
(@table.max_fields[field] || @original_field_lengths[field]) - @field_lengths[field]
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def sum(arr)
|
78
|
+
arr.inject {|t,e| t += e }
|
79
|
+
end
|
80
|
+
#:startdoc:
|
81
|
+
end
|
82
|
+
end
|