erbook 9.2.0 → 9.2.1
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/doc/api/classes/ERBook/Document.html +19 -210
- data/doc/api/classes/ERBook/Document/Node.html +42 -42
- data/doc/api/classes/ERBook/Template.html +5 -5
- data/doc/api/classes/ERBook/Template/Sandbox.html +5 -5
- data/doc/api/classes/RDoc/AnyMethod.html +20 -20
- data/doc/api/classes/RDoc/TopLevel.html +30 -30
- data/doc/api/classes/String.html +5 -5
- data/doc/api/created.rid +1 -1
- data/doc/api/files/lib/erbook/document_rb.html +3 -1
- data/doc/api/files/lib/erbook/to_xhtml_rb.html +1 -1
- data/doc/api/files/lib/erbook_rb.html +1 -3
- data/doc/api/panel/search_index.js +1 -1
- data/doc/history.erb +23 -0
- data/doc/index.html +450 -381
- data/fmt/xhtml.yaml +11 -5
- data/fmt/xhtml/icons/index.yaml +4 -0
- data/fmt/xhtml/icons/w3.org/README +2 -0
- data/fmt/xhtml/icons/w3.org/valid-xhtml10.png +0 -0
- data/fmt/xhtml/icons/w3.org/vcss.gif +0 -0
- data/lib/erbook.rb +2 -2
- data/lib/erbook/document.rb +216 -197
- metadata +8 -5
data/fmt/xhtml.yaml
CHANGED
@@ -131,7 +131,9 @@ code: |
|
|
131
131
|
# Returns a temporary data URI that will be replaced
|
132
132
|
# with the actual data URI at runtime by javascript.
|
133
133
|
#
|
134
|
-
|
134
|
+
def embed_uri
|
135
|
+
"cid:#{object_id}"
|
136
|
+
end
|
135
137
|
|
136
138
|
##
|
137
139
|
# Returns an image tag that renders an embedded data URI.
|
@@ -921,12 +923,12 @@ output: |
|
|
921
923
|
|
922
924
|
<p class="validations">
|
923
925
|
<a href="http://validator.w3.org/check?uri=referer"><img
|
924
|
-
src="
|
926
|
+
src="<%= ICON_BY_NAME['valid_html'].embed_uri %>"
|
925
927
|
alt="Valid XHTML 1.0 Strict" height="31" width="88" /></a>
|
926
928
|
|
927
929
|
<a href="http://jigsaw.w3.org/css-validator/check/referer?profile=css3"><img
|
928
|
-
src="
|
929
|
-
alt="Valid CSS!" height="31" width="88" /></a>
|
930
|
+
src="<%= ICON_BY_NAME['valid_css'].embed_uri %>"
|
931
|
+
alt="Valid CSS 3!" height="31" width="88" /></a>
|
930
932
|
</p>
|
931
933
|
</div>
|
932
934
|
</div>
|
@@ -1220,7 +1222,7 @@ javascript: |
|
|
1220
1222
|
var query = search_box.val().replace(/^\s+|\s+$/g, '');
|
1221
1223
|
|
1222
1224
|
// ignore empty queries
|
1223
|
-
if (!query.match(/\S/)) {
|
1225
|
+
if (search_box.is('.initial') || !query.match(/\S/)) {
|
1224
1226
|
return;
|
1225
1227
|
}
|
1226
1228
|
|
@@ -1692,6 +1694,10 @@ styles: # these are SASS templates
|
|
1692
1694
|
padding-left: 1em
|
1693
1695
|
|
1694
1696
|
|
1697
|
+
#_references > ol > li
|
1698
|
+
margin-bottom: 1em
|
1699
|
+
|
1700
|
+
|
1695
1701
|
#_footer
|
1696
1702
|
padding-top: 1em
|
1697
1703
|
text-align: center
|
data/fmt/xhtml/icons/index.yaml
CHANGED
@@ -20,3 +20,7 @@ nav_here: tango-icon-theme-0.8.90/32x32/status/software-update-available.png
|
|
20
20
|
nav_prev: tango-icon-theme-0.8.90/32x32/actions/go-previous.png
|
21
21
|
nav_next: tango-icon-theme-0.8.90/32x32/actions/go-next.png
|
22
22
|
nav_list: tango-icon-theme-0.8.90/32x32/places/start-here.png
|
23
|
+
|
24
|
+
# W3C validator badges
|
25
|
+
valid_html: w3.org/valid-xhtml10.png
|
26
|
+
valid_css: w3.org/vcss.gif
|
Binary file
|
Binary file
|
data/lib/erbook.rb
CHANGED
@@ -9,8 +9,8 @@ require 'inochi'
|
|
9
9
|
|
10
10
|
Inochi.init :ERBook,
|
11
11
|
:program => 'erbook',
|
12
|
-
:version => '9.2.
|
13
|
-
:release => '2009-11-
|
12
|
+
:version => '9.2.1',
|
13
|
+
:release => '2009-11-18',
|
14
14
|
:website => 'http://snk.tuxfamily.org/lib/erbook/',
|
15
15
|
:tagline => 'Write books, manuals, and documents in eRuby',
|
16
16
|
:require => {
|
data/lib/erbook/document.rb
CHANGED
@@ -40,204 +40,13 @@ module ERBook
|
|
40
40
|
# If true, all node content is unindented hierarchically.
|
41
41
|
#
|
42
42
|
def initialize format_name, input_text, input_file, options = {}
|
43
|
-
|
44
|
-
@format_file = format_name.to_s
|
43
|
+
load_format format_name
|
45
44
|
|
46
|
-
|
47
|
-
|
45
|
+
@input_text = input_text
|
46
|
+
@input_file = input_file
|
47
|
+
@options = options
|
48
48
|
|
49
|
-
|
50
|
-
@format = YAML.load_file(@format_file)
|
51
|
-
@format[:file] = File.expand_path(@format_file)
|
52
|
-
@format[:name] = File.basename(@format_file).sub(/\..*?$/, '')
|
53
|
-
|
54
|
-
if @format.key? 'code'
|
55
|
-
eval @format['code'].to_s, TOPLEVEL_BINDING, "#{@format_file}:code"
|
56
|
-
end
|
57
|
-
|
58
|
-
rescue Exception
|
59
|
-
error "Could not load format specification file #{@format_file.inspect}"
|
60
|
-
end
|
61
|
-
|
62
|
-
@node_defs = @format['nodes']
|
63
|
-
|
64
|
-
# process input document
|
65
|
-
begin
|
66
|
-
# create sandbox for input evaluation
|
67
|
-
template = Template.new(input_file, input_text, options[:unindent])
|
68
|
-
sandbox = template.sandbox
|
69
|
-
|
70
|
-
@template_vars = {
|
71
|
-
:@format => @format,
|
72
|
-
:@roots => @roots = [], # root nodes of all trees
|
73
|
-
:@nodes => @nodes = [], # all nodes in the forest
|
74
|
-
:@nodes_by_type => @nodes_by_type = Hash.new {|h,k| h[k] = [] },
|
75
|
-
:@stack => [], # stack for all nodes
|
76
|
-
}.each_pair {|k,v| sandbox.instance_variable_set(k, v) }
|
77
|
-
|
78
|
-
#:stopdoc:
|
79
|
-
|
80
|
-
##
|
81
|
-
# Handles the method call from a node
|
82
|
-
# placeholder in the input document.
|
83
|
-
#
|
84
|
-
def sandbox.__node_handler__ node_type, *node_args, &node_content
|
85
|
-
node = Node.new(
|
86
|
-
:type => node_type,
|
87
|
-
:definition => @format['nodes'][node_type],
|
88
|
-
:arguments => node_args,
|
89
|
-
:backtrace => caller,
|
90
|
-
:parent => @stack.last,
|
91
|
-
:children => []
|
92
|
-
)
|
93
|
-
|
94
|
-
Array(node.definition['params']).each do |param|
|
95
|
-
break if node_args.empty?
|
96
|
-
node.__send__ "#{param}=", node_args.shift
|
97
|
-
end
|
98
|
-
|
99
|
-
@nodes << node
|
100
|
-
@nodes_by_type[node.type] << node
|
101
|
-
|
102
|
-
# calculate ordinal number for this node
|
103
|
-
if node.ordinal_number?
|
104
|
-
@count_by_type ||= Hash.new {|h,k| h[k] = 0 }
|
105
|
-
node.ordinal_number = (@count_by_type[node.type] += 1)
|
106
|
-
end
|
107
|
-
|
108
|
-
# assign node family
|
109
|
-
if parent = node.parent
|
110
|
-
parent.children << node
|
111
|
-
node.parent = parent
|
112
|
-
node.depth = parent.depth
|
113
|
-
node.depth += 1 if node.anchor?
|
114
|
-
|
115
|
-
# calculate section number for this node
|
116
|
-
if node.section_number?
|
117
|
-
ancestor = @stack.reverse.find {|n| n.section_number }
|
118
|
-
branches = parent.children.select {|n| n.section_number }
|
119
|
-
|
120
|
-
node.section_number = [
|
121
|
-
ancestor.section_number,
|
122
|
-
branches.length + 1
|
123
|
-
].join('.')
|
124
|
-
end
|
125
|
-
else
|
126
|
-
@roots << node
|
127
|
-
node.parent = nil
|
128
|
-
node.depth = 0
|
129
|
-
|
130
|
-
# calculate section number for this node
|
131
|
-
if node.section_number?
|
132
|
-
branches = @roots.select {|n| n.section_number }
|
133
|
-
node.section_number = (branches.length + 1).to_s
|
134
|
-
end
|
135
|
-
end
|
136
|
-
|
137
|
-
# assign node content
|
138
|
-
if block_given?
|
139
|
-
@stack.push node
|
140
|
-
node.content = __block_content__(node, &node_content)
|
141
|
-
@stack.pop
|
142
|
-
end
|
143
|
-
|
144
|
-
@buffer << node
|
145
|
-
|
146
|
-
nil
|
147
|
-
end
|
148
|
-
|
149
|
-
#:startdoc:
|
150
|
-
|
151
|
-
@node_defs.each_key do |type|
|
152
|
-
# XXX: using a string because define_method()
|
153
|
-
# does not accept a block until Ruby 1.9
|
154
|
-
file, line = __FILE__, __LINE__; eval %{
|
155
|
-
def sandbox.#{type} *node_args, &node_content
|
156
|
-
__node_handler__ #{type.inspect}, *node_args, &node_content
|
157
|
-
end
|
158
|
-
}, binding, file, line
|
159
|
-
end
|
160
|
-
|
161
|
-
# evaluate the input & build the document tree
|
162
|
-
template.render
|
163
|
-
@processed_document = template.buffer
|
164
|
-
|
165
|
-
# chain block-level nodes together for local navigation
|
166
|
-
block_nodes = @nodes.select {|n| n.chain? }
|
167
|
-
|
168
|
-
require 'enumerator'
|
169
|
-
block_nodes.each_cons(2) do |a, b|
|
170
|
-
a.next_node = b
|
171
|
-
b.prev_node = a
|
172
|
-
end
|
173
|
-
|
174
|
-
# calculate output for all nodes
|
175
|
-
actual_output_by_node = {}
|
176
|
-
|
177
|
-
visitor = lambda do |n|
|
178
|
-
#
|
179
|
-
# allow child nodes to calculate their actual
|
180
|
-
# output and to set their identifier as Node#output
|
181
|
-
#
|
182
|
-
# we do this nodes first because this node's
|
183
|
-
# content contains the child nodes' output
|
184
|
-
#
|
185
|
-
n.children.each {|c| visitor.call c }
|
186
|
-
|
187
|
-
# calculate the output for this node
|
188
|
-
actual_output = Template.new(
|
189
|
-
"#{@format_file}:nodes:#{n.type}:output",
|
190
|
-
n.definition['output'].to_s.chomp
|
191
|
-
).render_with(@template_vars.merge(:@node => n))
|
192
|
-
|
193
|
-
# reveal child nodes' actual output in this node's actual output
|
194
|
-
n.children.each do |c|
|
195
|
-
if c.silent?
|
196
|
-
# this child's output is not meant to be revealed at this time
|
197
|
-
next
|
198
|
-
|
199
|
-
elsif c.inline?
|
200
|
-
actual_output[c.output] = actual_output_by_node[c]
|
201
|
-
|
202
|
-
else
|
203
|
-
# remove <p> around block-level child (added by Markdown)
|
204
|
-
actual_output.sub! %r{(<p>\s*)?#{
|
205
|
-
Regexp.quote c.output
|
206
|
-
}(\s*</p>)?} do
|
207
|
-
actual_output_by_node[c] +
|
208
|
-
if $1 and $2
|
209
|
-
''
|
210
|
-
else
|
211
|
-
[$1, $2].join
|
212
|
-
end
|
213
|
-
end
|
214
|
-
end
|
215
|
-
end
|
216
|
-
|
217
|
-
actual_output_by_node[n] = actual_output
|
218
|
-
|
219
|
-
#
|
220
|
-
# allow the parent node to calculate its actual
|
221
|
-
# output without interference from the output of
|
222
|
-
# this node (Node#to_s is aliased to Node#output)
|
223
|
-
#
|
224
|
-
# this assumes that having this node's string
|
225
|
-
# representation be a consecutive sequence of digits
|
226
|
-
# will not interfere with the text-to-whatever
|
227
|
-
# transformation defined by the format specification
|
228
|
-
#
|
229
|
-
n.output = Digest::SHA1.digest(n.object_id.to_s).unpack('I*').join
|
230
|
-
end
|
231
|
-
|
232
|
-
@roots.each {|n| visitor.call n }
|
233
|
-
|
234
|
-
# replace the temporary identifier with each node's actual output
|
235
|
-
@nodes.each {|n| n.output = actual_output_by_node[n] }
|
236
|
-
|
237
|
-
rescue Exception
|
238
|
-
puts input_text # so the user can debug line numbers in stack trace
|
239
|
-
error "Could not process input document #{input_file.inspect}"
|
240
|
-
end
|
49
|
+
process_input_document
|
241
50
|
end
|
242
51
|
|
243
52
|
##
|
@@ -245,7 +54,7 @@ module ERBook
|
|
245
54
|
#
|
246
55
|
def to_s
|
247
56
|
Template.new("#{@format_file}:output", @format['output'].to_s).
|
248
|
-
render_with(@template_vars.merge(:@content => @
|
57
|
+
render_with(@template_vars.merge(:@content => @evaluated_document.join))
|
249
58
|
end
|
250
59
|
|
251
60
|
require 'ostruct'
|
@@ -291,6 +100,216 @@ module ERBook
|
|
291
100
|
|
292
101
|
private
|
293
102
|
|
103
|
+
def load_format format_name
|
104
|
+
@format_file = format_name.to_s
|
105
|
+
|
106
|
+
File.file? @format_file or
|
107
|
+
@format_file = File.join(ERBook::FORMATS_DIR, @format_file + '.yaml')
|
108
|
+
|
109
|
+
begin
|
110
|
+
@format = YAML.load_file(@format_file)
|
111
|
+
@format[:file] = File.expand_path(@format_file)
|
112
|
+
@format[:name] = File.basename(@format_file).sub(/\..*?$/, '')
|
113
|
+
|
114
|
+
if @format.key? 'code'
|
115
|
+
eval @format['code'].to_s, TOPLEVEL_BINDING, "#{@format_file}:code"
|
116
|
+
end
|
117
|
+
|
118
|
+
rescue Exception
|
119
|
+
error "Could not load format specification file #{@format_file.inspect}"
|
120
|
+
end
|
121
|
+
|
122
|
+
@node_defs = @format['nodes']
|
123
|
+
end
|
124
|
+
|
125
|
+
def process_input_document
|
126
|
+
evaluate_input_document
|
127
|
+
process_document_tree
|
128
|
+
calculate_node_outputs
|
129
|
+
rescue Exception
|
130
|
+
puts @input_text # so the user can debug line numbers in stack trace
|
131
|
+
error "Could not process input document #{@input_file.inspect}"
|
132
|
+
end
|
133
|
+
|
134
|
+
def evaluate_input_document
|
135
|
+
template = create_sandboxed_template
|
136
|
+
template.render
|
137
|
+
@evaluated_document = template.buffer
|
138
|
+
end
|
139
|
+
|
140
|
+
def process_document_tree
|
141
|
+
# chain block-level nodes together for local navigation
|
142
|
+
block_nodes = @nodes.select {|n| n.chain? }
|
143
|
+
|
144
|
+
require 'enumerator'
|
145
|
+
block_nodes.each_cons(2) do |a, b|
|
146
|
+
a.next_node = b
|
147
|
+
b.prev_node = a
|
148
|
+
end
|
149
|
+
end
|
150
|
+
|
151
|
+
def calculate_node_outputs
|
152
|
+
actual_output_by_node = {}
|
153
|
+
|
154
|
+
visitor = lambda do |n|
|
155
|
+
#
|
156
|
+
# allow child nodes to calculate their actual
|
157
|
+
# output and to set their identifier as Node#output
|
158
|
+
#
|
159
|
+
# we do this nodes first because this node's
|
160
|
+
# content contains the child nodes' output
|
161
|
+
#
|
162
|
+
n.children.each {|c| visitor.call c }
|
163
|
+
|
164
|
+
# calculate the output for this node
|
165
|
+
actual_output = Template.new(
|
166
|
+
"#{@format_file}:nodes:#{n.type}:output",
|
167
|
+
n.definition['output'].to_s.chomp
|
168
|
+
).render_with(@template_vars.merge(:@node => n))
|
169
|
+
|
170
|
+
# reveal child nodes' actual output in this node's actual output
|
171
|
+
n.children.each do |c|
|
172
|
+
if c.silent?
|
173
|
+
# this child's output is not meant to be revealed at this time
|
174
|
+
next
|
175
|
+
|
176
|
+
elsif c.inline?
|
177
|
+
actual_output[c.output] = actual_output_by_node[c]
|
178
|
+
|
179
|
+
else
|
180
|
+
# remove <p> around block-level child (added by Markdown)
|
181
|
+
actual_output.sub! %r{(<p>\s*)?#{
|
182
|
+
Regexp.quote c.output
|
183
|
+
}(\s*</p>)?} do
|
184
|
+
actual_output_by_node[c] +
|
185
|
+
if $1 and $2
|
186
|
+
''
|
187
|
+
else
|
188
|
+
[$1, $2].join
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
end
|
193
|
+
|
194
|
+
actual_output_by_node[n] = actual_output
|
195
|
+
|
196
|
+
#
|
197
|
+
# allow the parent node to calculate its actual
|
198
|
+
# output without interference from the output of
|
199
|
+
# this node (Node#to_s is aliased to Node#output)
|
200
|
+
#
|
201
|
+
# this assumes that having this node's string
|
202
|
+
# representation be a consecutive sequence of digits
|
203
|
+
# will not interfere with the text-to-whatever
|
204
|
+
# transformation defined by the format specification
|
205
|
+
#
|
206
|
+
n.output = Digest::SHA1.digest(n.object_id.to_s).unpack('I*').join
|
207
|
+
end
|
208
|
+
|
209
|
+
@roots.each {|n| visitor.call n }
|
210
|
+
|
211
|
+
# replace the temporary identifier with each node's actual output
|
212
|
+
@nodes.each {|n| n.output = actual_output_by_node[n] }
|
213
|
+
end
|
214
|
+
|
215
|
+
def create_sandboxed_template
|
216
|
+
template = Template.new(@input_file, @input_text, @options[:unindent])
|
217
|
+
sandbox = template.sandbox
|
218
|
+
|
219
|
+
@template_vars = {
|
220
|
+
:@format => @format,
|
221
|
+
:@roots => @roots = [], # root nodes of all trees
|
222
|
+
:@nodes => @nodes = [], # all nodes in the forest
|
223
|
+
:@nodes_by_type => @nodes_by_type = Hash.new {|h,k| h[k] = [] },
|
224
|
+
:@stack => [], # stack for all nodes
|
225
|
+
}.each_pair {|k,v| sandbox.instance_variable_set(k, v) }
|
226
|
+
|
227
|
+
#:stopdoc:
|
228
|
+
|
229
|
+
##
|
230
|
+
# Handles the method call from a node
|
231
|
+
# placeholder in the input document.
|
232
|
+
#
|
233
|
+
def sandbox.__node_handler__ node_type, *node_args, &node_content
|
234
|
+
node = Node.new(
|
235
|
+
:type => node_type,
|
236
|
+
:definition => @format['nodes'][node_type],
|
237
|
+
:arguments => node_args,
|
238
|
+
:backtrace => caller,
|
239
|
+
:parent => @stack.last,
|
240
|
+
:children => []
|
241
|
+
)
|
242
|
+
|
243
|
+
Array(node.definition['params']).each do |param|
|
244
|
+
break if node_args.empty?
|
245
|
+
node.__send__ "#{param}=", node_args.shift
|
246
|
+
end
|
247
|
+
|
248
|
+
@nodes << node
|
249
|
+
@nodes_by_type[node.type] << node
|
250
|
+
|
251
|
+
# calculate ordinal number for this node
|
252
|
+
if node.ordinal_number?
|
253
|
+
@count_by_type ||= Hash.new {|h,k| h[k] = 0 }
|
254
|
+
node.ordinal_number = (@count_by_type[node.type] += 1)
|
255
|
+
end
|
256
|
+
|
257
|
+
# assign node family
|
258
|
+
if parent = node.parent
|
259
|
+
parent.children << node
|
260
|
+
node.parent = parent
|
261
|
+
node.depth = parent.depth
|
262
|
+
node.depth += 1 if node.anchor?
|
263
|
+
|
264
|
+
# calculate section number for this node
|
265
|
+
if node.section_number?
|
266
|
+
ancestor = @stack.reverse.find {|n| n.section_number }
|
267
|
+
branches = parent.children.select {|n| n.section_number }
|
268
|
+
|
269
|
+
node.section_number = [
|
270
|
+
ancestor.section_number,
|
271
|
+
branches.length + 1
|
272
|
+
].join('.')
|
273
|
+
end
|
274
|
+
else
|
275
|
+
@roots << node
|
276
|
+
node.parent = nil
|
277
|
+
node.depth = 0
|
278
|
+
|
279
|
+
# calculate section number for this node
|
280
|
+
if node.section_number?
|
281
|
+
branches = @roots.select {|n| n.section_number }
|
282
|
+
node.section_number = (branches.length + 1).to_s
|
283
|
+
end
|
284
|
+
end
|
285
|
+
|
286
|
+
# assign node content
|
287
|
+
if block_given?
|
288
|
+
@stack.push node
|
289
|
+
node.content = __block_content__(node, &node_content)
|
290
|
+
@stack.pop
|
291
|
+
end
|
292
|
+
|
293
|
+
@buffer << node
|
294
|
+
|
295
|
+
nil
|
296
|
+
end
|
297
|
+
|
298
|
+
#:startdoc:
|
299
|
+
|
300
|
+
@node_defs.each_key do |type|
|
301
|
+
# XXX: using a string because define_method()
|
302
|
+
# does not accept a block until Ruby 1.9
|
303
|
+
file, line = __FILE__, __LINE__; eval %{
|
304
|
+
def sandbox.#{type} *node_args, &node_content
|
305
|
+
__node_handler__ #{type.inspect}, *node_args, &node_content
|
306
|
+
end
|
307
|
+
}, binding, file, line
|
308
|
+
end
|
309
|
+
|
310
|
+
template
|
311
|
+
end
|
312
|
+
|
294
313
|
##
|
295
314
|
# Prints the given message and raises the given error.
|
296
315
|
#
|