erbook 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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_!)" %>