erbook 4.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.
Files changed (56) hide show
  1. data/LICENSE +13 -0
  2. data/README +14 -0
  3. data/Rakefile +77 -0
  4. data/bin/erbook +309 -0
  5. data/doc/HelloWorld.input +37 -0
  6. data/doc/HelloWorld.spec +62 -0
  7. data/doc/README +6 -0
  8. data/doc/api/classes/ERBook.html +164 -0
  9. data/doc/api/classes/RDoc.html +112 -0
  10. data/doc/api/classes/RDoc/AnyMethod.html +195 -0
  11. data/doc/api/classes/RDoc/AnyMethod.src/M000005.html +18 -0
  12. data/doc/api/classes/RDoc/AnyMethod.src/M000006.html +23 -0
  13. data/doc/api/classes/RDoc/AnyMethod.src/M000007.html +18 -0
  14. data/doc/api/classes/RDoc/AnyMethod.src/M000008.html +22 -0
  15. data/doc/api/classes/RDoc/TopLevel.html +250 -0
  16. data/doc/api/classes/RDoc/TopLevel.src/M000009.html +18 -0
  17. data/doc/api/classes/RDoc/TopLevel.src/M000010.html +18 -0
  18. data/doc/api/classes/RDoc/TopLevel.src/M000011.html +18 -0
  19. data/doc/api/classes/RDoc/TopLevel.src/M000012.html +29 -0
  20. data/doc/api/classes/RDoc/TopLevel.src/M000013.html +25 -0
  21. data/doc/api/classes/RDoc/TopLevel.src/M000014.html +18 -0
  22. data/doc/api/classes/String.html +261 -0
  23. data/doc/api/classes/String.src/M000001.html +18 -0
  24. data/doc/api/classes/String.src/M000002.html +34 -0
  25. data/doc/api/classes/String.src/M000003.html +20 -0
  26. data/doc/api/classes/String.src/M000004.html +26 -0
  27. data/doc/api/created.rid +1 -0
  28. data/doc/api/files/lib/erbook/html_rb.html +125 -0
  29. data/doc/api/files/lib/erbook/rdoc_rb.html +116 -0
  30. data/doc/api/files/lib/erbook_rb.html +107 -0
  31. data/doc/api/fr_class_index.html +31 -0
  32. data/doc/api/fr_file_index.html +29 -0
  33. data/doc/api/fr_method_index.html +40 -0
  34. data/doc/api/index.html +24 -0
  35. data/doc/api/rdoc-style.css +208 -0
  36. data/doc/erbook.png +0 -0
  37. data/doc/erbook.svg +349 -0
  38. data/doc/feed-icon-28x28.png +0 -0
  39. data/doc/index.html +2663 -0
  40. data/doc/manual.erb +769 -0
  41. data/fmt/html.icons/LICENSE +67 -0
  42. data/fmt/html.icons/README +31 -0
  43. data/fmt/html.icons/caution.png +0 -0
  44. data/fmt/html.icons/important.png +0 -0
  45. data/fmt/html.icons/note.png +0 -0
  46. data/fmt/html.icons/quote.png +0 -0
  47. data/fmt/html.icons/tip.png +0 -0
  48. data/fmt/html.icons/warning.png +0 -0
  49. data/fmt/html.yaml +1185 -0
  50. data/fmt/latex.yaml +2 -0
  51. data/fmt/man.yaml +2 -0
  52. data/fmt/text.yaml +2 -0
  53. data/lib/erbook.rb +13 -0
  54. data/lib/erbook/html.rb +145 -0
  55. data/lib/erbook/rdoc.rb +126 -0
  56. metadata +141 -0
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright 2006 Suraj N. Kurapati <sunaku@gmail.com>
2
+
3
+ Permission to use, copy, modify, and distribute this software for any
4
+ purpose with or without fee is hereby granted, provided that the above
5
+ copyright notice and this permission notice appear in all copies.
6
+
7
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
8
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
9
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
10
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
11
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
12
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
13
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
data/README ADDED
@@ -0,0 +1,14 @@
1
+ erbook : write books and documents in eRuby
2
+ ===========================================
3
+
4
+ erbook is an extensible document processor that emits HTML
5
+ (web page), LaTeX (PDF), man (UNIX manual page), plain text,
6
+ and any document format you can imagine from eRuby templates
7
+ that allow scripting and dynamic content generation.
8
+
9
+ erbook is a light (200 source lines of code), extensible
10
+ (create your own document formats), and flexible (your content
11
+ is scriptable) alternative to DocBook, Deplate, and SiSU.
12
+
13
+ Please see the ./doc/index.html file in your web browser or
14
+ visit http://snk.tuxfamily.org/lib/erbook/ for more information.
@@ -0,0 +1,77 @@
1
+ require 'rake/clean'
2
+
3
+ # documentation
4
+ desc "Build the documentation."
5
+ task :doc
6
+
7
+ # user manual
8
+ src = 'doc/manual.erb'
9
+ dst = 'doc/index.html'
10
+
11
+ task :manual => dst
12
+ task :doc => :manual
13
+
14
+ file dst => src do
15
+ sh "ruby bin/erbook -u html #{src} > #{dst}"
16
+ end
17
+
18
+ CLOBBER.include dst
19
+
20
+ # API reference
21
+ require 'rake/rdoctask'
22
+
23
+ Rake::RDocTask.new 'doc/api' do |t|
24
+ t.rdoc_dir = t.name
25
+ t.rdoc_files.exclude('pkg').include('**/*.rb')
26
+ end
27
+
28
+ task :doc => 'doc/api'
29
+
30
+ # packaging
31
+ require 'rake/gempackagetask'
32
+ require 'lib/erbook' # project info
33
+
34
+ spec = Gem::Specification.new do |s|
35
+ s.rubyforge_project = 'sunaku'
36
+ s.author, s.email = File.read('LICENSE').
37
+ scan(/Copyright \d+ (.*) <(.*?)>/).first
38
+
39
+ s.name = ERBook::PROJECT
40
+ s.version = ERBook::VERSION
41
+ s.summary = ERBook::SUMMARY
42
+ s.description = s.summary
43
+ s.homepage = ERBook::WEBSITE
44
+ s.files = FileList['**/*']
45
+ s.executables = s.name
46
+ s.has_rdoc = true
47
+
48
+ # gems needed by the default 'html' format
49
+ s.add_dependency 'maruku', '~> 0.5'
50
+ s.add_dependency 'coderay', '>= 0.7'
51
+ end
52
+
53
+ Rake::GemPackageTask.new(spec) do |pkg|
54
+ pkg.need_tar = true
55
+ pkg.need_zip = true
56
+ end
57
+
58
+ desc 'Build release packages.'
59
+ task :pack => [:clobber, :doc] do
60
+ sh $0, 'package'
61
+ end
62
+
63
+ # releasing
64
+ desc 'Upload to project website.'
65
+ task :website => :doc do
66
+ sh "rsync -av doc/ ~/www/lib/#{spec.name}"
67
+ sh "rsync -av doc/api/ ~/www/lib/#{spec.name}/api/ --delete"
68
+ end
69
+
70
+ desc 'Publish release packages.'
71
+ task :publish => :pack do
72
+ sh 'rubyforge', 'login'
73
+
74
+ Dir['pkg/*.[a-z]*'].each do |pkg|
75
+ sh 'rubyforge', 'add_release', '--release_date', ERBook::RELEASE, spec.rubyforge_project, spec.name, spec.version.to_s, pkg
76
+ end
77
+ end
@@ -0,0 +1,309 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # erbook is an extensible document processor based on eRuby.
4
+ #
5
+ # * STDIN will be read if no input files are specified.
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.
10
+ #
11
+
12
+ require 'erb'
13
+ include ERB::Util
14
+
15
+ require 'digest/sha1'
16
+ require 'yaml'
17
+ require 'ostruct'
18
+
19
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
20
+ require 'erbook'
21
+
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
27
+
28
+ class String
29
+ # Returns a digest of this string that's not altered by String#to_html.
30
+ def digest_id
31
+ # XXX: surround all digits with alphabets so
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
55
+ end
56
+
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}"
110
+
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]
116
+ end
117
+
118
+ exit
119
+ end
120
+
121
+ opts.on '-h', '--help', 'show usage information' do
122
+ show_help_and_exit.call
123
+ end
124
+
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
133
+
134
+ unindent = false
135
+ opts.on '-u', '--unindent', 'unindent hierarchically' do unindent = true end
136
+
137
+ begin
138
+ opts.parse! ARGV
139
+ rescue
140
+ show_help_and_exit.call
141
+ end
142
+
143
+ # load format specification file
144
+ spec_file = ARGV.shift or
145
+ raise ArgumentError, "Format was not specified. Run `#{$0} -h` for help."
146
+
147
+ File.file? spec_file or
148
+ spec_file = File.join(ERBook::FORMATS_DIR, spec_file + '.yaml')
149
+
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
158
+
159
+ if spec_data.key? 'code'
160
+ eval spec_data['code'].to_s, binding, "#{spec_file}:code"
161
+ end
162
+
163
+ # load input document
164
+ input = ARGF.read
165
+
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})%>"
170
+ end
171
+
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}/, '' )
185
+
186
+ case tag
187
+ when /<%[^%=].*?\bdo\b.*?%>/m
188
+ margins.push buffer[/^[ \t]*(?=\S)/]
189
+
190
+ when /<%\s*end\s*%>/m
191
+ margins.pop
192
+ end
193
+ end
194
+ result << buffer
195
+
196
+ input = result
197
+ end
198
+
199
+ # create sandbox for input evaluation
200
+ template = Template.new('INPUT', input)
201
+
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) }
208
+
209
+ node_defs = spec_data['nodes'].each_pair do |name, info|
210
+ template.instance_eval %{
211
+ #
212
+ # XXX: using a string because define_method()
213
+ # does not accept a block until Ruby 1.9
214
+ #
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
229
+ end
230
+
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
238
+
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('.')
243
+ 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
253
+ end
254
+ end
255
+
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
268
+
269
+ node.content = content
270
+ node.digest = digest
271
+
272
+ digest
273
+ end
274
+ }, __FILE__, Kernel.caller.first[/\d+/].to_i.next
275
+ end
276
+
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
293
+ end
294
+
295
+ # repeat for all child nodes
296
+ n.children.each {|c| expander[c, buf] }
297
+ end
298
+
299
+ roots.each {|n| expander[n, document] }
300
+
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
305
+
306
+ # emit output document
307
+ puts Template.new("#{spec_file}:output", spec_data['output'].to_s).
308
+ render_with(template_vars.merge(:@content => document))
309
+ end
@@ -0,0 +1,37 @@
1
+ <% $style = "border-left: thick dotted LightGrey; padding-left: 1em;" %>
2
+ <% hello "Pretentious", 1, 2, 3 do %>
3
+ <big>I'm</big> the very first node, oh _yes_ I am! *sneer*
4
+
5
+ <% hello "Bashful", 4, 5, 6 do %>
6
+ Hi, I... *hide*
7
+
8
+ <% hello "Hopeful", rand do %>
9
+ *sigh*
10
+
11
+ <% hello "Confused", (rand * rand) do %>
12
+ Huh?
13
+ <% end %>
14
+ <% end %>
15
+
16
+ <% hello "Raving", __FILE__ do %>
17
+ Oh it's *on* now! You're going *down*!
18
+ <% end %>
19
+ <% end %>
20
+
21
+ <% hello "Sleepy", Time.now do %>
22
+ *yawn* Just five more minutes...
23
+
24
+ <% hello "Peaceful", Dir.pwd do %>
25
+ So _be_ happy my friend, *happy*!
26
+
27
+ <%= hello "Lonely (as you can see, I have no block)" %>
28
+ <% end %>
29
+ <% end %>
30
+ <% end %>
31
+
32
+ <% hello "Snappy" do %>
33
+ Zip! Zap! Wake up, you sap!
34
+ _Whoo I'm wild!_ ;-)
35
+ <% end %>
36
+
37
+ <%= hello "Independent (no block, no parents, I am _free_!)" %>