pulo 0.1.1 → 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/pulo.rb +28 -0
- data/lib/pulo/exceptions.rb +6 -0
- data/lib/pulo/figure/figure2d.rb +248 -0
- data/lib/pulo/figure/figure3d.rb +166 -0
- data/lib/pulo/formatting.rb +140 -0
- data/lib/pulo/frames/frame.rb +289 -0
- data/lib/pulo/frames/frame_cell.rb +83 -0
- data/lib/pulo/frames/frame_column.rb +149 -0
- data/lib/pulo/frames/frame_row.rb +87 -0
- data/lib/pulo/helpers.rb +1 -0
- data/lib/pulo/machine/hydraulics/pipe.rb +71 -0
- data/lib/pulo/machine/machines.rb +10 -0
- data/lib/pulo/machine/mechanics/moments_of_inertia.rb +64 -0
- data/lib/pulo/machine/steam/boiler.rb +61 -0
- data/lib/pulo/machine/steam/boiler_deaerator.rb +64 -0
- data/lib/pulo/machine/steam/deaerator.rb +37 -0
- data/lib/pulo/machine/steam/desuperheater.rb +29 -0
- data/lib/pulo/machine/steam/header.rb +20 -0
- data/lib/pulo/machine/steam/if97.rb +378 -0
- data/lib/pulo/machine/steam/steam_process.rb +27 -0
- data/lib/pulo/machine/steam/steam_turbine.rb +42 -0
- data/lib/pulo/machine/steam/water_steam.rb +229 -0
- data/lib/pulo/material/water.rb +34 -0
- data/lib/pulo/quantity/dimension.rb +63 -0
- data/lib/pulo/quantity/numeric_overloads.rb +217 -0
- data/lib/pulo/quantity/quantity.rb +285 -0
- data/lib/pulo/quantity/quantity_builder.rb +185 -0
- data/lib/pulo/quantity/quantity_definitions.rb +8 -0
- data/lib/pulo/quantity/quantity_definitions/area_volume.rb +73 -0
- data/lib/pulo/quantity/quantity_definitions/basic.rb +157 -0
- data/lib/pulo/quantity/quantity_definitions/electric.rb +22 -0
- data/lib/pulo/quantity/quantity_definitions/energy.rb +50 -0
- data/lib/pulo/quantity/quantity_definitions/fluids.rb +23 -0
- data/lib/pulo/quantity/quantity_definitions/force_power.rb +82 -0
- data/lib/pulo/quantity/quantity_definitions/rotation.rb +49 -0
- data/lib/pulo/quantity/quantity_definitions/value.rb +65 -0
- data/lib/pulo/quantity/quantity_definitions/velocity_acc_flow.rb +66 -0
- data/lib/pulo/quantity/quantity_groups/quantity_groups.rb +36 -0
- data/lib/pulo/quantity/unit.rb +45 -0
- data/lib/pulo/quantity_checker.rb +13 -0
- data/lib/pulo/tables/density.rb +10 -0
- data/lib/pulo/tables/melting_temperature.rb +9 -0
- data/lib/pulo/tables/specific_energy.rb +10 -0
- data/lib/pulo/tables/speed_of_sound.rb +9 -0
- data/lib/pulo/tables/tables.rb +104 -0
- data/lib/pulo/tables/tensile_strength.rb +10 -0
- data/lib/pulo/tables/yield_strength.rb +10 -0
- data/lib/pulo/version.rb +3 -0
- metadata +51 -3
@@ -0,0 +1,140 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Pulo
|
4
|
+
class << self
|
5
|
+
|
6
|
+
def stats_format stats
|
7
|
+
ret=''
|
8
|
+
stats.each do |stat|
|
9
|
+
if stat[0]==:number
|
10
|
+
ret+='Count: ' + stat[1].to_s + "\n"
|
11
|
+
else
|
12
|
+
ret+=stat[0].to_s.capitalize + ': ' + stat[1].to_s + "\n"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
ret
|
16
|
+
end
|
17
|
+
|
18
|
+
def super_digit(val)
|
19
|
+
val.to_s.chars.inject('') do |res, chr|
|
20
|
+
res+= case chr
|
21
|
+
when '.'
|
22
|
+
"\u207B".encode('utf-8')
|
23
|
+
when '-'
|
24
|
+
"\u207B".encode('utf-8')
|
25
|
+
when '1'
|
26
|
+
"\u00B9".encode('utf-8')
|
27
|
+
when '2'
|
28
|
+
"\u00B2".encode('utf-8')
|
29
|
+
when '3'
|
30
|
+
"\u00B3".encode('utf-8')
|
31
|
+
when '0'
|
32
|
+
"\u2070".encode('utf-8')
|
33
|
+
when '4'
|
34
|
+
"\u2074".encode('utf-8')
|
35
|
+
when '5'
|
36
|
+
"\u2075".encode('utf-8')
|
37
|
+
when '6'
|
38
|
+
"\u2076".encode('utf-8')
|
39
|
+
when '7'
|
40
|
+
"\u2077".encode('utf-8')
|
41
|
+
when '8'
|
42
|
+
"\u2078".encode('utf-8')
|
43
|
+
when '9'
|
44
|
+
"\u2079".encode('utf-8')
|
45
|
+
else
|
46
|
+
''
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
class NumberToRoundedConverter
|
53
|
+
def self.convert(number, precision)
|
54
|
+
|
55
|
+
precision ||= Pulo.precision
|
56
|
+
|
57
|
+
if Pulo.significant_figures && precision > 0
|
58
|
+
digits, rounded_number = digits_and_rounded_number(number, precision)
|
59
|
+
precision -= digits
|
60
|
+
precision = 0 if precision < 0 # don't let it be negative
|
61
|
+
else
|
62
|
+
rounded_number = number.round(precision)
|
63
|
+
rounded_number = rounded_number.to_i if precision == 0
|
64
|
+
rounded_number = rounded_number.abs if rounded_number.zero? # prevent showing negative zeros
|
65
|
+
end
|
66
|
+
formatted_string = "%00.#{precision}f" % rounded_number
|
67
|
+
|
68
|
+
delimited_number = NumberToDelimitedConverter.convert(formatted_string)
|
69
|
+
format_number(delimited_number)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def self.digits_and_rounded_number(number,precision)
|
75
|
+
if zero?(number)
|
76
|
+
[1, 0]
|
77
|
+
else
|
78
|
+
digits = digit_count(number)
|
79
|
+
multiplier = 10 ** (digits - precision)
|
80
|
+
rounded_number = calculate_rounded_number(number,multiplier)
|
81
|
+
digits = digit_count(rounded_number) # After rounding, the number of digits may have changed
|
82
|
+
[digits, rounded_number]
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def self.calculate_rounded_number(number,multiplier)
|
87
|
+
(number / BigDecimal.new(multiplier.to_f.to_s)).round * multiplier
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.digit_count(number)
|
91
|
+
number.zero? ? 1 : (Math.log10(absolute_number(number)) + 1).floor
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.format_number(number)
|
95
|
+
escaped_separator = Regexp.escape('.')
|
96
|
+
number.sub(/(#{escaped_separator})(\d*[1-9])?0+\z/, '\1\2').sub(/#{escaped_separator}\z/, '')
|
97
|
+
end
|
98
|
+
|
99
|
+
def self.absolute_number(number)
|
100
|
+
number.respond_to?(:abs) ? number.abs : number.to_d.abs
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.zero?(number)
|
104
|
+
number.respond_to?(:zero?) ? number.zero? : number.to_d.zero?
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class NumberToDelimitedConverter
|
109
|
+
DELIMITED_REGEX = /(\d)(?=(\d\d\d)+(?!\d))/
|
110
|
+
|
111
|
+
def self.convert(number)
|
112
|
+
parts(number).join('.')
|
113
|
+
end
|
114
|
+
|
115
|
+
private
|
116
|
+
|
117
|
+
def self.parts(number)
|
118
|
+
left, right = number.to_s.split('.')
|
119
|
+
left.gsub!(DELIMITED_REGEX) do |digit_to_delimit|
|
120
|
+
"#{digit_to_delimit},"
|
121
|
+
end
|
122
|
+
[left, right].compact
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
#class BigDecimal
|
129
|
+
# DEFAULT_STRING_FORMAT = 'F'
|
130
|
+
# def to_formatted_s(*args)
|
131
|
+
# if args[0].is_a?(Symbol)
|
132
|
+
# super
|
133
|
+
# else
|
134
|
+
# format = args[0] || DEFAULT_STRING_FORMAT
|
135
|
+
# _original_to_s(format)
|
136
|
+
# end
|
137
|
+
# end
|
138
|
+
# alias_method :_original_to_s, :to_s
|
139
|
+
# alias_method :to_s, :to_formatted_s
|
140
|
+
#end
|
@@ -0,0 +1,289 @@
|
|
1
|
+
require 'csv'
|
2
|
+
require 'descriptive_statistics'
|
3
|
+
require_relative 'frame_cell'
|
4
|
+
require_relative 'frame_row'
|
5
|
+
require_relative 'frame_column'
|
6
|
+
|
7
|
+
module Pulo
|
8
|
+
|
9
|
+
class Frame
|
10
|
+
|
11
|
+
attr_reader :column_count,:row_count,:rows, :columns, :column_names
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@rows=[]
|
15
|
+
@columns=[]
|
16
|
+
@column_names={}
|
17
|
+
@column_count=0
|
18
|
+
@row_count=0
|
19
|
+
end
|
20
|
+
|
21
|
+
def export_csv(path)
|
22
|
+
|
23
|
+
CSV.open(path, 'wb') do |csv|
|
24
|
+
csv << @columns.map {|col| col.name}
|
25
|
+
csv << @columns.map {|col|
|
26
|
+
if col.column_class.respond_to?(:quantity_name)
|
27
|
+
col.column_class.quantity_name
|
28
|
+
else
|
29
|
+
col.column_class.name
|
30
|
+
end
|
31
|
+
}
|
32
|
+
csv << @columns.map {|col|
|
33
|
+
if col.column_class.respond_to?(:quantity_name)
|
34
|
+
col.column_unit.abbreviation
|
35
|
+
else
|
36
|
+
''
|
37
|
+
end
|
38
|
+
}
|
39
|
+
@rows.each do |row|
|
40
|
+
csv<<row.to_a.map{|cell|
|
41
|
+
if cell.class.respond_to?(:quantity_name)
|
42
|
+
cell.value
|
43
|
+
else
|
44
|
+
cell
|
45
|
+
end
|
46
|
+
}
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
#applies the given function to each row
|
52
|
+
#returns a hash where each element (keyed by the group) is a frame containing the group
|
53
|
+
def group(group_function)
|
54
|
+
#t=Time.now
|
55
|
+
groups={}
|
56
|
+
@rows.each do |row|
|
57
|
+
res=group_function.call(row)
|
58
|
+
if groups[res]
|
59
|
+
groups[res].append_row(row.to_a)
|
60
|
+
else
|
61
|
+
frm=self.copy_definition
|
62
|
+
frm.append_row(row.to_a)
|
63
|
+
groups.merge!({res=>frm})
|
64
|
+
end
|
65
|
+
end
|
66
|
+
#puts "Groups in #{((Time.now-t)*1000).to_i} ms."
|
67
|
+
groups
|
68
|
+
end
|
69
|
+
|
70
|
+
def group_reduce(group_function,column_defns)
|
71
|
+
#get groups
|
72
|
+
groups=group(group_function)
|
73
|
+
|
74
|
+
#t=Time.now
|
75
|
+
output=Frame.new
|
76
|
+
output.append_column 'Group'
|
77
|
+
|
78
|
+
#setup the columns
|
79
|
+
column_defns.keys.each do |col|
|
80
|
+
output.append_column(col)
|
81
|
+
end
|
82
|
+
|
83
|
+
#add a row to the output for each group produced in the earlier stage
|
84
|
+
groups.each do |group|
|
85
|
+
row=column_defns.map do |defn|
|
86
|
+
if defn[1].class==Proc
|
87
|
+
defn[1].call group[1]
|
88
|
+
else
|
89
|
+
#assume an array with method for descriptive statistics and a column name
|
90
|
+
c=group[1][defn[1][1]]
|
91
|
+
v=case defn[1][0]
|
92
|
+
when :min
|
93
|
+
c.to_a.min
|
94
|
+
when :max
|
95
|
+
c.to_a.max
|
96
|
+
else
|
97
|
+
DescriptiveStatistics.send(defn[1][0],c.to_a)
|
98
|
+
end
|
99
|
+
if c.column_class.respond_to?(:quantity_name) && defn[1][0]!=:number
|
100
|
+
c.column_class.send(c.column_unit.name,v)
|
101
|
+
else
|
102
|
+
v
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
row.insert 0,group[0]
|
107
|
+
output.append_row row
|
108
|
+
end
|
109
|
+
#puts "Reduce in #{((Time.now-t)*1000).to_i} ms."
|
110
|
+
output
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
def sort(&sort_function)
|
115
|
+
int=@rows.map do |row|
|
116
|
+
[row.row_number,sort_function.call(row)]
|
117
|
+
end.sort_by {|elm| elm[1]}.map{|elm| elm[0]}
|
118
|
+
frm=self.copy_definition
|
119
|
+
int.each do |row_no|
|
120
|
+
frm.append_row self.rows[row_no].to_a_values
|
121
|
+
end
|
122
|
+
frm
|
123
|
+
end
|
124
|
+
|
125
|
+
def clone
|
126
|
+
frame=copy_definition
|
127
|
+
@rows.each do |row|
|
128
|
+
frame.append_row row.to_a
|
129
|
+
end
|
130
|
+
frame
|
131
|
+
end
|
132
|
+
|
133
|
+
def copy_definition
|
134
|
+
frame=Frame.new
|
135
|
+
@columns.each do |col|
|
136
|
+
frame.append_column(col.name,col.hidden?,&col.formula)
|
137
|
+
end
|
138
|
+
frame
|
139
|
+
end
|
140
|
+
|
141
|
+
def delete_column(name_or_index)
|
142
|
+
if name_or_index.is_a?(Integer)
|
143
|
+
raise IndexError,"No column number #{name_or_index} defined." unless @columns[name_or_index]
|
144
|
+
index=name_or_index
|
145
|
+
name=@columns[index].name
|
146
|
+
else
|
147
|
+
index=@column_names[name_or_index]
|
148
|
+
raise IndexError,"Column with name '#{name_or_index}' not found." unless index
|
149
|
+
name=name_or_index
|
150
|
+
end
|
151
|
+
@rows.each {|row| row.delete_column(index)}
|
152
|
+
@column_names.delete(name)
|
153
|
+
@column_names.each do |item|
|
154
|
+
@column_names[item[0]]-=1 if @column_names[item[0]]>=index
|
155
|
+
end
|
156
|
+
@columns.delete_at(index)
|
157
|
+
@column_count-=1
|
158
|
+
end
|
159
|
+
|
160
|
+
def rename_column(old_name,new_name)
|
161
|
+
col=self[old_name]
|
162
|
+
col_no=@column_names[old_name]
|
163
|
+
@column_names.delete(old_name)
|
164
|
+
@column_names.merge!({new_name=>col_no})
|
165
|
+
col.name=new_name
|
166
|
+
end
|
167
|
+
|
168
|
+
def append_column(name,hidden=false,&formula)
|
169
|
+
col=FrameColumn.new(name,self, hidden, &formula)
|
170
|
+
|
171
|
+
@columns << col
|
172
|
+
@column_names.merge!({name=>@column_count})
|
173
|
+
|
174
|
+
@rows.each do |rw|
|
175
|
+
cell=FrameCell.new(col,rw)
|
176
|
+
col.append_row(cell)
|
177
|
+
rw.append_column(cell)
|
178
|
+
end
|
179
|
+
@column_count+=1
|
180
|
+
col
|
181
|
+
end
|
182
|
+
|
183
|
+
def append_row(values)
|
184
|
+
if values.is_a?(Array)
|
185
|
+
vals=values.each
|
186
|
+
row=FrameRow.new(self,@row_count)
|
187
|
+
@columns.each do |col|
|
188
|
+
if values.length==column_count
|
189
|
+
v = vals.next
|
190
|
+
else
|
191
|
+
v = col.value_column? ? vals.next : nil
|
192
|
+
end
|
193
|
+
cell=FrameCell.new(col,row,v)
|
194
|
+
col.append_row(cell)
|
195
|
+
row.append_column(cell)
|
196
|
+
end
|
197
|
+
@rows<<row
|
198
|
+
@row_count+=1
|
199
|
+
row
|
200
|
+
else
|
201
|
+
raise 'Expecting an array of values as the append row.'
|
202
|
+
end
|
203
|
+
end
|
204
|
+
def insert_row(previous_row,values)
|
205
|
+
if values.is_a?(Array)
|
206
|
+
row_no=previous_row.row_number+1
|
207
|
+
vals=values.each
|
208
|
+
row=FrameRow.new(self,row_no)
|
209
|
+
@columns.each do |col|
|
210
|
+
if values.length==column_count
|
211
|
+
v = vals.next
|
212
|
+
else
|
213
|
+
v = col.value_column? ? vals.next : nil
|
214
|
+
end
|
215
|
+
cell=FrameCell.new(col,row,v)
|
216
|
+
col.insert_row(row_no,cell)
|
217
|
+
row.append_column(cell)
|
218
|
+
end
|
219
|
+
@rows.insert(row_no,row)
|
220
|
+
@row_count+=1
|
221
|
+
@rows.drop(row_no+1).each {|r| r.row_number+=1}
|
222
|
+
else
|
223
|
+
raise 'Expecting an array of values as the append row.'
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
def recalc_all(with_timing=false)
|
228
|
+
formula_columns.each { |col| col.recalc with_timing }
|
229
|
+
end
|
230
|
+
|
231
|
+
def [](column)
|
232
|
+
if column.is_a?(Integer)
|
233
|
+
raise IndexError,"No column number #{column} defined." unless @columns[column]
|
234
|
+
@columns[column]
|
235
|
+
else
|
236
|
+
column_no=@column_names[column]
|
237
|
+
raise IndexError,"Column with name '#{column}' not found." unless column_no
|
238
|
+
@columns[column_no]
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def first_row
|
243
|
+
@rows.first
|
244
|
+
end
|
245
|
+
def last_row
|
246
|
+
@rows.last
|
247
|
+
end
|
248
|
+
|
249
|
+
def lookup(value,column)
|
250
|
+
self[column].lookup(value)
|
251
|
+
end
|
252
|
+
|
253
|
+
def to_s(&filter)
|
254
|
+
@columns.each {|c| c.recalc_width}
|
255
|
+
ret="\n "
|
256
|
+
ret+=@columns.select {|c| !c.hidden?}.map{|s| s.name.ljust(s.width,' ')}.join(' ') + "\n"
|
257
|
+
ret+='========= '
|
258
|
+
ret+=@columns.select {|c| !c.hidden?}.map{|s| ''.ljust(s.width,'=')}.join('= ') + "\n"
|
259
|
+
ret+=' '
|
260
|
+
ret+=@columns.select {|c| !c.hidden?}.map{|s|
|
261
|
+
if s.column_class.respond_to?(:quantity_name)
|
262
|
+
(s.column_class.quantity_name + (s.value_column? ? '' : '*')).ljust(s.width,' ')
|
263
|
+
else
|
264
|
+
(s.column_class.to_s + (s.value_column? ? '' : '*')).ljust(s.width,' ')
|
265
|
+
end
|
266
|
+
}.join(' ') + "\n"
|
267
|
+
ret+='--------- '
|
268
|
+
ret+=@columns.select {|c| !c.hidden?}.map{|s| ''.ljust(s.width,'-')}.join('- ') + "\n"
|
269
|
+
|
270
|
+
ret+=(@rows.select{|row| !block_given? || filter.call(row)}.map { |row| row.to_s}).join("\n")
|
271
|
+
|
272
|
+
ret+="\n" + @rows.length.to_s + " rows\n"
|
273
|
+
ret
|
274
|
+
end
|
275
|
+
|
276
|
+
def inspect
|
277
|
+
"Frame Object"
|
278
|
+
end
|
279
|
+
|
280
|
+
def value_columns
|
281
|
+
@columns.find_all { |col| col.value_column?}
|
282
|
+
end
|
283
|
+
|
284
|
+
def formula_columns
|
285
|
+
@columns.find_all { |col| !col.value_column?}
|
286
|
+
end
|
287
|
+
|
288
|
+
end
|
289
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module Pulo
|
2
|
+
class FrameCell
|
3
|
+
|
4
|
+
def initialize(parent_column,parent_row,value=nil)
|
5
|
+
@parent_column=parent_column
|
6
|
+
@parent_row=parent_row
|
7
|
+
@error=false
|
8
|
+
self.value=value
|
9
|
+
end
|
10
|
+
|
11
|
+
def column
|
12
|
+
@parent_column
|
13
|
+
end
|
14
|
+
|
15
|
+
def row
|
16
|
+
@parent_row
|
17
|
+
end
|
18
|
+
|
19
|
+
def empty?
|
20
|
+
@value.nil?
|
21
|
+
end
|
22
|
+
|
23
|
+
def value_column?
|
24
|
+
@parent_column.value_column?
|
25
|
+
end
|
26
|
+
|
27
|
+
def set_error
|
28
|
+
@error=true
|
29
|
+
end
|
30
|
+
def unset_error
|
31
|
+
@error=false
|
32
|
+
end
|
33
|
+
def error?
|
34
|
+
@error
|
35
|
+
end
|
36
|
+
|
37
|
+
def value
|
38
|
+
#raise "Column (#{@parent_column.name}) requires re-calc, value not available." if @parent_column.recalc_required
|
39
|
+
return "ERR" if error?
|
40
|
+
#return "UNK" if @parent_column.recalc_required
|
41
|
+
#return "NIL" if empty?
|
42
|
+
@value
|
43
|
+
end
|
44
|
+
|
45
|
+
def value=(val)
|
46
|
+
|
47
|
+
if @parent_column.column_class==NilClass
|
48
|
+
@parent_column.column_class=val.class
|
49
|
+
if val.class.respond_to?(:quantity_name)
|
50
|
+
@parent_column.column_unit=val.unit
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
if val.class.respond_to?(:quantity_name)
|
55
|
+
if val.unit.name!=@parent_column.column_unit.name
|
56
|
+
val=val.send(@parent_column.column_unit.name)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
if @parent_column.column_class!=NilClass && val.class!=NilClass
|
61
|
+
if val.class.respond_to?(:quantity_name)
|
62
|
+
unless val.class.dimensions==@parent_column.column_class.dimensions
|
63
|
+
raise "Tried to set a value (#{val.to_s}) of class #{val.class} with dimensions #{val.class.dimensions} on column #{@parent_column.name} which already has a defined class of #{@parent_column.column_class}."
|
64
|
+
end
|
65
|
+
else
|
66
|
+
unless val.class==@parent_column.column_class
|
67
|
+
raise "Tried to set a value (#{val.to_s}) of class #{val.class} on column #{@parent_column.name} which already has a defined class of #{@parent_column.column_class}."
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
@value=val
|
72
|
+
end
|
73
|
+
|
74
|
+
def to_s
|
75
|
+
begin
|
76
|
+
@parent_column.formatter.call(value).ljust(@parent_column.width,' ')
|
77
|
+
rescue Exception => e
|
78
|
+
value.to_s
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|