verku 0.8.0.p

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 (60) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +49 -0
  4. data/BUILD.md +6 -0
  5. data/Gemfile +14 -0
  6. data/Gemfile.lock +82 -0
  7. data/LICENSE.md +22 -0
  8. data/README.md +149 -0
  9. data/Rakefile +72 -0
  10. data/VERSION +1 -0
  11. data/bin/verku +5 -0
  12. data/lib/verku.rb +56 -0
  13. data/lib/verku/adapters/markdown.rb +44 -0
  14. data/lib/verku/cli.rb +93 -0
  15. data/lib/verku/dependency.rb +19 -0
  16. data/lib/verku/exporter.rb +77 -0
  17. data/lib/verku/extensions/string.rb +19 -0
  18. data/lib/verku/generator.rb +55 -0
  19. data/lib/verku/parser.rb +85 -0
  20. data/lib/verku/parser/epub.rb +187 -0
  21. data/lib/verku/parser/html.rb +245 -0
  22. data/lib/verku/parser/mobi.rb +17 -0
  23. data/lib/verku/parser/pdf.rb +54 -0
  24. data/lib/verku/parser/txt.rb +1 -0
  25. data/lib/verku/stats.rb +114 -0
  26. data/lib/verku/stream.rb +27 -0
  27. data/lib/verku/toc.rb +6 -0
  28. data/lib/verku/toc/epub.rb +41 -0
  29. data/lib/verku/toc/html.rb +78 -0
  30. data/lib/verku/version.rb +10 -0
  31. data/templates/config.erb +80 -0
  32. data/templates/cover.jpg +0 -0
  33. data/templates/dp-logo.png +0 -0
  34. data/templates/epub/back.erb +22 -0
  35. data/templates/epub/copyright.erb +46 -0
  36. data/templates/epub/cover.erb +12 -0
  37. data/templates/epub/cover.html +12 -0
  38. data/templates/epub/page.erb +15 -0
  39. data/templates/epub/user.css +500 -0
  40. data/templates/extras.tex +1 -0
  41. data/templates/html/copyright.erb +46 -0
  42. data/templates/html/layout.css +352 -0
  43. data/templates/html/layout.erb +45 -0
  44. data/templates/html/syntax.css +58 -0
  45. data/templates/html/thanks.erb +21 -0
  46. data/templates/html/user.css +7 -0
  47. data/templates/latex.erb +416 -0
  48. data/templates/merovex-logo.jpg +0 -0
  49. data/templates/merovex-logo.png +0 -0
  50. data/templates/pdf/layout.erb +418 -0
  51. data/templates/rakefile.rb +103 -0
  52. data/templates/readme.erb +3 -0
  53. data/templates/text/01-Getting-Started.md +27 -0
  54. data/templates/text/02-Creating-Chapters.md +22 -0
  55. data/templates/text/03-Generating-Output.md +2 -0
  56. data/templates/text/10-Test-Markdown.md +157 -0
  57. data/test/helper.rb +34 -0
  58. data/test/test_bookmaker.rb +7 -0
  59. data/verku.gemspec +142 -0
  60. metadata +317 -0
