erbook 4.0.0 → 5.0.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.
- data/README +3 -3
- data/Rakefile +6 -3
- data/bin/erbook +285 -251
- data/doc/README +2 -2
- data/doc/api/classes/ERBook.html +2 -2
- data/doc/api/classes/RDoc/AnyMethod.html +20 -20
- data/doc/api/classes/RDoc/AnyMethod.src/M000003.html +18 -0
- data/doc/api/classes/RDoc/AnyMethod.src/M000004.html +23 -0
- data/doc/api/classes/RDoc/AnyMethod.src/M000005.html +4 -4
- data/doc/api/classes/RDoc/AnyMethod.src/M000006.html +7 -8
- data/doc/api/classes/RDoc/TopLevel.html +30 -30
- data/doc/api/classes/RDoc/{AnyMethod.src → TopLevel.src}/M000007.html +4 -4
- data/doc/api/classes/RDoc/TopLevel.src/{M000014.html → M000008.html} +4 -4
- data/doc/api/classes/RDoc/TopLevel.src/M000009.html +4 -4
- data/doc/api/classes/RDoc/TopLevel.src/M000010.html +15 -4
- data/doc/api/classes/RDoc/TopLevel.src/M000011.html +11 -4
- data/doc/api/classes/RDoc/TopLevel.src/M000012.html +4 -15
- data/doc/api/classes/String.html +17 -82
- data/doc/api/classes/String.src/M000001.html +4 -4
- data/doc/api/classes/String.src/M000002.html +3 -6
- data/doc/api/created.rid +1 -1
- data/doc/api/files/lib/erbook/rdoc_rb.html +1 -1
- data/doc/api/files/lib/erbook/{html_rb.html → to_xhtml_rb.html} +7 -7
- data/doc/api/files/lib/erbook_rb.html +1 -1
- data/doc/api/fr_file_index.html +1 -1
- data/doc/api/fr_method_index.html +12 -14
- data/doc/api/index.html +1 -1
- data/doc/index.xhtml +2277 -0
- data/doc/manual.erb +198 -155
- data/fmt/xhtml.icons/index.yaml +20 -0
- data/fmt/xhtml.icons/mediawiki-1.13.2/COPYING +340 -0
- data/fmt/xhtml.icons/mediawiki-1.13.2/README +103 -0
- data/fmt/xhtml.icons/mediawiki-1.13.2/skins/simple/external.png +0 -0
- data/fmt/xhtml.icons/tango-icon-theme-0.8.1/48x48/README +2 -0
- data/fmt/{html.icons/note.png → xhtml.icons/tango-icon-theme-0.8.1/48x48/apps/accessories-text-editor.png} +0 -0
- data/fmt/{html.icons/quote.png → xhtml.icons/tango-icon-theme-0.8.1/48x48/apps/internet-group-chat.png} +0 -0
- data/fmt/{html.icons/important.png → xhtml.icons/tango-icon-theme-0.8.1/48x48/emblems/emblem-important.png} +0 -0
- data/fmt/{html.icons/warning.png → xhtml.icons/tango-icon-theme-0.8.1/48x48/status/dialog-error.png} +0 -0
- data/fmt/{html.icons/tip.png → xhtml.icons/tango-icon-theme-0.8.1/48x48/status/dialog-information.png} +0 -0
- data/fmt/{html.icons/caution.png → xhtml.icons/tango-icon-theme-0.8.1/48x48/status/dialog-warning.png} +0 -0
- data/fmt/{html.icons/LICENSE → xhtml.icons/tango-icon-theme-0.8.1/COPYING} +0 -0
- data/fmt/xhtml.icons/tango-icon-theme-0.8.1/README +12 -0
- data/fmt/{html.yaml → xhtml.yaml} +305 -247
- data/lib/erbook.rb +2 -2
- data/lib/erbook/{html.rb → to_xhtml.rb} +18 -24
- metadata +42 -21
- data/doc/api/classes/RDoc/AnyMethod.src/M000008.html +0 -22
- data/doc/api/classes/RDoc/TopLevel.src/M000013.html +0 -25
- data/doc/api/classes/String.src/M000003.html +0 -20
- data/doc/api/classes/String.src/M000004.html +0 -26
- data/doc/index.html +0 -2663
- data/fmt/html.icons/README +0 -31
data/README
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
erbook : write books and documents in eRuby
|
2
2
|
===========================================
|
3
3
|
|
4
|
-
erbook is an extensible document processor that emits
|
4
|
+
erbook is an extensible document processor that emits XHTML
|
5
5
|
(web page), LaTeX (PDF), man (UNIX manual page), plain text,
|
6
6
|
and any document format you can imagine from eRuby templates
|
7
7
|
that allow scripting and dynamic content generation.
|
8
8
|
|
9
|
-
erbook is a light (
|
9
|
+
erbook is a light (210 source lines of code), extensible
|
10
10
|
(create your own document formats), and flexible (your content
|
11
11
|
is scriptable) alternative to DocBook, Deplate, and SiSU.
|
12
12
|
|
13
|
-
Please see the ./doc/index.
|
13
|
+
Please see the ./doc/index.xhtml file in your web browser or
|
14
14
|
visit http://snk.tuxfamily.org/lib/erbook/ for more information.
|
data/Rakefile
CHANGED
@@ -6,13 +6,13 @@ require 'rake/clean'
|
|
6
6
|
|
7
7
|
# user manual
|
8
8
|
src = 'doc/manual.erb'
|
9
|
-
dst = 'doc/index.
|
9
|
+
dst = 'doc/index.xhtml'
|
10
10
|
|
11
11
|
task :manual => dst
|
12
12
|
task :doc => :manual
|
13
13
|
|
14
14
|
file dst => src do
|
15
|
-
sh "ruby bin/erbook -u
|
15
|
+
sh "ruby bin/erbook -u xhtml #{src} > #{dst}"
|
16
16
|
end
|
17
17
|
|
18
18
|
CLOBBER.include dst
|
@@ -45,7 +45,10 @@ require 'rake/clean'
|
|
45
45
|
s.executables = s.name
|
46
46
|
s.has_rdoc = true
|
47
47
|
|
48
|
-
# gems needed by the
|
48
|
+
# gems needed by the main program executable
|
49
|
+
s.add_dependency 'trollop', '~> 1.10'
|
50
|
+
|
51
|
+
# gems needed by the default 'xhtml' format
|
49
52
|
s.add_dependency 'maruku', '~> 0.5'
|
50
53
|
s.add_dependency 'coderay', '>= 0.7'
|
51
54
|
end
|
data/bin/erbook
CHANGED
@@ -1,309 +1,343 @@
|
|
1
|
-
#!/usr/bin/
|
1
|
+
#!/usr/bin/ruby -w
|
2
2
|
#
|
3
3
|
# erbook is an extensible document processor based on eRuby.
|
4
4
|
#
|
5
|
-
# *
|
5
|
+
# * The standard input stream will be read if an input file is not specified.
|
6
6
|
#
|
7
|
-
# *
|
8
|
-
#
|
9
|
-
#
|
7
|
+
# * The final output document will be written to the standard output stream.
|
8
|
+
#
|
9
|
+
# * If an error occurs, the input document will be written to the standard
|
10
|
+
# output stream, so that you can investigate line numbers in the error.
|
11
|
+
#
|
12
|
+
# Usage:
|
13
|
+
#
|
14
|
+
# erbook [Option...] FormatName [InputFile]
|
15
|
+
# erbook [Option...] FormatFile [InputFile]
|
10
16
|
#
|
11
|
-
|
12
|
-
require 'erb'
|
13
|
-
include ERB::Util
|
14
|
-
|
15
|
-
require 'digest/sha1'
|
16
|
-
require 'yaml'
|
17
|
-
require 'ostruct'
|
18
17
|
|
19
18
|
$:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
20
19
|
require 'erbook'
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
21
|
+
module ERBook
|
22
|
+
# Prints the given message and raises the given error.
|
23
|
+
def ERBook.error aMessage, aError = $!
|
24
|
+
STDERR.printf "%s:\n\n", aMessage
|
25
|
+
raise aError
|
26
|
+
end
|
27
27
|
|
28
|
-
|
28
|
+
require 'digest/sha1'
|
29
29
|
# Returns a digest of this string that's not altered by String#to_html.
|
30
|
-
def
|
30
|
+
def ERBook.digest aInput
|
31
31
|
# XXX: surround all digits with alphabets so
|
32
32
|
# Maruku doesn't change them into HTML
|
33
|
-
Digest::
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
class Template < ERB
|
38
|
-
# Returns the result of template evaluation thus far.
|
39
|
-
attr_reader :buffer
|
40
|
-
|
41
|
-
# aName:: String that replaces the ambiguous '(erb)' identifier in stack
|
42
|
-
# traces, so that the user can better determine the source of an
|
43
|
-
# error.
|
44
|
-
#
|
45
|
-
# args:: Arguments for ERB::new
|
46
|
-
def initialize aName, *args
|
47
|
-
# silence the code-only <% ... %> directive, just like PHP does
|
48
|
-
args[0].gsub!( /^[ \t]*(<%[^%=]((?!<%).)*?[^%]%>)[ \t]*\r?\n/m ) { $1 }
|
49
|
-
|
50
|
-
# use @buffer to store the result of the ERB template
|
51
|
-
args[3] = :@buffer
|
52
|
-
super(*args)
|
53
|
-
|
54
|
-
self.filename = aName
|
33
|
+
Digest::SHA1.hexdigest(aInput.to_s).gsub(/\d/, 'z\&z')
|
55
34
|
end
|
56
35
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
show_help_and_exit = lambda do
|
104
|
-
# show program description located at the top of this file
|
105
|
-
puts File.read(__FILE__).split(/^$\n/).first.gsub(/^# ?/, '').sub(/\A.*$\n/, '')
|
106
|
-
|
107
|
-
puts '', "Usage: #{File.basename $0} [Option...] {Format|SpecFile} [InputFile...]\n"
|
108
|
-
|
109
|
-
puts '', "Option: #{opts}"
|
36
|
+
require 'erb'
|
37
|
+
class Template < ERB
|
38
|
+
# The result of template evaluation thus far.
|
39
|
+
attr_reader :buffer
|
40
|
+
|
41
|
+
# aSource:: String that replaces the ambiguous '(erb)'
|
42
|
+
# identifier in stack traces, so that the user
|
43
|
+
# can better determine the source of an error.
|
44
|
+
#
|
45
|
+
# aInput:: String containing eRuby directives. This
|
46
|
+
# string will be modified by this method!
|
47
|
+
#
|
48
|
+
# aSafeLevel:: See safe_level in ERB::new().
|
49
|
+
#
|
50
|
+
def initialize aSource, aInput, aUnindent = false, aSafeLevel = nil
|
51
|
+
# convert "% at beginning of line" usage into <% normal %> usage
|
52
|
+
aInput.gsub! %r{^([ \t]*)(%[=# \t].*)$}, '\1<\2 %>'
|
53
|
+
aInput.gsub! %r{^([ \t]*)%%}, '\1%'
|
54
|
+
|
55
|
+
# silence the code-only <% ... %> directive, just like PHP does
|
56
|
+
aInput.gsub! %r{^[ \t]*(<%[^%=]((?!<%).)*?[^%]%>)[ \t]*\r?\n}m, '\1'
|
57
|
+
|
58
|
+
# unindent node content hierarchically
|
59
|
+
if aUnindent
|
60
|
+
tags = aInput.scan(/<%(?:.(?!<%))*?%>/m)
|
61
|
+
margins = []
|
62
|
+
result = []
|
63
|
+
|
64
|
+
buffer = aInput
|
65
|
+
tags.each do |tag|
|
66
|
+
chunk, buffer = buffer.split(tag, 2)
|
67
|
+
chunk << tag
|
68
|
+
|
69
|
+
# perform unindentation
|
70
|
+
result << chunk.gsub(/^#{margins.last}/, '')
|
71
|
+
|
72
|
+
# prepare for next unindentation
|
73
|
+
case tag
|
74
|
+
when /<%[^%=].*?\bdo\b.*?%>/m
|
75
|
+
margins.push buffer[/^[ \t]*(?=\S)/]
|
76
|
+
|
77
|
+
when /<%\s*end\s*%>/m
|
78
|
+
margins.pop
|
79
|
+
end
|
80
|
+
end
|
81
|
+
result << buffer
|
110
82
|
|
111
|
-
|
112
|
-
ERBook::FORMAT_FILES.each do |file|
|
113
|
-
name = File.basename(file, '.yaml')
|
114
|
-
desc = YAML.load_file(file)['desc'] rescue nil
|
115
|
-
puts ' %-32s %s' % [name, desc]
|
83
|
+
aInput = result.join
|
116
84
|
end
|
117
85
|
|
118
|
-
|
119
|
-
|
86
|
+
# use @buffer to store the result of the ERB template
|
87
|
+
super aInput, aSafeLevel, nil, :@buffer
|
120
88
|
|
121
|
-
|
122
|
-
show_help_and_exit.call
|
89
|
+
self.filename = aSource
|
123
90
|
end
|
124
91
|
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
end
|
92
|
+
# Renders this template within a fresh object that
|
93
|
+
# is populated with the given instance variables.
|
94
|
+
def render_with aInstVars = {}
|
95
|
+
context = Object.new.instance_eval do
|
96
|
+
aInstVars.each_pair do |var, val|
|
97
|
+
instance_variable_set var, val
|
98
|
+
end
|
133
99
|
|
134
|
-
|
135
|
-
|
100
|
+
binding
|
101
|
+
end
|
136
102
|
|
137
|
-
|
138
|
-
opts.parse! ARGV
|
139
|
-
rescue
|
140
|
-
show_help_and_exit.call
|
103
|
+
result(context)
|
141
104
|
end
|
142
105
|
|
143
|
-
|
144
|
-
spec_file = ARGV.shift or
|
145
|
-
raise ArgumentError, "Format was not specified. Run `#{$0} -h` for help."
|
106
|
+
private
|
146
107
|
|
147
|
-
|
148
|
-
|
108
|
+
# Returns the content that the given block wants to append to
|
109
|
+
# the buffer. If the given block does not want to append to the
|
110
|
+
# buffer, then returns the result of invoking the given block.
|
111
|
+
def content_from_block *aBlockArgs
|
112
|
+
raise ArgumentError, 'block must be given' unless block_given?
|
149
113
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
:name => File.basename(spec_file).sub(/\..*?$/, '')
|
154
|
-
|
155
|
-
rescue Exception
|
156
|
-
error "Error when loading the format specification file (#{spec_file.inspect})"
|
157
|
-
end
|
114
|
+
head = @buffer.length
|
115
|
+
body = yield(*aBlockArgs) # this will do: @buffer << content
|
116
|
+
tail = @buffer.length
|
158
117
|
|
159
|
-
|
160
|
-
|
118
|
+
if tail > head
|
119
|
+
@buffer.slice! head..tail
|
120
|
+
else
|
121
|
+
body
|
122
|
+
end.to_s
|
161
123
|
end
|
124
|
+
end
|
162
125
|
|
163
|
-
|
164
|
-
|
126
|
+
require 'ostruct'
|
127
|
+
class Node < OpenStruct
|
128
|
+
# deprecated in Ruby 1.8; removed in Ruby 1.9
|
129
|
+
undef id if respond_to? :id
|
130
|
+
undef type if respond_to? :type
|
131
|
+
end
|
165
132
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
133
|
+
# XXX: the basename() is for being launched by a RubyGems executable
|
134
|
+
if __FILE__ == $0 or File.basename(__FILE__) == File.basename($0)
|
135
|
+
require 'yaml'
|
136
|
+
|
137
|
+
# parse command-line options
|
138
|
+
begin require 'rubygems' rescue LoadError end
|
139
|
+
require 'trollop'
|
140
|
+
|
141
|
+
opts = Trollop::options do
|
142
|
+
# show program description located at the top of this file
|
143
|
+
banner File.read(__FILE__)[/\A.*?^$\n/m].
|
144
|
+
gsub(/^# ?/, '').sub(/\A.*?\n/, '')
|
145
|
+
banner ''
|
146
|
+
|
147
|
+
# show list of available formats
|
148
|
+
banner 'FormatName:'
|
149
|
+
ERBook::FORMAT_FILES.each do |file|
|
150
|
+
name = File.basename(file, '.yaml')
|
151
|
+
desc = YAML.load_file(file)['desc'] rescue nil
|
152
|
+
banner '%16s: %s' % [name, desc]
|
170
153
|
end
|
154
|
+
banner ''
|
155
|
+
|
156
|
+
# show list of command-line options
|
157
|
+
banner 'Option:'
|
158
|
+
opt :unindent, 'Unindent node content hierarchically'
|
159
|
+
|
160
|
+
# show program version information
|
161
|
+
version [
|
162
|
+
"project: #{ERBook::PROJECT}",
|
163
|
+
"version: #{ERBook::VERSION}",
|
164
|
+
"release: #{ERBook::RELEASE}",
|
165
|
+
"website: #{ERBook::WEBSITE}",
|
166
|
+
"install: #{ERBook::INSTALL_DIR}",
|
167
|
+
].join("\n")
|
168
|
+
end
|
171
169
|
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
margins = []
|
176
|
-
result = ''
|
177
|
-
|
178
|
-
buffer = input
|
179
|
-
tags.each do |tag|
|
180
|
-
chunk, buffer = buffer.split(tag, 2)
|
181
|
-
chunk << tag
|
182
|
-
|
183
|
-
# perform unindentation
|
184
|
-
result << chunk.gsub( /^#{margins.last}/, '' )
|
170
|
+
# load format specification file
|
171
|
+
spec_file = ARGV.shift or
|
172
|
+
raise ArgumentError, "Format was not specified. Run `#{$0} -h` for help."
|
185
173
|
|
186
|
-
|
187
|
-
|
188
|
-
margins.push buffer[/^[ \t]*(?=\S)/]
|
174
|
+
File.file? spec_file or
|
175
|
+
spec_file = File.join(ERBook::FORMATS_DIR, spec_file + '.yaml')
|
189
176
|
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
result << buffer
|
177
|
+
begin
|
178
|
+
spec_data = YAML.load_file(spec_file)
|
179
|
+
spec_data[:file] = File.expand_path(spec_file)
|
180
|
+
spec_data[:name] = File.basename(spec_file).sub(/\..*?$/, '')
|
195
181
|
|
196
|
-
|
182
|
+
if spec_data.key? 'code'
|
183
|
+
eval spec_data['code'].to_s, TOPLEVEL_BINDING, "#{spec_file}:code"
|
197
184
|
end
|
198
185
|
|
199
|
-
|
200
|
-
|
186
|
+
rescue Exception
|
187
|
+
error "Error when loading the format specification file (#{spec_file.inspect})"
|
188
|
+
end
|
201
189
|
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
190
|
+
# load input document
|
191
|
+
if input_file = ARGV.shift
|
192
|
+
input = File.read input_file
|
193
|
+
else
|
194
|
+
input_file, input = 'STDIN', STDIN.read
|
195
|
+
end
|
196
|
+
|
197
|
+
begin
|
198
|
+
# expand all "include" directives in the input
|
199
|
+
begin end while input.gsub! %r{<%#\s*include\s+(.+?)\s*#%>} do
|
200
|
+
file, line = $1, $`.count("\n").next
|
208
201
|
|
209
|
-
|
210
|
-
|
202
|
+
# provide more accurate stack trace for
|
203
|
+
# errors originating from included files.
|
211
204
|
#
|
212
|
-
#
|
213
|
-
#
|
205
|
+
# NOTE: eRuby does NOT seem to provide line numbers for trace
|
206
|
+
# entries that are deeper than the input document itself
|
214
207
|
#
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
208
|
+
"<%
|
209
|
+
begin
|
210
|
+
%>#{File.read file}<%
|
211
|
+
rescue Exception => err
|
212
|
+
bak = err.backtrace
|
213
|
+
|
214
|
+
# set the input document's originating line number to
|
215
|
+
# where this file was included in the input document
|
216
|
+
top = bak.find {|t| t =~ /#{/#{input_file}/}:\\d+$/ }
|
217
|
+
top.sub! %r/\\d+$/, '#{line}'
|
218
|
+
|
219
|
+
# add a stack trace entry mentioning this included file
|
220
|
+
ins = bak.index top
|
221
|
+
bak.insert ins, #{file.inspect}
|
222
|
+
|
223
|
+
raise err
|
229
224
|
end
|
225
|
+
%>"
|
226
|
+
end
|
230
227
|
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
228
|
+
# create sandbox for input evaluation
|
229
|
+
template = Template.new(input_file, input, opts[:unindent])
|
230
|
+
|
231
|
+
template_vars = {
|
232
|
+
:@spec => spec_data,
|
233
|
+
:@roots => roots = [], # root nodes of all trees
|
234
|
+
:@nodes => nodes = [], # all nodes in the forest
|
235
|
+
:@types => types = Hash.new {|h,k| h[k] = []}, # nodes by type
|
236
|
+
}.each_pair {|k,v| template.instance_variable_set(k, v) }
|
237
|
+
|
238
|
+
node_defs = spec_data['nodes'].each_pair do |type, defn|
|
239
|
+
template.instance_eval %{
|
240
|
+
#
|
241
|
+
# XXX: using a string because define_method()
|
242
|
+
# does not accept a block until Ruby 1.9
|
243
|
+
#
|
244
|
+
def #{type} *aArgs, &aBlock
|
245
|
+
node = Node.new(
|
246
|
+
:type => #{type.inspect},
|
247
|
+
:args => aArgs,
|
248
|
+
:trace => caller,
|
249
|
+
:children => []
|
250
|
+
)
|
251
|
+
@nodes << node
|
252
|
+
@types[node.type] << node
|
253
|
+
|
254
|
+
# calculate occurrence number for this node
|
255
|
+
if #{defn['number']}
|
256
|
+
@count ||= Hash.new {|h,k| h[k] = []}
|
257
|
+
node.number = (@count[node.type] << node).length
|
258
|
+
end
|
238
259
|
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
260
|
+
@stack ||= []
|
261
|
+
|
262
|
+
# assign node family
|
263
|
+
if parent = @stack.last
|
264
|
+
parent.children << node
|
265
|
+
node.parent = parent
|
266
|
+
node.depth = parent.depth.next
|
267
|
+
|
268
|
+
# calculate latex-style index number for this node
|
269
|
+
if #{defn['index']}
|
270
|
+
branches = parent.children.select {|n| n.index}
|
271
|
+
node.index = [parent.index, branches.length.next].join('.')
|
272
|
+
end
|
273
|
+
else
|
274
|
+
@roots << node
|
275
|
+
node.parent = nil
|
276
|
+
node.depth = 0
|
277
|
+
|
278
|
+
# calculate latex-style index number for this node
|
279
|
+
if #{defn['index']}
|
280
|
+
branches = @roots.select {|n| n.index}
|
281
|
+
node.index = branches.length.next.to_s
|
282
|
+
end
|
243
283
|
end
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
284
|
+
|
285
|
+
# assign node content
|
286
|
+
if block_given?
|
287
|
+
@stack.push node
|
288
|
+
content = content_from_block(node, &aBlock)
|
289
|
+
@stack.pop
|
290
|
+
|
291
|
+
digest = ERBook.digest(content)
|
292
|
+
self.buffer << digest
|
293
|
+
else
|
294
|
+
content = nil
|
295
|
+
digest = ERBook.digest(node.object_id)
|
253
296
|
end
|
254
|
-
end
|
255
297
|
|
256
|
-
|
257
|
-
|
258
|
-
@stack.push node
|
259
|
-
content = content_from_block(node, &aBlock).to_s
|
260
|
-
@stack.pop
|
261
|
-
|
262
|
-
digest = content.digest_id
|
263
|
-
@buffer << digest
|
264
|
-
else
|
265
|
-
content = nil
|
266
|
-
digest = node.object_id.to_s.digest_id
|
267
|
-
end
|
298
|
+
node.content = content
|
299
|
+
node.digest = digest
|
268
300
|
|
269
|
-
|
270
|
-
|
301
|
+
digest
|
302
|
+
end
|
303
|
+
}, __FILE__, Kernel.caller.first[/\d+/].to_i.next
|
304
|
+
end
|
271
305
|
|
272
|
-
|
306
|
+
# build the document tree
|
307
|
+
document = template.instance_eval { result(binding) }
|
308
|
+
|
309
|
+
# replace nodes with output
|
310
|
+
expander = lambda do |n, buf|
|
311
|
+
# calculate node output
|
312
|
+
source = "#{spec_file}:nodes:#{n.type}:output"
|
313
|
+
n.output = Template.new(
|
314
|
+
source, node_defs[n.type]['output'].to_s.chomp).
|
315
|
+
render_with(template_vars.merge(:@node => n))
|
316
|
+
|
317
|
+
# replace node with output
|
318
|
+
if node_defs[n.type]['silent']
|
319
|
+
buf[n.digest] = ''
|
320
|
+
buf = n.output
|
321
|
+
else
|
322
|
+
buf[n.digest] = n.output
|
273
323
|
end
|
274
|
-
}, __FILE__, Kernel.caller.first[/\d+/].to_i.next
|
275
|
-
end
|
276
324
|
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
# replace nodes with output
|
281
|
-
expander = lambda do |n, buf|
|
282
|
-
# calculate node output
|
283
|
-
source = "#{spec_file}:nodes:#{n.type}:output"
|
284
|
-
n.output = Template.new(source, node_defs[n.type]['output'].to_s.chomp).
|
285
|
-
render_with(template_vars.merge :@node => n)
|
286
|
-
|
287
|
-
# replace node with output
|
288
|
-
if node_defs[n.type]['silent']
|
289
|
-
buf[n.digest] = ''
|
290
|
-
buf = n.output
|
291
|
-
else
|
292
|
-
buf[n.digest] = n.output
|
325
|
+
# repeat for all child nodes
|
326
|
+
n.children.each {|c| expander[c, buf] }
|
293
327
|
end
|
294
328
|
|
295
|
-
|
296
|
-
n.children.each {|c| expander[c, buf] }
|
297
|
-
end
|
329
|
+
roots.each {|n| expander[n, document] }
|
298
330
|
|
299
|
-
|
331
|
+
rescue Exception => e
|
332
|
+
# omit erbook internals from the stack trace
|
333
|
+
e.backtrace.reject! {|t| t =~ /^#{$0}:\d+/ } unless $DEBUG
|
300
334
|
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
end
|
335
|
+
puts input # so the user can debug the line numbers in the stack trace
|
336
|
+
error "Error when processing the input document (#{input_file})"
|
337
|
+
end
|
305
338
|
|
306
|
-
|
307
|
-
|
308
|
-
|
339
|
+
# emit output document
|
340
|
+
puts Template.new("#{spec_file}:output", spec_data['output'].to_s).
|
341
|
+
render_with(template_vars.merge(:@content => document))
|
342
|
+
end
|
309
343
|
end
|