erbook 4.0.0 → 5.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. data/README +3 -3
  2. data/Rakefile +6 -3
  3. data/bin/erbook +285 -251
  4. data/doc/README +2 -2
  5. data/doc/api/classes/ERBook.html +2 -2
  6. data/doc/api/classes/RDoc/AnyMethod.html +20 -20
  7. data/doc/api/classes/RDoc/AnyMethod.src/M000003.html +18 -0
  8. data/doc/api/classes/RDoc/AnyMethod.src/M000004.html +23 -0
  9. data/doc/api/classes/RDoc/AnyMethod.src/M000005.html +4 -4
  10. data/doc/api/classes/RDoc/AnyMethod.src/M000006.html +7 -8
  11. data/doc/api/classes/RDoc/TopLevel.html +30 -30
  12. data/doc/api/classes/RDoc/{AnyMethod.src → TopLevel.src}/M000007.html +4 -4
  13. data/doc/api/classes/RDoc/TopLevel.src/{M000014.html → M000008.html} +4 -4
  14. data/doc/api/classes/RDoc/TopLevel.src/M000009.html +4 -4
  15. data/doc/api/classes/RDoc/TopLevel.src/M000010.html +15 -4
  16. data/doc/api/classes/RDoc/TopLevel.src/M000011.html +11 -4
  17. data/doc/api/classes/RDoc/TopLevel.src/M000012.html +4 -15
  18. data/doc/api/classes/String.html +17 -82
  19. data/doc/api/classes/String.src/M000001.html +4 -4
  20. data/doc/api/classes/String.src/M000002.html +3 -6
  21. data/doc/api/created.rid +1 -1
  22. data/doc/api/files/lib/erbook/rdoc_rb.html +1 -1
  23. data/doc/api/files/lib/erbook/{html_rb.html → to_xhtml_rb.html} +7 -7
  24. data/doc/api/files/lib/erbook_rb.html +1 -1
  25. data/doc/api/fr_file_index.html +1 -1
  26. data/doc/api/fr_method_index.html +12 -14
  27. data/doc/api/index.html +1 -1
  28. data/doc/index.xhtml +2277 -0
  29. data/doc/manual.erb +198 -155
  30. data/fmt/xhtml.icons/index.yaml +20 -0
  31. data/fmt/xhtml.icons/mediawiki-1.13.2/COPYING +340 -0
  32. data/fmt/xhtml.icons/mediawiki-1.13.2/README +103 -0
  33. data/fmt/xhtml.icons/mediawiki-1.13.2/skins/simple/external.png +0 -0
  34. data/fmt/xhtml.icons/tango-icon-theme-0.8.1/48x48/README +2 -0
  35. data/fmt/{html.icons/note.png → xhtml.icons/tango-icon-theme-0.8.1/48x48/apps/accessories-text-editor.png} +0 -0
  36. data/fmt/{html.icons/quote.png → xhtml.icons/tango-icon-theme-0.8.1/48x48/apps/internet-group-chat.png} +0 -0
  37. data/fmt/{html.icons/important.png → xhtml.icons/tango-icon-theme-0.8.1/48x48/emblems/emblem-important.png} +0 -0
  38. data/fmt/{html.icons/warning.png → xhtml.icons/tango-icon-theme-0.8.1/48x48/status/dialog-error.png} +0 -0
  39. data/fmt/{html.icons/tip.png → xhtml.icons/tango-icon-theme-0.8.1/48x48/status/dialog-information.png} +0 -0
  40. data/fmt/{html.icons/caution.png → xhtml.icons/tango-icon-theme-0.8.1/48x48/status/dialog-warning.png} +0 -0
  41. data/fmt/{html.icons/LICENSE → xhtml.icons/tango-icon-theme-0.8.1/COPYING} +0 -0
  42. data/fmt/xhtml.icons/tango-icon-theme-0.8.1/README +12 -0
  43. data/fmt/{html.yaml → xhtml.yaml} +305 -247
  44. data/lib/erbook.rb +2 -2
  45. data/lib/erbook/{html.rb → to_xhtml.rb} +18 -24
  46. metadata +42 -21
  47. data/doc/api/classes/RDoc/AnyMethod.src/M000008.html +0 -22
  48. data/doc/api/classes/RDoc/TopLevel.src/M000013.html +0 -25
  49. data/doc/api/classes/String.src/M000003.html +0 -20
  50. data/doc/api/classes/String.src/M000004.html +0 -26
  51. data/doc/index.html +0 -2663
  52. 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 HTML
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 (200 source lines of code), extensible
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.html file in your web browser or
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.html'
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 html #{src} > #{dst}"
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 default 'html' format
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/env ruby
1
+ #!/usr/bin/ruby -w
2
2
  #