@@ -0,0 +1,245 @@
1
+ require 'kramdown'
2
+ module Verku
3
+ module Parser
4
+ class HTML < Base
5
+ def content
6
+ raw = []
7
+ entries.keys.each do |chapter|
8
+ text = "\n\n## Chapter\n\n"
9
+ sections = []
10
+ entries[chapter].each do |section|
11
+ sections << read_content(section)[0]
12
+ # sections << "<p>#{read_content(section)[0].split(/\n{2,}/).map do |s|
13
+ # s.gsub!(/%.*/, '')
14
+ # s.squish
15
+ # end.join("</p>\n\n<p>")}</p>"
16
+ end
17
+ text << sections.join("\n\n* * *\n\n")
18
+ raw << "<div class='chapter'>\n#{text.to_html}\n</div>\n"
19
+ end
20
+ raw
21
+ end
22
+ def parse
23
+ puts "-- Exporting HTML"
24
+ html = parse_layout(content)
25
+ toc = TOC::HTML.generate(html)
26
+ locals = config.merge({
27
+ :contents => toc.content,
28
+ :toc => toc.to_html,
29
+ })
30
+ output = render_template(root_dir.join("_templates/html/layout.erb"), locals)
31
+ f = File.open(root_dir.join("builds/#{name}.html"), 'w')
32
+ f.write(output)
33
+ f.close
34
+ true
35
+ rescue Exception
36
+ p $!, $@
37
+ false
38
+ end
39
+ def parse_layout(chapters)
40
+ output = ''
41
+ chapters.each do |text|
42
+ # text.gsub!("{%", "{")
43
+ # text.gsub!(/%.*/,'')
44
+ text = text.split("\n\n").map{|s| s.gsub("\n", " ")}.join("\n\n")
45
+ text.gsub!(/``(.*?'?)''/) { "&ldquo;#{$1}&rdquo;"}
46
+ text.gsub!(/``(.*?'?)"/) { "&ldquo;#{$1}&rdquo;"}
47
+ text.gsub!(/``/, "&ldquo;")
48
+ text.gsub!(/\b'\b/) { "&rsquo;" }
49
+ text.gsub!(/`(.*?)'/) { "&lsquo;#{$1}&rsquo;"}
50
+ # \{([^\}]+?)\} Within the curly braces.
51
+ text.gsub!(/\\pf?break\{\}/,"<hr />")
52
+ text.gsub!(/\\pf?break/,"<hr />")
53
+ text.gsub!(/\\%/,'%')
54
+ text.gsub!(/\\`e/,'&eacute;')
55
+ text.gsub!(/\\textgreater\{?\}?/, "&gt;")
56
+ text.gsub!(/\\Bophendze/,'Bophendze')
57
+ text.gsub!(/\\ldots\{\}/,"&hellip;")
58
+ text.gsub!(/\\Dash\{\}/, "&mdash;")
59
+ text.gsub!(/\\Dash/, "&mdash;")
60
+ text.gsub!(/\\begin\{quote\}(.*?)\\end\{quote\}/m) { "<blockquote>#{$1.strip}</blockquote>"}
61
+ text.gsub!(/<\/blockquote>\s+?<blockquote>/m, "\n")
62
+ text.gsub!(/\\begin\{([^\}]+?)\}(.*?)\\end\{[^\}]+?\}/m) { "<div class='#{$1.strip}'>#{$2.strip}</div>"}
63
+ text.gsub!(/\\section\{([^\}]+?)\}/m) { "<h3>#{$1.strip}</h3>"}
64
+ ['Character','Equipment','Organization','Index'].each do |s|
65
+ text.gsub!(/\\#{s}\{[^\}]+?\}\{([^\}]+?)\}/) { "<span class='#{s.downcase}'>#{$1.strip}</span>"}
66
+ text.gsub!(/\\#{s}\{([^\}]+?)\}/) { ""}
67
+ # text.gsub!(/\\#{s}\{([^\}]+?)\}/) { "<span class='#{s.downcase}'>#{$1.strip}</span>"}
68
+ end
69
+ text.gsub!(/\\footnote\{([^\}]+?)\}/m) { ""}
70
+ text.gsub!(/\\emph\{([^\}]+?)\}/m) { "<em>#{$1.strip}</em>"}
71
+ text.gsub!(/\\thought\{([^\}]+?)\}/m) { "<em>#{$1.strip}</em>"}
72
+ text.gsub!(/\\(.*?)\{([^\}]+?)\}/) { "<span class='#{$1.downcase}'>#{$2.strip}</span>"}
73
+ text.gsub!(/\\(.*?)\{([^\}]+?)\}/) { "<span class='#{$1.downcase}'>#{$2.strip}</span>"}
74
+ text.gsub!(/<\/span>\{[^\}]+?}/, "</span>")
75
+ text.gsub!(/<p><h([1-6])>(.*?)<\/h[1-6]><\/p>/) { "<h#{$1}>#{$2.strip}</h#{$1}>"}
76
+ text.gsub!(/(\S+)~(\S+)/) { "#{$1}&nbsp;#{$2}"}
77
+ output << text
78
+ end
79
+ output.gsub!(/\n\n+/, "\n\n")
80
+ return output
81
+ end
82
+ end
83
+ # # List of directories that should be skipped.
84
+ # #
85
+ # IGNORE_DIR = %w[. .. .svn]
86
+ #
87
+ # # Files that should be skipped.
88
+ # #
89
+ # IGNORE_FILES = /^(CHANGELOG|TOC)\..*?$/
90
+ #
91
+ # # List of recognized extensions.
92
+ # #
93
+ # EXTENSIONS = %w[md mkdn markdown]
94
+ #
95
+ # class << self
96
+ # # The footnote index control. We have to manipulate footnotes
97
+ # # because each chapter starts from 1, so we have duplicated references.
98
+ # #
99
+ # attr_accessor :footnote_index
100
+ # end
101
+ #
102
+ # # Parse all files and save the parsed content
103
+ # # to <tt>output/book_name.html</tt>.
104
+ # #
105
+ # def parse
106
+ # reset_footnote_index!
107
+ #
108
+ # # File.open(root_dir.join("builds/#{name}.html"), "w") do |file|
109
+ # # file << parse_layout(content)
110
+ # # end
111
+ # true
112
+ # rescue Exception
113
+ # false
114
+ # end
115
+ #
116
+ # def reset_footnote_index!
117
+ # self.class.footnote_index = 1
118
+ # end
119
+ #
120
+ # private
121
+ # def chapter_files(entry)
122
+ # # Chapters can be files outside a directory.
123
+ # if File.file?(entry)
124
+ # [entry]
125
+ # else
126
+ # Dir.glob("#{entry}/**/*.{#{EXTENSIONS.join(",")}}").sort
127
+ # end
128
+ # end
129
+ #
130
+ # # Check if path is a valid entry.
131
+ # # Files/directories that start with a dot or underscore will be skipped.
132
+ # #
133
+ # def valid_entry?(entry)
134
+ # entry !~ /^(\.|_)/ && (valid_directory?(entry) || valid_file?(entry))
135
+ # end
136
+ #
137
+ # # Check if path is a valid directory.
138
+ # #
139
+ # def valid_directory?(entry)
140
+ # File.directory?(source.join(entry)) && !IGNORE_DIR.include?(File.basename(entry))
141
+ # end
142
+ #
143
+ # # Check if path is a valid file.
144
+ # #
145
+ # def valid_file?(entry)
146
+ # ext = File.extname(entry).gsub(/\./, "").downcase
147
+ # File.file?(source.join(entry)) && EXTENSIONS.include?(ext) && entry !~ IGNORE_FILES
148
+ # end
149
+ #
150
+ # # Render +file+ considering its extension.
151
+ # #
152
+ # def render_file(file, plain_syntax = false)
153
+ # file_format = format(file)
154
+ #
155
+ # content = Verku::Syntax.render(root_dir, file_format, File.read(file), plain_syntax)
156
+ #
157
+ # content = case file_format
158
+ # when :markdown
159
+ # Markdown.to_html(content)
160
+ # when :textile
161
+ # RedCloth.convert(content)
162
+ # else
163
+ # content
164
+ # end
165
+ #
166
+ # render_footnotes(content, plain_syntax)
167
+ # end
168
+ #
169
+ # def render_footnotes(content, plain_syntax = false)
170
+ # html = Nokogiri::HTML(content)
171
+ # footnotes = html.css("p[id^='fn']")
172
+ #
173
+ # return content if footnotes.empty?
174
+ #
175
+ # reset_footnote_index! unless self.class.footnote_index
176
+ #
177
+ # footnotes.each do |fn|
178
+ # index = self.class.footnote_index
179
+ # actual_index = fn["id"].gsub(/[^\d]/, "")
180
+ #
181
+ # fn.set_attribute("id", "_fn#{index}")
182
+ #
183
+ # html.css("a[href='#fn#{actual_index}']").each do |link|
184
+ # link.set_attribute("href", "#_fn#{index}")
185
+ # end
186
+ #
187
+ # html.css("a[href='#fnr#{actual_index}']").each do |link|
188
+ # link.set_attribute("href", "#_fnr#{index}")
189
+ # end
190
+ #
191
+ # html.css("[id=fnr#{actual_index}]").each do |tag|
192
+ # tag.set_attribute("id", "_fnr#{index}")
193
+ # end
194
+ #
195
+ # self.class.footnote_index += 1
196
+ # end
197
+ #
198
+ # html.css("body").inner_html
199
+ # end
200
+ #
201
+ # def format(file)
202
+ # case File.extname(file).downcase
203
+ # when ".markdown", ".mkdn", ".md"
204
+ # :markdown
205
+ # when ".textile"
206
+ # :textile
207
+ # else
208
+ # :html
209
+ # end
210
+ # end
211
+ #
212
+ # # Parse layout file, making available all configuration entries.
213
+ # #
214
+ # def parse_layout(html)
215
+ # puts "parse layout."
216
+ # toc = TOC::HTML.generate(html)
217
+ # locals = config.merge({
218
+ # :content => toc.content,
219
+ # :toc => toc.to_html,
220
+ # :changelog => render_changelog
221
+ # })
222
+ # render_template(root_dir.join("_templates/html/layout.erb"), locals)
223
+ # end
224
+ #
225
+ # # Render changelog file.
226
+ # # This file can be used to inform any book change.
227
+ # #
228
+ # def render_changelog
229
+ # changelog = Dir[root_dir.join("text/CHANGELOG.*")].first
230
+ # return render_file(changelog) if changelog
231
+ # nil
232
+ # end
233
+ #
234
+ # # Render all +files+ from a given chapter.
235
+ # #
236
+ # def render_chapter(files, plain_syntax = false)
237
+ # String.new.tap do |chapter|
238
+ # files.each do |file|
239
+ # chapter << render_file(file, plain_syntax) << "\n\n"
240
+ # end
241
+ # end
242
+ # end
243
+ # end
244
+ end
245
+ end
@@ -0,0 +1,17 @@
1
+ module Verku
2
+ module Parser
3
+ class Mobi < Base
4
+ def parse
5
+ puts "-- Exporting MOBI"
6
+ spawn_command ["kindlegen", epub_file.to_s,]
7
+ true
8
+ rescue Exception
9
+ p $!, $@
10
+ false
11
+ end
12
+ def epub_file
13
+ root_dir.join("builds/#{name}.epub")
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,54 @@
1
+ require 'kramdown'
2
+
3
+ module Verku
4
+ module Parser
5
+ class PDF < Base
6
+ def content
7
+ raw = []
8
+ entries.keys.each do |chapter|
9
+ title = (chapter.empty?) ? "Untitled" : chapter.split('_')[1]
10
+ title = 'Untitled' if title.nil?
11
+ raw << "\\Chapter{#{title.gsub('-',' ')}}\n\n"
12
+ entries[chapter].each do |section|
13
+ s = read_content(section)[0].to_latex.gsub(/\$(\\index\{[^\$]*?\})\$/) {"#{$1}"}
14
+ raw << "#{s}\n\n* * *"
15
+ end
16
+ end
17
+ raw
18
+ end
19
+
20
+ def parse
21
+ puts "-- Exporting PDF"
22
+ locals = config.merge({ :contents => parse_layout(content) })
23
+ locals['copyright'].gsub!("(C)", "\\copyright{}")
24
+ output = render_template(root_dir.join("_templates/pdf/layout.erb"), locals)
25
+ File.open(root_dir.join(tex_file), 'w').write(output)
26
+ puts " - Pass 1"
27
+ spawn_command ["xelatex", tex_file.to_s,]
28
+ puts " - Pass 2"
29
+ spawn_command ["xelatex", tex_file.to_s,]
30
+ if config['is_final'].to_i == 0
31
+ puts " - Pass 3 - Indexing"
32
+ spawn_command ["makeindex #{name}.idx"]
33
+ # spawn_command ["makeglossaries #{name}.glo"]
34
+ spawn_command ["xelatex", tex_file.to_s,]
35
+ spawn_command ["rm *ilg *ind "]
36
+ end
37
+
38
+ spawn_command ["rm *.glo *.idx *.log *.out *.toc *aux *ist"]
39
+ spawn_command ["mv #{name}.pdf builds/#{name}.pdf"]
40
+ true
41
+ rescue Exception
42
+ p $!, $@
43
+ false
44
+ end
45
+ def parse_layout(text)
46
+ text = text.join("\n\n")
47
+ text.gsub!('* * *', "\n\n\\pbreak{}\n\n")
48
+ end
49
+ def tex_file
50
+ root_dir.join("builds/#{name}.tex")
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1 @@
1
+ txt.rb
@@ -0,0 +1,114 @@
1
+ class Fixnum
2
+ def day
3
+ self * (60 * 60 * 24) # seconds * hours * minutes
4
+ end
5
+ def ago
6
+ Time.now - self
7
+ end
8
+ end
9
+ require 'json'
10
+ module Verku
11
+ class Stats
12
+ attr_reader :root_dir
13
+
14
+ def initialize(root_dir)
15
+ @root_dir = root_dir
16
+ @files = Dir["#{root_dir}/text/**/[0-9]*.tex"]
17
+ @words = 0
18
+ @progress = (File.exist?(log)) ? JSON.parse(File.open(log,'r').read).clone : {}
19
+ end
20
+
21
+ def log
22
+ "#{root_dir}/.wordcount"
23
+ end
24
+
25
+ def target
26
+ Verku.config(@root_dir)['wordcount']
27
+ end
28
+ def now
29
+ Date.today.to_s
30
+ end
31
+ def words
32
+ if @words == 0
33
+ most_recent = @files.max_by {|f| File.mtime(f)}
34
+ if !@progress[now].nil? and File.mtime(log) > File.mtime(most_recent)
35
+ @progress[now]
36
+ else
37
+ detex = "/usr/texbin/detex"
38
+
39
+ file = Tempfile.new('foo.tex')
40
+ file.write(text)
41
+ file.close
42
+ @progress[now] = `detex #{file.path}| wc -w`.to_i
43
+ file.unlink
44
+ # Do previous day's progress...if nil.
45
+ @progress[Date.yesterday.to_s] = @progress[now] if @progress.keys.count == 1
46
+ end
47
+ end
48
+ if (lasttime != Date.yesterday.to_s)
49
+ # Update the record.
50
+ n = lasttime
51
+ w = @progress[lasttime]
52
+ n.upto(Date.yesterday.to_s) do |k|
53
+ # puts "Upto #{k}: @progress[#{k}] = #{w}"
54
+ @progress[k] = w
55
+ end
56
+ end
57
+ File.open(log,'w').write( JSON.generate(@progress) )
58
+ @words = @progress[now]
59
+ end
60
+ def lasttime
61
+ p = nil
62
+ @progress.keys.sort.each do |k|
63
+ break if k == now
64
+ p = k
65
+ end
66
+ p
67
+ end
68
+ def yesterday
69
+ @progress[lasttime]
70
+ end
71
+ def today
72
+ @words - @progress[lasttime]
73
+ end
74
+ def text
75
+ @text = nil
76
+ @text = @files.map{|f| File.open(f,'r').read}.join("\n\n\n") if @text.nil?
77
+ end
78
+ def remaining
79
+ target - words
80
+ end
81
+
82
+ # def html
83
+ # @html ||= Nokogiri::HTML(content)
84
+ # end
85
+
86
+ # def words
87
+ # @words ||= text.split(" ").size
88
+ # end
89
+
90
+ # def chapters
91
+ # @chapters ||= html.css(".chapter").size
92
+ # end
93
+
94
+ # def images
95
+ # @images ||= html.css("img").size
96
+ # end
97
+
98
+ # def footnotes
99
+ # @footnotes ||= html.css("p.footnote").size
100
+ # end
101
+
102
+ # def links
103
+ # @links ||= html.css("[href^='http']").size
104
+ # end
105
+
106
+ # def code_blocks
107
+ # @code_blocks ||= html.css("pre").size
108
+ # end
109
+
110
+ # def content
111
+ # @content ||= Parser::HTML.new(root_dir).content
112
+ # end
113
+ end
114
+ end
@@ -0,0 +1,27 @@
1
+ module Verku
2
+ class Stream
3
+ attr_accessor :listener, :content
4
+ attr_reader :html
5
+
6
+ def initialize(content, listener)
7
+ @content = content
8
+ @listener = listener
9
+ @html = Nokogiri::HTML.parse(content)
10
+ end
11
+
12
+ def parse
13
+ traverse(html)
14
+ end
15
+
16
+ def traverse(node)
17
+ node.children.each do |child|
18
+ emit(child)
19
+ traverse(child)
20
+ end
21
+ end
22
+
23
+ def emit(node)
24
+ listener.send(:tag, node) if node.name =~ /h[1-6]/
25
+ end
26
+ end
27
+ end