erbook 4.0.0 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|