Class: Softcover::BookManifest
- Inherits:
-
OpenStruct
- Object
- OpenStruct
- Softcover::BookManifest
- Includes:
- Utils
- Defined in:
- lib/softcover/book_manifest.rb
Direct Known Subclasses
Defined Under Namespace
Classes: Chapter, NotFound, Section
Constant Summary collapse
- TXT_PATH =
'Book.txt'
- YAML_PATH =
File.join(Softcover::Directories::CONFIG, 'book.yml')
Constants included from Utils
Instance Attribute Summary collapse
-
#book_file ⇒ Object
Returns the value of attribute book_file.
Class Method Summary collapse
-
.find_book_root! ⇒ Object
Changes the directory until in the book’s root directory.
- .not_found! ⇒ Object
- .valid_directory? ⇒ Boolean
Instance Method Summary collapse
- #basenames ⇒ Object
-
#chapter_file_paths ⇒ Object
Returns an iterator for the chapter file paths.
-
#chapter_includes(string) ⇒ Object
Returns an array of the chapters to include.
- #chapter_objects ⇒ Object
-
#ensure_template_files ⇒ Object
Ensures the existence of needed template files like ‘marketing.yml’.
- #escaped_title ⇒ Object
- #extensions ⇒ Object
- #find_chapter_by_number(number) ⇒ Object
- #find_chapter_by_slug(slug) ⇒ Object
-
#first_chapter ⇒ Object
Returns the first full chapter.
-
#frontmatter? ⇒ Boolean
Returns true if the book has frontmatter.
-
#full_html_file ⇒ Object
Returns the name of the HTML file containing the full book.
-
#html_title ⇒ Object
Run the title through the Polytexnic pipeline to make an HTML title.
-
#initialize(options = {}) ⇒ BookManifest
constructor
A new instance of BookManifest.
-
#markdown? ⇒ Boolean
(also: #md?)
Returns true if converting Markdown source.
-
#pdf_chapter_filenames ⇒ Object
Returns the full chapter filenames for the PDF.
-
#pdf_chapter_names ⇒ Object
Returns chapters for the PDF.
-
#polytex? ⇒ Boolean
Returns true if converting PolyTeX source.
-
#polytex_dir ⇒ Object
Returns the directory where the LaTeX files are located.
-
#preview_chapter_range ⇒ Object
Returns the chapter range for book previews.
-
#preview_chapters ⇒ Object
Returns the chapters to use in the preview as a range.
- #read_from_md ⇒ Object
-
#remove_frontmatter(base_contents, frontmatter) ⇒ Object
Removes frontmatter.
-
#source_files ⇒ Object
Returns the source files specified by Book.txt.
-
#ungenerated_markdown? ⇒ Boolean
Handles case of Markdown books without running ‘softcover build`.
-
#url(chapter_number) ⇒ Object
Returns a URL for the chapter with the given number.
Methods included from Utils
#add_highlight_class!, #article?, #as_size, #book_file_lines, #chapter_label, #commands, #current_book, #dependency_filename, #digest, #executable, #execute, #filename_or_default, #first_path, #get_filename, #html_extension, #in_book_directory?, #language_labels, #linux?, #logged_in?, #master_content, #master_filename, #master_latex_header, #mkdir, #non_comment_lines, #os_x?, #path, #polytexnic_html, #raw_lines, #reset_current_book!, #rm, #rm_r, #silence, #silence_stream, #source, #template_dir, #tmpify, #unpublish_slug, #write_master_latex_file, #write_pygments_file
Constructor Details
#initialize(options = {}) ⇒ BookManifest
Returns a new instance of BookManifest.
104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 |
# File 'lib/softcover/book_manifest.rb', line 104 def initialize( = {}) @source = [:source] || :polytex @origin = [:origin] @book_file = TXT_PATH ensure_template_files if ungenerated_markdown? puts "Error: No book to publish" puts "Run `softcover build:<format>` for at least one format" exit 1 end yaml_attrs = read_from_yml attrs = case when polytex? then yaml_attrs when markdown? then yaml_attrs.merge(read_from_md) else self.class.not_found! end.symbolize_keys! marshal_load attrs write_master_latex_file(self) if polytex? tex_filename = filename + '.tex' self.chapters = [] self.frontmatter = [] base_contents = File.read(tex_filename) if base_contents.match(/frontmatter/) @frontmatter = true chapters.push Chapter.new(slug: 'frontmatter', title: language_labels["frontmatter"], sections: nil, chapter_number: 0) end raw_frontmatter = remove_frontmatter(base_contents, frontmatter) if frontmatter? self.frontmatter = chapter_includes(raw_frontmatter) else self.frontmatter = [] end chapter_includes(base_contents).each_with_index do |name, i| slug = File.basename(name, '.*') chapter_title_regex = /^\s*\\chapter{(.*)}/ filename = File.join(polytex_dir, slug + '.tex') content = File.read(filename) chapter_title = content[chapter_title_regex, 1] if article? && @origin == :markdown if chapter_title.nil? # Articles are "chapters" with the title of the full document. chapter_title = title else # Override the title based on the value of the top-level heading. self.title = chapter_title # Overwrite book.yml with the new title. book_yml = File.read(YAML_PATH) File.write(YAML_PATH, book_yml.sub(/title: .*/, "title: #{title}")) # Strip out the chapter line, which is invalid in articles. File.write(filename, content.sub(chapter_title_regex, '')) end end j = 0 sections = content.scan(/^\s*\\section{(.*)}/).flatten.map do |name| Section.new(name: name, section_number: j += 1) end chapter_title = title if article? chapters.push Chapter.new(slug: slug, title: chapter_title, sections: sections, chapter_number: i + 1) end end write_master_latex_file(self) verify_paths! if [:verify_paths] end |
Instance Attribute Details
#book_file ⇒ Object
Returns the value of attribute book_file.
10 11 12 |
# File 'lib/softcover/book_manifest.rb', line 10 def book_file @book_file end |
Class Method Details
.find_book_root! ⇒ Object
Changes the directory until in the book’s root directory.
351 352 353 354 355 356 357 |
# File 'lib/softcover/book_manifest.rb', line 351 def self.find_book_root! loop do return true if valid_directory? return not_found! if Dir.pwd == '/' Dir.chdir '..' end end |
.not_found! ⇒ Object
359 360 361 |
# File 'lib/softcover/book_manifest.rb', line 359 def self.not_found! raise NotFound end |
.valid_directory? ⇒ Boolean
341 342 343 344 345 346 347 348 |
# File 'lib/softcover/book_manifest.rb', line 341 def self.valid_directory? # Needed for backwards compatibility if File.exist?('book.yml') && !Dir.pwd.include?('config') Softcover::Utils.mkdir('config') FileUtils.mv('book.yml', 'config') end [YAML_PATH, TXT_PATH].any? { |f| File.exist?(f) } end |
Instance Method Details
#basenames ⇒ Object
371 372 373 |
# File 'lib/softcover/book_manifest.rb', line 371 def basenames source_files.map { |file| File.basename(file, '.*') } end |
#chapter_file_paths ⇒ Object
Returns an iterator for the chapter file paths.
269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 |
# File 'lib/softcover/book_manifest.rb', line 269 def chapter_file_paths pdf_chapter_names.map do |name| file_path = case when markdown? || @origin == :markdown chapter = chapters.find { |chapter| chapter.slug == name } extension = chapter.nil? ? '.md' : chapter.extension File.join("chapters", "#{name}#{extension}") when polytex? File.join("chapters", "#{name}.tex") end yield file_path if block_given? file_path end end |
#chapter_includes(string) ⇒ Object
Returns an array of the chapters to include.
224 225 226 227 |
# File 'lib/softcover/book_manifest.rb', line 224 def chapter_includes(string) chapter_regex = /^\s*\\include\{#{polytex_dir}\/(.*?)\}/ string.scan(chapter_regex).flatten end |
#chapter_objects ⇒ Object
379 380 381 382 383 |
# File 'lib/softcover/book_manifest.rb', line 379 def chapter_objects basenames.zip(extensions).map do |name, extension| Chapter.new(slug: name, extension: extension) end end |
#ensure_template_files ⇒ Object
Ensures the existence of needed template files like ‘marketing.yml’. We copy from the template if necessary. Needed for backwards compatibility.
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 |
# File 'lib/softcover/book_manifest.rb', line 184 def ensure_template_files self.class.find_book_root! template_dir = Softcover::Utils.template_dir(article: Softcover::Utils.article?) files = [File.join(Softcover::Directories::CONFIG, 'marketing.yml'), path('images/cover-web.png'), path('latex_styles/custom_pdf.sty'), path('latex_styles/applekeys.sty'), path('config/preamble.tex'), path('config/lang.yml'), path('epub/OEBPS/styles/custom_epub.css'), path('epub/OEBPS/styles/page-template.xpgt'), ] files.each do |file| unless File.exist?(file) puts "Copying missing file '#{file}' from template" FileUtils.mkdir_p(File.dirname(file)) FileUtils.cp(File.join(template_dir, file), file) end end end |
#escaped_title ⇒ Object
20 21 22 |
# File 'lib/softcover/book_manifest.rb', line 20 def escaped_title CGI.escape_html(title) end |
#extensions ⇒ Object
375 376 377 |
# File 'lib/softcover/book_manifest.rb', line 375 def extensions source_files.map { |file| File.extname(file) } end |
#find_chapter_by_number(number) ⇒ Object
307 308 309 |
# File 'lib/softcover/book_manifest.rb', line 307 def find_chapter_by_number(number) chapters.find { |chapter| chapter.chapter_number == number } end |
#find_chapter_by_slug(slug) ⇒ Object
303 304 305 |
# File 'lib/softcover/book_manifest.rb', line 303 def find_chapter_by_slug(slug) chapters.find { |chapter| chapter.slug == slug } end |
#first_chapter ⇒ Object
Returns the first full chapter. This arranges to skip the frontmatter, if any.
253 254 255 |
# File 'lib/softcover/book_manifest.rb', line 253 def first_chapter frontmatter? ? chapters[1] : chapters[0] end |
#frontmatter? ⇒ Boolean
Returns true if the book has frontmatter.
247 248 249 |
# File 'lib/softcover/book_manifest.rb', line 247 def frontmatter? @frontmatter end |
#full_html_file ⇒ Object
Returns the name of the HTML file containing the full book.
287 288 289 |
# File 'lib/softcover/book_manifest.rb', line 287 def full_html_file path("html/#{slug}.#{html_extension}") end |
#html_title ⇒ Object
Run the title through the Polytexnic pipeline to make an HTML title.
25 26 27 |
# File 'lib/softcover/book_manifest.rb', line 25 def html_title polytexnic_html(title) end |
#markdown? ⇒ Boolean Also known as: md?
Returns true if converting Markdown source.
258 259 260 |
# File 'lib/softcover/book_manifest.rb', line 258 def markdown? @source == :markdown || @source == :md end |
#pdf_chapter_filenames ⇒ Object
Returns the full chapter filenames for the PDF.
299 300 301 |
# File 'lib/softcover/book_manifest.rb', line 299 def pdf_chapter_filenames pdf_chapter_names.map { |name| File.join(polytex_dir, "#{name}.tex") } end |
#pdf_chapter_names ⇒ Object
Returns chapters for the PDF.
292 293 294 295 296 |
# File 'lib/softcover/book_manifest.rb', line 292 def pdf_chapter_names chaps = chapters.reject { |chapter| chapter.slug == 'frontmatter' }. collect(&:slug) frontmatter? ? frontmatter + chaps : chaps end |
#polytex? ⇒ Boolean
Returns true if converting PolyTeX source.
264 265 266 |
# File 'lib/softcover/book_manifest.rb', line 264 def polytex? @source == :polytex end |
#polytex_dir ⇒ Object
Returns the directory where the LaTeX files are located. We put them in the a separate directory when using them as an intermediate format when working with Markdown books. Otherwise, we use the chapters directory, which is the default location when writing LaTeX/PolyTeX books.
217 218 219 220 221 |
# File 'lib/softcover/book_manifest.rb', line 217 def polytex_dir dir = (markdown? || @origin == :markdown) ? 'generated_polytex' : 'chapters' mkdir dir dir end |
#preview_chapter_range ⇒ Object
Returns the chapter range for book previews. We could ‘eval` the range, but that would allow users to execute arbitrary code (maybe not a big problem on their system, but it would be a Bad Thing on a server).
324 325 326 327 328 329 330 331 332 333 334 |
# File 'lib/softcover/book_manifest.rb', line 324 def preview_chapter_range unless respond_to?(:epub_mobi_preview_chapter_range) $stderr.puts("Error: Preview not built") $stderr.puts("Define epub_mobi_preview_chapter_range in config/book.yml") $stderr.puts("See http://manual.softcover.io/book/getting_started#sec-build_preview") exit(1) end first, last = epub_mobi_preview_chapter_range.split('..').map(&:to_i) first..last end |
#preview_chapters ⇒ Object
Returns the chapters to use in the preview as a range.
337 338 339 |
# File 'lib/softcover/book_manifest.rb', line 337 def preview_chapters chapters[preview_chapter_range] end |
#read_from_md ⇒ Object
385 386 387 |
# File 'lib/softcover/book_manifest.rb', line 385 def read_from_md { chapters: chapter_objects, filename: book_file } end |
#remove_frontmatter(base_contents, frontmatter) ⇒ Object
Removes frontmatter. The frontmatter shouldn’t be included in the chapter slugs, so we remove it. For example, in
\frontmatter
\maketitle
\tableofcontents
% List frontmatter sections here (preface, foreword, etc.).
\include{chapters/preface}
\mainmatter
% List chapters here in the order they should appear in the book.
\include{chapters/a_chapter}
we don’t want to include the preface.
241 242 243 244 |
# File 'lib/softcover/book_manifest.rb', line 241 def remove_frontmatter(base_contents, frontmatter) base_contents.gsub!(/\\frontmatter(.*)\\mainmatter/m, '') $1 end |
#source_files ⇒ Object
Returns the source files specified by Book.txt. Allows a mixture of Markdown and PolyTeX files.
365 366 367 368 369 |
# File 'lib/softcover/book_manifest.rb', line 365 def source_files self.class.find_book_root! md_tex = /.*(?:\.md|\.tex)/ book_file_lines(self).select { |path| path =~ md_tex }.map(&:strip) end |
#ungenerated_markdown? ⇒ Boolean
Handles case of Markdown books without running ‘softcover build`.
207 208 209 210 211 |
# File 'lib/softcover/book_manifest.rb', line 207 def ungenerated_markdown? dir = 'generated_polytex' @origin == :markdown && (!File.directory?(dir) || Dir.glob(path("#{dir}/*")).empty?) end |
#url(chapter_number) ⇒ Object
Returns a URL for the chapter with the given number.
312 313 314 315 316 317 318 |
# File 'lib/softcover/book_manifest.rb', line 312 def url(chapter_number) if (chapter = find_chapter_by_number(chapter_number)) chapter.slug else '#' end end |