ruby-graphviz_c 1.1.0

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.
Files changed (184) hide show
  1. checksums.yaml +7 -0
  2. data/.gemrc +0 -0
  3. data/.gitignore +9 -0
  4. data/.travis.yml +7 -0
  5. data/AUTHORS.rdoc +33 -0
  6. data/CHANGELOG.rdoc +287 -0
  7. data/COPYING.rdoc +133 -0
  8. data/Gemfile +4 -0
  9. data/README.rdoc +206 -0
  10. data/Rakefile +71 -0
  11. data/bin/dot2ruby +91 -0
  12. data/bin/gem2gv +165 -0
  13. data/bin/git2gv +167 -0
  14. data/bin/ruby2gv +234 -0
  15. data/bin/xml2gv +96 -0
  16. data/examples/dot/JSP.dot +52 -0
  17. data/examples/dot/balanced.dot +36 -0
  18. data/examples/dot/cluster.dot +30 -0
  19. data/examples/dot/dotgraph.dot +28 -0
  20. data/examples/dot/fsm.dot +20 -0
  21. data/examples/dot/genetic.dot +118 -0
  22. data/examples/dot/hello.dot +1 -0
  23. data/examples/dot/hello_test.rb +33 -0
  24. data/examples/dot/lion_share.dot +103 -0
  25. data/examples/dot/prof.dot +150 -0
  26. data/examples/dot/psg.dot +28 -0
  27. data/examples/dot/rank.dot +6 -0
  28. data/examples/dot/sdh.dot +284 -0
  29. data/examples/dot/siblings.dot +492 -0
  30. data/examples/dot/so-sample001.gv +30 -0
  31. data/examples/dot/so-sample002.gv +33 -0
  32. data/examples/dot/so-sample003.gv +45 -0
  33. data/examples/dot/test.dot +17 -0
  34. data/examples/dot/test_parse.rb +13 -0
  35. data/examples/dot/this_crach_with_dot_2.20.dot +24 -0
  36. data/examples/dot/unix.dot +104 -0
  37. data/examples/graphml/attributes.ext.graphml +12 -0
  38. data/examples/graphml/attributes.graphml +40 -0
  39. data/examples/graphml/cluster.graphml +75 -0
  40. data/examples/graphml/failed_graph.graphml +461 -0
  41. data/examples/graphml/hyper.graphml +29 -0
  42. data/examples/graphml/nested.graphml +54 -0
  43. data/examples/graphml/port.graphml +32 -0
  44. data/examples/graphml/simple.graphml +30 -0
  45. data/examples/hello.png +0 -0
  46. data/examples/rgv/rgv.ps +125 -0
  47. data/examples/rgv/test_rgv.rb +12 -0
  48. data/examples/sample01.rb +32 -0
  49. data/examples/sample02.rb +42 -0
  50. data/examples/sample03.rb +31 -0
  51. data/examples/sample04.rb +22 -0
  52. data/examples/sample05.rb +32 -0
  53. data/examples/sample06.rb +46 -0
  54. data/examples/sample07.rb +23 -0
  55. data/examples/sample08.rb +34 -0
  56. data/examples/sample09.rb +50 -0
  57. data/examples/sample10.rb +50 -0
  58. data/examples/sample11.rb +42 -0
  59. data/examples/sample12.rb +55 -0
  60. data/examples/sample13.rb +48 -0
  61. data/examples/sample14.rb +44 -0
  62. data/examples/sample15.rb +25 -0
  63. data/examples/sample16.rb +8 -0
  64. data/examples/sample17.rb +92 -0
  65. data/examples/sample18.rb +24 -0
  66. data/examples/sample19.rb +59 -0
  67. data/examples/sample20.rb +47 -0
  68. data/examples/sample21.rb +12 -0
  69. data/examples/sample22.rb +10 -0
  70. data/examples/sample23.rb +11 -0
  71. data/examples/sample24.rb +11 -0
  72. data/examples/sample25.rb +11 -0
  73. data/examples/sample26.rb +8 -0
  74. data/examples/sample27.rb +8 -0
  75. data/examples/sample28.rb +12 -0
  76. data/examples/sample29.rb +8 -0
  77. data/examples/sample30.rb +12 -0
  78. data/examples/sample31.rb +10 -0
  79. data/examples/sample32.rb +14 -0
  80. data/examples/sample33.rb +43 -0
  81. data/examples/sample34.rb +29 -0
  82. data/examples/sample35.rb +43 -0
  83. data/examples/sample36.rb +35 -0
  84. data/examples/sample37.rb +87 -0
  85. data/examples/sample38.rb +12 -0
  86. data/examples/sample39.rb +11 -0
  87. data/examples/sample40.rb +17 -0
  88. data/examples/sample41.rb +8 -0
  89. data/examples/sample42.rb +35 -0
  90. data/examples/sample43.rb +26 -0
  91. data/examples/sample44.rb +97 -0
  92. data/examples/sample45.rb +24 -0
  93. data/examples/sample46.rb +43 -0
  94. data/examples/sample47.rb +7 -0
  95. data/examples/sample48.rb +62 -0
  96. data/examples/sample49.rb +10 -0
  97. data/examples/sample50.rb +215 -0
  98. data/examples/sample51.rb +37 -0
  99. data/examples/sample52.rb +62 -0
  100. data/examples/sample53.rb +26 -0
  101. data/examples/sample54.rb +26 -0
  102. data/examples/sample55.rb +9 -0
  103. data/examples/sample56.rb +10 -0
  104. data/examples/sample57.rb +8 -0
  105. data/examples/sample58.rb +33 -0
  106. data/examples/sample59.rb +14 -0
  107. data/examples/sample60.rb +12 -0
  108. data/examples/sample61.rb +12 -0
  109. data/examples/sample62.rb +24 -0
  110. data/examples/sample63.rb +32 -0
  111. data/examples/sample64.rb +31 -0
  112. data/examples/sample65.rb +9 -0
  113. data/examples/sample66.rb +4 -0
  114. data/examples/sample67.rb +10 -0
  115. data/examples/sample68.rb +27 -0
  116. data/examples/sample69.rb +23 -0
  117. data/examples/sample70.rb +9 -0
  118. data/examples/sample99.rb +70 -0
  119. data/examples/sdlshapes/README +2 -0
  120. data/examples/sdlshapes/sdl.ps +655 -0
  121. data/examples/sdlshapes/sdlshapes.dot +78 -0
  122. data/examples/test.xml +26 -0
  123. data/examples/theory/pert.rb +47 -0
  124. data/examples/theory/tests.rb +87 -0
  125. data/lib/ext/gvpr/dot2ruby.g +185 -0
  126. data/lib/graphviz/attrs.rb +73 -0
  127. data/lib/graphviz/constants.rb +294 -0
  128. data/lib/graphviz/core_ext.rb +64 -0
  129. data/lib/graphviz/dot2ruby.rb +59 -0
  130. data/lib/graphviz/dot_script.rb +109 -0
  131. data/lib/graphviz/dsl.rb +67 -0
  132. data/lib/graphviz/edge.rb +197 -0
  133. data/lib/graphviz/elements.rb +39 -0
  134. data/lib/graphviz/ext.rb +17 -0
  135. data/lib/graphviz/family_tree/couple.rb +63 -0
  136. data/lib/graphviz/family_tree/generation.rb +39 -0
  137. data/lib/graphviz/family_tree/person.rb +120 -0
  138. data/lib/graphviz/family_tree/sibling.rb +13 -0
  139. data/lib/graphviz/family_tree.rb +118 -0
  140. data/lib/graphviz/graphml.rb +268 -0
  141. data/lib/graphviz/math/matrix.rb +221 -0
  142. data/lib/graphviz/node.rb +160 -0
  143. data/lib/graphviz/nothugly/nothugly.xsl +321 -0
  144. data/lib/graphviz/nothugly.rb +63 -0
  145. data/lib/graphviz/theory.rb +321 -0
  146. data/lib/graphviz/types/arrow_type.rb +32 -0
  147. data/lib/graphviz/types/color.rb +58 -0
  148. data/lib/graphviz/types/color_list.rb +24 -0
  149. data/lib/graphviz/types/esc_string.rb +20 -0
  150. data/lib/graphviz/types/gv_bool.rb +49 -0
  151. data/lib/graphviz/types/gv_double.rb +32 -0
  152. data/lib/graphviz/types/html_string.rb +18 -0
  153. data/lib/graphviz/types/lbl_string.rb +22 -0
  154. data/lib/graphviz/types/rect.rb +35 -0
  155. data/lib/graphviz/types/spline_type.rb +77 -0
  156. data/lib/graphviz/types.rb +22 -0
  157. data/lib/graphviz/utils/colors.rb +1018 -0
  158. data/lib/graphviz/utils.rb +70 -0
  159. data/lib/graphviz/xml.rb +119 -0
  160. data/lib/graphviz.rb +967 -0
  161. data/lib/ruby-graphviz.rb +1 -0
  162. data/man/dot2ruby.1 +66 -0
  163. data/man/dot2ruby.1.ronn +55 -0
  164. data/man/gem2gv.1 +60 -0
  165. data/man/gem2gv.1.ronn +47 -0
  166. data/man/git2gv.1 +48 -0
  167. data/man/git2gv.1.ronn +40 -0
  168. data/man/ruby2gv.1 +60 -0
  169. data/man/ruby2gv.1.ronn +47 -0
  170. data/man/xml2gv.1 +48 -0
  171. data/man/xml2gv.1.ronn +39 -0
  172. data/ruby-graphviz.gemspec +47 -0
  173. data/setup.rb +1585 -0
  174. data/test/helper.rb +13 -0
  175. data/test/support.rb +95 -0
  176. data/test/test_dot_script.rb +47 -0
  177. data/test/test_examples.rb +151 -0
  178. data/test/test_graph.rb +115 -0
  179. data/test/test_search.rb +29 -0
  180. data/test/test_subgraph.rb +27 -0
  181. data/test/test_theory.rb +98 -0
  182. data/test/test_types.rb +65 -0
  183. data/test/test_utils_colors.rb +52 -0
  184. metadata +301 -0
