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.
-
#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, #get_filename, #in_book_directory?, #language_labels, #linux?, #logged_in?, #master_content, #master_filename, #master_latex_header, #mkdir, #non_comment_lines, #os_x?, #path, #raw_lines, #reset_current_book!, #rm, #rm_r, #silence, #source, #template_dir, #tmpify, #unpublish_slug, #write_master_latex_file, #write_pygments_file
Constructor Details
#initialize(options = {}) ⇒ BookManifest
Returns a new instance of BookManifest.
100 101 102 103 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 |
# File 'lib/softcover/book_manifest.rb', line 100 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? # *** test # 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.
347 348 349 350 351 352 353 |
# File 'lib/softcover/book_manifest.rb', line 347 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
355 356 357 |
# File 'lib/softcover/book_manifest.rb', line 355 def self.not_found! raise NotFound end |
.valid_directory? ⇒ Boolean
337 338 339 340 341 342 343 344 |
# File 'lib/softcover/book_manifest.rb', line 337 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
367 368 369 |
# File 'lib/softcover/book_manifest.rb', line 367 def basenames source_files.map { |file| File.basename(file, '.*') } end |
#chapter_file_paths ⇒ Object
Returns an iterator for the chapter file paths.
265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 |
# File 'lib/softcover/book_manifest.rb', line 265 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.
220 221 222 223 |
# File 'lib/softcover/book_manifest.rb', line 220 def chapter_includes(string) chapter_regex = /^\s*\\include\{#{polytex_dir}\/(.*?)\}/ string.scan(chapter_regex).flatten end |
#chapter_objects ⇒ Object
375 376 377 378 379 |
# File 'lib/softcover/book_manifest.rb', line 375 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.
181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 |
# File 'lib/softcover/book_manifest.rb', line 181 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('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
371 372 373 |
# File 'lib/softcover/book_manifest.rb', line 371 def extensions source_files.map { |file| File.extname(file) } end |
#find_chapter_by_number(number) ⇒ Object
303 304 305 |
# File 'lib/softcover/book_manifest.rb', line 303 def find_chapter_by_number(number) chapters.find { |chapter| chapter.chapter_number == number } end |
#find_chapter_by_slug(slug) ⇒ Object
299 300 301 |
# File 'lib/softcover/book_manifest.rb', line 299 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.
249 250 251 |
# File 'lib/softcover/book_manifest.rb', line 249 def first_chapter frontmatter? ? chapters[1] : chapters[0] end |
#frontmatter? ⇒ Boolean
Returns true if the book has frontmatter.
243 244 245 |
# File 'lib/softcover/book_manifest.rb', line 243 def frontmatter? @frontmatter end |
#full_html_file ⇒ Object
Returns the name of the HTML file containing the full book.
283 284 285 |
# File 'lib/softcover/book_manifest.rb', line 283 def full_html_file path("html/#{slug}.html") end |
#markdown? ⇒ Boolean Also known as: md?
Returns true if converting Markdown source.
254 255 256 |
# File 'lib/softcover/book_manifest.rb', line 254 def markdown? @source == :markdown || @source == :md end |
#pdf_chapter_filenames ⇒ Object
Returns the full chapter filenames for the PDF.
295 296 297 |
# File 'lib/softcover/book_manifest.rb', line 295 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.
288 289 290 291 292 |
# File 'lib/softcover/book_manifest.rb', line 288 def pdf_chapter_names chaps = chapters.reject { |chapter| chapter.slug.match(/frontmatter/) }. collect(&:slug) frontmatter? ? frontmatter + chaps : chaps end |
#polytex? ⇒ Boolean
Returns true if converting PolyTeX source.
260 261 262 |
# File 'lib/softcover/book_manifest.rb', line 260 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.
213 214 215 216 217 |
# File 'lib/softcover/book_manifest.rb', line 213 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).
320 321 322 323 324 325 326 327 328 329 330 |
# File 'lib/softcover/book_manifest.rb', line 320 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.
333 334 335 |
# File 'lib/softcover/book_manifest.rb', line 333 def preview_chapters chapters[preview_chapter_range] end |
#read_from_md ⇒ Object
381 382 383 |
# File 'lib/softcover/book_manifest.rb', line 381 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.
237 238 239 240 |
# File 'lib/softcover/book_manifest.rb', line 237 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.
361 362 363 364 365 |
# File 'lib/softcover/book_manifest.rb', line 361 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`.
203 204 205 206 207 |
# File 'lib/softcover/book_manifest.rb', line 203 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.
308 309 310 311 312 313 314 |
# File 'lib/softcover/book_manifest.rb', line 308 def url(chapter_number) if (chapter = find_chapter_by_number(chapter_number)) chapter.slug else '#' end end |