class Softcover::BookManifest

Constants

TXT_PATH
YAML_PATH

Attributes

book_file[RW]

Public Class Methods

find_book_root!() click to toggle source

Changes the directory until in the book's root directory.

# 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
new(options = {}) click to toggle source
# File lib/softcover/book_manifest.rb, line 104
def initialize(options = {})
  @source = options[:source] || :polytex
  @origin = options[: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 options[:verify_paths]
end
not_found!() click to toggle source
# File lib/softcover/book_manifest.rb, line 359
def self.not_found!
  raise NotFound
end
valid_directory?() click to toggle source
# 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

Public Instance Methods

basenames() click to toggle source
# File lib/softcover/book_manifest.rb, line 371
def basenames
  source_files.map { |file| File.basename(file, '.*') }
end
chapter_file_paths() { |file_path| ... } click to toggle source

Returns an iterator for the chapter file paths.

# 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) click to toggle source

Returns an array of the chapters to include.

# 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() click to toggle source
# 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() click to toggle source

Ensures the existence of needed template files like 'marketing.yml'. We copy from the template if necessary. Needed for backwards compatibility.

# 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() click to toggle source
# File lib/softcover/book_manifest.rb, line 20
def escaped_title
  CGI.escape_html(title)
end
extensions() click to toggle source
# File lib/softcover/book_manifest.rb, line 375
def extensions
  source_files.map { |file| File.extname(file) }
end
find_chapter_by_number(number) click to toggle source
# 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) click to toggle source
# File lib/softcover/book_manifest.rb, line 303
def find_chapter_by_slug(slug)
  chapters.find { |chapter| chapter.slug == slug }
end
first_chapter() click to toggle source

Returns the first full chapter. This arranges to skip the frontmatter, if any.

# File lib/softcover/book_manifest.rb, line 253
def first_chapter
  frontmatter? ? chapters[1] : chapters[0]
end
frontmatter?() click to toggle source

Returns true if the book has frontmatter.

# File lib/softcover/book_manifest.rb, line 247
def frontmatter?
  @frontmatter
end
full_html_file() click to toggle source

Returns the name of the HTML file containing the full book.

# File lib/softcover/book_manifest.rb, line 287
def full_html_file
  path("html/#{slug}.#{html_extension}")
end
html_title() click to toggle source

Run the title through the Polytexnic pipeline to make an HTML title.

# File lib/softcover/book_manifest.rb, line 25
def html_title
  polytexnic_html(title)
end
markdown?() click to toggle source

Returns true if converting Markdown source.

# File lib/softcover/book_manifest.rb, line 258
def markdown?
  @source == :markdown || @source == :md
end
Also aliased as: md?
md?()
Alias for: markdown?
pdf_chapter_filenames() click to toggle source

Returns the full chapter filenames for the PDF.

# 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() click to toggle source

Returns chapters for the PDF.

# 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?() click to toggle source

Returns true if converting PolyTeX source.

# File lib/softcover/book_manifest.rb, line 264
def polytex?
  @source == :polytex
end
polytex_dir() click to toggle source

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.

# 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() click to toggle source

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).

# 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() click to toggle source

Returns the chapters to use in the preview as a range.

# File lib/softcover/book_manifest.rb, line 337
def preview_chapters
  chapters[preview_chapter_range]
end
read_from_md() click to toggle source
# File lib/softcover/book_manifest.rb, line 385
def read_from_md
  { chapters: chapter_objects, filename: book_file }
end
remove_frontmatter(base_contents, frontmatter) click to toggle source

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.

# File lib/softcover/book_manifest.rb, line 241
def remove_frontmatter(base_contents, frontmatter)
  base_contents.gsub!(/\\frontmatter(.*)\\mainmatter/m, '')
  $1
end
source_files() click to toggle source

Returns the source files specified by Book.txt. Allows a mixture of Markdown and PolyTeX files.

# 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?() click to toggle source

Handles case of Markdown books without running `softcover build`.

# 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) click to toggle source

Returns a URL for the chapter with the given number.

# 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

Private Instance Methods

ensure_book_yml() click to toggle source

Ensures that the book.yml file is in the right directory. This is for backwards compatibility.

# File lib/softcover/book_manifest.rb, line 402
def ensure_book_yml
  path = self.class::YAML_PATH
  unless File.exist?(path)
    base = File.basename(path)
    Softcover::Utils.mkdir Softcover::Directories::CONFIG
    FileUtils.mv base, Softcover::Directories::CONFIG
  end
end
read_from_yml() click to toggle source
# File lib/softcover/book_manifest.rb, line 392
def read_from_yml
  require 'softcover/config'
  require 'yaml/store'
  self.class.find_book_root!
  ensure_book_yml
  YAML.load_file(self.class::YAML_PATH)
end
verify_paths!() click to toggle source
# File lib/softcover/book_manifest.rb, line 412
def verify_paths!
  chapter_file_paths do |chapter_path|
    next if chapter_path =~ /frontmatter/
    unless File.exist?(chapter_path)
      $stderr.puts "ERROR -- document not built"
      $stderr.puts "Chapter file '#{chapter_path}'' not found"
      exit 1
    end
  end
end