sqlpostgres 1.2.4
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +8 -0
- data/Gemfile.lock +22 -0
- data/LICENSE.md +23 -0
- data/README.rdoc +59 -0
- data/Rakefile +32 -0
- data/VERSION +1 -0
- data/doc/BUGS +2 -0
- data/doc/examples/README +6 -0
- data/doc/examples/connection.rb +16 -0
- data/doc/examples/connection_auto.rb +22 -0
- data/doc/examples/connection_ctor.rb +18 -0
- data/doc/examples/connection_default.rb +15 -0
- data/doc/examples/connection_exec.rb +18 -0
- data/doc/examples/connection_manual.rb +12 -0
- data/doc/examples/connection_wrapped_new.rb +13 -0
- data/doc/examples/connection_wrapped_open.rb +13 -0
- data/doc/examples/cursor.rb +38 -0
- data/doc/examples/include_module.rb +9 -0
- data/doc/examples/include_module2.rb +12 -0
- data/doc/examples/insert.rb +30 -0
- data/doc/examples/insert2.rb +36 -0
- data/doc/examples/insert_bytea.rb +16 -0
- data/doc/examples/insert_bytea_array.rb +17 -0
- data/doc/examples/insert_default_values.rb +16 -0
- data/doc/examples/insert_insert.rb +16 -0
- data/doc/examples/insert_insert_default.rb +16 -0
- data/doc/examples/insert_insert_select.rb +20 -0
- data/doc/examples/insert_select.rb +20 -0
- data/doc/examples/interval.rb +17 -0
- data/doc/examples/savepoint.rb +38 -0
- data/doc/examples/select.rb +33 -0
- data/doc/examples/select2.rb +36 -0
- data/doc/examples/select_cross_join.rb +18 -0
- data/doc/examples/select_distinct.rb +18 -0
- data/doc/examples/select_distinct_on +19 -0
- data/doc/examples/select_for_update.rb +18 -0
- data/doc/examples/select_from.rb +17 -0
- data/doc/examples/select_from_subselect.rb +20 -0
- data/doc/examples/select_group_by.rb +19 -0
- data/doc/examples/select_having.rb +20 -0
- data/doc/examples/select_join_on.rb +18 -0
- data/doc/examples/select_join_using.rb +18 -0
- data/doc/examples/select_limit.rb +19 -0
- data/doc/examples/select_natural_join.rb +18 -0
- data/doc/examples/select_offset.rb +19 -0
- data/doc/examples/select_order_by.rb +20 -0
- data/doc/examples/select_select.rb +30 -0
- data/doc/examples/select_select_alias.rb +30 -0
- data/doc/examples/select_select_expression.rb +31 -0
- data/doc/examples/select_select_literal.rb +24 -0
- data/doc/examples/select_union.rb +21 -0
- data/doc/examples/select_where_array.rb +18 -0
- data/doc/examples/select_where_in.rb +18 -0
- data/doc/examples/select_where_string.rb +18 -0
- data/doc/examples/simple.rb +34 -0
- data/doc/examples/transaction.rb +30 -0
- data/doc/examples/transaction_abort.rb +30 -0
- data/doc/examples/transaction_commit.rb +34 -0
- data/doc/examples/translate_substitute_values.rb +17 -0
- data/doc/examples/update.rb +32 -0
- data/doc/examples/update2.rb +44 -0
- data/doc/examples/update_only.rb +17 -0
- data/doc/examples/update_set.rb +17 -0
- data/doc/examples/update_set_array.rb +16 -0
- data/doc/examples/update_set_bytea.rb +16 -0
- data/doc/examples/update_set_expression.rb +16 -0
- data/doc/examples/update_set_subselect.rb +20 -0
- data/doc/examples/update_where.rb +17 -0
- data/doc/examples/use_prefix.rb +8 -0
- data/doc/examples/use_prefix2.rb +11 -0
- data/doc/index.html +31 -0
- data/doc/insertexamples.rb +9 -0
- data/doc/makemanual +4 -0
- data/doc/makerdoc +5 -0
- data/doc/manual.dbk +622 -0
- data/lib/sqlpostgres/Connection.rb +198 -0
- data/lib/sqlpostgres/Cursor.rb +157 -0
- data/lib/sqlpostgres/Delete.rb +67 -0
- data/lib/sqlpostgres/Exceptions.rb +15 -0
- data/lib/sqlpostgres/Insert.rb +279 -0
- data/lib/sqlpostgres/NullConnection.rb +22 -0
- data/lib/sqlpostgres/PgBit.rb +73 -0
- data/lib/sqlpostgres/PgBox.rb +37 -0
- data/lib/sqlpostgres/PgCidr.rb +21 -0
- data/lib/sqlpostgres/PgCircle.rb +75 -0
- data/lib/sqlpostgres/PgInet.rb +21 -0
- data/lib/sqlpostgres/PgInterval.rb +208 -0
- data/lib/sqlpostgres/PgLineSegment.rb +37 -0
- data/lib/sqlpostgres/PgMacAddr.rb +21 -0
- data/lib/sqlpostgres/PgPath.rb +64 -0
- data/lib/sqlpostgres/PgPoint.rb +65 -0
- data/lib/sqlpostgres/PgPolygon.rb +56 -0
- data/lib/sqlpostgres/PgTime.rb +77 -0
- data/lib/sqlpostgres/PgTimeWithTimeZone.rb +98 -0
- data/lib/sqlpostgres/PgTimestamp.rb +93 -0
- data/lib/sqlpostgres/PgTwoPoints.rb +54 -0
- data/lib/sqlpostgres/PgType.rb +34 -0
- data/lib/sqlpostgres/PgWrapper.rb +41 -0
- data/lib/sqlpostgres/Savepoint.rb +98 -0
- data/lib/sqlpostgres/Select.rb +855 -0
- data/lib/sqlpostgres/Transaction.rb +120 -0
- data/lib/sqlpostgres/Translate.rb +436 -0
- data/lib/sqlpostgres/Update.rb +188 -0
- data/lib/sqlpostgres.rb +67 -0
- data/test/Assert.rb +72 -0
- data/test/Connection.test.rb +246 -0
- data/test/Cursor.test.rb +190 -0
- data/test/Delete.test.rb +68 -0
- data/test/Insert.test.rb +123 -0
- data/test/MockPGconn.rb +62 -0
- data/test/NullConnection.test.rb +32 -0
- data/test/PgBit.test.rb +98 -0
- data/test/PgBox.test.rb +108 -0
- data/test/PgCidr.test.rb +61 -0
- data/test/PgCircle.test.rb +107 -0
- data/test/PgInet.test.rb +61 -0
- data/test/PgInterval.test.rb +180 -0
- data/test/PgLineSegment.test.rb +108 -0
- data/test/PgMacAddr.test.rb +61 -0
- data/test/PgPath.test.rb +106 -0
- data/test/PgPoint.test.rb +100 -0
- data/test/PgPolygon.test.rb +95 -0
- data/test/PgTime.test.rb +120 -0
- data/test/PgTimeWithTimeZone.test.rb +117 -0
- data/test/PgTimestamp.test.rb +134 -0
- data/test/RandomThings.rb +25 -0
- data/test/Savepoint.test.rb +286 -0
- data/test/Select.test.rb +930 -0
- data/test/Test.rb +62 -0
- data/test/TestConfig.rb +21 -0
- data/test/TestSetup.rb +13 -0
- data/test/TestUtil.rb +92 -0
- data/test/Transaction.test.rb +275 -0
- data/test/Translate.test.rb +354 -0
- data/test/Update.test.rb +227 -0
- data/test/roundtrip.test.rb +565 -0
- data/test/test +34 -0
- data/tools/exampleinserter/ExampleInserter.rb +177 -0
- data/tools/rdoc/ChangeLog +796 -0
- data/tools/rdoc/EXAMPLE.rb +48 -0
- data/tools/rdoc/MANIFEST +58 -0
- data/tools/rdoc/Makefile +27 -0
- data/tools/rdoc/NEW_FEATURES +226 -0
- data/tools/rdoc/README +390 -0
- data/tools/rdoc/ToDo +6 -0
- data/tools/rdoc/contrib/Index +6 -0
- data/tools/rdoc/contrib/xslfo/ChangeLog +181 -0
- data/tools/rdoc/contrib/xslfo/README +106 -0
- data/tools/rdoc/contrib/xslfo/TODO +10 -0
- data/tools/rdoc/contrib/xslfo/convert.xsl +151 -0
- data/tools/rdoc/contrib/xslfo/demo/README +21 -0
- data/tools/rdoc/contrib/xslfo/demo/rdocfo +99 -0
- data/tools/rdoc/contrib/xslfo/fcm.xsl +54 -0
- data/tools/rdoc/contrib/xslfo/files.xsl +62 -0
- data/tools/rdoc/contrib/xslfo/labeled-lists.xsl +66 -0
- data/tools/rdoc/contrib/xslfo/lists.xsl +44 -0
- data/tools/rdoc/contrib/xslfo/modules.xsl +152 -0
- data/tools/rdoc/contrib/xslfo/rdoc.xsl +75 -0
- data/tools/rdoc/contrib/xslfo/source.xsl +66 -0
- data/tools/rdoc/contrib/xslfo/styles.xsl +69 -0
- data/tools/rdoc/contrib/xslfo/tables.xsl +67 -0
- data/tools/rdoc/contrib/xslfo/utils.xsl +21 -0
- data/tools/rdoc/debian/changelog +33 -0
- data/tools/rdoc/debian/compat +1 -0
- data/tools/rdoc/debian/control +20 -0
- data/tools/rdoc/debian/copyright +10 -0
- data/tools/rdoc/debian/dirs +2 -0
- data/tools/rdoc/debian/docs +2 -0
- data/tools/rdoc/debian/rdoc.1 +252 -0
- data/tools/rdoc/debian/rdoc.manpages +1 -0
- data/tools/rdoc/debian/rdoc.pod +149 -0
- data/tools/rdoc/debian/rules +9 -0
- data/tools/rdoc/dot/dot.rb +255 -0
- data/tools/rdoc/etc/rdoc.dtd +203 -0
- data/tools/rdoc/install.rb +137 -0
- data/tools/rdoc/markup/install.rb +43 -0
- data/tools/rdoc/markup/sample/sample.rb +42 -0
- data/tools/rdoc/markup/simple_markup/fragments.rb +323 -0
- data/tools/rdoc/markup/simple_markup/inline.rb +348 -0
- data/tools/rdoc/markup/simple_markup/lines.rb +147 -0
- data/tools/rdoc/markup/simple_markup/preprocess.rb +68 -0
- data/tools/rdoc/markup/simple_markup/to_html.rb +281 -0
- data/tools/rdoc/markup/simple_markup.rb +474 -0
- data/tools/rdoc/markup/test/AllTests.rb +2 -0
- data/tools/rdoc/markup/test/TestInline.rb +151 -0
- data/tools/rdoc/markup/test/TestParse.rb +411 -0
- data/tools/rdoc/rdoc/code_objects.rb +536 -0
- data/tools/rdoc/rdoc/diagram.rb +331 -0
- data/tools/rdoc/rdoc/generators/chm_generator.rb +112 -0
- data/tools/rdoc/rdoc/generators/html_generator.rb +1268 -0
- data/tools/rdoc/rdoc/generators/template/chm/chm.rb +86 -0
- data/tools/rdoc/rdoc/generators/template/html/html.rb +705 -0
- data/tools/rdoc/rdoc/generators/template/html/kilmer.rb +377 -0
- data/tools/rdoc/rdoc/generators/template/xml/rdf.rb +110 -0
- data/tools/rdoc/rdoc/generators/template/xml/xml.rb +110 -0
- data/tools/rdoc/rdoc/generators/xml_generator.rb +130 -0
- data/tools/rdoc/rdoc/options.rb +451 -0
- data/tools/rdoc/rdoc/parsers/parse_c.rb +287 -0
- data/tools/rdoc/rdoc/parsers/parse_f95.rb +118 -0
- data/tools/rdoc/rdoc/parsers/parse_rb.rb +2311 -0
- data/tools/rdoc/rdoc/parsers/parse_simple.rb +37 -0
- data/tools/rdoc/rdoc/parsers/parserfactory.rb +75 -0
- data/tools/rdoc/rdoc/rdoc.rb +219 -0
- data/tools/rdoc/rdoc/template.rb +234 -0
- data/tools/rdoc/rdoc/tokenstream.rb +25 -0
- data/tools/rdoc/rdoc.rb +9 -0
- metadata +291 -0
@@ -0,0 +1,37 @@
|
|
1
|
+
# Parse a non-source file. We basically take the whole thing
|
2
|
+
# as one big comment. If the first character in the file
|
3
|
+
# is '#', we strip leading pound signs.
|
4
|
+
|
5
|
+
|
6
|
+
require "rdoc/code_objects"
|
7
|
+
require "markup/simple_markup/preprocess"
|
8
|
+
|
9
|
+
module RDoc
|
10
|
+
# See rdoc/parsers/parse_c.rb
|
11
|
+
|
12
|
+
class SimpleParser
|
13
|
+
|
14
|
+
# prepare to parse a plain file
|
15
|
+
def initialize(top_level, file_name, body, options)
|
16
|
+
|
17
|
+
preprocess = SM::PreProcess.new(file_name, options.rdoc_include)
|
18
|
+
|
19
|
+
preprocess.handle(body) do |directive, param|
|
20
|
+
$stderr.puts "Unrecognized directive '#{directive}' in #{file_name}"
|
21
|
+
end
|
22
|
+
|
23
|
+
@body = body
|
24
|
+
@options = options
|
25
|
+
@top_level = top_level
|
26
|
+
end
|
27
|
+
|
28
|
+
# Extract the file contents and attach them to the toplevel as a
|
29
|
+
# comment
|
30
|
+
|
31
|
+
def scan
|
32
|
+
# @body.gsub(/^(\s\n)+/, '')
|
33
|
+
@top_level.comment = @body
|
34
|
+
@top_level
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require "rdoc/parsers/parse_simple"
|
2
|
+
|
3
|
+
module RDoc
|
4
|
+
|
5
|
+
# A parser is simple a class that implements
|
6
|
+
#
|
7
|
+
# #initialize(file_name, body, options)
|
8
|
+
#
|
9
|
+
# and
|
10
|
+
#
|
11
|
+
# #scan
|
12
|
+
#
|
13
|
+
# The initialize method takes a file name to be used, the body of the
|
14
|
+
# file, and an RDoc::Options object. The scan method is then called
|
15
|
+
# to return an appropriately parsed TopLevel code object.
|
16
|
+
|
17
|
+
# The ParseFactory is used to redirect to the correct parser given a filename
|
18
|
+
# extension. This magic works because individual parsers have to register
|
19
|
+
# themselves with us as they are loaded in. The do this using the following
|
20
|
+
# incantation
|
21
|
+
#
|
22
|
+
#
|
23
|
+
# require "rdoc/parsers/parsefactory"
|
24
|
+
#
|
25
|
+
# module RDoc
|
26
|
+
#
|
27
|
+
# class XyzParser
|
28
|
+
# extend ParseFactory <<<<
|
29
|
+
# parse_files_matching /\.xyz$/ <<<<
|
30
|
+
#
|
31
|
+
# def initialize(file_name, body, options)
|
32
|
+
# ...
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# def scan
|
36
|
+
# ...
|
37
|
+
# end
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
|
41
|
+
|
42
|
+
|
43
|
+
module ParserFactory
|
44
|
+
|
45
|
+
@@parsers = []
|
46
|
+
|
47
|
+
Parsers = Struct.new(:regexp, :parser)
|
48
|
+
|
49
|
+
# Record the fact that a particular class parses files that
|
50
|
+
# match a given extension
|
51
|
+
|
52
|
+
def parse_files_matching(regexp)
|
53
|
+
@@parsers.unshift Parsers.new(regexp, self)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Return a parser that can handle a particular extension
|
57
|
+
|
58
|
+
def ParserFactory.can_parse(file_name)
|
59
|
+
@@parsers.find {|p| p.regexp.match(file_name) }
|
60
|
+
end
|
61
|
+
|
62
|
+
# Find the correct parser for a particular file name. Return a
|
63
|
+
# SimpleParser for ones that we don't know
|
64
|
+
|
65
|
+
def ParserFactory.parser_for(top_level, file_name, body, options)
|
66
|
+
parser_description = can_parse(file_name)
|
67
|
+
if parser_description
|
68
|
+
parser = parser_description.parser
|
69
|
+
else
|
70
|
+
parser = SimpleParser
|
71
|
+
end
|
72
|
+
parser.new(top_level, file_name, body, options)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,219 @@
|
|
1
|
+
# See README.
|
2
|
+
#
|
3
|
+
|
4
|
+
|
5
|
+
RDOC_VERSION = "0.9.0"
|
6
|
+
|
7
|
+
rcs = '$Date: 2003/10/10 08:00:05 $ $Revision: 1.1 $'.
|
8
|
+
gsub(/\$/, '').
|
9
|
+
sub(/Date: /, ': ').
|
10
|
+
sub(/ Revision: (\S+)/) { "(#$1)" }
|
11
|
+
|
12
|
+
VERSION_STRING = %{RDoc V} + RDOC_VERSION + rcs
|
13
|
+
|
14
|
+
|
15
|
+
require 'rdoc/parsers/parse_rb.rb'
|
16
|
+
require 'rdoc/parsers/parse_c.rb'
|
17
|
+
require 'rdoc/parsers/parse_f95.rb'
|
18
|
+
|
19
|
+
require 'rdoc/parsers/parse_simple.rb'
|
20
|
+
require 'rdoc/options'
|
21
|
+
|
22
|
+
require 'rdoc/diagram'
|
23
|
+
|
24
|
+
require 'find'
|
25
|
+
require 'ftools'
|
26
|
+
|
27
|
+
# We put rdoc stuff in the RDoc module to avoid namespace
|
28
|
+
# clutter.
|
29
|
+
#
|
30
|
+
# ToDo: This isn't universally true.
|
31
|
+
|
32
|
+
module RDoc
|
33
|
+
|
34
|
+
# Exception thrown by any rdoc error. Only the #message part is
|
35
|
+
# of use externally.
|
36
|
+
|
37
|
+
class RDocError < Exception
|
38
|
+
end
|
39
|
+
|
40
|
+
# Encapsulate the production of rdoc documentation. Basically
|
41
|
+
# you can use this as you would invoke rdoc from the command
|
42
|
+
# line:
|
43
|
+
#
|
44
|
+
# rdoc = RDoc::RDoc.new
|
45
|
+
# rdoc.document(args)
|
46
|
+
#
|
47
|
+
# where _args_ is an array of strings, each corresponding to
|
48
|
+
# an argument you'd give rdoc on the command line. See rdoc/rdoc.rb
|
49
|
+
# for details.
|
50
|
+
|
51
|
+
class RDoc
|
52
|
+
|
53
|
+
##
|
54
|
+
# This is the list of output generators that we
|
55
|
+
# support
|
56
|
+
|
57
|
+
Generator = Struct.new(:file_name, :class_name, :key)
|
58
|
+
|
59
|
+
GENERATORS = {}
|
60
|
+
$:.collect {|d|
|
61
|
+
File::expand_path(d)
|
62
|
+
}.find_all {|d|
|
63
|
+
File::directory?("#{d}/rdoc/generators")
|
64
|
+
}.each {|dir|
|
65
|
+
Dir::entries("#{dir}/rdoc/generators").each {|gen|
|
66
|
+
next unless /(\w+)_generator.rb$/ =~ gen
|
67
|
+
type = $1
|
68
|
+
unless GENERATORS.has_key? type
|
69
|
+
GENERATORS[type] = Generator.new("rdoc/generators/#{gen}",
|
70
|
+
"#{type.upcase}Generator".intern,
|
71
|
+
type)
|
72
|
+
end
|
73
|
+
}
|
74
|
+
}
|
75
|
+
|
76
|
+
#######
|
77
|
+
private
|
78
|
+
#######
|
79
|
+
|
80
|
+
##
|
81
|
+
# Report an error message and exit
|
82
|
+
|
83
|
+
def error(msg)
|
84
|
+
raise RDocError.new(msg)
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# Create an output dir if it doesn't exist. If it does
|
89
|
+
# exist, but doesn't contain the flag file <tt>created.rid</tt>
|
90
|
+
# then we refuse to use it, as we may clobber some
|
91
|
+
# manually generated documentation
|
92
|
+
|
93
|
+
def setup_output_dir(op_dir)
|
94
|
+
flag_file = File.join(op_dir, "created.rid")
|
95
|
+
if File.exist?(op_dir)
|
96
|
+
unless File.directory?(op_dir)
|
97
|
+
error "'#{op_dir}' exists, and is not a directory"
|
98
|
+
end
|
99
|
+
unless File.file?(flag_file)
|
100
|
+
error "\nDirectory #{op_dir} already exists, but it looks like it\n" +
|
101
|
+
"isn't an RDoc directory. Because RDoc doesn't want to risk\n" +
|
102
|
+
"destroying any of your existing files, you'll need to\n" +
|
103
|
+
"specify a different output directory name (using the\n" +
|
104
|
+
"--op <dir> option).\n\n"
|
105
|
+
end
|
106
|
+
else
|
107
|
+
File.makedirs(op_dir)
|
108
|
+
end
|
109
|
+
File.open(flag_file, "w") {|f| f.puts Time.now }
|
110
|
+
end
|
111
|
+
|
112
|
+
|
113
|
+
# Given a list of files and directories, create a list
|
114
|
+
# of all the Ruby files they contain.
|
115
|
+
|
116
|
+
def normalized_file_list(options, *relative_files)
|
117
|
+
file_list = []
|
118
|
+
|
119
|
+
relative_files.each do |rel_file_name|
|
120
|
+
|
121
|
+
case type = File.ftype(rel_file_name)
|
122
|
+
when "file"
|
123
|
+
file_list << rel_file_name
|
124
|
+
when "directory"
|
125
|
+
next if options.exclude && options.exclude =~ rel_file_name
|
126
|
+
Find.find(rel_file_name) do |fn|
|
127
|
+
next if options.exclude && options.exclude =~ fn
|
128
|
+
next unless ParserFactory.can_parse(fn)
|
129
|
+
next unless File.file?(fn)
|
130
|
+
|
131
|
+
file_list << fn.sub(%r{\./}, '')
|
132
|
+
end
|
133
|
+
else
|
134
|
+
raise RDocError.new("I can't deal with a #{type} #{rel_file_name}")
|
135
|
+
end
|
136
|
+
end
|
137
|
+
file_list
|
138
|
+
end
|
139
|
+
|
140
|
+
# Parse each file on the command line, recursively entering
|
141
|
+
# directories
|
142
|
+
|
143
|
+
def parse_files(options)
|
144
|
+
|
145
|
+
file_info = []
|
146
|
+
|
147
|
+
files = options.files
|
148
|
+
files = ["."] if files.empty?
|
149
|
+
|
150
|
+
file_list = normalized_file_list(options, *files)
|
151
|
+
|
152
|
+
file_list.each do |fn|
|
153
|
+
$stderr.printf("\n%35s: ", File.basename(fn)) unless options.quiet
|
154
|
+
|
155
|
+
content = File.open(fn, "r") {|f| f.read}
|
156
|
+
|
157
|
+
top_level = TopLevel.new(fn)
|
158
|
+
parser = ParserFactory.parser_for(top_level, fn, content, options)
|
159
|
+
file_info << parser.scan
|
160
|
+
end
|
161
|
+
|
162
|
+
file_info
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
public
|
167
|
+
|
168
|
+
###################################################################
|
169
|
+
#
|
170
|
+
# Format up one or more files according to the given arguments.
|
171
|
+
# For simplicity, _argv_ is an array of strings, equivalent to the
|
172
|
+
# strings that would be passed on the command line. (This isn't a
|
173
|
+
# coincidence, as we _do_ pass in ARGV when running
|
174
|
+
# interactively). For a list of options, see rdoc/rdoc.rb. By
|
175
|
+
# default, output will be stored in a directory called +doc+ below
|
176
|
+
# the current directory, so make sure you're somewhere writable
|
177
|
+
# before invoking.
|
178
|
+
#
|
179
|
+
# Throws: RDocError on error
|
180
|
+
|
181
|
+
def document(argv)
|
182
|
+
|
183
|
+
TopLevel::reset
|
184
|
+
|
185
|
+
options = Options.instance
|
186
|
+
options.parse(argv, GENERATORS)
|
187
|
+
|
188
|
+
file_info = parse_files(options)
|
189
|
+
|
190
|
+
gen = options.generator
|
191
|
+
|
192
|
+
$stderr.puts "\nGenerating #{gen.key.upcase}..." unless options.quiet
|
193
|
+
|
194
|
+
require gen.file_name
|
195
|
+
|
196
|
+
gen_class = Generators.const_get(gen.class_name)
|
197
|
+
|
198
|
+
unless file_info.empty?
|
199
|
+
gen = gen_class.for(options)
|
200
|
+
|
201
|
+
pwd = Dir.pwd
|
202
|
+
|
203
|
+
unless options.all_one_file
|
204
|
+
setup_output_dir(options.op_dir)
|
205
|
+
Dir.chdir(options.op_dir)
|
206
|
+
end
|
207
|
+
|
208
|
+
begin
|
209
|
+
Diagram.new(file_info, options).draw if options.diagram
|
210
|
+
gen.generate(file_info)
|
211
|
+
ensure
|
212
|
+
Dir.chdir(pwd)
|
213
|
+
end
|
214
|
+
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
@@ -0,0 +1,234 @@
|
|
1
|
+
# Cheap-n-cheerful HTML page template system. You create a
|
2
|
+
# template containing:
|
3
|
+
#
|
4
|
+
# * variable names between percent signs (<tt>%fred%</tt>)
|
5
|
+
# * blocks of repeating stuff:
|
6
|
+
#
|
7
|
+
# START:key
|
8
|
+
# ... stuff
|
9
|
+
# END:key
|
10
|
+
#
|
11
|
+
# You feed the code a hash. For simple variables, the values
|
12
|
+
# are resolved directly from the hash. For blocks, the hash entry
|
13
|
+
# corresponding to +key+ will be an array of hashes. The block will
|
14
|
+
# be generated once for each entry. Blocks can be nested arbitrarily
|
15
|
+
# deeply.
|
16
|
+
#
|
17
|
+
# The template may also contain
|
18
|
+
#
|
19
|
+
# IF:key
|
20
|
+
# ... stuff
|
21
|
+
# ENDIF:key
|
22
|
+
#
|
23
|
+
# _stuff_ will only be included in the output if the corresponding
|
24
|
+
# key is set in the value hash.
|
25
|
+
#
|
26
|
+
# Usage: Given a set of templates <tt>T1, T2,</tt> etc
|
27
|
+
#
|
28
|
+
# values = { "name" => "Dave", state => "TX" }
|
29
|
+
#
|
30
|
+
# t = TemplatePage.new(T1, T2, T3)
|
31
|
+
# File.open(name, "w") {|f| t.write_html_on(f, values)}
|
32
|
+
# or
|
33
|
+
# res = ''
|
34
|
+
# t.write_html_on(res, values)
|
35
|
+
#
|
36
|
+
#
|
37
|
+
|
38
|
+
class TemplatePage
|
39
|
+
|
40
|
+
##########
|
41
|
+
# A context holds a stack of key/value pairs (like a symbol
|
42
|
+
# table). When asked to resolve a key, it first searches the top of
|
43
|
+
# the stack, then the next level, and so on until it finds a match
|
44
|
+
# (or runs out of entries)
|
45
|
+
|
46
|
+
class Context
|
47
|
+
def initialize
|
48
|
+
@stack = []
|
49
|
+
end
|
50
|
+
|
51
|
+
def push(hash)
|
52
|
+
@stack.push(hash)
|
53
|
+
end
|
54
|
+
|
55
|
+
def pop
|
56
|
+
@stack.pop
|
57
|
+
end
|
58
|
+
|
59
|
+
# Find a scalar value, throwing an exception if not found. This
|
60
|
+
# method is used when substituting the %xxx% constructs
|
61
|
+
|
62
|
+
def find_scalar(key)
|
63
|
+
@stack.reverse_each do |level|
|
64
|
+
if val = level[key]
|
65
|
+
return val unless val.kind_of? Array
|
66
|
+
end
|
67
|
+
end
|
68
|
+
raise "Template error: can't find variable '#{key}'"
|
69
|
+
end
|
70
|
+
|
71
|
+
# Lookup any key in the stack of hashes
|
72
|
+
|
73
|
+
def lookup(key)
|
74
|
+
@stack.reverse_each do |level|
|
75
|
+
val = level[key]
|
76
|
+
return val if val
|
77
|
+
end
|
78
|
+
nil
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
#########
|
83
|
+
# Simple class to read lines out of a string
|
84
|
+
|
85
|
+
class LineReader
|
86
|
+
# we're initialized with an array of lines
|
87
|
+
def initialize(lines)
|
88
|
+
@lines = lines
|
89
|
+
end
|
90
|
+
|
91
|
+
# read the next line
|
92
|
+
def read
|
93
|
+
@lines.shift
|
94
|
+
end
|
95
|
+
|
96
|
+
# Return a list of lines up to the line that matches
|
97
|
+
# a pattern. That last line is discarded.
|
98
|
+
def read_up_to(pattern)
|
99
|
+
res = []
|
100
|
+
while line = read
|
101
|
+
if pattern.match(line)
|
102
|
+
return LineReader.new(res)
|
103
|
+
else
|
104
|
+
res << line
|
105
|
+
end
|
106
|
+
end
|
107
|
+
raise "Missing end tag in template: #{pattern.source}"
|
108
|
+
end
|
109
|
+
|
110
|
+
# Return a copy of ourselves that can be modified without
|
111
|
+
# affecting us
|
112
|
+
def dup
|
113
|
+
LineReader.new(@lines.dup)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
|
118
|
+
|
119
|
+
# +templates+ is an array of strings containing the templates.
|
120
|
+
# We start at the first, and substitute in subsequent ones
|
121
|
+
# where the string <tt>!INCLUDE!</tt> occurs. For example,
|
122
|
+
# we could have the overall page template containing
|
123
|
+
#
|
124
|
+
# <html><body>
|
125
|
+
# <h1>Master</h1>
|
126
|
+
# !INCLUDE!
|
127
|
+
# </bost></html>
|
128
|
+
#
|
129
|
+
# and substitute subpages in to it by passing [master, sub_page].
|
130
|
+
# This gives us a cheap way of framing pages
|
131
|
+
|
132
|
+
def initialize(*templates)
|
133
|
+
result = "!INCLUDE!"
|
134
|
+
templates.each do |content|
|
135
|
+
result.sub!(/!INCLUDE!/, content)
|
136
|
+
end
|
137
|
+
@lines = LineReader.new(result.split($/))
|
138
|
+
end
|
139
|
+
|
140
|
+
# Render the templates into HTML, storing the result on +op+
|
141
|
+
# using the method <tt><<</tt>. The <tt>value_hash</tt> contains
|
142
|
+
# key/value pairs used to drive the substitution (as described above)
|
143
|
+
|
144
|
+
def write_html_on(op, value_hash)
|
145
|
+
@context = Context.new
|
146
|
+
op << substitute_into(@lines, value_hash).tr("\000", '\\')
|
147
|
+
end
|
148
|
+
|
149
|
+
|
150
|
+
# Substitute a set of key/value pairs into the given template.
|
151
|
+
# Keys with scalar values have them substituted directly into
|
152
|
+
# the page. Those with array values invoke <tt>substitute_array</tt>
|
153
|
+
# (below), which examples a block of the template once for each
|
154
|
+
# row in the array.
|
155
|
+
#
|
156
|
+
# This routine also copes with the <tt>IF:</tt>_key_ directive,
|
157
|
+
# removing chunks of the template if the corresponding key
|
158
|
+
# does not appear in the hash, and the START: directive, which
|
159
|
+
# loops its contents for each value in an array
|
160
|
+
|
161
|
+
def substitute_into(lines, values)
|
162
|
+
@context.push(values)
|
163
|
+
skip_to = nil
|
164
|
+
result = []
|
165
|
+
|
166
|
+
while line = lines.read
|
167
|
+
|
168
|
+
case line
|
169
|
+
|
170
|
+
when /^IF:(\w+)/
|
171
|
+
lines.read_up_to(/^ENDIF:#$1/) unless @context.lookup($1)
|
172
|
+
|
173
|
+
when /^IFNOT:(\w+)/
|
174
|
+
lines.read_up_to(/^ENDIF:#$1/) if @context.lookup($1)
|
175
|
+
|
176
|
+
when /^ENDIF:/
|
177
|
+
;
|
178
|
+
|
179
|
+
when /^START:(\w+)/
|
180
|
+
tag = $1
|
181
|
+
body = lines.read_up_to(/^END:#{tag}/)
|
182
|
+
inner_values = @context.lookup(tag)
|
183
|
+
raise "unknown tag: #{tag}" unless inner_values
|
184
|
+
raise "not array: #{tag}" unless inner_values.kind_of?(Array)
|
185
|
+
inner_values.each do |vals|
|
186
|
+
result << substitute_into(body.dup, vals)
|
187
|
+
end
|
188
|
+
else
|
189
|
+
result << expand_line(line.dup)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
@context.pop
|
194
|
+
|
195
|
+
result.join("\n")
|
196
|
+
end
|
197
|
+
|
198
|
+
# Given an individual line, we look for %xxx% constructs and
|
199
|
+
# HREF:ref:name: constructs, substituting for each.
|
200
|
+
|
201
|
+
def expand_line(line)
|
202
|
+
# Generate a cross reference if a reference is given,
|
203
|
+
# otherwise just fill in the name part
|
204
|
+
|
205
|
+
line.gsub!(/HREF:(\w+?):(\w+?):/) {
|
206
|
+
ref = @context.lookup($1)
|
207
|
+
name = @context.find_scalar($2)
|
208
|
+
|
209
|
+
if ref and !ref.kind_of?(Array)
|
210
|
+
"<a href=\"#{ref}\">#{name}</a>"
|
211
|
+
else
|
212
|
+
name
|
213
|
+
end
|
214
|
+
}
|
215
|
+
|
216
|
+
# Substitute in values for %xxx% constructs. This is made complex
|
217
|
+
# because the replacement string may contain characters that are
|
218
|
+
# meaningful to the regexp (like \1)
|
219
|
+
|
220
|
+
line = line.gsub(/%([a-zA-Z]\w*)%/) {
|
221
|
+
val = @context.find_scalar($1)
|
222
|
+
val.tr('\\', "\000")
|
223
|
+
}
|
224
|
+
|
225
|
+
|
226
|
+
line
|
227
|
+
rescue Exception => e
|
228
|
+
$stderr.puts "Error in template: #{e}"
|
229
|
+
$stderr.puts "Original line: #{line}"
|
230
|
+
exit
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
234
|
+
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# A TokenStream is a list of tokens, gathered during the parse
|
2
|
+
# of some entity (say a method). Entities populate these streams
|
3
|
+
# by being registered with the lexer. Any class can collect tokens
|
4
|
+
# by including TokenStream. From the outside, you use such an object
|
5
|
+
# by calling the start_collecting_tokens method, followed by calls
|
6
|
+
# to add_token and pop_token
|
7
|
+
|
8
|
+
module TokenStream
|
9
|
+
def token_stream
|
10
|
+
@token_stream
|
11
|
+
end
|
12
|
+
|
13
|
+
def start_collecting_tokens
|
14
|
+
@token_stream = []
|
15
|
+
end
|
16
|
+
def add_token(tk)
|
17
|
+
@token_stream << tk
|
18
|
+
end
|
19
|
+
def add_tokens(tks)
|
20
|
+
tks.each {|tk| add_token(tk)}
|
21
|
+
end
|
22
|
+
def pop_token
|
23
|
+
@token_stream.pop
|
24
|
+
end
|
25
|
+
end
|