3
3
  # erbook is an extensible document processor based on eRuby.
4
4
  #
5
- # * STDIN will be read if no input files are specified.
5
+ # * The standard input stream will be read if an input file is not specified.
6
6
  #
7
- # * If an error occurs, the input document will be printed to STDOUT
8
- # so you can investigate line numbers in the error's stack trace.
9
- # Otherwise, the final output document will be printed to STDOUT.
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
- # Prints the given message and raises the given error.
23
- def error aMessage, aError = $!
24
- STDERR.printf "%s:\n\n", aMessage
25
- raise aError
26
- end
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
- class String
28
+ require 'digest/sha1'
29
29
  # Returns a digest of this string that's not altered by String#to_html.
30
- def digest_id
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::SHA2.hexdigest(self).gsub(/\d/, 'z\&z')
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
- # Renders this template within a fresh object that
58
- # is populated with the given instance variables.
59
- def render_with aInstVars = {}
60
- context = Object.new.instance_eval do
61
- aInstVars.each_pair do |var, val|
62
- instance_variable_set var, val
63
- end
64
-
65
- binding
66
- end
67
-
68
- result(context)
69
- end
70
-
71
- private
72
-
73
- # Returns the content that the given block wants to append to
74
- # the buffer. If the given block does not want to append to the
75
- # buffer, then returns the result of invoking the given block.
76
- def content_from_block *aBlockArgs
77
- raise ArgumentError, 'block must be given' unless block_given?
78
-
79
- start = @buffer.length
80
- value = yield(*aBlockArgs) # this will do: buffer << content
81
- finish = @buffer.length
82
-
83
- if finish > start
84
- @buffer.slice! start .. -1
85
- else
86
- value
87
- end.to_s
88
- end
89
- end
90
-
91
- class Node < OpenStruct
92
- undef id if respond_to? :id # deprecated in Ruby 1.8; removed in Ruby 1.9
93
- undef type if respond_to? :type # deprecated in Ruby 1.8; removed in Ruby 1.9
94
- end
95
-
96
- # XXX: the basename() is for being launched by a RubyGems executable
97
- if __FILE__ == $0 or File.basename(__FILE__) == File.basename($0)
98
- # parse command-line options
99
- require 'optparse'
100
-
101
- opts = OptionParser.new('')
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
- puts '', "Format:"
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
- exit
119
- end
86
+ # use @buffer to store the result of the ERB template
87
+ super aInput, aSafeLevel, nil, :@buffer
120
88
 
121
- opts.on '-h', '--help', 'show usage information' do
122
- show_help_and_exit.call
89
+ self.filename = aSource
123
90
  end
124
91
 
125
- opts.on '-v', '--version', 'show version information' do
126
- puts "project: #{ERBook::PROJECT}",
127
- "version: #{ERBook::VERSION}",
128
- "release: #{ERBook::RELEASE}",
129
- "website: #{ERBook::WEBSITE}",
130
- "install: #{ERBook::INSTALL_DIR}"
131
- exit
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
- unindent = false
135
- opts.on '-u', '--unindent', 'unindent hierarchically' do unindent = true end
100
+ binding
101
+ end
136
102
 
137
- begin
138
- opts.parse! ARGV
139
- rescue
140
- show_help_and_exit.call
103
+ result(context)
141
104
  end
142
105
 
143
- # load format specification file
144
- spec_file = ARGV.shift or
145
- raise ArgumentError, "Format was not specified. Run `#{$0} -h` for help."
106
+ private
146
107
 
