twb 0.0.31 → 0.0.33
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/bg.cmd +5 -0
- data/lib/twb.rb +7 -2
- data/lib/twb/dashboard.rb +7 -7
- data/lib/twb/datasource.rb +38 -9
- data/lib/twb/docdashboard.rb +123 -0
- data/lib/twb/field.rb +39 -0
- data/lib/twb/hashtohtml.rb +39 -0
- data/lib/twb/htmllistcollapsible.rb +154 -0
- data/lib/twb/localfield.rb +42 -0
- data/lib/twb/metadatafield.rb +55 -0
- data/lib/twb/util/UpLeftArrowsNav.png +0 -0
- data/lib/twb/util/hashtohtml.rb +45 -0
- data/lib/twb/util/htmllistcollapsible.rb +195 -0
- data/lib/twb/window.rb +31 -0
- data/lib/twb/workbook.rb +77 -12
- data/lib/twb/worksheet.rb +29 -1
- data/test/No Content.injected.twb +82 -0
- data/test/No Content.twb +68 -0
- data/test/No Dashboards.injected.twb +618 -0
- data/test/No Dashboards.twb +604 -0
- data/test/Special Documentation.html +198 -0
- data/test/TableauDocInlineCSS.html +165 -0
- data/test/UpLeftArrowsNav.png +0 -0
- data/test/Web Page Dashboards.injected.twb +1361 -0
- data/test/Web Page Dashboards.twb +1347 -0
- data/test/collapsibleList.html +198 -0
- data/test/testDocDashboard.rb +53 -0
- data/test/testDocDashboardCreate.rb +33 -0
- data/test/testHTMLList.rb +23 -0
- data/test/testTwbGem.rb +67 -0
- data/testTwbGem.rb +23 -9
- data/testTwbWrite.rb +22 -0
- data/twb-0.0.32.gem +0 -0
- metadata +29 -12
- data/twb-0.0.1.gem +0 -0
- data/twb-0.0.2.gem +0 -0
- data/twb-0.0.21.gem +0 -0
- data/twb-0.0.22.gem +0 -0
- data/twb-0.0.23.gem +0 -0
- data/twb-0.0.24.gem +0 -0
- data/twb-0.0.25.gem +0 -0
- data/twb-0.0.26.gem +0 -0
- data/twb-0.0.27.gem +0 -0
- data/twb-0.0.29.gem +0 -0
- data/twb-0.0.30.gem +0 -0
data/bg.cmd
ADDED
data/lib/twb.rb
CHANGED
@@ -3,10 +3,15 @@ require_relative 'twb/dashboard'
|
|
3
3
|
require_relative 'twb/worksheet'
|
4
4
|
require_relative 'twb/datasource'
|
5
5
|
require_relative 'twb/storyboard'
|
6
|
+
require_relative 'twb/window'
|
7
|
+
require_relative 'twb/localfield'
|
8
|
+
require_relative 'twb/metadatafield'
|
9
|
+
require_relative 'twb/docdashboard'
|
10
|
+
require_relative 'twb/util/htmllistcollapsible'
|
6
11
|
|
7
|
-
# Represents Tableau Workbooks and
|
12
|
+
# Represents Tableau Workbooks and their contents.
|
8
13
|
#
|
9
14
|
module Twb
|
10
|
-
VERSION = '0.0.
|
15
|
+
VERSION = '0.0.33'
|
11
16
|
end
|
12
17
|
|
data/lib/twb/dashboard.rb
CHANGED
@@ -22,25 +22,25 @@ module Twb
|
|
22
22
|
|
23
23
|
@@hasher = Digest::SHA256.new
|
24
24
|
|
25
|
-
attr_reader :node, :name, :worksheets, :autosize, :size, :maxheight, :maxwidth, :minheight, :minwidth, :rangesize, :dimensions
|
25
|
+
attr_reader :node, :name, :worksheets, :autosize, :size, :maxheight, :maxwidth, :minheight, :minwidth, :rangesize, :dimensions, :zonecount
|
26
26
|
|
27
|
-
def initialize dashboardNode
|
28
|
-
"initialize Dashboard"
|
27
|
+
def initialize dashboardNode, twbworksheets
|
29
28
|
@node = dashboardNode
|
30
29
|
@name = @node.attr('name')
|
31
30
|
@size = @node.xpath('./size')
|
32
31
|
@autosize = @size.empty?
|
33
32
|
loadSize @size unless @autosize
|
34
|
-
loadSheets
|
33
|
+
loadSheets twbworksheets
|
34
|
+
@zonecount = @node.xpath('.//zone').length
|
35
35
|
return self
|
36
36
|
end
|
37
37
|
|
38
|
-
def loadSheets
|
38
|
+
def loadSheets twbworksheets
|
39
39
|
@sheets = {}
|
40
40
|
dsheets = @node.xpath('.//zone[@name]').to_a
|
41
41
|
dsheets.each do |sheetNode|
|
42
|
-
|
43
|
-
@sheets[
|
42
|
+
sheetname = sheetNode.attr('name')
|
43
|
+
@sheets[sheetname] = twbworksheets[sheetname]
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
data/lib/twb/datasource.rb
CHANGED
@@ -22,7 +22,7 @@ module Twb
|
|
22
22
|
|
23
23
|
@@hasher = Digest::SHA256.new
|
24
24
|
|
25
|
-
attr_reader :node, :name, :caption, :uiname, :connHash, :class, :connection, :tables
|
25
|
+
attr_reader :node, :name, :caption, :uiname, :connHash, :class, :connection, :tables, :localfields, :metadatafields
|
26
26
|
|
27
27
|
def initialize dataSourceNode
|
28
28
|
@node = dataSourceNode
|
@@ -30,25 +30,33 @@ module Twb
|
|
30
30
|
@caption = @node.attr('caption')
|
31
31
|
@uiname = if @caption.nil? || @caption == '' then @name else @caption end
|
32
32
|
processConnection
|
33
|
+
processFields
|
33
34
|
return self
|
34
35
|
end
|
35
36
|
|
36
37
|
def processConnection
|
37
|
-
@connHash = ''
|
38
38
|
@connection = @node.at_xpath('./connection')
|
39
39
|
unless @connection.nil?
|
40
40
|
@class = @connection.attribute('class').text
|
41
|
-
|
42
|
-
dsConnStr = ''
|
43
|
-
dsAttributes.each do |attr|
|
44
|
-
dsConnStr += attr.text
|
45
|
-
# Note: '' attributes with value '' don't contribute to the hash
|
46
|
-
end
|
47
|
-
@connHash = Digest::MD5.hexdigest(dsConnStr)
|
41
|
+
setConnectionHash
|
48
42
|
loadTables @connection
|
49
43
|
end
|
50
44
|
end
|
51
45
|
|
46
|
+
# Notes:
|
47
|
+
# - TODO: need to determine which, if any, of the connection attributes should be
|
48
|
+
# included in the hash in order to identify it unambiguously - without
|
49
|
+
# local values that obscure the data source's 'real' identity
|
50
|
+
# - attributes with value '' don't contribute to the hash
|
51
|
+
def setConnectionHash
|
52
|
+
dsAttributes = @node.xpath('./connection/@*')
|
53
|
+
dsConnStr = ''
|
54
|
+
dsAttributes.each do |attr|
|
55
|
+
dsConnStr += attr.text
|
56
|
+
end
|
57
|
+
@connHash = Digest::MD5.hexdigest(dsConnStr)
|
58
|
+
end
|
59
|
+
|
52
60
|
def loadTables connection
|
53
61
|
@tables = {}
|
54
62
|
nodes = connection.xpath(".//relation[@type='table']")
|
@@ -61,6 +69,27 @@ module Twb
|
|
61
69
|
@name == 'Parameters'
|
62
70
|
end
|
63
71
|
|
72
|
+
def processFields
|
73
|
+
# --
|
74
|
+
@localfields = {}
|
75
|
+
nodes = @node.xpath(".//column")
|
76
|
+
nodes.each do |node|
|
77
|
+
field = Twb::LocalField.new(node)
|
78
|
+
@localfields[field.name] = field
|
79
|
+
end
|
80
|
+
# --
|
81
|
+
@metadatafields = {}
|
82
|
+
nodes = @node.xpath("./connection/metadata-records/metadata-record")
|
83
|
+
nodes.each do |node|
|
84
|
+
field = Twb::MetadataField.new(node)
|
85
|
+
@metadatafields[field.name] = field
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def field name
|
90
|
+
field = if localfields[name].nil? then metadatafields[name] end
|
91
|
+
end
|
92
|
+
|
64
93
|
end
|
65
94
|
|
66
95
|
end
|
@@ -0,0 +1,123 @@
|
|
1
|
+
# Copyright (C) 2014, 2015 Chris Gerrard
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require 'nokogiri'
|
17
|
+
require 'set'
|
18
|
+
|
19
|
+
module Twb
|
20
|
+
|
21
|
+
class DocDashboard
|
22
|
+
|
23
|
+
@@types = Set.new( ['h', 'horiz', 'horizontal',
|
24
|
+
'v', 'vert', 'vertical' ] )
|
25
|
+
|
26
|
+
attr_reader :title, :dashnode, :winnode, :type #, :panels, :autosize, :size
|
27
|
+
|
28
|
+
@@oneColWebPageDash = Nokogiri::XML::Document.parse <<-ONECOlWEBDASH
|
29
|
+
<dashdoc>
|
30
|
+
<dashboard name='Single Column Web Page Automatic'>
|
31
|
+
<style></style>
|
32
|
+
<zones>
|
33
|
+
<zone h='100000' id='3' param='vert' type='layout-flow' w='100000' x='0' y='0'>
|
34
|
+
<zone h='6221' id='1' type='title' w='100000' x='0' y='0'></zone>
|
35
|
+
<zone h='93157' id='4' param='horz' type='layout-flow' w='100000' x='0' y='6221'>
|
36
|
+
<zone forceUpdate='' h='93157' id='6' param='URL' type='web' w='99655' x='0' y='6221'></zone>
|
37
|
+
</zone>
|
38
|
+
</zone>
|
39
|
+
</zones>
|
40
|
+
</dashboard>
|
41
|
+
<window auto-hidden='0' class='dashboard' maximized='1' name='Single Column Web Page Automatic'>
|
42
|
+
<zones>
|
43
|
+
<zone h='6221' id='1' name='' type='title' w='100000' x='0' y='0' />
|
44
|
+
<zone forceUpdate='' h='93157' id='5' name='' param='Web Page' type='web' w='50000' x='50000' y='6221' />
|
45
|
+
</zones>
|
46
|
+
</window>
|
47
|
+
</dashdoc>
|
48
|
+
ONECOlWEBDASH
|
49
|
+
# notes:
|
50
|
+
# - adding a size element to the <dashboard element will change it from automatic, e.g.
|
51
|
+
#
|
52
|
+
# <dashboard name='One Column Web Page Laptop (800w 600h)'>
|
53
|
+
# <style>
|
54
|
+
# </style>
|
55
|
+
# <size maxheight='600' maxwidth='800' minheight='600' minwidth='800' />
|
56
|
+
#
|
57
|
+
# - the 'name' sttributes for the window and dashboard must match
|
58
|
+
|
59
|
+
def title=(title)
|
60
|
+
@title = title
|
61
|
+
@dashnode['name'] = title
|
62
|
+
@winnode['name'] = title
|
63
|
+
end
|
64
|
+
|
65
|
+
def size=(size)
|
66
|
+
@size = size
|
67
|
+
end
|
68
|
+
|
69
|
+
def initialize type
|
70
|
+
dashboard = if @@types.include?(type)
|
71
|
+
then if type[0] == 'v'
|
72
|
+
then DocDashboardVert.new
|
73
|
+
else DocDashboardHoriz.new
|
74
|
+
end
|
75
|
+
else nil
|
76
|
+
end
|
77
|
+
return dashboard
|
78
|
+
end
|
79
|
+
|
80
|
+
def to_s
|
81
|
+
return title + ' :: ' + type
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
class DocDashboardWebVert < DocDashboard
|
87
|
+
|
88
|
+
attr_reader :url
|
89
|
+
|
90
|
+
def initialize
|
91
|
+
@type = 'columnar Web Page'
|
92
|
+
@dashnode = @@oneColWebPageDash.at_xpath('//dashboard')
|
93
|
+
@winnode = @@oneColWebPageDash.at_xpath('//window')
|
94
|
+
end
|
95
|
+
|
96
|
+
def title=(title)
|
97
|
+
@title = title
|
98
|
+
@dashnode['name'] = title
|
99
|
+
@winnode['name'] = title
|
100
|
+
end
|
101
|
+
|
102
|
+
def url=(url)
|
103
|
+
dashwebzone = @dashnode.at_xpath('.//zone[@type="web"]')
|
104
|
+
dashwebzone['param'] = url # unless dashwebzone.nil?
|
105
|
+
winwebzone = @winnode.at_xpath('.//zone[@type="web"]')
|
106
|
+
winwebzone['param'] = url # unless winwebzone.nil?
|
107
|
+
end
|
108
|
+
|
109
|
+
def url
|
110
|
+
dashurl = @dashnode.at_xpath('.//zone[@type="web"]').attribute('param').value
|
111
|
+
winurl = @winnode.at_xpath( './/zone[@type="web"]').attribute('param').value
|
112
|
+
url = if dashurl == winurl then dashurl end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
class DocDashboardWebHoriz < DocDashboard
|
118
|
+
def initialize
|
119
|
+
@type = 'horizontal'
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
end
|
data/lib/twb/field.rb
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
# Copyright (C) 2014, 2015 Chris Gerrard
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require 'nokogiri'
|
17
|
+
|
18
|
+
module Twb
|
19
|
+
|
20
|
+
class Field
|
21
|
+
|
22
|
+
attr_reader :node, :name, :datatype, :role, :type, :hidden, :caption, :aggregation, :uiname
|
23
|
+
|
24
|
+
def initialize fieldNode
|
25
|
+
@node = fieldNode
|
26
|
+
@name = @node.attr('name')
|
27
|
+
@datatype = @node.attr('datatype')
|
28
|
+
@role = @node.attr('role')
|
29
|
+
@type = @node.attr('type')
|
30
|
+
@hidden = @node.attr('hidden')
|
31
|
+
@caption = @node.attr('caption')
|
32
|
+
@aggregation = @node.attr('aggregation')
|
33
|
+
@uiname = if @caption.nil? || @caption == '' then @name else @caption end
|
34
|
+
return self
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
class HashToHTMLList
|
2
|
+
def initialize(hash)
|
3
|
+
@hash = hash
|
4
|
+
@indent = " "
|
5
|
+
@tag_space = ""
|
6
|
+
@level = 0
|
7
|
+
@out = []
|
8
|
+
end
|
9
|
+
|
10
|
+
def append(tag,value=nil)
|
11
|
+
str = @indent * @level + "#{tag}"
|
12
|
+
str += @tag_space + value unless value.nil?
|
13
|
+
str += "\n"
|
14
|
+
@out << str
|
15
|
+
end
|
16
|
+
|
17
|
+
def ul(hash)
|
18
|
+
open_tag('ul') { li(hash) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def li(hash)
|
22
|
+
@level += 1
|
23
|
+
hash.each do |key,value|
|
24
|
+
open_tag('li',key) { ul(value) if value.is_a?(Hash) }
|
25
|
+
end
|
26
|
+
@level -= 1
|
27
|
+
end
|
28
|
+
|
29
|
+
def list
|
30
|
+
ul(@hash)
|
31
|
+
@out.join
|
32
|
+
end
|
33
|
+
|
34
|
+
def open_tag(tag,value=nil,&block)
|
35
|
+
append("<#{tag}>",value)
|
36
|
+
yield if block_given?
|
37
|
+
append("</#{tag}>")
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,154 @@
|
|
1
|
+
# Copyright (C) 2014, 2015 Chris Gerrard
|
2
|
+
#
|
3
|
+
# This program is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# This program is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require 'nokogiri'
|
17
|
+
|
18
|
+
module Twb
|
19
|
+
|
20
|
+
class HTMLListCollapsible
|
21
|
+
|
22
|
+
@@doc = Nokogiri::HTML::Document.parse <<-COLLAPSIBLELIST
|
23
|
+
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
|
24
|
+
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
|
25
|
+
<head>
|
26
|
+
<title>Tableau Documentation</title>
|
27
|
+
|
28
|
+
<style type="text/css">
|
29
|
+
body, a {
|
30
|
+
color: #3B4C56;
|
31
|
+
font-family: sans-serif;
|
32
|
+
font-size: 14px;
|
33
|
+
text-decoration: none;
|
34
|
+
}
|
35
|
+
#pgtitle
|
36
|
+
{
|
37
|
+
margin: 0px 0px 20px;
|
38
|
+
font-size: 14pt;
|
39
|
+
text-align: center;
|
40
|
+
}
|
41
|
+
a{
|
42
|
+
cursor:pointer;
|
43
|
+
}
|
44
|
+
.tree ul {
|
45
|
+
list-style: none outside none;
|
46
|
+
}
|
47
|
+
.tree li a {
|
48
|
+
line-height: 25px;
|
49
|
+
}
|
50
|
+
.tree > ul > li > a {
|
51
|
+
color: #3B4C56;
|
52
|
+
display: block;
|
53
|
+
font-weight: normal;
|
54
|
+
position: relative;
|
55
|
+
text-decoration: none;
|
56
|
+
}
|
57
|
+
.tree li.parent > a {
|
58
|
+
padding: 0 0 0 28px;
|
59
|
+
}
|
60
|
+
.tree li.parent > a:before {
|
61
|
+
background-image: url("Controls.png");
|
62
|
+
background-position: 20px center;
|
63
|
+
content: "";
|
64
|
+
display: block;
|
65
|
+
height: 20px;
|
66
|
+
left: 0;
|
67
|
+
position: absolute;
|
68
|
+
top: 2px;
|
69
|
+
vertical-align: middle;
|
70
|
+
width: 20px;
|
71
|
+
}
|
72
|
+
.tree ul li.active > a:before {
|
73
|
+
background-position: 0 center;
|
74
|
+
}
|
75
|
+
.tree ul li ul {
|
76
|
+
border-left: 1px solid #D9DADB;
|
77
|
+
display: none;
|
78
|
+
margin: 0 0 0 12px;
|
79
|
+
overflow: hidden;
|
80
|
+
padding: 0 0 0 25px;
|
81
|
+
}
|
82
|
+
.tree ul li ul li {
|
83
|
+
position: relative;
|
84
|
+
}
|
85
|
+
.tree ul li ul li:before {
|
86
|
+
border-bottom: 1px dashed #E2E2E3;
|
87
|
+
content: "";
|
88
|
+
left: -20px;
|
89
|
+
position: absolute;
|
90
|
+
top: 12px;
|
91
|
+
width: 15px;
|
92
|
+
}
|
93
|
+
#wrapper {
|
94
|
+
margin: 0 auto;
|
95
|
+
width: 300px;
|
96
|
+
}
|
97
|
+
</style>
|
98
|
+
|
99
|
+
<script src="http://code.jquery.com/jquery-1.7.2.min.js" type="text/javascript" > </script>
|
100
|
+
|
101
|
+
<script type="text/javascript">
|
102
|
+
$( document ).ready( function( ) {
|
103
|
+
$( '.tree li' ).each( function() {
|
104
|
+
if( $( this ).children( 'ul' ).length > 0 ) {
|
105
|
+
$( this ).addClass( 'parent' );
|
106
|
+
}
|
107
|
+
});
|
108
|
+
|
109
|
+
$( '.tree li.parent > a' ).click( function( ) {
|
110
|
+
$( this ).parent().toggleClass( 'active' );
|
111
|
+
$( this ).parent().children( 'ul' ).slideToggle( 'fast' );
|
112
|
+
});
|
113
|
+
|
114
|
+
$( '#all' ).click( function() {
|
115
|
+
|
116
|
+
$( '.tree li' ).each( function() {
|
117
|
+
$( this ).toggleClass( 'active' );
|
118
|
+
$( this ).children( 'ul' ).slideToggle( 'fast' );
|
119
|
+
});
|
120
|
+
});
|
121
|
+
|
122
|
+
$( '.tree li' ).each( function() {
|
123
|
+
$( this ).toggleClass( 'active' );
|
124
|
+
$( this ).children( 'ul' ).slideToggle( 'fast' );
|
125
|
+
});
|
126
|
+
|
127
|
+
});
|
128
|
+
|
129
|
+
</script>
|
130
|
+
|
131
|
+
</head>
|
132
|
+
<body>
|
133
|
+
<div id="pgtitle">
|
134
|
+
Expandable nested list.
|
135
|
+
</div>
|
136
|
+
<div id="wrapper">
|
137
|
+
<div class="tree">
|
138
|
+
<button id="all">Toggle all</button>
|
139
|
+
<ul>
|
140
|
+
</ul>
|
141
|
+
</div>
|
142
|
+
</div>
|
143
|
+
|
144
|
+
</body>
|
145
|
+
</html>
|
146
|
+
COLLAPSIBLELIST
|
147
|
+
|
148
|
+
def html
|
149
|
+
" here's the @@doc"
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
end
|