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
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’.
- #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.
-
#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!, #as_size, #book_txt_lines, #chapter_label, #commands, #current_book, #dependency_filename, #digest, #executable, #execute, #in_book_directory?, #language_labels, #linux?, #logged_in?, #master_content, #master_filename, #master_latex_header, #mkdir, #os_x?, #path, #raw_lines, #reset_current_book!, #rm, #silence, #source, #tmpify, #unpublish_slug, #write_master_latex_file, #write_pygments_file
Constructor Details
#initialize(options = {}) ⇒ BookManifest
Returns a new instance of BookManifest.
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 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 |
# File 'lib/softcover/book_manifest.rb', line 80 def initialize( = {}) @source = [:source] || :polytex @origin = [:origin] 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{(.*)}/ content = File.read(File.join(polytex_dir, slug + '.tex')) chapter_title = content[chapter_title_regex, 1] j = 0 sections = content.scan(/^\s*\\section{(.*)}/).flatten.map do |name| Section.new(name: name, section_number: j += 1) end chapters.push Chapter.new(slug: slug, title: chapter_title, sections: sections, chapter_number: i + 1) end end verify_paths! if [:verify_paths] end |
Class Method Details
.find_book_root! ⇒ Object
Changes the directory until in the book’s root directory.
295 296 297 298 299 300 301 |
# File 'lib/softcover/book_manifest.rb', line 295 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
303 304 305 |
# File 'lib/softcover/book_manifest.rb', line 303 def self.not_found! raise NotFound end |
.valid_directory? ⇒ Boolean
285 286 287 288 289 290 291 292 |
# File 'lib/softcover/book_manifest.rb', line 285 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
315 316 317 |
# File 'lib/softcover/book_manifest.rb', line 315 def basenames source_files.map { |file| File.basename(file, '.*') } end |
#chapter_file_paths ⇒ Object
Returns an iterator for the chapter file paths.
225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 |
# File 'lib/softcover/book_manifest.rb', line 225 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.
180 181 182 183 |
# File 'lib/softcover/book_manifest.rb', line 180 def chapter_includes(string) chapter_regex = /^\s*\\include\{#{polytex_dir}\/(.*?)\}/ string.scan(chapter_regex).flatten end |
#chapter_objects ⇒ Object
323 324 325 326 327 |
# File 'lib/softcover/book_manifest.rb', line 323 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.
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 |
# File 'lib/softcover/book_manifest.rb', line 143 def ensure_template_files self.class.find_book_root! template_dir = File.join(File.dirname(__FILE__), 'template') 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') ] 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 |
#extensions ⇒ Object
319 320 321 |
# File 'lib/softcover/book_manifest.rb', line 319 def extensions source_files.map { |file| File.extname(file) } end |
#find_chapter_by_number(number) ⇒ Object
258 259 260 |
# File 'lib/softcover/book_manifest.rb', line 258 def find_chapter_by_number(number) chapters.find { |chapter| chapter.chapter_number == number } end |
#find_chapter_by_slug(slug) ⇒ Object
254 255 256 |
# File 'lib/softcover/book_manifest.rb', line 254 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.
209 210 211 |
# File 'lib/softcover/book_manifest.rb', line 209 def first_chapter frontmatter? ? chapters[1] : chapters[0] end |
#frontmatter? ⇒ Boolean
Returns true if the book has frontmatter.
203 204 205 |
# File 'lib/softcover/book_manifest.rb', line 203 def frontmatter? @frontmatter end |
#markdown? ⇒ Boolean Also known as: md?
Returns true if converting Markdown source.
214 215 216 |
# File 'lib/softcover/book_manifest.rb', line 214 def markdown? @source == :markdown || @source == :md end |
#pdf_chapter_filenames ⇒ Object
Returns the full chapter filenames for the PDF.
250 251 252 |
# File 'lib/softcover/book_manifest.rb', line 250 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.
243 244 245 246 247 |
# File 'lib/softcover/book_manifest.rb', line 243 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.
220 221 222 |
# File 'lib/softcover/book_manifest.rb', line 220 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.
173 174 175 176 177 |
# File 'lib/softcover/book_manifest.rb', line 173 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).
275 276 277 278 |
# File 'lib/softcover/book_manifest.rb', line 275 def preview_chapter_range 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.
281 282 283 |
# File 'lib/softcover/book_manifest.rb', line 281 def preview_chapters chapters[preview_chapter_range] end |
#read_from_md ⇒ Object
329 330 331 |
# File 'lib/softcover/book_manifest.rb', line 329 def read_from_md { chapters: chapter_objects, filename: TXT_PATH } 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.
197 198 199 200 |
# File 'lib/softcover/book_manifest.rb', line 197 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.
309 310 311 312 313 |
# File 'lib/softcover/book_manifest.rb', line 309 def source_files self.class.find_book_root! md_tex = /.*(?:\.md|\.tex)/ book_txt_lines.select { |path| path =~ md_tex }.map(&:strip) end |
#ungenerated_markdown? ⇒ Boolean
Handles case of Markdown books without running ‘softcover build`.
163 164 165 166 167 |
# File 'lib/softcover/book_manifest.rb', line 163 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.
263 264 265 266 267 268 269 |
# File 'lib/softcover/book_manifest.rb', line 263 def url(chapter_number) if (chapter = find_chapter_by_number(chapter_number)) chapter.slug else '#' end end |