Graphiclious 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (50) hide show
  1. data/README.txt +126 -0
  2. data/Rakefile +28 -0
  3. data/bin/graphiclious +3 -0
  4. data/doc/cloud.jpg +0 -0
  5. data/doc/index.html +32 -0
  6. data/doc/screenshots/default.css +44 -0
  7. data/doc/screenshots/original/blue-style-1.html +37 -0
  8. data/doc/screenshots/original/blue-style-1.jpg +0 -0
  9. data/doc/screenshots/original/cornflower.html +37 -0
  10. data/doc/screenshots/original/cornflower.jpg +0 -0
  11. data/doc/screenshots/original/custom-style.html +37 -0
  12. data/doc/screenshots/original/custom-style.jpg +0 -0
  13. data/doc/screenshots/original/sample-graph.html +37 -0
  14. data/doc/screenshots/original/sample-graph.jpg +0 -0
  15. data/doc/screenshots/original/user-interface.html +37 -0
  16. data/doc/screenshots/original/user-interface.jpg +0 -0
  17. data/doc/screenshots/thumb.html +46 -0
  18. data/doc/screenshots/thumb/t_blue-style-1.jpg +0 -0
  19. data/doc/screenshots/thumb/t_cornflower.jpg +0 -0
  20. data/doc/screenshots/thumb/t_custom-style.jpg +0 -0
  21. data/doc/screenshots/thumb/t_sample-graph.jpg +0 -0
  22. data/doc/screenshots/thumb/t_user-interface.jpg +0 -0
  23. data/doc/style.css +60 -0
  24. data/gemspec.rb +45 -0
  25. data/lib/graphiclious.rb +78 -0
  26. data/lib/graphiclious/delicious2yaml.rb +235 -0
  27. data/lib/graphiclious/dot_attributes.rb +35 -0
  28. data/lib/graphiclious/environment.rb +36 -0
  29. data/lib/graphiclious/gui.rb +260 -0
  30. data/lib/graphiclious/help.rb +22 -0
  31. data/lib/graphiclious/require_fox.rb +12 -0
  32. data/lib/graphiclious/url_graph.rb +135 -0
  33. data/lib/graphiclious/utf8.rb +19 -0
  34. data/lib/graphiclious/version.rb +8 -0
  35. data/lib/graphiclious/yaml-links2html.rb +476 -0
  36. data/options.yaml +7 -0
  37. data/styles/blue-style-1/dot-attributes.yaml +21 -0
  38. data/styles/blue-style-1/extern.gif +0 -0
  39. data/styles/blue-style-1/style.css +128 -0
  40. data/styles/cornflower/cornflower.png +0 -0
  41. data/styles/cornflower/dot-attributes.yaml +21 -0
  42. data/styles/cornflower/extern.gif +0 -0
  43. data/styles/cornflower/h3.gif +0 -0
  44. data/styles/cornflower/menuitem.png +0 -0
  45. data/styles/cornflower/menuselected.png +0 -0
  46. data/styles/cornflower/style.css +149 -0
  47. data/styles/cornflower/tr-edge.png +0 -0
  48. data/test/test_delicious2yaml.rb +97 -0
  49. data/wickie +249 -0
  50. metadata +119 -0
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+ #$Id: dot_attributes.rb,v 1.1 2005/04/13 21:16:14 wsdng Exp $
3
+
4
+ class DotAttributes
5
+ attr_accessor(:node_attributes,
6
+ :edge_attributes,
7
+ :graph_attributes)
8
+
9
+ def initialize
10
+ @node_attributes = {
11
+ 'shape' => 'box',
12
+ 'style' => 'filled',
13
+ 'height' => '0.2',
14
+ 'width' => '1',
15
+ #'fixedsize' => 'true',
16
+ 'fontname' => 'Arial',
17
+ 'fontsize' => '10',
18
+ 'fontcolor' => 'blue',
19
+ 'color' => 'orange',
20
+ 'fillcolor' => 'orange'
21
+ }
22
+ @edge_attributes = {}
23
+ @graph_attributes = {
24
+ 'bgcolor' => 'firebrick',
25
+ 'rankdir' => 'LR',
26
+ 'ranksep' => '0.2',
27
+ 'nodesep' => '0.1',
28
+ 'orientation' => 'portrait',
29
+ 'labelloc' => 'top',
30
+ 'label' => 'Tag Relations',
31
+ 'name' => 'UrlGraph'
32
+ }
33
+ end
34
+ end
35
+
@@ -0,0 +1,36 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # Require this file will set GRAPHICLIOUS_PATH to an appropriate value.
4
+ #
5
+ # The standard method is to find the installed gem and let
6
+ # GRAPHICLIOUS_PATH point to the gem's installation directory.
7
+ #
8
+ # If the gem isn't found, we assume that Graphiclious has been started
9
+ # from within the graphiclious directory itself and therefore set
10
+ # GRAPHICLIOUS_PATH = '.'. (This is often the case during development.)
11
+ #
12
+ # Note that GRAPHICLIOUS_PATH uses the Ruby notation of the path.
13
+ #
14
+ # So, what ist this good for? We need to find the style directories
15
+ # provided with the gem to make those styles availiable without any
16
+ # additional user interaction. We might use the path for finding icons
17
+ # or other user-independent ressource files, too.
18
+
19
+ begin
20
+ require 'rubygems'
21
+ require 'graphiclious/version.rb'
22
+
23
+ # Try to find lib directory of this specific Graphiclious gem version
24
+ def findGraphicliousDirectory
25
+ graphicliousGemDir = "Graphiclious-#{Graphiclious.version}"
26
+ all = Gem::path.collect { |gempath|
27
+ File.join(gempath, 'gems', graphicliousGemDir)
28
+ }.select { |f|
29
+ File.directory?(f)
30
+ }
31
+ all.empty? ? '.' : all.first
32
+ end
33
+ GRAPHICLIOUS_PATH = findGraphicliousDirectory
34
+ rescue LoadError
35
+ GRAPHICLIOUS_PATH = '.'
36
+ end
@@ -0,0 +1,260 @@
1
+ #!/usr/bin/env ruby
2
+ #$Id: gui.rb,v 1.18 2006/03/08 20:28:47 wsdng Exp $
3
+
4
+ require 'graphiclious/require_fox'
5
+ require 'graphiclious/delicious2yaml'
6
+ require 'graphiclious/yaml-links2html'
7
+
8
+ class GraphicliousMainWindow < Fox::FXMainWindow
9
+ include Fox
10
+ include Responder
11
+
12
+ def initialize(app, user=nil, password=nil, style=nil)
13
+ super(app, "Graphiclious")
14
+ @user = user
15
+ @password = password
16
+ @style = style
17
+ @stylesFolder = YamlLinksToHtml::STYLES_FOLDER
18
+ @workingDir = Dir.getwd
19
+ initView
20
+ updateUserList
21
+ updateStylesList
22
+ setOptionDelicious(false)
23
+ connect(SEL_CLOSE, method(:onCmdQuit))
24
+ end
25
+
26
+ def newButton(frame, txt)
27
+ b = FXButton.new(frame, txt)
28
+ b.setLayoutHints(LAYOUT_LEFT)
29
+ b.width = 100
30
+ b.height = 24
31
+ b
32
+ end
33
+
34
+ def newCheckBox(frame, txt)
35
+ cbFlags = ICON_BEFORE_TEXT|LAYOUT_SIDE_TOP
36
+ cb = FXCheckButton.new(frame, txt, nil, 0, cbFlags)
37
+ cb.setCheck(true)
38
+ cb
39
+ end
40
+
41
+ def newFrame(frame)
42
+ frame.setFrameStyle(FRAME_NONE)
43
+ frame.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X)
44
+ frame
45
+ end
46
+
47
+ def noPaddingForFrame(frame)
48
+ frame.padBottom = 0
49
+ frame.padLeft = 0
50
+ frame.padRight = 0
51
+ frame.padTop = 0
52
+ end
53
+
54
+ def initView
55
+ basicFrame = newFrame(FXVerticalFrame.new(self))
56
+ basicFrame.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_Y)
57
+
58
+ # input user
59
+ userAndPasswordFrame = newFrame(FXMatrix.new(basicFrame, 2, MATRIX_BY_COLUMNS))
60
+ userAndPasswordFrame.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
61
+ FXLabel.new(userAndPasswordFrame, "Working directory:")
62
+ wdFrame = newFrame(FXHorizontalFrame.new(userAndPasswordFrame))
63
+ wdFrame.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
64
+ noPaddingForFrame(wdFrame)
65
+ @workingDirTextField = FXTextField.new(wdFrame,
66
+ 32, nil, 0,
67
+ TEXTFIELD_NORMAL)
68
+ @workingDirTextField.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
69
+ @workingDirTextField.setText(@workingDir)
70
+ wdChooseButton = newButton(wdFrame, '..')
71
+ wdChooseButton.connect(SEL_COMMAND, method(:onChooseWorkingDir))
72
+
73
+ FXLabel.new(userAndPasswordFrame, "Enter User:")
74
+ @userTextField = newCombobox(userAndPasswordFrame, 32,
75
+ 10.size,
76
+ nil, 0, COMBOBOX_INSERT_LAST)
77
+ @userTextField.setFrameStyle(FRAME_SUNKEN|FRAME_THICK)
78
+ @userTextField.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
79
+
80
+ # input password
81
+ FXLabel.new(userAndPasswordFrame, "Password:")
82
+ @passwordTextField = FXTextField.new(userAndPasswordFrame,
83
+ 32, nil, 0,
84
+ TEXTFIELD_NORMAL|TEXTFIELD_PASSWD)
85
+ @passwordTextField.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
86
+ @passwordTextField.setText(@password) if @password
87
+
88
+ # input style
89
+ FXLabel.new(userAndPasswordFrame, "Style:")
90
+ @styleTextField = newCombobox(userAndPasswordFrame, 32,
91
+ 10,
92
+ nil, 0, COMBOBOX_INSERT_LAST)
93
+ @styleTextField.setFrameStyle(FRAME_SUNKEN|FRAME_THICK)
94
+ @styleTextField.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
95
+
96
+ # set actions
97
+
98
+ optionsFrame = newFrame(FXMatrix.new(basicFrame, 2, MATRIX_BY_COLUMNS))
99
+ optionsFrame.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
100
+
101
+ @checkFetch = newCheckBox(optionsFrame, 'Fetch links from del.icio.us')
102
+ fetchFrame = newFrame(FXVerticalFrame.new(optionsFrame))
103
+ noPaddingForFrame(fetchFrame)
104
+ fetchFrame.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
105
+
106
+ @fetchMode = newCombobox(fetchFrame, 10,
107
+ Delicious2Yaml::MODES.size,
108
+ nil, 0, COMBOBOX_STATIC)
109
+ @fetchMode.setFrameStyle(FRAME_SUNKEN|FRAME_THICK)
110
+ modeKeys = Delicious2Yaml::MODES.keys.sort {|m1, m2| m1.to_s <=> m2.to_s}
111
+ modeKeys.each { |m| @fetchMode.appendItem(Delicious2Yaml::MODES[m]) }
112
+ @fetchMode.setText(Delicious2Yaml::MODES[modeKeys.first])
113
+ @fetchMode.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
114
+
115
+ @checkGenerate = newCheckBox(optionsFrame, 'Generate HTML')
116
+ generateFrame = newFrame(FXVerticalFrame.new(optionsFrame))
117
+ noPaddingForFrame(generateFrame)
118
+ generateFrame.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
119
+
120
+ @checkDelicious = newCheckBox(generateFrame, 'Link to del.icio.us')
121
+ @checkBundles = newCheckBox(generateFrame, 'Include bundles')
122
+ @checkGraphViews = newCheckBox(generateFrame, 'Include graph views')
123
+ @checkGraphThumbs = newCheckBox(generateFrame, 'Graph thumbnails')
124
+ includePrivateFrame = newFrame(FXHorizontalFrame.new(generateFrame))
125
+ noPaddingForFrame(includePrivateFrame)
126
+ includePrivateFrame.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_COLUMN)
127
+ @checkIncludePrivate = newCheckBox(includePrivateFrame, 'Include private?')
128
+ @checkReallyIncludePrivate = newCheckBox(includePrivateFrame, 'Really?')
129
+ @checkIncludePrivate.setCheck(false)
130
+ @checkReallyIncludePrivate.setCheck(false)
131
+
132
+ # buttons
133
+ buttonFrame = newFrame(FXHorizontalFrame.new(basicFrame))
134
+ buttonFrame.setPackingHints(PACK_UNIFORM_WIDTH)
135
+ okButton = newButton(buttonFrame, 'Ok')
136
+ okButton.connect(SEL_COMMAND, method(:onCmdOk))
137
+ cancelButton = newButton(buttonFrame, 'Cancel')
138
+ cancelButton.connect(SEL_COMMAND, method(:onCmdQuit))
139
+
140
+ @cmdOutput = FXText.new(basicFrame, nil, 0,
141
+ TEXT_READONLY|TEXT_WORDWRAP|TEXT_SHOWACTIVE)
142
+ @cmdOutput.setLayoutHints(LAYOUT_LEFT|LAYOUT_FILL_X|LAYOUT_FILL_Y)
143
+ end
144
+
145
+ def setOptionFetch(bool)
146
+ @checkFetch.setCheck(bool ? true : false)
147
+ end
148
+
149
+ def setOptionGenerate(bool)
150
+ @checkGenerate.setCheck(bool ? true : false)
151
+ end
152
+
153
+ def setOptionDelicious(bool)
154
+ @checkDelicious.setCheck(bool ? true : false)
155
+ end
156
+
157
+ def setOptionBundles(bool)
158
+ @checkBundles.setCheck(bool ? true : false)
159
+ end
160
+
161
+ def setOptionGraphViews(bool)
162
+ @checkGraphViews.setCheck(bool ? true : false)
163
+ end
164
+
165
+ def setOptionGraphThumbs(bool)
166
+ @checkGraphThumbs.setCheck(bool ? true : false)
167
+ end
168
+
169
+ def create
170
+ resize(340, 400)
171
+ place(PLACEMENT_SCREEN)
172
+ super
173
+ show
174
+ end
175
+
176
+ def appendText(txt)
177
+ @cmdOutput.appendText(txt, true)
178
+ @cmdOutput.makePositionVisible(@cmdOutput.text.size)
179
+ @cmdOutput.repaint
180
+ end
181
+
182
+ def updateUserList
183
+ cachedUsers = YamlLinksToHtml.guess_users_within(@workingDir).collect {
184
+ |userDir|
185
+ File.basename(userDir)
186
+ }
187
+ @userTextField
188
+ cachedUsers.each { |m| @userTextField.appendItem(m) }
189
+ @userTextField.setText(@user ? @user : cachedUsers.first)
190
+ end
191
+
192
+ def updateStylesList
193
+ @styleTextField.clearItems
194
+ [@workingDir, @stylesFolder].each do
195
+ |folder|
196
+ YamlLinksToHtml.guess_styles_within(folder).each do
197
+ |styleDir|
198
+ @styleTextField.appendItem(styleDir)
199
+ end
200
+ end
201
+ @styleTextField.setText(@style ? @style : "")
202
+ end
203
+
204
+ def onChooseWorkingDir(sender, sel, ptr)
205
+ newFolder = FXDirDialog.getOpenDirectory(self, "Choose working directory", @workingDir)
206
+ return if newFolder.nil?
207
+ @workingDir = newFolder.gsub(/\\/, File::SEPARATOR)
208
+ @workingDirTextField.setText(@workingDir)
209
+ updateUserList
210
+ updateStylesList
211
+ end
212
+
213
+ def onCmdOk(sender, sel, ptr)
214
+ @user = @userTextField.getText
215
+ @password = @passwordTextField.getText
216
+ @style = @styleTextField.getText
217
+ @workingDir = @workingDirTextField.getText
218
+ mode = Delicious2Yaml::MODES.index(@fetchMode.getText)
219
+ outputBlock = proc { |line| appendText("#{line}\n") }
220
+ appendText("-Start-\n")
221
+ begin
222
+ getApp.beginWaitCursor
223
+ if @checkFetch.checked?
224
+ appendText("-Start import-\n")
225
+ importer = Delicious2Yaml.new(@user, @password)
226
+ importer.set_protocol_block(outputBlock)
227
+ importer.set_working_dir(@workingDir)
228
+ importer.set_mode(mode)
229
+ importer.run
230
+ setOptionFetch(false)
231
+ appendText("-Done import-\n")
232
+ end
233
+ if @checkGenerate.checked?
234
+ appendText("-Start generation-\n")
235
+ exporter = YamlLinksToHtml.new
236
+ exporter.set_protocol_block(outputBlock)
237
+ exporter.set_working_dir(@workingDir)
238
+ exporter.set_user(@user)
239
+ exporter.set_style(@style)
240
+ exporter.link_to_delicious_if_possible = @checkDelicious.checked?
241
+ exporter.use_bundles = @checkBundles.checked?
242
+ exporter.build_graph_thumbs = @checkGraphThumbs.checked?
243
+ exporter.include_graph_views = @checkGraphViews.checked?
244
+ exporter.include_private = @checkIncludePrivate.checked? && @checkReallyIncludePrivate.checked?
245
+ exporter.run
246
+ appendText("-Done generation-\n")
247
+ end
248
+ appendText("-Done-\n")
249
+ rescue StandardError => bang
250
+ appendText("Fatal error: " + bang + "\n-\n")
251
+ ensure
252
+ getApp.endWaitCursor
253
+ end
254
+
255
+ end
256
+
257
+ def onCmdQuit(sender, sel, ptr)
258
+ getApp.exit(0)
259
+ end
260
+ end
@@ -0,0 +1,22 @@
1
+ #!/usr/bin/ruby
2
+ #$Id: help.rb,v 1.3 2005/11/26 14:26:21 wsdng Exp $
3
+
4
+ module Graphiclious
5
+ def self.help
6
+ h = String.new
7
+ h <<
8
+ "Commandline options:\n" <<
9
+ "--bundles/-b include bundles\n" <<
10
+ "--delicious/-d prefer links to del.icio.us\n" <<
11
+ "--gui/-g will start a graphical user interface\n" <<
12
+ "--help/-h will display this text\n" <<
13
+ "--noupdate/-l do not connect to del.icio.us\n" <<
14
+ "--nohtml/-o ommit html generation\n" <<
15
+ "--password/-p specify password for given user\n" <<
16
+ "--user/-u specify a del.icio.us user\n" <<
17
+ "--version/-v prints actual version\n" <<
18
+ "Without gui option you'll need to specify user and\n" <<
19
+ "password."
20
+ h
21
+ end
22
+ end
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require_gem 'fxruby', '>= 1.2'
5
+
6
+ def newCombobox(p1, p2, p3, p4, p5, p6)
7
+ box = Fox::FXComboBox.new(p1, p2, p4, p5, p6)
8
+ box.setNumVisible(p3)
9
+ box
10
+ end
11
+
12
+
@@ -0,0 +1,135 @@
1
+ #!/usr/bin/env ruby
2
+ #$Id: url_graph.rb,v 1.8 2006/03/09 22:57:52 wsdng Exp $
3
+
4
+ begin
5
+ require 'rubygems'
6
+ require_gem 'rgl'
7
+ rescue LoadError
8
+ end
9
+ require 'rgl/adjacency'
10
+ require 'rgl/dot'
11
+ require 'rgl/implicit'
12
+ require 'graphiclious/dot_attributes'
13
+
14
+ # UrlGraph models relations between tags
15
+
16
+ class UrlGraph < RGL::ImplicitGraph
17
+
18
+ attr_accessor(:fixed_dot_attributes, :filter_set)
19
+
20
+ def initialize
21
+ @fixed_dot_attributes = DotAttributes.new
22
+ @wrapped_graph = RGL::AdjacencyGraph.new
23
+ @filter = proc { |v| @filter_set.nil? || @filter_set.include?(v) }
24
+ @filter_set = nil
25
+ super { |g|
26
+ g.vertex_iterator { |b|
27
+ @wrapped_graph.each_vertex { |v| b.call(v) if @filter.call(v) }
28
+ }
29
+ g.adjacent_iterator { |v, b|
30
+ @wrapped_graph.each_adjacent(v) { |u| b.call(u) if @filter.call(u) }
31
+ }
32
+ }
33
+ end
34
+
35
+ def add_vertex(vertex)
36
+ @wrapped_graph.add_vertex(vertex)
37
+ end
38
+
39
+ def add_edge(tag, otherTag)
40
+ @wrapped_graph.add_edge(tag, otherTag)
41
+ end
42
+
43
+ # some characters are not allowed in filenames and need to be replaced
44
+ def self.tag2file(tag)
45
+ # Windows: \/:*?"<>|
46
+ # Linux: ()
47
+ # Call to Graphviz: Space
48
+ tag.gsub(/[\\\/:*?"<>|() ]/, '_')
49
+ end
50
+
51
+ def self.ankor(url, name)
52
+ "<a class=\"extern\" href=\"#{url}\" target=\"_top\">#{name}</a>"
53
+ end
54
+
55
+ def self.intern_ankor(url, name, style=nil)
56
+ link = "<a href=\"#{tag2file(url)}\">#{name}</a>"
57
+ link = "<span class=\"#{style}\">#{link}</span>" unless style.nil?
58
+ link
59
+ end
60
+
61
+ def self.node_url(tag)
62
+ "#{tag2file(tag)}.htm"
63
+ end
64
+
65
+ def self.node_ankor(tag, style=nil)
66
+ intern_ankor(node_url(tag), tag, style)
67
+ end
68
+
69
+ def self.edge_name(tag1, tag2)
70
+ if tag1 <= tag2
71
+ tag1 + '-' + tag2
72
+ else
73
+ tag2 + '-' + tag1
74
+ end
75
+ end
76
+
77
+ def self.edge_url(tag1, tag2)
78
+ node_url(edge_name(tag1, tag2))
79
+ end
80
+
81
+ def self.edge_ankor(tag1, tag2, style=nil)
82
+ intern_ankor(edge_url(tag1, tag2), edge_name(tag1, tag2), style)
83
+ end
84
+
85
+ def fixed_dot_node_attributes
86
+ fixed_dot_attributes.node_attributes
87
+ end
88
+
89
+ def fixed_dot_edge_attributes
90
+ fixed_dot_attributes.edge_attributes
91
+ end
92
+
93
+ def fixed_dot_graph_attributes
94
+ fixed_dot_attributes.graph_attributes
95
+ end
96
+
97
+ def d_quote(string)
98
+ "\"#{string}\""
99
+ end
100
+
101
+ def to_dot_graph(params = {})
102
+ fixed_dot_graph_attributes.each { |k, v|
103
+ params[k] ||= v
104
+ }
105
+ graph = (directed? ? DOT::DOTDigraph : DOT::DOTSubgraph).new(params)
106
+ edge_class = directed? ? DOT::DOTDirectedEdge : DOT::DOTEdge
107
+ dot_node_attributes = fixed_dot_node_attributes.clone
108
+ each_vertex do |v|
109
+ name = v.to_s
110
+ dot_node_attributes['name'] = d_quote(name)
111
+ dot_node_attributes['URL'] = d_quote(UrlGraph.node_url(name))
112
+ dot_node_attributes['label'] = name
113
+ graph << DOT::DOTNode.new(dot_node_attributes)
114
+ end
115
+ dot_edge_attributes = fixed_dot_edge_attributes.clone
116
+ each_edge do |u,v|
117
+ tag1 = u.to_s
118
+ tag2 = v.to_s
119
+ dot_edge_attributes['from'] = d_quote(tag1)
120
+ dot_edge_attributes['to'] = d_quote(tag2)
121
+ dot_edge_attributes['URL'] = d_quote(UrlGraph.edge_url(tag1, tag2))
122
+ graph << edge_class.new(dot_edge_attributes)
123
+ end
124
+ graph
125
+ end
126
+
127
+ # Output the DOT-graph to a file.
128
+
129
+ def print_dotted_on_file(filename = 'graph.dot', params = {})
130
+ File.open(filename, "w") {|f|
131
+ print_dotted_on(params, f)
132
+ }
133
+ end
134
+ end
135
+