rpub 0.4.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +1 -1
- data/.rspec +0 -1
- data/.travis.yml +3 -4
- data/.yardopts +1 -0
- data/Gemfile +1 -1
- data/Gemfile.lock +70 -46
- data/Guardfile +6 -0
- data/HISTORY.md +10 -0
- data/README.md +1 -1
- data/Rakefile +7 -2
- data/bin/rpub +64 -1
- data/doc/.gitignore +8 -0
- data/doc/api/Rpub.html +339 -0
- data/doc/api/Rpub/Book.html +983 -0
- data/doc/api/Rpub/Chapter.html +737 -0
- data/doc/api/Rpub/Command.html +356 -0
- data/doc/api/Rpub/Commands.html +117 -0
- data/doc/api/Rpub/Commands/Clean.html +216 -0
- data/doc/api/Rpub/Commands/Compile.html +218 -0
- data/doc/api/Rpub/Commands/Generate.html +242 -0
- data/doc/api/Rpub/Commands/Package.html +230 -0
- data/doc/api/Rpub/Commands/Preview.html +216 -0
- data/doc/api/Rpub/Commands/Stats.html +226 -0
- data/doc/api/Rpub/Compressor.html +687 -0
- data/doc/api/Rpub/Context.html +553 -0
- data/doc/api/Rpub/Document.html +647 -0
- data/doc/api/Rpub/Document/OutlineElement.html +388 -0
- data/doc/api/Rpub/Epub.html +465 -0
- data/doc/api/Rpub/Epub/Container.html +224 -0
- data/doc/api/Rpub/Epub/Content.html +344 -0
- data/doc/api/Rpub/Epub/Cover.html +236 -0
- data/doc/api/Rpub/Epub/HtmlToc.html +240 -0
- data/doc/api/Rpub/Epub/Toc.html +244 -0
- data/doc/api/Rpub/FilesystemSource.html +544 -0
- data/doc/api/Rpub/HashDelegation.html +281 -0
- data/doc/api/Rpub/HashDelegation/ClassMethods.html +175 -0
- data/doc/api/Rpub/MediaType.html +192 -0
- data/doc/api/Rpub/Preview.html +464 -0
- data/doc/api/Rpub/XmlFile.html +457 -0
- data/doc/api/_index.html +405 -0
- data/doc/api/class_list.html +54 -0
- data/doc/api/css/common.css +1 -0
- data/doc/api/css/full_list.css +57 -0
- data/doc/api/css/style.css +339 -0
- data/doc/api/file.HISTORY.html +131 -0
- data/doc/api/file.LICENSE.html +92 -0
- data/doc/api/file.README.html +337 -0
- data/doc/api/file_list.html +62 -0
- data/doc/api/frames.html +26 -0
- data/doc/api/index.html +337 -0
- data/doc/api/js/app.js +219 -0
- data/doc/api/js/full_list.js +178 -0
- data/doc/api/js/jquery.js +4 -0
- data/doc/api/method_list.html +533 -0
- data/doc/api/top-level-namespace.html +112 -0
- data/doc/index.html +312 -0
- data/doc/javascripts/scale.fix.js +17 -0
- data/doc/params.json +1 -0
- data/doc/stylesheets/pygment_trac.css +69 -0
- data/doc/stylesheets/styles.css +255 -0
- data/features/clean.feature +31 -0
- data/features/compile.feature +49 -0
- data/features/embedded_assets.feature +51 -0
- data/features/generate.feature +63 -0
- data/features/layout.feature +41 -0
- data/features/package.feature +30 -0
- data/features/previews.feature +66 -0
- data/features/stats.feature +18 -0
- data/features/step_definitions/general_steps.rb +60 -0
- data/features/styles.feature +33 -0
- data/features/support/env.rb +7 -0
- data/features/table_of_contents.feature +35 -0
- data/lib/rpub.rb +28 -31
- data/lib/rpub/book.rb +9 -16
- data/lib/rpub/chapter.rb +4 -54
- data/lib/rpub/command.rb +32 -0
- data/lib/rpub/commands/clean.rb +4 -49
- data/lib/rpub/commands/compile.rb +4 -49
- data/lib/rpub/commands/generate.rb +10 -67
- data/lib/rpub/commands/package.rb +9 -37
- data/lib/rpub/commands/preview.rb +4 -54
- data/lib/rpub/commands/stats.rb +6 -10
- data/lib/rpub/compressor.rb +3 -3
- data/lib/rpub/context.rb +48 -0
- data/lib/rpub/document.rb +68 -0
- data/lib/rpub/epub.rb +8 -7
- data/lib/rpub/epub/content.rb +9 -30
- data/lib/rpub/epub/cover.rb +1 -8
- data/lib/rpub/epub/html_toc.rb +4 -9
- data/lib/rpub/epub/toc.rb +16 -13
- data/lib/rpub/filesystem_source.rb +47 -0
- data/lib/rpub/media_type.rb +16 -0
- data/lib/rpub/preview.rb +29 -0
- data/lib/rpub/version.rb +1 -1
- data/lib/rpub/xml_file.rb +4 -1
- data/rpub.gemspec +6 -7
- data/spec/rpub/book_spec.rb +45 -45
- data/spec/rpub/chapter_spec.rb +87 -22
- data/spec/rpub/epub/container_spec.rb +3 -5
- data/spec/rpub/epub/content_spec.rb +62 -41
- data/spec/rpub/epub/cover_spec.rb +3 -5
- data/spec/rpub/epub/html_toc_spec.rb +8 -8
- data/spec/rpub/epub/toc_spec.rb +20 -22
- data/spec/rpub_spec.rb +1 -3
- data/spec/spec_helper.rb +28 -0
- data/support/config.yml +1 -0
- data/support/styles.css +3 -3
- metadata +131 -120
- data/lib/rpub/commander.rb +0 -23
- data/lib/rpub/commands/base.rb +0 -33
- data/lib/rpub/commands/help.rb +0 -37
- data/lib/rpub/commands/main.rb +0 -45
- data/lib/rpub/compilation_helpers.rb +0 -73
- data/lib/rpub/subclass_tracker.rb +0 -53
- data/spec/rpub/commands/clean_spec.rb +0 -46
- data/spec/rpub/commands/generate_spec.rb +0 -52
- data/spec/rpub/commands/main_spec.rb +0 -26
- data/spec/rpub/commands/package_spec.rb +0 -33
- data/spec/rpub/commands/preview_spec.rb +0 -43
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module Rpub
|
|
2
|
+
class Document
|
|
3
|
+
KRAMDOWN_OPTIONS = {
|
|
4
|
+
:coderay_line_numbers => nil
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
OutlineElement = Struct.new(:level, :text, :html_id)
|
|
8
|
+
|
|
9
|
+
def initialize(content, layout)
|
|
10
|
+
@document = Kramdown::Document.new(content, KRAMDOWN_OPTIONS.merge(:template => layout))
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# @return [Kramdown::Element] Toc elements hierarchy
|
|
14
|
+
def toc
|
|
15
|
+
Kramdown::Converter::Toc.convert(document.root).first
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# Ordered headers for this chapter, each header as an object responding
|
|
19
|
+
# to #level and #text.
|
|
20
|
+
#
|
|
21
|
+
# @return [Array<#text,#level,#html_id>] list of headers for this chapter
|
|
22
|
+
def outline
|
|
23
|
+
@outline ||= elements(:header).map do |element|
|
|
24
|
+
OutlineElement.new(
|
|
25
|
+
element.options[:level],
|
|
26
|
+
element_text(element),
|
|
27
|
+
Kramdown::Converter::Html.send(:new, document, { :auto_id_prefix => '' }).generate_id(element.options[:raw_text])
|
|
28
|
+
)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @return [String] content parsed to HTML by the markdown engine.
|
|
33
|
+
def to_html
|
|
34
|
+
@to_html ||= Typogruby.improve(@document.to_html)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @return [String] Text of the first heading in this chapter
|
|
38
|
+
def title
|
|
39
|
+
@title ||= begin
|
|
40
|
+
(heading = outline.first) ? heading.text : 'untitled'
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# @return [Array<String>] list of all image references
|
|
45
|
+
def images
|
|
46
|
+
@images ||= elements(:img).map { |e| e.attr['src'] }
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
private
|
|
50
|
+
|
|
51
|
+
attr_reader :document
|
|
52
|
+
|
|
53
|
+
def element_text(element)
|
|
54
|
+
elements(:text, element).map { |e| e.value }.join
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def elements(type, root = @document.root)
|
|
58
|
+
collector = lambda do |element|
|
|
59
|
+
element.children.select { |e|
|
|
60
|
+
e.type == type
|
|
61
|
+
} + element.children.map { |e|
|
|
62
|
+
collector.call(e)
|
|
63
|
+
}.flatten
|
|
64
|
+
end
|
|
65
|
+
collector.call(root)
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
data/lib/rpub/epub.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
module Rpub
|
|
2
2
|
class Epub
|
|
3
|
+
include Rpub.source
|
|
3
4
|
attr_reader :book, :styles
|
|
4
5
|
|
|
5
6
|
def initialize(book, styles)
|
|
@@ -9,26 +10,26 @@ module Rpub
|
|
|
9
10
|
def manifest_in(target)
|
|
10
11
|
target.store_file 'mimetype', 'application/epub+zip'
|
|
11
12
|
target.compress_file 'META-INF/container.xml', Container.new
|
|
12
|
-
target.compress_file 'OEBPS/content.opf',
|
|
13
|
-
target.compress_file 'OEBPS/toc.ncx',
|
|
14
|
-
target.compress_file 'OEBPS/styles.css',
|
|
13
|
+
target.compress_file 'OEBPS/content.opf', Content.new(book)
|
|
14
|
+
target.compress_file 'OEBPS/toc.ncx', Toc.new(book)
|
|
15
|
+
target.compress_file 'OEBPS/styles.css', styles
|
|
15
16
|
if book.has_cover?
|
|
16
17
|
target.compress_file 'OEBPS/cover.html', Cover.new(book)
|
|
17
|
-
target.compress_file File.join('OEBPS', book.cover_image),
|
|
18
|
+
target.compress_file File.join('OEBPS', book.config.cover_image), read(book.config.cover_image)
|
|
18
19
|
end
|
|
19
20
|
if book.has_toc?
|
|
20
21
|
target.compress_file 'OEBPS/toc.html', toc { HtmlToc.new(book).render }
|
|
21
22
|
end
|
|
22
23
|
if book.has_fonts?
|
|
23
24
|
book.fonts.each do |font|
|
|
24
|
-
target.compress_file File.join('OEBPS', font),
|
|
25
|
+
target.compress_file File.join('OEBPS', font), read(font)
|
|
25
26
|
end
|
|
26
27
|
end
|
|
27
28
|
book.each do |chapter|
|
|
28
29
|
target.compress_file File.join('OEBPS', chapter.filename), chapter.to_html
|
|
29
30
|
end
|
|
30
31
|
book.images.each do |image|
|
|
31
|
-
target.compress_file File.join('OEBPS', image),
|
|
32
|
+
target.compress_file File.join('OEBPS', image), read(image)
|
|
32
33
|
end
|
|
33
34
|
end
|
|
34
35
|
|
|
@@ -36,7 +37,7 @@ module Rpub
|
|
|
36
37
|
|
|
37
38
|
def toc
|
|
38
39
|
@body = yield
|
|
39
|
-
ERB.new(
|
|
40
|
+
ERB.new(read(book.layout)).result(binding)
|
|
40
41
|
end
|
|
41
42
|
end
|
|
42
43
|
end
|
data/lib/rpub/epub/content.rb
CHANGED
|
@@ -1,33 +1,19 @@
|
|
|
1
1
|
module Rpub
|
|
2
2
|
class Epub
|
|
3
3
|
class Content < XmlFile
|
|
4
|
-
attr_reader :book
|
|
5
|
-
|
|
6
|
-
MEDIA_TYPES = {
|
|
7
|
-
'png' => 'image/png',
|
|
8
|
-
'gif' => 'image/gif',
|
|
9
|
-
'jpg' => 'image/jpeg',
|
|
10
|
-
'svg' => 'image/svg+xml'
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
def initialize(book)
|
|
14
|
-
@book = book
|
|
15
|
-
super()
|
|
16
|
-
end
|
|
17
|
-
|
|
18
4
|
def render
|
|
19
5
|
xml.instruct!
|
|
20
6
|
xml.declare! :DOCTYPE, :package, :PUBLIC, '-//W3C//DTD XHTML 1.1//EN', 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'
|
|
21
7
|
xml.package 'xmlns' => 'http://www.idpf.org/2007/opf', 'unique-identifier' => 'BookId', 'version' => '2.0' do
|
|
22
8
|
xml.metadata 'xmlns:dc' => 'http://purl.org/dc/elements/1.1/', 'xmlns:opf' => 'http://www.idpf.org/2007/opf' do
|
|
23
|
-
xml.dc :language, book.language
|
|
24
|
-
xml.dc :title, book.title
|
|
25
|
-
xml.dc :creator, book.creator, 'opf:role' => 'aut'
|
|
26
|
-
xml.dc :publisher, book.publisher
|
|
27
|
-
xml.dc :subject, book.subject
|
|
9
|
+
xml.dc :language, book.config.language
|
|
10
|
+
xml.dc :title, book.config.title
|
|
11
|
+
xml.dc :creator, book.config.creator, 'opf:role' => 'aut'
|
|
12
|
+
xml.dc :publisher, book.config.publisher
|
|
13
|
+
xml.dc :subject, book.config.subject
|
|
28
14
|
xml.dc :identifier, book.uid, :id => 'BookId'
|
|
29
|
-
xml.dc :rights, book.rights
|
|
30
|
-
xml.dc :description, book.description
|
|
15
|
+
xml.dc :rights, book.config.rights
|
|
16
|
+
xml.dc :description, book.config.description
|
|
31
17
|
|
|
32
18
|
if book.has_cover?
|
|
33
19
|
xml.meta :name => 'cover', :content => 'cover-image'
|
|
@@ -46,11 +32,11 @@ module Rpub
|
|
|
46
32
|
|
|
47
33
|
if book.has_cover?
|
|
48
34
|
xml.item 'id' => 'cover', 'href' => 'cover.html', 'media-type' => 'application/xhtml+xml'
|
|
49
|
-
xml.item 'id' => 'cover-image', 'href' => book.cover_image, 'media-type' => guess_media_type(book.cover_image)
|
|
35
|
+
xml.item 'id' => 'cover-image', 'href' => book.config.cover_image, 'media-type' => MediaType.guess_media_type(book.config.cover_image)
|
|
50
36
|
end
|
|
51
37
|
|
|
52
38
|
book.images.each do |image|
|
|
53
|
-
xml.item 'id' => File.basename(image), 'href' => image, 'media-type' => guess_media_type(image)
|
|
39
|
+
xml.item 'id' => File.basename(image), 'href' => image, 'media-type' => MediaType.guess_media_type(image)
|
|
54
40
|
end
|
|
55
41
|
|
|
56
42
|
if book.has_toc?
|
|
@@ -83,13 +69,6 @@ module Rpub
|
|
|
83
69
|
end
|
|
84
70
|
end
|
|
85
71
|
end
|
|
86
|
-
|
|
87
|
-
private
|
|
88
|
-
|
|
89
|
-
# TODO refactor into separate guesser object
|
|
90
|
-
def guess_media_type(filename)
|
|
91
|
-
MEDIA_TYPES.fetch(filename[/\.(gif|png|jpe?g|svg)$/, 1]) { 'image/png' }
|
|
92
|
-
end
|
|
93
72
|
end
|
|
94
73
|
end
|
|
95
74
|
end
|
data/lib/rpub/epub/cover.rb
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
module Rpub
|
|
2
2
|
class Epub
|
|
3
3
|
class Cover < XmlFile
|
|
4
|
-
attr_reader :book
|
|
5
|
-
|
|
6
|
-
def initialize(book)
|
|
7
|
-
@book = book
|
|
8
|
-
super()
|
|
9
|
-
end
|
|
10
|
-
|
|
11
4
|
def render
|
|
12
5
|
xml.declare! :DOCTYPE, :html, :PUBLIC, '-//W3C//DTD XHTML 1.0 Strict//EN', 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'
|
|
13
6
|
xml.html :xmlns => 'http://www.w3.org/1999/xhtml' do
|
|
@@ -17,7 +10,7 @@ module Rpub
|
|
|
17
10
|
end
|
|
18
11
|
xml.body do
|
|
19
12
|
xml.div :id => 'cover-image' do
|
|
20
|
-
xml.img :src => book.cover_image, :alt => book.title
|
|
13
|
+
xml.img :src => book.config.cover_image, :alt => book.config.title
|
|
21
14
|
end
|
|
22
15
|
end
|
|
23
16
|
end
|
data/lib/rpub/epub/html_toc.rb
CHANGED
|
@@ -1,21 +1,16 @@
|
|
|
1
1
|
module Rpub
|
|
2
2
|
class Epub
|
|
3
3
|
class HtmlToc < XmlFile
|
|
4
|
-
attr_reader :book
|
|
5
|
-
|
|
6
|
-
def initialize(book)
|
|
7
|
-
@book = book
|
|
8
|
-
super()
|
|
9
|
-
end
|
|
10
|
-
|
|
11
4
|
def render
|
|
12
5
|
xml.div :id => 'toc' do
|
|
13
6
|
xml.h1 'Table of Contents'
|
|
14
7
|
xml.div :class => 'toc' do
|
|
15
8
|
book.outline.each do |(filename, headings)|
|
|
16
9
|
headings.each do |heading|
|
|
17
|
-
|
|
18
|
-
xml.
|
|
10
|
+
if heading.level <= (book.config.max_level || 2)
|
|
11
|
+
xml.div :class => "level-#{heading.level}" do
|
|
12
|
+
xml.a heading.text, :href => [filename, heading.html_id].join('#')
|
|
13
|
+
end
|
|
19
14
|
end
|
|
20
15
|
end
|
|
21
16
|
end
|
data/lib/rpub/epub/toc.rb
CHANGED
|
@@ -1,26 +1,17 @@
|
|
|
1
1
|
module Rpub
|
|
2
2
|
class Epub
|
|
3
3
|
class Toc < XmlFile
|
|
4
|
-
attr_reader :book
|
|
5
|
-
|
|
6
|
-
def initialize(book)
|
|
7
|
-
@book = book
|
|
8
|
-
@play_order = -1
|
|
9
|
-
@max_level = book.config.fetch(:max_level) { 2 }
|
|
10
|
-
super()
|
|
11
|
-
end
|
|
12
|
-
|
|
13
4
|
def render
|
|
14
5
|
xml.instruct!
|
|
15
6
|
xml.declare! :DOCTYPE, :ncx, :PUBLIC, "-//W3C//DTD XHTML 1.1//EN", 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'
|
|
16
7
|
xml.ncx :xmlns => 'http://www.daisy.org/z3986/2005/ncx/', :version => '2005-1' do
|
|
17
8
|
xml.head do
|
|
18
9
|
xml.meta :name => 'dtb:uid', :content => book.uid
|
|
19
|
-
xml.meta :name => 'dtb:depth', :content =>
|
|
10
|
+
xml.meta :name => 'dtb:depth', :content => max_level
|
|
20
11
|
xml.meta :name => 'dtb:totalPageCount', :content => '0'
|
|
21
12
|
xml.meta :name => 'dtb:maxPageNumber', :content => '0'
|
|
22
13
|
end
|
|
23
|
-
xml.docTitle { xml.text book.title }
|
|
14
|
+
xml.docTitle { xml.text book.config.title }
|
|
24
15
|
xml.navMap do
|
|
25
16
|
book.chapters.each do |chapter|
|
|
26
17
|
nav_points_nested_by_level chapter.toc, chapter.filename
|
|
@@ -31,15 +22,27 @@ module Rpub
|
|
|
31
22
|
|
|
32
23
|
private
|
|
33
24
|
|
|
25
|
+
def max_level
|
|
26
|
+
book.config.max_level || 2
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def play_order
|
|
30
|
+
@play_order ||= 0
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def play_order=(v)
|
|
34
|
+
@play_order = v
|
|
35
|
+
end
|
|
36
|
+
|
|
34
37
|
def next_play_order
|
|
35
|
-
|
|
38
|
+
self.play_order = play_order + 1
|
|
36
39
|
end
|
|
37
40
|
|
|
38
41
|
def nav_points_nested_by_level(heading, filename, level = 1)
|
|
39
42
|
heading.children.each do |heading|
|
|
40
43
|
html_id = heading.attr[:id]
|
|
41
44
|
source = filename+'#'+html_id
|
|
42
|
-
if heading.value.options[:level] <=
|
|
45
|
+
if heading.value.options[:level] <= max_level
|
|
43
46
|
nav_point html_id, next_play_order, heading.value.options[:raw_text], source do
|
|
44
47
|
nav_points_nested_by_level heading, filename, level + 1
|
|
45
48
|
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
module Rpub
|
|
2
|
+
module FilesystemSource
|
|
3
|
+
module_function
|
|
4
|
+
|
|
5
|
+
def read(filename)
|
|
6
|
+
File.read(filename)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def exists?(filename)
|
|
10
|
+
File.exists?(filename)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def source_files
|
|
14
|
+
Dir['*{.md,.markdown,.mdown,.markd}']
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def write(filename, content, force = false)
|
|
18
|
+
return if content.nil?
|
|
19
|
+
if !force && File.exist?(filename)
|
|
20
|
+
warn "Not overriding #{filename}"
|
|
21
|
+
return
|
|
22
|
+
end
|
|
23
|
+
File.open(filename, 'w') do |f|
|
|
24
|
+
f.write content
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def force_write(filename, content)
|
|
29
|
+
write filename, content, true
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def remove(filename, dry_run = false)
|
|
33
|
+
if File.exist?(filename)
|
|
34
|
+
if dry_run
|
|
35
|
+
puts filename
|
|
36
|
+
else
|
|
37
|
+
File.unlink(filename)
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def own_or_support_file(filename)
|
|
43
|
+
return filename if exists?(filename)
|
|
44
|
+
Rpub.support_file(filename)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
module Rpub
|
|
2
|
+
module MediaType
|
|
3
|
+
MEDIA_TYPES = {
|
|
4
|
+
'png' => 'image/png',
|
|
5
|
+
'gif' => 'image/gif',
|
|
6
|
+
'jpg' => 'image/jpeg',
|
|
7
|
+
'svg' => 'image/svg+xml'
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
module_function
|
|
11
|
+
|
|
12
|
+
def guess_media_type(filename)
|
|
13
|
+
MEDIA_TYPES.fetch(filename[/\.(gif|png|jpe?g|svg)$/, 1]) { 'image/png' }
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
data/lib/rpub/preview.rb
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Rpub
|
|
2
|
+
class Preview
|
|
3
|
+
attr_reader :context, :source
|
|
4
|
+
|
|
5
|
+
def initialize(context, source)
|
|
6
|
+
@context, @source = context, source
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def formatted
|
|
10
|
+
return unless context.chapter_files.any?
|
|
11
|
+
move_styles_inline(plain)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def text
|
|
15
|
+
Nokogiri::HTML(plain).xpath('//text()').to_s
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def plain
|
|
21
|
+
Rpub.document_factory.call(context.chapter_files.join("\n"), context.layout).to_html
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def move_styles_inline(html)
|
|
25
|
+
style_block = %Q{<style>\n#{source.read(context.styles)}\n</style>}
|
|
26
|
+
html.gsub %r{</head>}, style_block + "\n</head>"
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/rpub/version.rb
CHANGED
data/lib/rpub/xml_file.rb
CHANGED
|
@@ -3,8 +3,11 @@ module Rpub
|
|
|
3
3
|
# @return [Builder::XmlMarkup]
|
|
4
4
|
attr_reader :xml
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
attr_reader :book
|
|
7
|
+
|
|
8
|
+
def initialize(book = nil)
|
|
7
9
|
@xml = Builder::XmlMarkup.new :indent => 2
|
|
10
|
+
@book = book
|
|
8
11
|
end
|
|
9
12
|
|
|
10
13
|
# @return [String] render this file and output as string
|
data/rpub.gemspec
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
|
2
|
-
require '
|
|
2
|
+
require File.expand_path('../lib/rpub/version', __FILE__)
|
|
3
3
|
|
|
4
4
|
Gem::Specification.new do |s|
|
|
5
5
|
# Metadata
|
|
@@ -42,14 +42,13 @@ EOS
|
|
|
42
42
|
s.add_runtime_dependency 'builder'
|
|
43
43
|
s.add_runtime_dependency 'nokogiri'
|
|
44
44
|
s.add_runtime_dependency 'textstats'
|
|
45
|
-
s.add_runtime_dependency '
|
|
45
|
+
s.add_runtime_dependency 'commander'
|
|
46
46
|
s.add_development_dependency 'yard'
|
|
47
|
-
s.add_development_dependency 'rspec'
|
|
47
|
+
s.add_development_dependency 'rspec', '~> 3.0'
|
|
48
48
|
s.add_development_dependency 'rake'
|
|
49
|
-
s.add_development_dependency 'guard'
|
|
50
|
-
s.add_development_dependency 'guard-rspec'
|
|
51
|
-
s.add_development_dependency 'rb-fsevent'
|
|
52
|
-
s.add_development_dependency 'growl'
|
|
53
49
|
s.add_development_dependency 'simplecov'
|
|
50
|
+
s.add_development_dependency 'cucumber'
|
|
51
|
+
s.add_development_dependency 'aruba'
|
|
52
|
+
s.add_development_dependency 'coveralls'
|
|
54
53
|
end
|
|
55
54
|
|
data/spec/rpub/book_spec.rb
CHANGED
|
@@ -1,115 +1,115 @@
|
|
|
1
|
-
require 'spec_helper'
|
|
2
|
-
|
|
3
1
|
describe Rpub::Book do
|
|
4
|
-
let(:
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
it { should respond_to(:title) }
|
|
11
|
-
its(:title) { should == 'My "Awesome" Title!' }
|
|
12
|
-
its(:filename) { should == 'my-awesome-title-1.0.3.epub' }
|
|
2
|
+
let(:context) { Rpub::Context.new }
|
|
3
|
+
let(:subject) { described_class.new(context) }
|
|
4
|
+
before { allow(context).to receive(:chapter_files).and_return(['foo']) }
|
|
5
|
+
it { is_expected.to respond_to(:config) }
|
|
13
6
|
|
|
14
7
|
describe 'chapters' do
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
8
|
+
it 'has 1 chapter' do
|
|
9
|
+
expect(subject.chapters.size).to eq(1)
|
|
10
|
+
end
|
|
18
11
|
|
|
19
|
-
it {
|
|
12
|
+
it { is_expected.to be_kind_of(Enumerable) }
|
|
20
13
|
|
|
21
|
-
it 'should start with
|
|
22
|
-
|
|
14
|
+
it 'should start with context chapters' do
|
|
15
|
+
expect(subject.chapters.size).to eq(1)
|
|
23
16
|
end
|
|
24
17
|
|
|
25
18
|
it 'should allow chaining multiple calls' do
|
|
26
19
|
subject << 'foo' << 'bar'
|
|
27
|
-
subject.
|
|
20
|
+
expect(subject.chapters.size).to eq(3)
|
|
28
21
|
end
|
|
29
22
|
|
|
30
23
|
it 'should yield chapters' do
|
|
31
24
|
yielded = false
|
|
32
25
|
subject.each { |c| yielded = true }
|
|
33
|
-
yielded.
|
|
26
|
+
expect(yielded).to be_truthy
|
|
34
27
|
end
|
|
35
28
|
end
|
|
36
29
|
|
|
37
30
|
describe '#has_fonts?' do
|
|
38
31
|
it 'should not have a font by default' do
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
it 'should not have a font with an empty array' do
|
|
43
|
-
described_class.new(nil, nil, []).should_not have_fonts
|
|
32
|
+
expect(context).to receive(:fonts).and_return([])
|
|
33
|
+
is_expected.not_to have_fonts
|
|
44
34
|
end
|
|
45
35
|
|
|
46
36
|
it 'should have a font with a non-empty array' do
|
|
47
|
-
|
|
37
|
+
expect(context).to receive(:fonts).and_return(['foo'])
|
|
38
|
+
is_expected.to have_fonts
|
|
48
39
|
end
|
|
49
40
|
end
|
|
50
41
|
|
|
51
42
|
describe '#uid' do
|
|
52
43
|
it 'should change when chapters change' do
|
|
53
|
-
|
|
44
|
+
expect(described_class.new(context).add_chapter('foo')).to_not eql(subject.uid)
|
|
54
45
|
end
|
|
55
46
|
|
|
56
47
|
it 'should change when config changes' do
|
|
57
|
-
|
|
48
|
+
expect(described_class.new(context)).to_not eql(subject.uid)
|
|
58
49
|
end
|
|
59
50
|
end
|
|
60
51
|
|
|
61
52
|
describe '#outline' do
|
|
53
|
+
before { expect(context).to receive(:chapter_files).and_return([]) }
|
|
54
|
+
|
|
62
55
|
it 'should return empty array when there are no chapters' do
|
|
63
|
-
subject.outline.
|
|
56
|
+
expect(subject.outline).to be_empty
|
|
64
57
|
end
|
|
65
58
|
|
|
66
59
|
it 'should return combination of all chapter outlines with filename' do
|
|
67
60
|
subject << '# foo' << '# bar'
|
|
68
|
-
subject.outline.
|
|
61
|
+
expect(subject.outline.size).to eq(2)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
it 'should combine chapter outlines' do
|
|
65
|
+
subject << '# foo' << '# bar'
|
|
66
|
+
expect(subject.outline.first[0]).to eql('chapter-0-foo.html')
|
|
67
|
+
expect(subject.outline.first[1][0].text).to eql('foo')
|
|
69
68
|
end
|
|
70
69
|
end
|
|
71
70
|
|
|
72
71
|
describe '#has_cover?' do
|
|
73
72
|
it 'should not have a cover without a config key' do
|
|
74
|
-
|
|
73
|
+
is_expected.not_to have_cover
|
|
75
74
|
end
|
|
76
75
|
|
|
77
76
|
it 'should not have a cover with a config key that is false' do
|
|
78
|
-
|
|
77
|
+
expect(context).to receive(:config).and_return(OpenStruct.new({ 'cover_image' => false }))
|
|
78
|
+
is_expected.not_to have_cover
|
|
79
79
|
end
|
|
80
80
|
|
|
81
81
|
it 'should have a cover with a config key' do
|
|
82
|
-
|
|
82
|
+
expect(context).to receive(:config).and_return(OpenStruct.new({ 'cover_image' => true }))
|
|
83
|
+
is_expected.to have_cover
|
|
83
84
|
end
|
|
84
85
|
end
|
|
85
86
|
|
|
86
87
|
describe '#has_toc?' do
|
|
87
88
|
it 'should not have a toc without a config key' do
|
|
88
|
-
|
|
89
|
+
expect(context).to receive(:config).and_return(OpenStruct.new)
|
|
90
|
+
is_expected.not_to have_toc
|
|
89
91
|
end
|
|
90
92
|
|
|
91
93
|
it 'should not have a toc with a config key that is false' do
|
|
92
|
-
|
|
94
|
+
expect(context).to receive(:config).and_return(OpenStruct.new({ 'toc' => false }))
|
|
95
|
+
is_expected.not_to have_toc
|
|
93
96
|
end
|
|
94
97
|
|
|
95
98
|
it 'should have a toc with a config key' do
|
|
96
|
-
|
|
99
|
+
expect(context).to receive(:config).and_return(OpenStruct.new({ 'toc' => true }))
|
|
100
|
+
is_expected.to have_toc
|
|
97
101
|
end
|
|
98
102
|
end
|
|
99
103
|
|
|
100
104
|
describe '#images' do
|
|
101
105
|
before { subject << '' << '' << '' }
|
|
102
|
-
it { should have(2).images }
|
|
103
|
-
its(:images) { should include('bar') }
|
|
104
|
-
its(:images) { should include('qux') }
|
|
105
|
-
end
|
|
106
106
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
subject.
|
|
107
|
+
it 'has 2 images' do
|
|
108
|
+
expect(subject.images.size).to eq(2)
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
it 'has includes each unique image' do
|
|
112
|
+
expect(subject.images).to include('bar', 'qux')
|
|
113
113
|
end
|
|
114
114
|
end
|
|
115
115
|
end
|