Module: Softcover::Utils

Constant Summary collapse

UNITS =
%W(B KB MB GB TB).freeze

Instance Method Summary collapse

Instance Method Details

#add_highlight_class!(pygments_css) ⇒ Object

Adds a ‘highlight’ class for MathJax compatibility.



164
165
166
# File 'lib/softcover/utils.rb', line 164

def add_highlight_class!(pygments_css)
  pygments_css.gsub!(/^/, '.highlight ')
end

#article?Boolean

Returns true if document is an article.

Returns:

  • (Boolean)


293
294
295
# File 'lib/softcover/utils.rb', line 293

def article?
  !!File.readlines(path('config/preamble.tex')).first.match(/extarticle/)
end

#as_size(number) ⇒ Object



46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
# File 'lib/softcover/utils.rb', line 46

def as_size(number)
  if number.to_i < 1024
    exponent = 0

  else
    max_exp  = UNITS.size - 1

    exponent = ( Math.log( number ) / Math.log( 1024 ) ).to_i
    exponent = max_exp if exponent > max_exp

    number  /= 1024 ** exponent
  end

  "#{number.round} #{UNITS[ exponent ]}"
end

#book_file_lines(manifest) ⇒ Object

Returns the lines of book file as an array, removing commented-out lines.



78
79
80
# File 'lib/softcover/utils.rb', line 78

def book_file_lines(manifest)
  non_comment_lines(raw_lines(manifest))
end

#chapter_label(chapter_number) ⇒ Object



278
279
280
281
282
283
284
# File 'lib/softcover/utils.rb', line 278

def chapter_label(chapter_number)
  if language_labels["chapter"]["order"] == "reverse"
    "#{chapter_number} #{language_labels['chapter']['word']}"
  else
    "#{language_labels['chapter']['word']} #{chapter_number}"
  end
end

#commands(lines) ⇒ Object

Returns the commands from the given lines. We skip comments and blank lines.



236
237
238
239
# File 'lib/softcover/utils.rb', line 236