@@ -0,0 +1,70 @@
1
+ require 'rbconfig'
2
+
3
+ class GraphViz
4
+ module Utils
5
+ # Since this code is an adaptation of Launchy::Application#find_executable
6
+ # (http://copiousfreetime.rubyforge.org/launchy/Launchy/Application.html)
7
+ # it follow is licence :
8
+ #
9
+ # Permission to use, copy, modify, and/or distribute this software for any
10
+ # purpose with or without fee is hereby granted, provided that the above
11
+ # copyright notice and this permission notice appear in all copies.
12
+ #
13
+ # THE SOFTWARE IS PROVIDED AS IS AND THE AUTHOR DISCLAIMS ALL WARRANTIES
14
+ # WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
15
+ # MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
16
+ # SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
17
+ # WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
18
+ # OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
19
+ # CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
20
+ def find_executable(bin, custom_paths) #:nodoc:
21
+ system_path = ENV['PATH']
22
+ user_given_path = Array(custom_paths).join(File::PATH_SEPARATOR)
23
+ search_path = system_path + File::PATH_SEPARATOR + user_given_path
24
+
25
+ search_path.split(File::PATH_SEPARATOR).each do |path|
26
+ file_path = File.join(path,bin)
27
+ return file_path if File.executable?(file_path) and File.file?(file_path)
28
+
29
+ if RbConfig::CONFIG['host_os'] =~ /mswin|mingw/ # WAS: elsif RUBY_PLATFORM =~ /mswin|mingw/
30
+ found_ext = (ENV['PATHEXT'] || '.exe;.bat;.com').split(";").find {|ext| File.executable?(file_path + ext) }
31
+ return file_path + found_ext if found_ext
32
+ end
33
+ end
34
+ return nil
35
+ end
36
+
37
+ def output_and_errors_from_command(cmd) #:nodoc:
38
+ unless defined? Open3
39
+ begin
40
+ require 'open3'
41
+ require 'win32/open3'
42
+ rescue LoadError
43
+ end
44
+ end
45
+ begin
46
+ Open3.popen3( *cmd ) do |stdin, stdout, stderr|
47
+ stdin.close
48
+ stdout.binmode
49
+ [stdout.read, stderr.read]
50
+ end
51
+ rescue NotImplementedError, NoMethodError
52
+ IO.popen( *cmd ) do |stdout|
53
+ stdout.binmode
54
+ [stdout.read, nil]
55
+ end
56
+ end
57
+ end
58
+
59
+ def output_from_command(cmd) #:nodoc:
60
+ output, errors = output_and_errors_from_command(cmd)
61
+ if errors.nil? || errors.strip.empty?
62
+ output
63
+ else
64
+ raise "Error from #{cmd}:\n#{errors}"
65
+ end
66
+ end
67
+
68
+ end
69
+ end
70
+
@@ -0,0 +1,119 @@
1
+ # Copyright (C) 2004 - 2012 Gregoire Lejeune <gregoire.lejeune@free.fr>
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 2 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, write to the Free Software
15
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
+
17
+ require 'graphviz'
18
+ require 'rexml/document'
19
+
20
+ class GraphViz
21
+ class XML
22
+
23
+ # The GraphViz object
24
+ attr_accessor :graph
25
+
26
+ #
27
+ # Generate the graph
28
+ #
29
+ # THIS METHOD IS DEPRECATED, PLEASE USE GraphViz::XML.graph.output
30
+ #
31
+ def output( *options )
32
+ warn "GraphViz::XML.output is deprecated, use GraphViz::XML.graph.output"
33
+ @graph.output( *options )
34
+ end
35
+
36
+ private
37
+
38
+ #
39
+ # Create a graph from a XML file
40
+ #
41
+ # In:
42
+ # * xml_file : XML File
43
+ # * *options : Graph options:
44
+ # * :text : show text nodes (default true)
45
+ # * :attrs : show XML attributes (default true)
46
+ #
47
+ def initialize( xml_file, *options )
48
+ @node_name = "00000"
49
+ @show_text = true
50
+ @show_attributes = true
51
+
52
+ if options and options[0]
53
+ options[0].each do |xKey, xValue|
54
+ case xKey.to_s
55
+ when "text"
56
+ @show_text = xValue
57
+ options[0].delete( xKey )
58
+ when "attrs"
59
+ @show_attributes = xValue
60
+ options[0].delete( xKey )
61
+ end
62
+ end
63
+ end
64
+
65
+ @rexml_document = REXML::Document::new( File::new( xml_file ) )
66
+ @graph = GraphViz::new( "XML", *options )
67
+ parse_xml_node( @rexml_document.root() )
68
+ end
69
+
70
+ def parse_xml_node( xml_node ) #:nodoc:
71
+ local_node_name = @node_name.clone
72
+ @node_name.succ!
73
+
74
+ label = xml_node.name
75
+ if xml_node.has_attributes? and @show_attributes
76
+ label = "{ " + xml_node.name
77
+
78
+ xml_node.attributes.each do |xName, xValue|
79
+ label << "| { #{xName} | #{xValue} } "
80
+ end
81
+
82
+ label << "}"
83
+ end
84
+ @graph.add_nodes( local_node_name, "label" => label, "color" => "blue", "shape" => "record" )
85
+
86
+ ## Act: Search and add Text nodes
87
+ if xml_node.has_text? and @show_text
88
+ text_node_name = local_node_name.clone
89
+ text_node_name << "111"
90
+
91
+ xText = ""
92
+ xSep = ""
93
+ xml_node.texts().each do |l|
94
+ x = l.value.chomp.strip
95
+ if x.length > 0
96
+ xText << xSep << x
97
+ xSep = "\n"
98
+ end
99
+ end
100
+
101
+ if xText.length > 0
102
+ @graph.add_nodes( text_node_name, "label" => xText, "color" => "black", "shape" => "ellipse" )
103
+ @graph.add_edges( local_node_name, text_node_name )
104
+ end
105
+ end
106
+
107
+ ## Act: Search and add attributes
108
+ ## TODO
109
+
110
+ xml_node.each_element( ) do |xml_child_node|
111
+ child_node_name = parse_xml_node( xml_child_node )
112
+ @graph.add_edges( local_node_name, child_node_name )
113
+ end
114
+
115
+ return( local_node_name )
116
+ end
117
+
118
+ end
119
+ end
data/lib/graphviz.rb ADDED
@@ -0,0 +1,967 @@
1
+ # Copyright (C) 2003 - 2012 Gregoire Lejeune <gregoire.lejeune@free.fr>
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 2 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, write to the Free Software
15
+ # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
16
+
17
+
18
+ IS_JRUBY = (defined?( JRUBY_VERSION ) != nil)
19
+ IS_CYGWIN = ((RUBY_PLATFORM =~ /cygwin/) != nil)
20
+
21
+ require 'tempfile'
22
+
23
+ require 'graphviz/utils'
24
+ require 'graphviz/node'
25
+ require 'graphviz/edge'
26
+ require 'graphviz/attrs'
27
+ require 'graphviz/constants'
28
+ require 'graphviz/elements'
29
+ require 'graphviz/dot_script'
30
+
31
+ require 'graphviz/dot2ruby'
32
+ require 'graphviz/types'
33
+ require 'graphviz/core_ext'
34
+
35
+ if /^1.8/.match RUBY_VERSION
36
+ $KCODE = "UTF8"
37
+ end
38
+
39
+ class GraphViz
40
+ include GraphViz::Constants
41
+ include GraphViz::Utils
42
+
43
+ public
44
+
45
+ ## Var: Output format (dot, png, jpeg, ...)
46
+ @@format = nil #"canon"
47
+ @format = nil
48
+ ## Var: Output file name
49
+ @filename = nil
50
+ ## Var: Output format and file
51
+ @output = nil
52
+ ## Var: program to use (dot|twopi)
53
+ @@prog = "dot"
54
+ @prog = nil
55
+ ## Var: program path
56
+ @@path = []
57
+ @path = nil
58
+ ## Var: Error level
59
+ @@errors = 1
60
+ @errors = nil
61
+ ## Var: External libraries
62
+ @@extlibs = []
63
+ @extlibs = nil
64
+
65
+ ## Var: Graph name
66
+ @name = nil
67
+
68
+ ## Var: defined attributes
69
+ @graph = nil
70
+ @node = nil
71
+ @edge = nil
72
+
73
+ # This accessor allow you to set global graph attributes
74
+ attr_accessor :graph
75
+ alias_method :graph_attrs, :graph
76
+
77
+ # This accessor allow you to set global nodes attributes
78
+ attr_accessor :node
79
+ alias_method :node_attrs, :node
80
+
81
+ # This accessor allow you to set global edges attributes
82
+ attr_accessor :edge
83
+ alias_method :edge_attrs, :edge
84
+
85
+ @elements_order = nil
86
+
87
+ def add_node( xNodeName, hOpts = {} )
88
+ if xNodeName.kind_of? Array
89
+ raise ArgumentError, "use `add_nodes' to add several nodes at the same time"
90
+ end
91
+ return add_nodes(xNodeName, hOpts)
92
+ end
93
+
94
+ # Create a new node
95
+ #
96
+ # In:
97
+ # * xNodeName : Name of the new node
98
+ # * hOpts : Node attributes
99
+ #
100
+ # Return the GraphViz::Node object created
101
+ def add_nodes(node_name, options = {})
102
+ if node_name.kind_of? Array
103
+ node_name.each { |n| add_nodes(n, options.clone) }
104
+ else
105
+ node = @hoNodes[node_name]
106
+
107
+ if node.nil?
108
+ @hoNodes[node_name] = GraphViz::Node::new( node_name, self )
109
+ @hoNodes[node_name].index = @elements_order.size_of( "node" )
110
+
111
+ unless options.keys.include?(:label) or options.keys.include?("label")
112
+ options[:label] = node_name
113
+ end
114
+
115
+ @elements_order.push( {
116
+ "type" => "node",
117
+ "name" => node_name,
118
+ "value" => @hoNodes[node_name]
119
+ } )
120
+
121
+ node = @hoNodes[node_name]
122
+ end
123
+
124
+ options.each do |xKey, xValue|
125
+ @hoNodes[node_name][xKey.to_s] = xValue
126
+ end
127
+
128
+ return node
129
+ end
130
+ end
131
+
132
+ # Return the node object for the given name (or nil) in the current graph
133
+ def get_node( xNodeName, &block )
134
+ node = @hoNodes[xNodeName] || nil
135
+
136
+ yield( node ) if( block and node )
137
+
138
+ return node
139
+ end
140
+
141
+ # Returns the first node found in the entire graph, starting from the root graph
142
+ def find_node(name)
143
+ root = root_graph
144
+ return root.search_node(name)
145
+ end
146
+
147
+ # Return the first node found in the current graph, and it subgraphs
148
+ def search_node(name)
149
+ n = get_node(name)
150
+ return n unless n.nil?
151
+ each_graph { |_, g|
152
+ n = g.search_node(name)
153
+ return n unless n.nil?
154
+ }
155
+ return nil
156
+ end
157
+
158
+ #
159
+ # Return the node object for the given index
160
+ #
161
+ def get_node_at_index( index )
162
+ element = @elements_order[index, "node"]
163
+ (element.nil?) ? nil : element["value"]
164
+ end
165
+
166
+ #
167
+ # Allow you to traverse nodes
168
+ #
169
+ def each_node( &block )
170
+ if block_given?
171
+ @hoNodes.each do |name, node|
172
+ yield( name, node )
173
+ end
174
+ else
175
+ return( @hoNodes )
176
+ end
177
+ end
178
+
179
+ # Get the number of nodes
180
+ def node_count
181
+ @hoNodes.size
182
+ end
183
+
184
+ def add_edge( oNodeOne, oNodeTwo, hOpts = {} )
185
+ if oNodeTwo.kind_of? Array or oNodeOne.kind_of? Array
186
+ raise ArgumentError, "use `add_edges' to add several edges at the same time"
187
+ end
188
+ add_edges(oNodeOne, oNodeTwo, hOpts)
189
+ end
190
+
191
+ # Create a new edge
192
+ #
193
+ # In:
194
+ # * node_one : First node (or node list)
195
+ # * node_two : Second Node (or node list)
196
+ # * options : Edge attributes
197
+ def add_edges( node_one, node_two, options = {} )
198
+
199
+ if( node_one.class == Array )
200
+ node_one.each do |no|
201
+ add_edges( no, node_two, options )
202
+ end
203
+ else
204
+ if( node_two.class == Array )
205
+ node_two.each do |nt|
206
+ add_edges( node_one, nt, options )
207
+ end
208
+ else
209
+ edge = GraphViz::Edge::new( node_one, node_two, self )
210
+ edge.index = @elements_order.size_of( "edge" )
211
+
212
+ options.each do |xKey, xValue|
213
+ edge[xKey.to_s] = xValue
214
+ end
215
+
216
+ @elements_order.push( {
217
+ "type" => "edge",
218
+ "value" => edge
219
+ } )
220
+ @loEdges.push( edge )
221
+
222
+ return( edge )
223
+ end
224
+ end
225
+ end
226
+
227
+ #
228
+ # Allow you to traverse edges
229
+ #
230
+ def each_edge( &block )
231
+ if block_given?
232
+ @loEdges.each do |edge|
233
+ yield(edge)
234
+ end
235
+ else
236
+ return @loEdges
237
+ end
238
+ end
239
+
240
+ #
241
+ # Get the number of edges
242
+ #
243
+ def edge_count
244
+ @loEdges.size
245
+ end
246
+
247
+ #
248
+ # Return the edge object for the given index
249
+ #
250
+ def get_edge_at_index( index )
251
+ element = @elements_order[index, "edge"]
252
+ (element.nil?) ? nil : element["value"]
253
+ end
254
+
255
+ #
256
+ # Create a new graph
257
+ #
258
+ # In:
259
+ # * xGraphName : Graph name
260
+ # * hOpts : Graph attributes
261
+ #
262
+ def add_graph( xGraphName = nil, hOpts = {}, &block )
263
+ if xGraphName.kind_of?(GraphViz)
264
+ xGraphID = xGraphName.id
265
+ @hoGraphs[xGraphID] = xGraphName.clone
266
+ @hoGraphs[xGraphID].type = @oGraphType
267
+ @hoGraphs[xGraphID].pg = self
268
+ xGraphName = xGraphID
269
+ else
270
+ if xGraphName.kind_of?(Hash)
271
+ hOpts = xGraphName
272
+ xGraphName = nil
273
+ end
274
+
275
+ if xGraphName.nil?
276
+ xGraphID = String.random(11)
277
+ xGraphName = ""
278
+ else
279
+ xGraphID = xGraphName
280
+ end
281
+
282
+ @hoGraphs[xGraphID] = GraphViz::new( xGraphName, {:parent => self, :type => @oGraphType}, &block )
283
+
284
+ hOpts.each do |xKey, xValue|
285
+ @hoGraphs[xGraphID][xKey.to_s] = xValue
286
+ end
287
+ end
288
+
289
+ @elements_order.push( {
290
+ "type" => "graph",
291
+ "name" => xGraphName,
292
+ "value" => @hoGraphs[xGraphID]
293
+ } )
294
+
295
+ return( @hoGraphs[xGraphID] )
296
+ end
297
+ alias :subgraph :add_graph
298
+ #
299
+ # Return the graph object for the given name (or nil)
300
+ #
301
+ def get_graph( xGraphName, &block )
302
+ graph = @hoGraphs[xGraphName] || nil
303
+
304
+ yield( graph ) if( block and graph )
305
+
306
+ return graph
307
+ end
308
+
309
+ #
310
+ # Allow you to traverse graphs
311
+ #
312
+ def each_graph( &block )
313
+ if block_given?
314
+ @hoGraphs.each do |name, graph|
315
+ yield( name, graph )
316
+ end
317
+ else
318
+ return @hoGraphs
319
+ end
320
+ end
321
+
322
+ #
323
+ # Add nodes and edges defined by a Hash
324
+ #
325
+ def add(h)
326
+ if h.kind_of? Hash
327
+ h.each do |node, data|
328
+ add_hash_edge(node, data)
329
+ end
330
+ end
331
+ end
332
+
333
+ #
334
+ # Return the graph type (graph digraph)
335
+ #
336
+ def type
337
+ @oGraphType
338
+ end
339
+ def type=(x) #:nodoc:
340
+ @oGraphType = x
341
+ end
342
+
343
+ #
344
+ # Get the number of graphs
345
+ #
346
+ def graph_count
347
+ @hoGraphs.size
348
+ end
349
+
350
+ def method_missing( idName, *args, &block ) #:nodoc:
351
+ xName = idName.id2name
352
+
353
+ rCod = nil
354
+
355
+ if block
356
+ # Creating a cluster named '#{xName}'
357
+ rCod = add_graph( xName, args[0]||{} )
358
+ yield( rCod )
359
+ else
360
+ # Create a node named '#{xName}' or search for a node, edge or cluster
361
+ if @hoNodes.keys.include?( xName )
362
+ if( args[0] )
363
+ return { xName => args[0] }
364
+ else
365
+ return( @hoNodes[xName] )
366
+ end
367
+ end
368
+ return( @hoGraphs[xName] ) if @hoGraphs.keys.include?( xName )
369
+
370
+ rCod = add_nodes( xName, args[0]||{} )
371
+ end
372
+
373
+ return rCod
374
+ end
375
+
376
+ #
377
+ # Set value +xValue+ to the graph attribute +xAttrName+
378
+ #
379
+ def []=( xAttrName, xValue )
380
+ xValue = xValue.to_s if xValue.class == Symbol
381
+ @graph[xAttrName] = xValue
382
+ end
383
+
384
+ #
385
+ # Get the value of the graph attribute +xAttrName+
386
+ #
387
+ def []( xAttrName )
388
+ if Hash === xAttrName
389
+ xAttrName.each do |key, value|
390
+ self[key] = value
391
+ end
392
+ else
393
+ attr = @graph[xAttrName]
394
+ if attr.nil?
395
+ return nil
396
+ else
397
+ return( @graph[xAttrName].clone )
398
+ end
399
+ end
400
+ end
401
+
402
+ #
403
+ # Calls block once for each attribute of the graph, passing the name and value to the
404
+ # block as a two-element array.
405
+ #
406
+ def each_attribute(&b)
407
+ @graph.each do |k,v|
408
+ yield(k,v)
409
+ end
410
+ end
411
+ def each_attribut(&b)
412
+ warn "`GraphViz#each_attribut` is deprecated, please use `GraphViz#each_attribute`"
413
+ each_attribute(&b)
414
+ end
415
+
416
+ # Create a new graph from the current subgraph
417
+ def to_graph
418
+ graph = self.clone
419
+ graph.pg = nil
420
+ return graph
421
+ end
422
+
423
+ #
424
+ # Generate the graph
425
+ #
426
+ # Options :
427
+ # * :output : Output format (GraphViz::Constants::FORMATS)
428
+ # * :file : Output file name
429
+ # * :use : Program to use (GraphViz::Constants::PROGRAMS)
430
+ # * :path : Program PATH
431
+ # * :<format> => <file> : <file> can be
432
+ # * a file name
433
+ # * nil, then the output will be printed to STDOUT
434
+ # * String, then the output will be returned as a String
435
+ # * :errors : DOT error level (default 1)
436
+ # * 0 = Error + Warning
437
+ # * 1 = Error
438
+ # * 2 = none
439
+ #
440
+ def output( hOpts = {} )
441
+ xDOTScript = DOTScript.new
442
+ lNotHugly = []
443
+
444
+ append_attributes_and_types(xDOTScript)
445
+
446
+ xDOTScript << "}"
447
+
448
+ if has_parent_graph?
449
+ xDOTScript.make_subgraph("#{GraphViz.escape(@name, :unquote_empty => true)}")
450
+ else
451
+ hOutput = {}
452
+
453
+ hOpts.each do |xKey, xValue|
454
+ xValue = xValue.to_s unless xValue.nil? or [Class, TrueClass, FalseClass].include?(xValue.class)
455
+ case xKey.to_s
456
+ when "use"
457
+ if PROGRAMS.index( xValue ).nil?
458
+ raise ArgumentError, "can't use '#{xValue}'"
459
+ end
460
+ @prog = xValue
461
+ when "path"
462
+ @path = xValue && xValue.split( "," ).map{ |x| x.strip }
463
+ when "errors"
464
+ @errors = xValue
465
+ when "extlib"
466
+ @extlibs = xValue.split( "," ).map{ |x| x.strip }
467
+ when "scale"
468
+ # Scale input by 'v' (=72)
469
+ @scale = xValue
470
+ when "inverty"
471
+ # Invert y coordinate in output
472
+ @inverty = xValue
473
+ when "no_layout"
474
+ # No layout mode 'v' (=1)
475
+ @no_layout = xValue
476
+ when "reduce"
477
+ # Reduce graph
478
+ @reduce_graph = xValue
479
+ when "Lg"
480
+ # Don't use grid
481
+ @Lg = xValue
482
+ when "LO"
483
+ # Use old attractive force
484
+ @LO = xValue
485
+ when "Ln"
486
+ # Set number of iterations to i
487
+ @Ln = xValue
488
+ when "LU"
489
+ # Set unscaled factor to i
490
+ @LU = xValue
491
+ when "LC"
492
+ # Set overlap expansion factor to v
493
+ @LC = xValue
494
+ when "LT"
495
+ # Set temperature (temperature factor) to v
496
+ @LT = xValue
497
+ when "nothugly"
498
+ begin
499
+ require 'graphviz/nothugly'
500
+ @nothugly = true
501
+ rescue LoadError
502
+ warn "You must install ruby-xslt or libxslt-ruby to use nothugly option!"
503
+ @nothugly = false
504
+ end
505
+ else
506
+ if FORMATS.index( xKey.to_s ).nil?
507
+ raise ArgumentError, "output format '#{xValue}' invalid"
508
+ end
509
+ hOutput[xKey.to_s] = xValue
510
+ end
511
+ end
512
+
513
+ @output = hOutput if hOutput.size > 0
514
+
515
+ xStict = ((@strict && @oGraphType == "digraph") ? "strict " : "")
516
+ xDOTScript.prepend(
517
+ "#{xStict}#{@oGraphType} #{GraphViz.escape(@name, :unquote_empty => true)} {"
518
+ )
519
+
520
+ xOutputString = (@filename == String ||
521
+ @output.any? {|format, file| file == String })
522
+
523
+ xOutput = ""
524
+ if @format.to_s == "none" or @output.any? {|fmt, fn| fmt.to_s == "none"}
525
+ if xOutputString
526
+ xOutput << xDOTScript
527
+ else
528
+ xFileName = @output["none"] || @filename
529
+ open( xFileName, "w" ) do |h|
530
+ h.puts xDOTScript
531
+ end
532
+ end
533
+ end
534
+
535
+ if (@format.to_s != "none" and not @format.nil?) or (@output.any? {|format, file| format != "none" } and @output.size > 0)
536
+ ## Act: Save script and send it to dot
537
+ t = Tempfile::open( File.basename(__FILE__) )
538
+ t.print( xDOTScript )
539
+ t.close
540
+
541
+ cmd = find_executable( @prog, @path )
542
+ if cmd == nil
543
+ raise StandardError, "GraphViz not installed or #{@prog} not in PATH. Install GraphViz or use the 'path' option"
544
+ end
545
+
546
+ xOutputWithFile = []
547
+ xOutputWithoutFile = []
548
+ unless @format.nil? or @format == "none"
549
+ lNotHugly << @filename if @format.to_s == "svg" and @nothugly
550
+ if @filename.nil? or @filename == String
551
+ xOutputWithoutFile = ["-T#{@format}"]
552
+ else
553
+ xOutputWithFile = ["-T#{@format}", "-o#{@filename}"]
554
+ end
555
+ end
556
+ @output.each_except( :key => ["none"] ) do |format, file|
557
+ lNotHugly << file if format.to_s == "svg" and @nothugly
558
+ if file.nil? or file == String
559
+ xOutputWithoutFile += ["-T#{format}"]
560
+ else
561
+ xOutputWithFile += ["-T#{format}", "-o#{file}"]
562
+ end
563
+ end
564
+
565
+ xExternalLibraries = @extlibs.map { |lib| "-l#{lib}" }
566
+
567
+ xOtherOptions = []
568
+ xOtherOptions << "-s#{@scale}" if @scale
569
+ xOtherOptions << "-y" if @inverty
570
+ xOtherOptions << "-n#{@no_layout}" if @no_layout
571
+ xOtherOptions << "-x" if @reduce_graph
572
+ xOtherOptions << "-Lg" if @Lg
573
+ xOtherOptions << "-LO" if @LO
574
+ xOtherOptions << "-Ln#{@Ln}" if @Ln
575
+ xOtherOptions << "-LU#{@LU}" if @LU
576
+ xOtherOptions << "-LC#{@LC}" if @LC
577
+ xOtherOptions << "-LT#{@LT}" if @LT
578
+
579
+ tmpPath = if IS_JRUBY
580
+ t.path
581
+ elsif IS_CYGWIN
582
+ begin
583
+ IO.popen("cygpath", "-w", t.path).chomp
584
+ rescue
585
+ warn "cygpath is not installed!"
586
+ t.path
587
+ end
588
+ else
589
+ t.path
590
+ end
591
+
592
+ xCmd = [cmd, "-q#{@errors}"] +
593
+ xExternalLibraries +
594
+ xOtherOptions +
595
+ xOutputWithFile +
596
+ xOutputWithoutFile +
597
+ [tmpPath]
598
+
599
+ xOutput << output_from_command( xCmd )
600
+ end
601
+
602
+ # Not Hugly
603
+ lNotHugly.each do |f|
604
+ if f.nil? or f == String
605
+ xOutput = GraphViz.nothugly( xOutput, false )
606
+ else
607
+ GraphViz.nothugly( f, true )
608
+ end
609
+ end
610
+
611
+ if xOutputString
612
+ xOutput
613
+ else
614
+ print xOutput
615
+ end
616
+ end
617
+ end
618
+ alias :save :output
619
+
620
+ def append_attributes_and_types(script)
621
+ script_data = DOTScriptData.new
622
+
623
+ @elements_order.each { |kElement|
624
+ is_new_type = script_data.type != kElement["type"]
625
+ if is_new_type
626
+ script << script_data unless script_data.type.nil? or script_data.empty?
627
+ script_data = DOTScriptData.new(kElement["type"])
628
+ end
629
+
630
+ # Modified by Brandon Coleman verify value is NOT NULL
631
+ kElement["value"] or raise ArgumentError, "#{kElement["name"]} is nil!"
632
+
633
+ case kElement["type"]
634
+ when "graph_attr", "node_attr", "edge_attr"
635
+ script_data.add_attribute(kElement["name"], kElement["value"].to_gv)
636
+ when "node", "graph"
637
+ script << kElement["value"].output()
638
+ when "edge"
639
+ script << " " + kElement["value"].output( @oGraphType )
640
+ else
641
+ raise ArgumentError,
642
+ "Don't know what to do with element type '#{kElement['type']}'"
643
+ end
644
+ }
645
+ script << script_data unless script_data.type.nil? or script_data.empty?
646
+ end
647
+
648
+ def to_s
649
+ self.output(:none => String)
650
+ end
651
+
652
+ #
653
+ # Get the graph name
654
+ #
655
+ def name
656
+ @name.clone
657
+ end
658
+ alias :id :name
659
+
660
+ #
661
+ # Create an edge between the current cluster and the node or cluster +oNode+
662
+ #
663
+ def <<( oNode )
664
+ raise( ArgumentError, "Edge between root graph and node or cluster not allowed!" ) if self.pg.nil?
665
+
666
+ if( oNode.class == Array )
667
+ oNode.each do |no|
668
+ self << no
669
+ end
670
+ else
671
+ return GraphViz::commonGraph( oNode, self ).add_edges( self, oNode )
672
+ end
673
+ end
674
+ alias :> :<<
675
+ alias :- :<<
676
+ alias :>> :<<
677
+
678
+ def pg #:nodoc:
679
+ @oParentGraph
680
+ end
681
+ def pg=(x) #:nodoc:
682
+ @oParentGraph = x
683
+ end
684
+
685
+ #
686
+ # Return the root graph
687
+ #
688
+ def root_graph
689
+ return( (self.pg.nil?) ? self : self.pg.root_graph )
690
+ end
691
+
692
+ def self.commonGraph( o1, o2 ) #:nodoc:
693
+ g1 = o1.pg
694
+ g2 = o2.pg
695
+
696
+ return o1 if g1.nil?
697
+ return o2 if g2.nil?
698
+
699
+ return g1 if g1.object_id == g2.object_id
700
+
701
+ return GraphViz::commonGraph( g1, g2 )
702
+ end
703
+
704
+ def set_position( xType, xKey, xValue ) #:nodoc:
705
+ @elements_order.push( {
706
+ "type" => "#{xType}_attr",
707
+ "name" => xKey,
708
+ "value" => xValue
709
+ } )
710
+ end
711
+
712
+ ## ----------------------------------------------------------------------------
713
+
714
+ #
715
+ # Change default options (:use, :path, :errors and :output)
716
+ #
717
+ def self.default( hOpts )
718
+ hOpts.each do |k, v|
719
+ case k.to_s
720
+ when "use"
721
+ @@prog = v
722
+ when "path"
723
+ @@path = v.split( "," ).map{ |x| x.strip }
724
+ when "errors"
725
+ @@errors = v
726
+ when "extlibs"
727
+ @@extlibs = v.split( "," ).map{ |x| x.strip }
728
+ else
729
+ warn "Invalid option #{k}!"
730
+ end
731
+ end
732
+ end
733
+
734
+ def self.options( hOpts )
735
+ GraphViz::default( hOpts )
736
+ end
737
+
738
+ ## ----------------------------------------------------------------------------
739
+
740
+ # Create a new graph from a GraphViz File
741
+ #
742
+ # Options :
743
+ # * :output : Output format (GraphViz::Constants::FORMATS) (default : dot)
744
+ # * :file : Output file name (default : none)
745
+ # * :use : Program to use (GraphViz::Constants::PROGRAMS) (default : dot)
746
+ # * :path : Program PATH
747
+ #
748
+ def self.parse( xFile, hOpts = {}, &block )
749
+ graph = Dot2Ruby::new( hOpts[:path], nil, nil ).eval( xFile )
750
+ yield( graph ) if( block and graph )
751
+ return graph
752
+ end
753
+
754
+ # Create a new graph from a GraphViz File
755
+ #
756
+ # Options :
757
+ # * :output : Output format (GraphViz::Constants::FORMATS) (default : dot)
758
+ # * :file : Output file name (default : none)
759
+ # * :use : Program to use (GraphViz::Constants::PROGRAMS) (default : dot)
760
+ # * :path : Program PATH
761
+ #
762
+ def self.parse_string( str, hOpts = {}, &block )
763
+ graph = Dot2Ruby::new( hOpts[:path], nil, nil ).eval_string( str )
764
+ yield( graph ) if( block and graph )
765
+ return graph
766
+ end
767
+
768
+ # Return a new completed graph
769
+ def complete
770
+ GraphViz.parse_string( root_graph.output( :dot => String ) )
771
+ end
772
+
773
+ # Complete the current graph
774
+ def complete!
775
+ # TODO: Keep options
776
+ complete
777
+ end
778
+
779
+ # Return true if the graph is directed.
780
+ def directed?
781
+ not((/digraph/ =~ "bla digraph bla").nil?)
782
+ end
783
+
784
+ def has_parent_graph?
785
+ not @oParentGraph.nil?
786
+ end
787
+ ## ----------------------------------------------------------------------------
788
+
789
+ private
790
+
791
+ ## Var: Nodes, Edges and Graphs tables
792
+ @hoNodes = nil
793
+ @loEdges = nil
794
+ @hoGraphs = nil
795
+
796
+ ## Var: Parent graph
797
+ @oParentGraph = nil
798
+
799
+ ## Var: Type de graphe (orienté ou non)
800
+ @oGraphType = nil
801
+
802
+ #
803
+ # Create a new graph object
804
+ #
805
+ # Options :
806
+ # * :output : Output format (GraphViz::Constants::FORMATS) (default : dot)
807
+ # * :file : Output file name (default : nil)
808
+ # * :use : Program to use (GraphViz::Constants::PROGRAMS) (default : dot)
809
+ # * :path : Program PATH
810
+ # * :parent : Parent graph (default : nil)
811
+ # * :type : Graph type (GraphViz::Constants::GRAPHTYPE) (default : digraph)
812
+ # * :errors : DOT error level (default 1)
813
+ # * 0 = Error + Warning
814
+ # * 1 = Error
815
+ # * 2 = none
816
+ #
817
+ def initialize( xGraphName, hOpts = {}, &block )
818
+ @filename = nil
819
+ @name = xGraphName.to_s
820
+ @format = @@format
821
+ @prog = @@prog
822
+ @path = @@path
823
+ @errors = @@errors
824
+ @extlibs = @@extlibs
825
+ @output = {}
826
+ @nothugly = false
827
+ @strict = false
828
+
829
+ @scale = nil
830
+ @inverty = nil
831
+ @no_layout = nil
832
+ @reduce_graph = nil
833
+ @Lg = nil
834
+ @LO = nil
835
+ @Ln = nil
836
+ @LU = nil
837
+ @LC = nil
838
+ @LT = nil
839
+
840
+ @elements_order = GraphViz::Elements::new()
841
+
842
+ @oParentGraph = nil
843
+ @oGraphType = "digraph"
844
+
845
+ @hoNodes = Hash::new()
846
+ @loEdges = Array::new()
847
+ @hoGraphs = Hash::new()
848
+
849
+ @node = GraphViz::Attrs::new( self, "node", NODESATTRS )
850
+ @edge = GraphViz::Attrs::new( self, "edge", EDGESATTRS )
851
+ @graph = GraphViz::Attrs::new( self, "graph", GRAPHSATTRS )
852
+
853
+ hOpts.each do |xKey, xValue|
854
+ case xKey.to_s
855
+ when "use"
856
+ if PROGRAMS.index( xValue.to_s ).nil?
857
+ raise ArgumentError, "can't use '#{xValue}'"
858
+ end
859
+ @prog = xValue.to_s
860
+ when "parent"
861
+ @oParentGraph = xValue
862
+ when "type"
863
+ if GRAPHTYPE.index( xValue.to_s ).nil?
864
+ raise ArgumentError, "graph type '#{xValue}' unknow"
865
+ end
866
+ @oGraphType = xValue.to_s
867
+ when "path"
868
+ @path = xValue.split( "," ).map{ |x| x.strip }
869
+ when "strict"
870
+ @strict = (xValue ? true : false)
871
+ when "errors"
872
+ @errors = xValue
873
+ when "extlibs"
874
+ @extlibs = xValue.split( "," ).map{ |x| x.strip }
875
+ else
876
+ self[xKey.to_s] = xValue.to_s
877
+ end
878
+ end
879
+
880
+ yield( self ) if( block )
881
+ end
882
+
883
+ # Edge between a node and a Hash
884
+ # Used by GraphViz#add
885
+ def add_hash_edge(node, hash)
886
+ if hash.kind_of? Hash
887
+ hash.each do |nt, data|
888
+ add_edges(node, nt)
889
+ add_hash_edge(nt, data)
890
+ end
891
+ else
892
+ add_edges(node, hash)
893
+ end
894
+ end
895
+
896
+ #
897
+ # Create a new undirected graph
898
+ #
899
+ # See also GraphViz::new
900
+ #
901
+ def self.graph( xGraphName, hOpts = {}, &block )
902
+ new( xGraphName, hOpts.symbolize_keys.merge( {:type => "graph"} ), &block )
903
+ end
904
+
905
+ #
906
+ # Create a new directed graph
907
+ #
908
+ # See also GraphViz::new
909
+ #
910
+ def self.digraph( xGraphName, hOpts = {}, &block )
911
+ new( xGraphName, hOpts.symbolize_keys.merge( {:type => "digraph"} ), &block )
912
+ end
913
+
914
+ # Create a new strict directed graph
915
+ #
916
+ # See also GraphViz::new
917
+ def self.strict_digraph( xGraphName, hOpts = {}, &block )
918
+ new( xGraphName, hOpts.symbolize_keys.merge( {:type => "digraph", :strict => true} ), &block )
919
+ end
920
+
921
+ # Create a random graph.
922
+ def self.generate(num_nodes, num_edges, directed = false, weight_range = (1..1))
923
+ g = nil
924
+ if directed
925
+ g = GraphViz.digraph(:G)
926
+ else
927
+ g = GraphViz.graph(:G)
928
+ end
929
+
930
+ nodes = (1..num_nodes).map{ |e| e.to_s }
931
+ g.add_nodes(nodes)
932
+
933
+ edges = []
934
+ nodes.each do |head|
935
+ nodes.each do |tail|
936
+ if (directed and head != tail) or (head < tail)
937
+ edges << {:head => head, :tail => tail, :weight => weight_range.to_a.shuffle[0]}
938
+ end
939
+ end
940
+ end
941
+ edges.shuffle!
942
+
943
+ (num_edges - 1).times do |i|
944
+ g.add_edges(edges[i][:head], edges[i][:tail], :label => edges[i][:weight].to_s, :weight => edges[i][:weight])
945
+ end
946
+
947
+ return g
948
+ end
949
+
950
+ #
951
+ # Escape a string to be acceptable as a node name in a graphviz input file
952
+ #
953
+ def self.escape(str, opts = {} ) #:nodoc:
954
+ options = {
955
+ :force => false,
956
+ :unquote_empty => false,
957
+ }.merge(opts)
958
+
959
+ if (options[:force] or str.match( /\A[a-zA-Z_]+[a-zA-Z0-9_]*\Z/ ).nil?)
960
+ unless options[:unquote_empty] and str.size == 0
961
+ '"' + str.gsub('"', '\\"').gsub("\n", '\\\\n') + '"'
962
+ end
963
+ else
964
+ str
965
+ end
966
+ end
967
+ end