Class: Softcover::BookManifest

Inherits:
OpenStruct
  • Object
show all
Includes:
Utils
Defined in:
lib/softcover/book_manifest.rb

Defined Under Namespace

Classes: Chapter, NotFound, Section

Constant Summary collapse

MD_PATH =
'Book.txt'
YAML_PATH =
"book.yml"

Constants included from Utils

Utils::UNITS

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Utils

#add_highlight_class!, #as_size, #current_book, #digest, #executable, #execute, #in_book_directory?, #logged_in?, #mkdir, #path, #reset_current_book!, #rm, #silence, #source, #tmpify, #write_pygments_file

Constructor Details

#initialize(options = {}) ⇒ BookManifest

Returns a new instance of BookManifest.



47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
# File 'lib/softcover/book_manifest.rb', line 47

def initialize(options = {})
  @source = options[:source] || :polytex
  @origin = options[:origin]
  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

  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: '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
    self.author = base_contents.scan(/^\s*\\author\{(.*?)\}/).flatten.first
    chapter_includes(base_contents).each_with_index do |name, i|
      slug = File.basename(name, '.*')
      title_regex = /^\s*\\chapter{(.*)}/
      content = File.read(File.join(polytex_dir, slug + '.tex'))
      title = content[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: title,
                                sections: sections,
                                chapter_number: i + 1)
    end
  end
  verify_paths! if options[:verify_paths]
end

Class Method Details

.find_book_root!Object

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



218
219
220
221
222
223
224
# File 'lib/softcover/book_manifest.rb', line 218

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

Raises:



226
227
228
# File 'lib/softcover/book_manifest.rb', line 226

def self.not_found!
  raise NotFound
end

.valid_directory?Boolean

Returns:

  • (Boolean)


213
214
215
# File 'lib/softcover/book_manifest.rb', line 213

def self.valid_directory?
  [YAML_PATH, MD_PATH].any? { |f| File.exist?(f) }
end

Instance Method Details

#chapter_file_pathsObject

Returns an iterator for the chapter file paths.



153
154
155
156
157
158
159
160
161
162
163
164
165
166
# File 'lib/softcover/book_manifest.rb', line 153

def chapter_file_paths
  pdf_chapter_names.map do |name|
    file_path = case
    when markdown? || @origin == :markdown
      File.join("chapters", "#{name}.md")
    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.



108
109
110
111
# File 'lib/softcover/book_manifest.rb', line 108

def chapter_includes(string)
  chapter_regex = /^\s*\\include\{#{polytex_dir}\/(.*?)\}/
  string.scan(chapter_regex).flatten
end

#find_chapter_by_number(number) ⇒ Object



186
187
188
# File 'lib/softcover/book_manifest.rb', line 186

def find_chapter_by_number(number)
  chapters.find { |chapter| chapter.chapter_number == number }
end

#find_chapter_by_slug(slug) ⇒ Object



182
183
184
# File 'lib/softcover/book_manifest.rb', line 182

def find_chapter_by_slug(slug)
  chapters.find { |chapter| chapter.slug == slug }
end

#first_chapterObject

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



137
138
139
# File 'lib/softcover/book_manifest.rb', line 137

def first_chapter
  frontmatter? ? chapters[1] : chapters[0]
end

#frontmatter?Boolean

Returns true if the book has frontmatter.

Returns:

  • (Boolean)


131
132
133
# File 'lib/softcover/book_manifest.rb', line 131

def frontmatter?
  @frontmatter
end

#markdown?Boolean Also known as: md?

Returns true if converting Markdown source.

Returns:

  • (Boolean)


142
143
144
# File 'lib/softcover/book_manifest.rb', line 142

def markdown?
  @source == :markdown || @source == :md
end

#pdf_chapter_filenamesObject

Returns the full chapter filenames for the PDF.



178
179
180
# File 'lib/softcover/book_manifest.rb', line 178

def pdf_chapter_filenames
  pdf_chapter_names.map { |name| File.join(polytex_dir, "#{name}.tex") }
end

#pdf_chapter_namesObject

Returns chapters for the PDF. The frontmatter pseudo-chapter exists for the sake of HTML/EPUB/MOBI, so it’s not returned as part of the chapters.



171
172
173
174
175
# File 'lib/softcover/book_manifest.rb', line 171

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.

Returns:

  • (Boolean)


148
149
150
# File 'lib/softcover/book_manifest.rb', line 148

def polytex?
  @source == :polytex
end

#polytex_dirObject

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.



101
102
103
104
105
# File 'lib/softcover/book_manifest.rb', line 101

def polytex_dir
  dir = (markdown? || @origin == :markdown) ? 'generated_polytex' : 'chapters'
  mkdir dir
  dir
end

#preview_chapter_rangeObject

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



203
204
205
206
# File 'lib/softcover/book_manifest.rb', line 203

def preview_chapter_range
  first, last = epub_mobi_preview_chapter_range.split('..').map(&:to_i)
  first..last
end

#preview_chaptersObject

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



209
210
211
# File 'lib/softcover/book_manifest.rb', line 209

def preview_chapters
  chapters[preview_chapter_range]
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.



125
126
127
128
# File 'lib/softcover/book_manifest.rb', line 125

def remove_frontmatter(base_contents, frontmatter)
  base_contents.gsub!(/\\frontmatter(.*)\\mainmatter/m, '')
  $1
end

#url(chapter_number) ⇒ Object

Returns a URL for the chapter with the given number.



191
192
193
194
195
196
197
# File 'lib/softcover/book_manifest.rb', line 191

def url(chapter_number)
  if (chapter = find_chapter_by_number(chapter_number))
    chapter.slug
  else
    '#'
  end
end