def commands(lines)
  skip = /(^\s*#|^\s*$)/
  lines.reject { |line| line =~ skip }.join("\n")
end

#current_bookObject



4
5
6
7
8
9
# File 'lib/softcover/utils.rb', line 4

def current_book
  # using module level variable because it should be context independent
  @@current_book ||= begin
    in_book_directory? ? Softcover::Book.new(origin: source) : false
  end
end

#dependency_filename(label) ⇒ Object

Returns the filename of a dependency given a label.



242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
# File 'lib/softcover/utils.rb', line 242

def dependency_filename(label)
  case label
  when :latex
    get_filename(:xelatex)
  when :ghostscript
    get_filename(:gs)
  when :calibre
    get_filename(:'ebook-convert')
  when :epubcheck
    # Finds EpubCheck anywhere on the path.
    cmd_path = ['epubcheck-3.0', 'epubcheck-3.0.jar']
    possible_paths = ENV['PATH'].split(File::PATH_SEPARATOR).
                                 collect { |x| File.join(x, cmd_path) }
    possible_paths.select { |f| File.file?(f) }.first || ""
  when :inkscape
    default = '/Applications/Inkscape.app/Contents/Resources/bin/inkscape'
    filename_or_default(:inkscape, default)
  else
    get_filename(label)
  end
end

#digest(string) ⇒ Object

Returns a digest of the string.



169
170
171
# File 'lib/softcover/utils.rb', line 169

def digest(string)
  Digest::SHA1.hexdigest(string)
end

#executable(filename) ⇒ Object

Returns the executable if it exists, raising an error otherwise.



174
175
176
177
178
179
180
181
182
# File 'lib/softcover/utils.rb', line 174

def executable(filename)
  filename.tap do |f|
    unless File.exist?(f)
      $stderr.puts "Document not built due to missing dependency"
      $stderr.puts "Run `softcover check` to check dependencies"
      exit 1
    end
  end
end

#execute(command) ⇒ Object

Execute a command. The issue here is that ‘exec` is awful in tests, since it exits the process. This command arranges to use `system` in tests instead.



212
213
214
# File 'lib/softcover/utils.rb', line 212

def execute(command)
  Softcover.test? ? system(command) : exec(command)
end

#filename_or_default(name, default) ⇒ Object

Returns the filename if it exists on the path and a default otherwise.



269
270
271
# File 'lib/softcover/utils.rb', line 269

def filename_or_default(name, default)
  (f = get_filename(name)).empty? ? default : f
end

#get_filename(name) ⇒ Object



264
265
266
# File 'lib/softcover/utils.rb', line 264

def get_filename(name)
  `which #{name}`.chomp
end

#in_book_directory?Boolean

Returns:

  • (Boolean)


25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/softcover/utils.rb', line 25

def in_book_directory?
  Softcover::BookManifest::find_book_root!

  files = Dir['**/*']

  Softcover::FORMATS.each do |format|
    unless files.any?{ |file| File.extname(file) == ".#{format}" }
      puts "No #{format} found, skipping."
    end
  end

  return Softcover::BookManifest::valid_directory?
end

#language_labelsObject

Returns the language labels from the config file.



274
275
276
# File 'lib/softcover/utils.rb', line 274

def language_labels
  YAML.load_file(File.join(Softcover::Directories::CONFIG, 'lang.yml'))
end

#linux?Boolean

Returns true if platform is Linux.

Returns:

  • (Boolean)


230
231
232
# File 'lib/softcover/utils.rb', line 230

def linux?
  RUBY_PLATFORM.match(/linux/)
end

#logged_in?Boolean

Returns:

  • (Boolean)


39
40
41
42
# File 'lib/softcover/utils.rb', line 39

def logged_in?
  require 'softcover/config'
  Softcover::Config['api_key'].present?
end

#master_content(manifest) ⇒ Object

Returns the content for the master LaTeX file.



94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/softcover/utils.rb', line 94

def master_content(manifest)
  front_or_mainmatter = /(.*):\s*$/
  source_file = /(.*)(?:\.md|\.tex)\s*$/

  tex_file = [master_latex_header(manifest)]
  book_file_lines(manifest).each do |line|
    if line.match(source_file)
      tex_file << "\\include{#{manifest.polytex_dir}/#{$1}}"
    elsif line.match(front_or_mainmatter)  # frontmatter or mainmatter
      tex_file << "\\#{$1}"
    elsif line.strip == 'cover'
      tex_file << '\\includepdf{images/cover.pdf}'
    else # raw command, like 'maketitle' or 'tableofcontents'
      tex_file << "\\#{line.strip}"
    end
  end
  tex_file << '\end{document}'
  tex_file.join("\n") + "\n"
end

#master_filename(manifest) ⇒ Object

Returns the name of the master LaTeX file.



73
74
75
# File 'lib/softcover/utils.rb', line 73

def master_filename(manifest)
  "#{manifest.filename}.tex"
end

#master_latex_header(manifest) ⇒ Object



114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
# File 'lib/softcover/utils.rb', line 114

def master_latex_header(manifest)
  preamble = File.read(path('config/preamble.tex'))
  subtitle = manifest.subtitle.nil? ? "" : "\\subtitle{#{manifest.subtitle}}"
  <<-EOS
#{preamble}
\\usepackage{#{Softcover::Directories::STYLES}/softcover}
\\VerbatimFootnotes % Allows verbatim text in footnotes
\\title{#{manifest.title}}
#{subtitle}
\\author{#{manifest.author}}
\\date{#{manifest.date}}

\\begin{document}
  EOS
end

#mkdir(dir) ⇒ Object



184
185
186
# File 'lib/softcover/utils.rb', line 184

def mkdir(dir)
  Dir.mkdir(dir) unless File.directory?(dir)
end

#non_comment_lines(lines) ⇒ Object

Returns only non-comment lines.



83
84
85
86
# File 'lib/softcover/utils.rb', line 83

def non_comment_lines(lines)
  comment = /^\s*#.*$/
  lines.reject { |line| line.match(comment) }
end

#os_x?Boolean

Returns true if platform is OS X.

Returns:

  • (Boolean)


225
226
227
# File 'lib/softcover/utils.rb', line 225

def os_x?
  RUBY_PLATFORM.match(/darwin/)
end

#path(path_string = '') ⇒ Object

Returns the system-independent file path. It’s nicer to write ‘path(’foo/bar/baz’)‘ than `File.join(’foo’, ‘bar’, ‘baz’)‘.



205
206
207
# File 'lib/softcover/utils.rb', line 205

def path(path_string='')
  File.join(*path_string.split('/'))
end

#raw_lines(manifest) ⇒ Object

Returns all the lines in Book.txt.



89
90
91
# File 'lib/softcover/utils.rb', line 89

def raw_lines(manifest)
  File.readlines(manifest.book_file)
end

#reset_current_book!Object



21
22
23
# File 'lib/softcover/utils.rb', line 21

def reset_current_book!
  @@current_book = nil
end

#rm(file) ⇒ Object

Removes a file (or list of files).



189
190
191
192
193
194
195
# File 'lib/softcover/utils.rb', line 189

def rm(file)
  if file.is_a?(Array)
    file.each { |f| rm(f) }
  else
    FileUtils.rm(file) if File.exist?(file)
  end
end

#rm_r(directory) ⇒ Object

Removes a directory recursively.



198
199
200
# File 'lib/softcover/utils.rb', line 198

def rm_r(directory)
  FileUtils.rm_r(directory) if File.directory?(directory)
end

#silenceObject



216
217
218
219
220
221
222
# File 'lib/softcover/utils.rb', line 216

def silence
  return yield if ENV['silence'] == 'false'

  silence_stream(STDOUT) do
    yield
  end
end

#sourceObject

Returns the source type (PolyTeX or Markdown) of the current book.



12
13
14
# File 'lib/softcover/utils.rb', line 12

def source
  Dir.glob(path('chapters/*.md')).empty? ? :polytex : :markdown
end

#template_dir(options) ⇒ Object

Returns the directory of the document template.



287
288
289
290
# File 'lib/softcover/utils.rb', line 287

def template_dir(options)
  doc = options[:article] ? 'article' : 'book'
  File.expand_path File.join(File.dirname(__FILE__), "#{doc}_template")
end

#tmpify(manifest, filename) ⇒ Object

Returns the tmp version of a filename. E.g., tmpify(‘foo.tex’) => ‘foo.tmp.tex’



132
133
134
135
136
137
138
# File 'lib/softcover/utils.rb', line 132

def tmpify(manifest, filename)
  tmp = Softcover::Directories::TMP
  mkdir tmp
  sep = File::SEPARATOR
  filename.sub(manifest.polytex_dir + sep, tmp + sep).
           sub('.tex', '.tmp.tex')
end

#unpublish_slugObject

Returns the slug to be unpublished.



17
18
19
# File 'lib/softcover/utils.rb', line 17

def unpublish_slug
  Softcover::BookManifest.new(origin: source).slug
end

#write_master_latex_file(manifest) ⇒ Object

Writes the master LaTeX file <name>.tex to use chapters from Book.txt. We skip this step if Book.txt doesn’t exist, as that means the user is writing raw LaTeX.



66
67
68
69
70
# File 'lib/softcover/utils.rb', line 66

def write_master_latex_file(manifest)
  if File.exist?(manifest.book_file)
    File.write(master_filename(manifest), master_content(manifest))
  end
end

#write_pygments_file(format, path) ⇒ Object

Writes a Pygments style file. We support both :html (outputting CSS) and :latex (outputting a LaTeX style file).



143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
# File 'lib/softcover/utils.rb', line 143

def write_pygments_file(format, path)
  require 'pygments'
  extension = case format
              when :html
                'css'
              when :latex
                'sty'
              end
  # Here we burrow into the private 'Pygments#mentos' method.
  # Pygments exposes a 'css' method to return the CSS,
  # but we want to be able to output a LaTeX style file as well.
  # The inclusion of the ':css' symbol is necessary but doesn't actually
  # result in CSS being output unless the format is 'html'.
  pygments = Pygments.send(:mentos, :css, [format.to_s, ''])
  add_highlight_class!(pygments) if format == :html
  File.open(File.join(path, "pygments.#{extension}"), 'w') do |f|
    f.write(pygments)
  end
end