twb 1.0.5 → 1.9.1
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.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/lib/twb.rb +10 -6
- data/lib/twb/analysis/CalculatedFields/CSVEmitter.rb +154 -0
- data/lib/twb/analysis/CalculatedFields/CalculatedFieldsAnalyzer.rb +527 -0
- data/lib/twb/analysis/CalculatedFields/MarkdownEmitter.rb +71 -0
- data/lib/twb/analysis/DataSources/DataSourceTableFieldsCSVEmitter.rb +117 -0
- data/lib/twb/analysis/Sheets/WorksheetDataStructureCSVEmitter.rb +192 -0
- data/lib/twb/calculatedfield.rb +47 -0
- data/lib/twb/columnfield.rb +94 -0
- data/lib/twb/dashboard.rb +1 -1
- data/lib/twb/datasource.rb +368 -121
- data/lib/twb/fieldcalculation.rb +157 -81
- data/lib/twb/localfield.rb +32 -29
- data/lib/twb/metadatafield.rb +57 -15
- data/lib/twb/util/ftpPublisher.rb +48 -0
- data/lib/twb/util/joinUtilities.rb +52 -0
- data/lib/twb/workbook.rb +27 -10
- data/lib/twb/worksheet.rb +146 -53
- metadata +11 -5
- data/lib/twb/analysis/calculatedfieldsanalyzer.rb +0 -508
- data/lib/twb/apps/X-Ray Dashboards.rb +0 -80
- data/lib/twb/countNodes.rb +0 -98
data/lib/twb/fieldcalculation.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2014, 2015 Chris Gerrard
|
1
|
+
# Copyright (C) 2014, 2015, 2017 Chris Gerrard
|
2
2
|
#
|
3
3
|
# This program is free software: you can redistribute it and/or modify
|
4
4
|
# it under the terms of the GNU General Public License as published by
|
@@ -19,141 +19,144 @@ module Twb
|
|
19
19
|
|
20
20
|
class FieldCalculation
|
21
21
|
|
22
|
-
attr_reader :node, :fieldNode,
|
23
|
-
attr_reader :
|
24
|
-
attr_reader :
|
22
|
+
attr_reader :node, :field, :fieldNode, :dataSource
|
23
|
+
attr_reader :uiname, :techname, :caption
|
24
|
+
attr_reader :has_formula
|
25
|
+
attr_reader :formula
|
26
|
+
attr_reader :formulaUC
|
27
|
+
attr_reader :formulaResolved
|
28
|
+
attr_reader :formulaFlat
|
29
|
+
attr_reader :formulaFlatResolved
|
30
|
+
attr_reader :formulaLines
|
31
|
+
attr_reader :is_tableCalc
|
32
|
+
attr_reader :is_lod, :lodCodePos
|
25
33
|
attr_reader :class, :scopeIsolation
|
26
|
-
attr_reader :fields, :
|
34
|
+
attr_reader :fields, :remoteFields, :calcFields
|
27
35
|
attr_reader :comments
|
28
36
|
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
37
|
+
@@tableCalcs = [ 'FIRST', 'INDEX', 'LAST', 'SIZE',
|
38
|
+
'LOOKUP', 'PREVIOUS_VALUE',
|
39
|
+
'RANK', 'RANK_DENSE', 'RANK_MODIFIED', 'RANK_PERCENTILE', 'RANK_UNIQUE',
|
40
|
+
'RUNNING_AVG', 'RUNNING_COUNT', 'RUNNING_MAX', 'RUNNING_MIN', 'RUNNING_SUM',
|
41
|
+
'SCRIPT_BOOL', 'SCRIPT_INT', 'SCRIPT_REAL', 'SCRIPT_STR',
|
42
|
+
'TOTAL',
|
43
|
+
'WINDOW_SUM', 'WINDOW_AVG', 'WINDOW_COUNT',
|
44
|
+
'WINDOW_MIN', 'WINDOW_MEDIAN', 'WINDOW_MAX', 'WINDOW_PERCENTILE',
|
45
|
+
'WINDOW_STDEV', 'WINDOW_STDEVP',
|
46
|
+
'WINDOW_CORR', 'WINDOW_COVAR', 'WINDOW_COVARP',
|
47
|
+
'WINDOW_VAR', 'WINDOW_VARP'
|
48
|
+
]
|
49
|
+
|
50
|
+
def initialize(calcField, datasource=nil)
|
51
|
+
raise ArgumentError.new("FieldCalculation must be initialized with a CalculatedField, has been provided with a #{calcField.class}") if calcField.class != Twb::CalculatedField
|
52
|
+
@field = calcField
|
53
|
+
calcNode = calcField.node
|
54
|
+
@istableCalc = false
|
55
|
+
# puts "FieldCalculation calcNode.nil? :: #{calcNode.nil?} "
|
56
|
+
unless calcNode.nil?
|
57
|
+
@node = calcNode.at_xpath('./calculation')
|
58
|
+
@fieldNode = calcField.node
|
33
59
|
@dataSource = datasource
|
34
60
|
@class = attribText(@node, 'class')
|
35
61
|
@remoteFields = {}
|
36
62
|
if 'categorical-bin'.eql? @class
|
37
|
-
# puts calcNode
|
38
63
|
# <calculation class='categorical-bin'
|
39
64
|
# column='[Calculation_507569757376950272]'
|
40
65
|
# default='"Other"'
|
41
66
|
# new-bin='true'>
|
42
|
-
@techname =
|
67
|
+
@techname = @node.attribute('column').text.gsub(/^\[|\]$/,'') # assumes the column attribute exists
|
43
68
|
@caption = attribText(@fieldNode, 'caption')
|
44
69
|
uiname = if datasource.nil?
|
45
70
|
@techName
|
46
71
|
else
|
47
72
|
datasource.fieldUIName(@techname)
|
48
73
|
end
|
49
|
-
@uiname = "#{uiname}
|
50
|
-
@
|
51
|
-
@
|
52
|
-
@
|
74
|
+
@uiname = "#{uiname} <<group>>"
|
75
|
+
@has_formula = true
|
76
|
+
@formula = "grouped <<#{uiname}>> values"
|
77
|
+
@formulaLines = [ @formula ]
|
78
|
+
@formulaFlat = @formula
|
79
|
+
@comments = [ ]
|
80
|
+
@is_lod = false
|
53
81
|
else
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
@caption = @fieldNode.attribute('caption').nil? ? nil : @fieldNode.attribute('caption').text
|
58
|
-
@techname = @fieldNode.attribute('name').text.gsub(/^\[|\]$/,'') # assumes the name attribute exists
|
59
|
-
@uiname = @caption.nil? ? @techname : @caption
|
60
|
-
# puts " caption: #{@caption}"
|
61
|
-
# puts "techname: #{@techname}"
|
62
|
-
# puts " uiname: #{@uiname}"
|
82
|
+
@caption = calcField.caption
|
83
|
+
@techname = calcField.name
|
84
|
+
@uiname = calcField.uiname
|
63
85
|
#--
|
64
86
|
@scopeIsolation = attribText(@node, 'scope-isolation')
|
65
87
|
#-- Formula --
|
66
88
|
@has_formula = @node.has_attribute?('formula')
|
67
89
|
if @has_formula
|
68
|
-
@formula = @node.attribute('formula').text
|
90
|
+
@formula = @node.attribute('formula').text.gsub(/\r\n/,"\n")
|
91
|
+
@formulaUC = @formula.upcase
|
69
92
|
@formulaLines = formula.split(/\n|\r\n/)
|
70
93
|
@formulaFlat = flattenFormula(@formulaLines)
|
71
94
|
@comments = getComments(@formulaLines)
|
72
|
-
|
73
|
-
@
|
74
|
-
@
|
75
|
-
@
|
76
|
-
@comments = [ ]
|
95
|
+
@lodCodePos = @formula =~ /^[ ]*{[ ]*(fixed|include|exclude)[ ]*/i
|
96
|
+
@is_lod = !lodCodePos.nil? && @lodCodePos >= 0
|
97
|
+
@is_tableCalc = @@tableCalcs.any? { |tc| @formulaUC.include?(tc) } #assessTableCalc @formula
|
98
|
+
# puts "#{@lodCodePos} \t #{@is_lod} \t #{@is_lod.class} \t => #{@formula}"
|
77
99
|
end
|
78
|
-
#-- Fields --
|
79
|
-
parseFormFields # establishes @ fields
|
80
|
-
resolveFields # establishes @ resolvedFields
|
81
100
|
end
|
82
101
|
end
|
83
102
|
end
|
84
103
|
|
104
|
+
# def assessTableCalc formula
|
105
|
+
# @@tableCalcs.any? { |tc| string.include?(tc) }
|
106
|
+
# end
|
107
|
+
|
85
108
|
def attribText(node, attribute)
|
86
109
|
node.attribute(attribute).nil? ? nil : node.attribute(attribute).text
|
87
110
|
end
|
88
111
|
|
89
112
|
|
90
113
|
def parseFormFields
|
91
|
-
@fields
|
92
|
-
|
114
|
+
@fields = Set.new []
|
115
|
+
@calcFields = SortedSet.new []
|
116
|
+
formula = @formulaFlat
|
93
117
|
if !formula.nil? && formula.include?('[') && formula.include?(']')
|
94
|
-
noSqLits = formula.gsub(/'[\[\.\]]+'/, ' ')
|
118
|
+
noSqLits = formula.gsub( /'[\[\.\]]+'/, ' ')
|
95
119
|
flatForm = noSqLits.gsub( /\n/, ' ')
|
96
120
|
stripFrt = flatForm.gsub( /^[^\[]*[\[]/ , '[' )
|
97
121
|
stripBck = stripFrt.gsub( /\][^\]]+$/ , ']' )
|
98
122
|
stripMid = stripBck.gsub( /\][^\]]{2,}\[/ , ']]..[[' )
|
99
123
|
stripCom = stripMid.gsub( /\][ ]*,[ ]*\[/ , ']]..[[' )
|
100
|
-
stripFns = stripMid.gsub( /\][ ]*[
|
124
|
+
stripFns = stripMid.gsub( /\][ ]*[\*\/+\-,=][ ]*\[/ , ']]..[[' )
|
101
125
|
fields = stripFns.split(']..[')
|
102
126
|
fields.each { |field| @fields.add field.gsub(/^\[|\]$/, '')}
|
127
|
+
fields.each do |field|
|
128
|
+
cf = CalculationField.new( field.gsub(/^\[|\]$/, ''), @dataSource, @dataSource.workbook )
|
129
|
+
@calcFields.add cf
|
130
|
+
end
|
103
131
|
end
|
104
132
|
end
|
105
133
|
|
106
|
-
|
107
|
-
|
108
|
-
@resolvedFields = []
|
109
|
-
@fields.each do |field|
|
110
|
-
rawField = field.gsub(/^\[|\]$/,'')
|
111
|
-
parts = rawField.split('].[')
|
112
|
-
if parts.length > 1
|
113
|
-
source = parts[0]
|
114
|
-
fieldCalc = parts[1]
|
115
|
-
fieldui = fieldCalc
|
116
|
-
else
|
117
|
-
source = nil
|
118
|
-
fieldCalc = parts[0]
|
119
|
-
fieldui = @dataSource.fieldUIName(fieldCalc)
|
120
|
-
end
|
121
|
-
hash = { :field => fieldCalc, :fieldui => fieldui, :source => source }
|
122
|
-
@resolvedFields << hash
|
123
|
-
end
|
124
|
-
end
|
125
|
-
|
126
|
-
|
127
|
-
def resolvedFormula
|
128
|
-
@resolvedFormula ||= resolveFormula
|
134
|
+
def formulaResolved
|
135
|
+
@formulaResolved ||= resolveFormula
|
129
136
|
end
|
130
|
-
|
137
|
+
|
131
138
|
def resolveFormula
|
132
|
-
# puts "resolveFormula :\n-- formula --\n %s \n-- @dataSource.nil? : %s \n-- @resolvedFields.nil? : %s \n--" % [ @formula, @dataSource.nil?, @resolvedFields.nil? ]
|
133
139
|
formula = @formula
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
fieldInternal = field # "[#{rf[:field]}]"
|
142
|
-
fieldUI = @dataSource.fieldUIName(field)
|
143
|
-
# puts " f?: #{formula.include?(field)}"
|
144
|
-
# puts " fui: #{fieldUI}"
|
145
|
-
formula = formula.gsub(field,fieldUI) unless fieldUI.nil?
|
146
|
-
# puts "formula:\n--\n#{formula}"
|
147
|
-
else
|
148
|
-
@remoteFields[source] = SortedSet.new unless @remoteFields.include? source
|
149
|
-
@remoteFields[source].add field
|
150
|
-
end
|
140
|
+
parseFormFields # - extracts the fields from the formula; as persisted they're the internal names
|
141
|
+
# puts "::++ #{formula}"
|
142
|
+
@calcFields.each do |calcField|
|
143
|
+
if calcField.techUIdiff
|
144
|
+
# puts ":::: #{calcField.techCode} // #{calcField.uiCode}"
|
145
|
+
formula = formula.gsub(calcField.techCode,calcField.uiCode)
|
146
|
+
# puts ":--: #{formula}"
|
151
147
|
end
|
152
148
|
end
|
153
|
-
# puts "resolvedFormula:\n==\n#{formula}\n--"
|
154
149
|
return formula
|
155
150
|
end
|
156
151
|
|
152
|
+
def formulaFlatResolved
|
153
|
+
@formulaFlatResolved ||= flattenResolvedFormula
|
154
|
+
end
|
155
|
+
|
156
|
+
def flattenResolvedFormula
|
157
|
+
formula = formulaResolved
|
158
|
+
formula.gsub(/\n/, ' ')
|
159
|
+
end
|
157
160
|
|
158
161
|
def flattenFormula lines
|
159
162
|
formula = ''
|
@@ -175,7 +178,80 @@ module Twb
|
|
175
178
|
return comments.strip
|
176
179
|
end
|
177
180
|
|
178
|
-
|
179
181
|
end # class FieldCalculation
|
180
182
|
|
183
|
+
|
184
|
+
|
185
|
+
class CalculationField
|
186
|
+
# is a field used in a calculation, resolved into it's human-meaningful form
|
187
|
+
|
188
|
+
include Comparable
|
189
|
+
|
190
|
+
attr_reader :techName, :techCode
|
191
|
+
attr_reader :uiName, :uiCode
|
192
|
+
attr_reader :dataSource, :dataSourceRef, :dataSourceExists
|
193
|
+
attr_reader :fqName
|
194
|
+
attr_reader :techUIdiff
|
195
|
+
|
196
|
+
def initialize code, datasource, workbook
|
197
|
+
# puts "\nCalculationField <= \n\t #{code}"
|
198
|
+
@dataSource = datasource.uiname
|
199
|
+
@dataSourceRef = :local
|
200
|
+
@dataSourceExists = true
|
201
|
+
@techUIdiff = false
|
202
|
+
@uiName = ''
|
203
|
+
rawCode = code.gsub(/^\[|\]$/,'')
|
204
|
+
parts = rawCode.split('].[')
|
205
|
+
# puts "\n\nField: #{code} \t parts: #{parts.length} - #{parts.inspect}"
|
206
|
+
if parts.length == 1
|
207
|
+
@techName = parts[0]
|
208
|
+
@techCode = "[#{parts[0]}]"
|
209
|
+
if datasource.nil?
|
210
|
+
@uiName = @techName
|
211
|
+
@uiCode = @techCode
|
212
|
+
@techUIdiff = false
|
213
|
+
else # !datasource.nil?
|
214
|
+
# puts "#{@techName} \t:: #{datasource.fieldUIName(@techName).nil?} \t:: #{datasource.fieldUIName(@techName)} "
|
215
|
+
@uiName = datasource.fieldUIName(@techName).nil? ? @techName : datasource.fieldUIName(@techName)
|
216
|
+
@uiCode = @uiName.nil? ? @techCode : "[#{@uiName}]"
|
217
|
+
@techUIdiff = !@techCode.eql?(@uiCode)
|
218
|
+
end
|
219
|
+
else # parts.length <> 1
|
220
|
+
rdstech = parts[0]
|
221
|
+
calcField = parts[1]
|
222
|
+
@uiName = calcField
|
223
|
+
@dataSource = rdstech
|
224
|
+
@dataSourceRef = :remote
|
225
|
+
@techCode = "[#{rdstech}].[#{calcField}]"
|
226
|
+
workbook = datasource.workbook
|
227
|
+
remoteds = workbook.nil? ? nil : workbook.datasource(rdstech)
|
228
|
+
# puts "\t twb: #{workbook.class} / remoteds: #{remoteds.class} : #{remoteds.nil? ? "<<NOT FOUND:#{rdstech}:>>" : remoteds.uiname} "
|
229
|
+
#--
|
230
|
+
if remoteds.nil? || remoteds.fieldUIName(calcField).nil?
|
231
|
+
@uiName = calcField
|
232
|
+
@uiCode = "[<<NOT FOUND>>#{rdstech}].[#{calcField}]"
|
233
|
+
@techUIdiff = true
|
234
|
+
@dataSourceExists = false
|
235
|
+
else !remoteds.nil?
|
236
|
+
@dataSource = remoteds.uiname
|
237
|
+
@uiName = remoteds.fieldUIName(calcField)
|
238
|
+
@uiCode = "[#{@dataSource}].[#{@uiName}]"
|
239
|
+
@techUIdiff = !@techCode.eql?(@uiCode)
|
240
|
+
@dataSourceExists = true
|
241
|
+
end
|
242
|
+
end
|
243
|
+
@fqName = "#{@dataSource}::#{@uiName}"
|
244
|
+
end # initialize
|
245
|
+
|
246
|
+
def <=>(other)
|
247
|
+
# myName = @uiName.nil? ? '' : @uiName
|
248
|
+
# otherName = other.uiName.nil? ? "" : other.uiName
|
249
|
+
# # puts "#{@uiName} / #{myName} <=> #{otherName} / #{other.uiName}"
|
250
|
+
# # puts "#{@uiName.nil?} // #{other.uiName.nil?}"
|
251
|
+
# myName <=> otherName
|
252
|
+
@fqName <=> other.fqName
|
253
|
+
end
|
254
|
+
|
255
|
+
end # class CalculationField
|
256
|
+
|
181
257
|
end # module Twb
|
data/lib/twb/localfield.rb
CHANGED
@@ -21,44 +21,47 @@ module Twb
|
|
21
21
|
|
22
22
|
class LocalField
|
23
23
|
|
24
|
-
attr_reader :
|
24
|
+
attr_reader :node, :type, :datatype, :name, :ordinal
|
25
25
|
|
26
26
|
def initialize fieldNode
|
27
|
+
puts "lf:: #{fieldNode}"
|
27
28
|
@node = fieldNode
|
28
29
|
@type = 'local'
|
29
|
-
@tableauname = @node.attr('name')
|
30
|
-
@dbname = @node.attr('name').gsub(/^\[/,'').gsub(/\]$/,'')
|
31
30
|
@datatype = @node.attr('datatype')
|
32
|
-
@
|
33
|
-
@
|
34
|
-
@
|
35
|
-
@
|
36
|
-
@
|
37
|
-
@
|
38
|
-
@
|
39
|
-
@
|
40
|
-
|
31
|
+
@name = @node.attr('name')
|
32
|
+
@ordinal = @node.attr('ordinal')
|
33
|
+
# @dbname = @node.attr('name').gsub(/^\[/,'').gsub(/\]$/,'')
|
34
|
+
# @datatype = @node.attr('datatype')
|
35
|
+
# @role = @node.attr('role')
|
36
|
+
# @type = @node.attr('type')
|
37
|
+
# @hidden = @node.attr('hidden')
|
38
|
+
# @caption = @node.attr('caption')
|
39
|
+
# @aggregation = @node.attr('aggregation')
|
40
|
+
# @calculation = getCalculation
|
41
|
+
# @comments = getComments
|
42
|
+
# @uiname = if @caption.nil? || @caption == '' then @dbname else @caption end
|
43
|
+
# return self
|
41
44
|
end
|
42
45
|
|
43
|
-
def getCalculation
|
44
|
-
|
45
|
-
|
46
|
-
end
|
46
|
+
# def getCalculation
|
47
|
+
# calcNode = @node.at_xpath("./calculation")
|
48
|
+
# FieldCalculation.new(calcNode) unless calcNode.nil?
|
49
|
+
# end
|
47
50
|
|
48
|
-
def getComments
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
51
|
+
# def getComments
|
52
|
+
# comments = ''
|
53
|
+
# runs = node.xpath('./desc/formatted-text/run')
|
54
|
+
# runs.each do |run|
|
55
|
+
# unless run.nil?
|
56
|
+
# comments += run.text
|
57
|
+
# end
|
58
|
+
# end
|
59
|
+
# return comments
|
60
|
+
# end
|
58
61
|
|
59
|
-
def remove_attribute attribute
|
60
|
-
|
61
|
-
end
|
62
|
+
# def remove_attribute attribute
|
63
|
+
# @node.remove_attribute(attribute)
|
64
|
+
# end
|
62
65
|
|
63
66
|
end
|
64
67
|
|
data/lib/twb/metadatafield.rb
CHANGED
@@ -17,37 +17,79 @@ require 'nokogiri'
|
|
17
17
|
|
18
18
|
module Twb
|
19
19
|
|
20
|
-
# Assumption: A field can only be either a MetadataField or a LocalField, not both in a given Workbook data connection.
|
21
|
-
|
22
20
|
class MetadataField
|
23
21
|
|
24
|
-
|
25
|
-
|
22
|
+
include Comparable
|
23
|
+
|
24
|
+
attr_reader :node, :class
|
25
|
+
attr_accessor :source
|
26
|
+
attr_reader :parentName, :table # parentName is the Db table
|
27
|
+
attr_reader :name, :caption, :family
|
28
|
+
attr_reader :localName, :localType
|
29
|
+
attr_reader :remoteName, :remoteAlias
|
30
|
+
attr_reader :remoteType, :aggregation
|
31
|
+
attr_reader :containsNull, :ordinal
|
32
|
+
attr_reader :precision, :width, :scale
|
33
|
+
|
34
|
+
# Child nodes of <metadata-record nodes
|
35
|
+
# <aggregation
|
36
|
+
# <approx-count
|
37
|
+
# <attributes
|
38
|
+
# <caption
|
39
|
+
# <collation
|
40
|
+
# <contains-null
|
41
|
+
# <family
|
42
|
+
# <layered
|
43
|
+
# <local-name
|
44
|
+
# <local-type
|
45
|
+
# <ordinal
|
46
|
+
# <parent-name
|
47
|
+
# <precision
|
48
|
+
# <remote-alias
|
49
|
+
# <remote-name
|
50
|
+
# <remote-type
|
51
|
+
# <scale
|
52
|
+
# <statistics
|
53
|
+
# <width
|
54
|
+
|
26
55
|
|
27
56
|
def initialize fieldNode
|
57
|
+
@class = fieldNode.attribute('class').text
|
28
58
|
@node = fieldNode
|
59
|
+
@parentName = load 'parent-name'
|
60
|
+
@family = load 'family'
|
61
|
+
@table = @family.nil? ? @parentName : @family
|
62
|
+
@remoteName = load 'remote-name'
|
63
|
+
@remoteAlias = load 'remote-alias'
|
64
|
+
@remoteType = load 'remote-type'
|
65
|
+
@caption = load 'caption'
|
66
|
+
@localName = load 'local-name'
|
67
|
+
@name = @caption.nil? ? @localName : @caption
|
29
68
|
@aggregation = load 'aggregation'
|
30
|
-
@
|
31
|
-
@
|
32
|
-
@localtype = load 'local-type'
|
69
|
+
@containsNull = load 'contains-null'
|
70
|
+
@localType = load 'local-type'
|
33
71
|
@ordinal = load 'ordinal'
|
34
|
-
@parentname = load 'parent-name'
|
35
72
|
@precision = load 'precision'
|
36
|
-
@
|
37
|
-
@remotename = load 'remote-name'
|
38
|
-
@name = @remotename
|
39
|
-
@remotetype = load 'remote-type'
|
73
|
+
@scale = load 'scale'
|
40
74
|
@width = load 'width'
|
41
|
-
|
75
|
+
@id = "'%s::%s' " % [@table,@remoteName]
|
76
|
+
# return self
|
42
77
|
end
|
43
78
|
|
44
79
|
def load nodeName
|
45
80
|
node = @node.at_xpath(nodeName)
|
46
|
-
val
|
47
|
-
# puts "==== MD node:'#{nodeName}' \t nil?'#{node.nil?}' \t == val:#{val} \t = '#{node}' "
|
81
|
+
val = node.nil? ? nil : node.text.strip.gsub(/^\[|\]$/,'')
|
48
82
|
return val
|
49
83
|
end
|
50
84
|
|
85
|
+
def to_s
|
86
|
+
@id
|
87
|
+
end
|
88
|
+
|
89
|
+
def <=>(other)
|
90
|
+
@id <=> other.id
|
91
|
+
end
|
92
|
+
|
51
93
|
end
|
52
94
|
|
53
95
|
end
|