147
- File.file? spec_file or
148
- spec_file = File.join(ERBook::FORMATS_DIR, spec_file + '.yaml')
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
- begin
151
- spec_data = YAML.load_file(spec_file).merge! \
152
- :file => File.expand_path(spec_file),
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
- if spec_data.key? 'code'
160
- eval spec_data['code'].to_s, binding, "#{spec_file}:code"
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
- # load input document
164
- input = ARGF.read
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
- begin
167
- # expand all "include" directives in the input
168
- begin end while input.gsub! %r{<%#\s*include\s+(.+?)\s*#%>} do
169
- "<%#begin(#{name = $1.inspect})%>#{File.read $1}<%#end(#{name})%>"
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
- # unindent node content
173
- if unindent
174
- tags = input.scan(/<%(?:.(?!<%))*?%>/m)
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
- case tag
187
- when /<%[^%=].*?\bdo\b.*?%>/m
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
- when /<%\s*end\s*%>/m
191
- margins.pop
192
- end
193
- end
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
- input = result
182
+ if spec_data.key? 'code'
183
+ eval spec_data['code'].to_s, TOPLEVEL_BINDING, "#{spec_file}:code"
197
184
  end
198
185
 
199
- # create sandbox for input evaluation
200
- template = Template.new('INPUT', input)
186
+ rescue Exception
187
+ error "Error when loading the format specification file (#{spec_file.inspect})"
188
+ end
201
189
 
202
- template_vars = {
203
- :@spec => spec_data,
204
- :@roots => roots = [], # root nodes of all trees
205
- :@nodes => nodes = [], # all nodes in the forest
206
- :@types => types = Hash.new {|h,k| h[k] = []}, # nodes by type
207
- }.each_pair {|k,v| template.instance_variable_set(k, v) }
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
- node_defs = spec_data['nodes'].each_pair do |name, info|
210
- template.instance_eval %{
202
+ # provide more accurate stack trace for
203
+ # errors originating from included files.
211
204
  #
212
- # XXX: using a string because define_method()
213
- # does not accept a block until Ruby 1.9
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
- def #{name} *aArgs, &aBlock
216
- node = Node.new(
217
- :type => #{name.inspect},
218
- :args => aArgs,
219
- :trace => caller,
220
- :children => []
221
- )
222
- @nodes << node
223
- @types[node.type] << node
224
-
225
- # calculate occurrence number for this node
226
- if #{info['number']}
227
- @count ||= Hash.new {|h,k| h[k] = []}
228
- node.number = (@count[node.type] << node).length
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
- @stack ||= []
232
-
233
- # assign node family
234
- if parent = @stack.last
235
- parent.children << node
236
- node.parent = parent
237
- node.depth = parent.depth.next
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
- # calculate latex-style index number for this node
240
- if #{info['index']}
241
- branches = parent.children.select {|n| n.index}
242
- node.index = [parent.index, branches.length.next].join('.')
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
- else
245
- @roots << node
246
- node.parent = nil
247
- node.depth = 0
248
-
249
- # calculate latex-style index number for this node
250
- if #{info['index']}
251
- branches = @roots.select {|n| n.index}
252
- node.index = branches.length.next.to_s
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
- # assign node content
257
- if block_given?
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
- node.content = content
270
- node.digest = digest
301
+ digest
302
+ end
303
+ }, __FILE__, Kernel.caller.first[/\d+/].to_i.next
304
+ end
271
305
 
272
- digest
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
- # build the document tree
278
- document = template.instance_eval { result(binding) }
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
- # repeat for all child nodes
296
- n.children.each {|c| expander[c, buf] }
297
- end
329
+ roots.each {|n| expander[n, document] }
298
330
 
299
- roots.each {|n| expander[n, document] }
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
- rescue Exception
302
- puts input # so the user can debug the line numbers in the stack trace
303
- error 'Error when processing the input document (INPUT)'
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
- # emit output document
307
- puts Template.new("#{spec_file}:output", spec_data['output'].to_s).
308
- render_with(template_vars.merge(:@content => document))
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