rpub 0.1.0 → 0.2.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.
- data/Gemfile.lock +3 -1
- data/HISTORY.md +9 -0
- data/README.md +6 -8
- data/lib/rpub.rb +1 -0
- data/lib/rpub/book.rb +7 -2
- data/lib/rpub/chapter.rb +6 -6
- data/lib/rpub/commands/generate.rb +80 -0
- data/lib/rpub/commands/preview.rb +6 -1
- data/lib/rpub/epub.rb +5 -2
- data/lib/rpub/epub/content.rb +16 -5
- data/lib/rpub/epub/html_toc.rb +5 -5
- data/lib/rpub/epub/toc.rb +3 -3
- data/lib/rpub/version.rb +1 -1
- data/rpub.gemspec +3 -3
- data/spec/rpub/book_spec.rb +57 -0
- data/spec/rpub/chapter_spec.rb +22 -2
- data/spec/rpub/commands/preview_spec.rb +1 -0
- data/spec/rpub/epub/container_spec.rb +8 -0
- data/spec/rpub/epub/content_spec.rb +75 -0
- data/spec/rpub/epub/cover_spec.rb +9 -0
- data/spec/rpub/epub/html_toc_spec.rb +19 -0
- data/spec/rpub/epub/toc_spec.rb +25 -0
- data/spec/spec_helper.rb +7 -0
- data/support/config.yml +14 -0
- metadata +34 -6
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
rpub (0.
|
4
|
+
rpub (0.2.0)
|
5
5
|
builder
|
6
6
|
kramdown
|
7
7
|
rubyzip
|
@@ -20,6 +20,7 @@ GEM
|
|
20
20
|
guard-rspec (0.7.0)
|
21
21
|
guard (>= 0.10.0)
|
22
22
|
kramdown (0.13.5)
|
23
|
+
nokogiri (1.5.2)
|
23
24
|
rake (0.9.2.2)
|
24
25
|
rb-fsevent (0.9.1)
|
25
26
|
rspec (2.9.0)
|
@@ -44,6 +45,7 @@ DEPENDENCIES
|
|
44
45
|
growl
|
45
46
|
guard
|
46
47
|
guard-rspec
|
48
|
+
nokogiri
|
47
49
|
rake
|
48
50
|
rb-fsevent
|
49
51
|
rpub!
|
data/HISTORY.md
CHANGED
@@ -1,5 +1,14 @@
|
|
1
1
|
# History
|
2
2
|
|
3
|
+
## 0.2.0
|
4
|
+
|
5
|
+
* Prefixed book query methods with `has_?`
|
6
|
+
* Added generate command
|
7
|
+
* Use inline styles for preview file
|
8
|
+
* Allow font embedding
|
9
|
+
* Improved tests
|
10
|
+
* Remove mention of validate command
|
11
|
+
|
3
12
|
## 0.1.0
|
4
13
|
|
5
14
|
* Initial gem release
|
data/README.md
CHANGED
@@ -57,14 +57,6 @@ in a special configuration file called `config.yml`:
|
|
57
57
|
|
58
58
|
This file is written in [YAML](http://yaml.org) and sets basic properties of your book project.
|
59
59
|
|
60
|
-
To make sure the book you generated is _valid_ -- meaning it is likely that most
|
61
|
-
readers out there will be able to read it -- you can use the special `validate`
|
62
|
-
command:
|
63
|
-
|
64
|
-
$ rpub validate
|
65
|
-
|
66
|
-
rPub will report any errors it finds. No output means everything is alright.
|
67
|
-
|
68
60
|
Since regenerating your ePub file and opening it in a suitable reader
|
69
61
|
application is cumbersome, rPub can generate a simple preview document for you:
|
70
62
|
|
@@ -139,11 +131,17 @@ or `preview` commands, using the `-l` or `-s` options:
|
|
139
131
|
|
140
132
|
$ rpub compile -l /tmp/my-layout.html
|
141
133
|
|
134
|
+
If you like the default layout or styles, but want to adapt them, you can copy
|
135
|
+
those files into your project using the `generate` subcommand:
|
136
|
+
|
137
|
+
$ rpub generate
|
138
|
+
|
142
139
|
### Command reference
|
143
140
|
|
144
141
|
* `rpub compile` -- generate .epub file
|
145
142
|
* `rpub package` -- create zip file with compiled book and other listed files
|
146
143
|
* `rpub preview` -- generate preview HTML file
|
144
|
+
* `rpub generate` -- copy default layout.html, styles.css and config.yml
|
147
145
|
* `rpub help` -- get help on subcommands
|
148
146
|
* `rpub clean` -- remove generated files
|
149
147
|
|
data/lib/rpub.rb
CHANGED
data/lib/rpub/book.rb
CHANGED
@@ -25,11 +25,16 @@ module Rpub
|
|
25
25
|
chapters.each(&block)
|
26
26
|
end
|
27
27
|
|
28
|
-
def
|
28
|
+
def has_fonts?
|
29
|
+
fonts = config.fetch('fonts') { [] }
|
30
|
+
fonts.respond_to?(:any?) && fonts.any?
|
31
|
+
end
|
32
|
+
|
33
|
+
def has_toc?
|
29
34
|
!!config.fetch('toc') { false }
|
30
35
|
end
|
31
36
|
|
32
|
-
def
|
37
|
+
def has_cover?
|
33
38
|
!!config.fetch('cover_image') { false }
|
34
39
|
end
|
35
40
|
|
data/lib/rpub/chapter.rb
CHANGED
@@ -20,11 +20,11 @@ module Rpub
|
|
20
20
|
|
21
21
|
# @return [String] Unique identifier for this chapter.
|
22
22
|
def uid
|
23
|
-
@uid ||= Digest::SHA1.hexdigest([content,
|
23
|
+
@uid ||= Digest::SHA1.hexdigest([content, xml_id.to_s, layout].join)
|
24
24
|
end
|
25
25
|
|
26
26
|
# @return [String] XML-friendly slug for this chapter based on its number.
|
27
|
-
def
|
27
|
+
def xml_id
|
28
28
|
@id ||= "chapter-#{number}"
|
29
29
|
end
|
30
30
|
|
@@ -35,7 +35,7 @@ module Rpub
|
|
35
35
|
|
36
36
|
# @return [String] name for the file in the zip to use, based on the title
|
37
37
|
def filename
|
38
|
-
@filename ||=
|
38
|
+
@filename ||= xml_id.to_s + '-' + title.gsub(/[^\w\.]/i, '-').squeeze('-').downcase.chomp('-') + '.html'
|
39
39
|
end
|
40
40
|
|
41
41
|
# Ordered headers for this chapter, each header as an object responding
|
@@ -45,9 +45,9 @@ module Rpub
|
|
45
45
|
def outline
|
46
46
|
@outline ||= elements(:header).map do |element|
|
47
47
|
OpenStruct.new({
|
48
|
-
:level
|
49
|
-
:text
|
50
|
-
:
|
48
|
+
:level => element.options[:level],
|
49
|
+
:text => element_text(element),
|
50
|
+
:html_id => Kramdown::Converter::Html.send(:new, @document, { :auto_id_prefix => '' }).generate_id(element.options[:raw_text])
|
51
51
|
})
|
52
52
|
end
|
53
53
|
end
|
@@ -0,0 +1,80 @@
|
|
1
|
+
module Rpub
|
2
|
+
module Commands
|
3
|
+
class Generate < Base
|
4
|
+
identifier 'generate'
|
5
|
+
|
6
|
+
def initialize(*args)
|
7
|
+
super
|
8
|
+
@all = true
|
9
|
+
end
|
10
|
+
|
11
|
+
def invoke
|
12
|
+
super
|
13
|
+
|
14
|
+
if (@styles.nil? && @all) || (!@styles.nil? && @styles)
|
15
|
+
write_file Rpub.support_file('styles.css')
|
16
|
+
end
|
17
|
+
|
18
|
+
if (@layout.nil? && @all) || (!@layout.nil? && @layout)
|
19
|
+
write_file Rpub.support_file('layout.html')
|
20
|
+
end
|
21
|
+
|
22
|
+
if (@config.nil? && @all) || (!@config.nil? && @config)
|
23
|
+
write_file Rpub.support_file('config.yml')
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def write_file(file)
|
30
|
+
output_file = File.basename(file)
|
31
|
+
if File.exist?(output_file)
|
32
|
+
warn "Not overriding #{output_file}"
|
33
|
+
return
|
34
|
+
end
|
35
|
+
File.open(output_file, 'w') do |f|
|
36
|
+
f.write File.read(file)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def parser
|
41
|
+
OptionParser.new do |opts|
|
42
|
+
opts.banner = <<-EOS
|
43
|
+
Usage: rpub generate [-slach]
|
44
|
+
|
45
|
+
Generate one or more standard files to get started with a new project. By
|
46
|
+
default an entire skeleton project is generated, but by passing the -s, -l, -c
|
47
|
+
options you can generate just a single file.
|
48
|
+
|
49
|
+
Options:
|
50
|
+
EOS
|
51
|
+
opts.separator ' '
|
52
|
+
|
53
|
+
opts.on '-s', '--[no-]styles', 'Generate default stylesheet' do |v|
|
54
|
+
@all = false if v
|
55
|
+
@styles = v
|
56
|
+
end
|
57
|
+
|
58
|
+
opts.on '-l', '--[no-]layout', 'Generate default HTML layout' do |v|
|
59
|
+
@all = false if v
|
60
|
+
@layout = v
|
61
|
+
end
|
62
|
+
|
63
|
+
opts.on '-c', '--[no-]config', 'Generate default configuration' do |v|
|
64
|
+
@all = false if v
|
65
|
+
@config = v
|
66
|
+
end
|
67
|
+
|
68
|
+
opts.separator ''
|
69
|
+
opts.separator 'Generic options:'
|
70
|
+
opts.separator ''
|
71
|
+
|
72
|
+
opts.on_tail '-h', '--help', 'Display this message' do
|
73
|
+
puts opts
|
74
|
+
exit
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -15,12 +15,17 @@ module Rpub
|
|
15
15
|
return unless markdown_files.any?
|
16
16
|
concatenation = markdown_files.join("\n")
|
17
17
|
File.open(@filename, 'w') do |f|
|
18
|
-
f.write Kramdown::Document.new(concatenation, KRAMDOWN_OPTIONS.merge(:template => layout)).to_html
|
18
|
+
f.write move_styles_inline(Kramdown::Document.new(concatenation, KRAMDOWN_OPTIONS.merge(:template => layout)).to_html)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
22
|
private
|
23
23
|
|
24
|
+
def move_styles_inline(html)
|
25
|
+
style_block = %Q{<style>\n#{File.read(styles)}\n</style>}
|
26
|
+
html.gsub %r{</head>}, style_block + "\n</head>"
|
27
|
+
end
|
28
|
+
|
24
29
|
def parser
|
25
30
|
OptionParser.new do |opts|
|
26
31
|
opts.banner = <<-EOS
|
data/lib/rpub/epub.rb
CHANGED
@@ -12,13 +12,16 @@ module Rpub
|
|
12
12
|
target.compress_file 'OEBPS/content.opf', Content.new(book)
|
13
13
|
target.compress_file 'OEBPS/toc.ncx', Toc.new(book)
|
14
14
|
target.compress_file 'OEBPS/styles.css', styles
|
15
|
-
if book.
|
15
|
+
if book.has_cover?
|
16
16
|
target.compress_file 'OEBPS/cover.html', Cover.new(book)
|
17
17
|
target.compress_file File.join('OEBPS', book.cover_image), File.read(book.cover_image)
|
18
18
|
end
|
19
|
-
if book.
|
19
|
+
if book.has_toc?
|
20
20
|
target.compress_file 'OEBPS/toc.html', toc { HtmlToc.new(book).render }
|
21
21
|
end
|
22
|
+
book.fonts.each do |font|
|
23
|
+
target.compress_file File.join('OEBPS', font), File.read(font)
|
24
|
+
end
|
22
25
|
book.each do |chapter|
|
23
26
|
target.compress_file File.join('OEBPS', chapter.filename), chapter.to_html
|
24
27
|
end
|
data/lib/rpub/epub/content.rb
CHANGED
@@ -29,7 +29,7 @@ module Rpub
|
|
29
29
|
xml.dc :rights, book.rights
|
30
30
|
xml.dc :description, book.description
|
31
31
|
|
32
|
-
if book.
|
32
|
+
if book.has_cover?
|
33
33
|
xml.meta :name => 'cover', :content => 'cover-image'
|
34
34
|
end
|
35
35
|
end
|
@@ -38,7 +38,13 @@ module Rpub
|
|
38
38
|
xml.item 'id' => 'ncx', 'href' => 'toc.ncx', 'media-type' => 'application/x-dtbncx+xml'
|
39
39
|
xml.item 'id' => 'css', 'href' => 'styles.css', 'media-type' => 'text/css'
|
40
40
|
|
41
|
-
if book.
|
41
|
+
if book.has_fonts?
|
42
|
+
book.fonts.each do |font|
|
43
|
+
xml.item 'id' => File.basename(font), 'href' => font, 'media-type' => 'font/opentype'
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
if book.has_cover?
|
42
48
|
xml.item 'id' => 'cover', 'href' => 'cover.html', 'media-type' => 'application/xhtml+xml'
|
43
49
|
xml.item 'id' => 'cover-image', 'href' => book.cover_image, 'media-type' => guess_media_type(book.cover_image)
|
44
50
|
end
|
@@ -46,14 +52,18 @@ module Rpub
|
|
46
52
|
book.images.each do |image|
|
47
53
|
xml.item 'id' => File.basename(image), 'href' => image, 'media-type' => guess_media_type(image)
|
48
54
|
end
|
49
|
-
|
55
|
+
|
56
|
+
if book.has_toc?
|
57
|
+
xml.item 'id' => 'toc', 'href' => 'toc.html', 'media-type' => 'application/xhtml+xml'
|
58
|
+
end
|
59
|
+
|
50
60
|
book.chapters.each do |chapter|
|
51
61
|
xml.item 'id' => chapter.id, 'href' => chapter.filename, 'media-type' => 'application/xhtml+xml'
|
52
62
|
end
|
53
63
|
end
|
54
64
|
|
55
65
|
xml.spine 'toc' => 'ncx' do
|
56
|
-
if book.
|
66
|
+
if book.has_cover?
|
57
67
|
xml.itemref 'idref' => 'cover', 'linear' => 'no'
|
58
68
|
end
|
59
69
|
book.chapters.each do |chapter|
|
@@ -61,7 +71,7 @@ module Rpub
|
|
61
71
|
end
|
62
72
|
end
|
63
73
|
|
64
|
-
if book.
|
74
|
+
if book.has_cover?
|
65
75
|
xml.guide do
|
66
76
|
xml.reference :type => 'cover', :title => 'Cover', :href => 'cover.html'
|
67
77
|
end
|
@@ -71,6 +81,7 @@ module Rpub
|
|
71
81
|
|
72
82
|
private
|
73
83
|
|
84
|
+
# TODO refactor into separate guesser object
|
74
85
|
def guess_media_type(filename)
|
75
86
|
MEDIA_TYPES.fetch(filename[/\.(gif|png|jpe?g|svg)$/, 1]) { 'image/png' }
|
76
87
|
end
|
data/lib/rpub/epub/html_toc.rb
CHANGED
@@ -9,12 +9,12 @@ module Rpub
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def render
|
12
|
-
xml.
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
xml.div :id => 'toc' do
|
13
|
+
xml.h1 'Table of Contents'
|
14
|
+
xml.div :class => 'toc' do
|
15
|
+
book.outline.each do |(filename, heading)|
|
16
16
|
xml.div :class => "level-#{heading.level}" do
|
17
|
-
xml.a heading.text, :href => [filename, heading.
|
17
|
+
xml.a heading.text, :href => [filename, heading.html_id].join('#')
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
data/lib/rpub/epub/toc.rb
CHANGED
@@ -13,14 +13,14 @@ module Rpub
|
|
13
13
|
xml.declare! :DOCTYPE, :ncx, :PUBLIC, "-//W3C//DTD XHTML 1.1//EN", 'http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd'
|
14
14
|
xml.ncx :xmlns => 'http://www.daisy.org/z3986/2005/ncx/', :version => '2005-1' do
|
15
15
|
xml.head do
|
16
|
-
xml.meta :name => 'dtb:uid', :content =>
|
16
|
+
xml.meta :name => 'dtb:uid', :content => book.uid
|
17
17
|
xml.meta :name => 'dtb:depth', :content => '1'
|
18
18
|
xml.meta :name => 'dtb:totalPageCount', :content => '0'
|
19
19
|
xml.meta :name => 'dtb:maxPageNumber', :content => '0'
|
20
20
|
end
|
21
|
-
xml.docTitle { xml.text
|
21
|
+
xml.docTitle { xml.text book.title }
|
22
22
|
xml.navMap do
|
23
|
-
|
23
|
+
book.chapters.each_with_index do |chapter, n|
|
24
24
|
xml.navPoint :id => chapter.id, :playOrder => n do
|
25
25
|
xml.navLabel { xml.text chapter.title }
|
26
26
|
xml.content :src => chapter.filename
|
data/lib/rpub/version.rb
CHANGED
data/rpub.gemspec
CHANGED
@@ -16,10 +16,9 @@ files into an eBook in ePub format. It provides several related functions to
|
|
16
16
|
make working with ePub files a little easier:
|
17
17
|
|
18
18
|
* Generation of table of contents
|
19
|
-
* Tracking of references to tables or figures
|
20
|
-
* Validation of output file
|
21
19
|
* Packaging your eBook in an archive with additional README file
|
22
|
-
*
|
20
|
+
* Embedding fonts
|
21
|
+
* Easy previewing as you write
|
23
22
|
EOS
|
24
23
|
|
25
24
|
# Files
|
@@ -42,6 +41,7 @@ EOS
|
|
42
41
|
s.add_runtime_dependency 'rubyzip'
|
43
42
|
s.add_runtime_dependency 'builder'
|
44
43
|
s.add_development_dependency 'yard'
|
44
|
+
s.add_development_dependency 'nokogiri'
|
45
45
|
s.add_development_dependency 'rspec'
|
46
46
|
s.add_development_dependency 'rake'
|
47
47
|
s.add_development_dependency 'guard'
|
data/spec/rpub/book_spec.rb
CHANGED
@@ -34,6 +34,24 @@ describe Rpub::Book do
|
|
34
34
|
end
|
35
35
|
end
|
36
36
|
|
37
|
+
describe '#has_fonts?' do
|
38
|
+
it 'should not have a font without a config key' do
|
39
|
+
described_class.new(nil, {}).should_not have_fonts
|
40
|
+
end
|
41
|
+
|
42
|
+
it 'should not have a font with a config key that is false' do
|
43
|
+
described_class.new(nil, { 'fonts' => false }).should_not have_fonts
|
44
|
+
end
|
45
|
+
|
46
|
+
it 'should not have a font with a config key that is empty' do
|
47
|
+
described_class.new(nil, { 'fonts' => [] }).should_not have_fonts
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'should have a font with a non-empty config key' do
|
51
|
+
described_class.new(nil, { 'fonts' => ['foo']}).should have_fonts
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
37
55
|
describe '#uid' do
|
38
56
|
it 'should change when chapters change' do
|
39
57
|
Rpub::Book.new('bar').add_chapter('foo').should_not == subject.uid
|
@@ -44,6 +62,45 @@ describe Rpub::Book do
|
|
44
62
|
end
|
45
63
|
end
|
46
64
|
|
65
|
+
describe '#outline' do
|
66
|
+
it 'should return empty array when there are no chapters' do
|
67
|
+
subject.outline.should be_empty
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should return combination of all chapter outlines with filename' do
|
71
|
+
subject << '# foo' << '# bar'
|
72
|
+
subject.outline.should have(2).elements
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe '#has_cover?' do
|
77
|
+
it 'should not have a cover without a config key' do
|
78
|
+
described_class.new(nil, {}).should_not have_cover
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should not have a cover with a config key that is false' do
|
82
|
+
described_class.new(nil, { 'cover_image' => false }).should_not have_cover
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should have a cover with a config key' do
|
86
|
+
described_class.new(nil, { 'cover_image' => true}).should have_cover
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
describe '#has_toc?' do
|
91
|
+
it 'should not have a toc without a config key' do
|
92
|
+
described_class.new(nil, {}).should_not have_toc
|
93
|
+
end
|
94
|
+
|
95
|
+
it 'should not have a toc with a config key that is false' do
|
96
|
+
described_class.new(nil, { 'toc' => false }).should_not have_toc
|
97
|
+
end
|
98
|
+
|
99
|
+
it 'should have a toc with a config key' do
|
100
|
+
described_class.new(nil, { 'toc' => true }).should have_toc
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
47
104
|
describe '#images' do
|
48
105
|
before { subject << '' << '' << '' }
|
49
106
|
it { should have(2).images }
|
data/spec/rpub/chapter_spec.rb
CHANGED
@@ -21,14 +21,34 @@ describe Rpub::Chapter do
|
|
21
21
|
end
|
22
22
|
end
|
23
23
|
|
24
|
-
describe '#
|
25
|
-
its(:
|
24
|
+
describe '#xml_id' do
|
25
|
+
its(:xml_id) { should == 'chapter-1' }
|
26
26
|
end
|
27
27
|
|
28
28
|
describe '#filename' do
|
29
29
|
its(:filename) { should == 'chapter-1-untitled.html' }
|
30
30
|
end
|
31
31
|
|
32
|
+
describe '#outline' do
|
33
|
+
context 'when there are no headings' do
|
34
|
+
let(:subject) { described_class.new('foo', 1, 'document') }
|
35
|
+
its(:outline) { should have(0).elements }
|
36
|
+
its(:outline) { should be_empty }
|
37
|
+
end
|
38
|
+
|
39
|
+
context 'when there are headings' do
|
40
|
+
let(:subject) { described_class.new('# foo', 1, 'document') }
|
41
|
+
its(:outline) { should have(1).elements }
|
42
|
+
|
43
|
+
context 'a single heading entry' do
|
44
|
+
let(:subject) { described_class.new('# foo', 1, 'document').outline.first }
|
45
|
+
its(:level) { should == 1 }
|
46
|
+
its(:text) { should == 'foo' }
|
47
|
+
its(:html_id) { should == 'foo' }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
32
52
|
describe '#title' do
|
33
53
|
context 'without a suitable markdown title' do
|
34
54
|
its(:title) { should == 'untitled' }
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rpub::Epub::Container do
|
4
|
+
let(:subject) { described_class.new.render }
|
5
|
+
it { should have_xpath('/xmlns:container') }
|
6
|
+
it { should have_xpath('/xmlns:container[@version="1.0"]') }
|
7
|
+
it { should have_xpath('/xmlns:container/xmlns:rootfiles/xmlns:rootfile[@media-type="application/oebps-package+xml"]') }
|
8
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rpub::Epub::Content do
|
4
|
+
let(:book) do
|
5
|
+
double('book', {
|
6
|
+
:creator => 'anonymous',
|
7
|
+
:title => 'title',
|
8
|
+
:language => 'en',
|
9
|
+
:publisher => 'none',
|
10
|
+
:description => 'foo bar',
|
11
|
+
:subject => 'baz qux',
|
12
|
+
:rights => 'copyright',
|
13
|
+
:uid => 'abcd',
|
14
|
+
:has_cover? => false,
|
15
|
+
:has_fonts? => false,
|
16
|
+
:has_toc? => false,
|
17
|
+
:images => [],
|
18
|
+
:chapters => []
|
19
|
+
})
|
20
|
+
end
|
21
|
+
let(:subject) { described_class.new(book).render }
|
22
|
+
|
23
|
+
def self.it_should_have_metadata(name, value, options = {})
|
24
|
+
attr = options.inject('') do |str, (k, v)|
|
25
|
+
str << "[@#{k}=\"#{v}\"]"
|
26
|
+
end
|
27
|
+
it { should have_xpath(%Q{/xmlns:package/xmlns:metadata/dc:#{name}[text()="#{value}"]#{attr}}, 'dc' => 'http://purl.org/dc/elements/1.1/', 'xmlns' => 'http://www.idpf.org/2007/opf') }
|
28
|
+
end
|
29
|
+
|
30
|
+
context 'with an empty book' do
|
31
|
+
|
32
|
+
it { should have_xpath('/xmlns:package[@unique-identifier="BookId"][@version="2.0"]') }
|
33
|
+
it { should have_xpath('/xmlns:package[@unique-identifier="BookId"][@version="2.0"]') }
|
34
|
+
|
35
|
+
it_should_have_metadata 'title', "title"
|
36
|
+
it_should_have_metadata 'creator', "anonymous", 'xmlns:role' => 'aut'
|
37
|
+
it_should_have_metadata 'publisher', "none"
|
38
|
+
it_should_have_metadata 'subject', "baz qux"
|
39
|
+
it_should_have_metadata 'identifier', "abcd", :id => 'BookId'
|
40
|
+
it_should_have_metadata 'rights', "copyright"
|
41
|
+
it_should_have_metadata 'description', "foo bar"
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'when the book has a cover' do
|
45
|
+
before { book.stub! :has_cover? => true, :cover_image => 'foo.jpg' }
|
46
|
+
it { should have_xpath('/xmlns:package/xmlns:manifest/xmlns:item[@id="cover"][@href="cover.html"][@media-type="application/xhtml+xml"]') }
|
47
|
+
it { should have_xpath('/xmlns:package/xmlns:manifest/xmlns:item[@id="cover-image"][@href="foo.jpg"][@media-type="image/jpeg"]') }
|
48
|
+
it { should have_xpath('/xmlns:package/xmlns:metadata/xmlns:meta[@name="cover"][@content="cover-image"]') }
|
49
|
+
it { should have_xpath('/xmlns:package/xmlns:guide/xmlns:reference[@type="cover"][@title="Cover"][@href="cover.html"]') }
|
50
|
+
it { should have_xpath('/xmlns:package/xmlns:spine[@toc="ncx"]/xmlns:itemref[@idref="cover"][@linear="no"]') }
|
51
|
+
end
|
52
|
+
|
53
|
+
context 'when the book has embedded fonts' do
|
54
|
+
before { book.stub! :has_fonts? => true, :fonts => ['font.otf'] }
|
55
|
+
it { should have_xpath('/xmlns:package/xmlns:manifest/xmlns:item[@id="font.otf"][@href="font.otf"][@media-type="font/opentype"]') }
|
56
|
+
end
|
57
|
+
|
58
|
+
context 'when the book has a ToC' do
|
59
|
+
before { book.stub! :has_toc? => true }
|
60
|
+
it { should have_xpath('/xmlns:package/xmlns:manifest/xmlns:item[@id="toc"][@href="toc.html"][@media-type="application/xhtml+xml"]') }
|
61
|
+
end
|
62
|
+
|
63
|
+
context 'when the book has images' do
|
64
|
+
before { book.stub! :images => ['foo.png', 'bar.gif'] }
|
65
|
+
it { should have_xpath('/xmlns:package/xmlns:manifest/xmlns:item[@id="foo.png"][@href="foo.png"][@media-type="image/png"]') }
|
66
|
+
it { should have_xpath('/xmlns:package/xmlns:manifest/xmlns:item[@id="bar.gif"][@href="bar.gif"][@media-type="image/gif"]') }
|
67
|
+
end
|
68
|
+
|
69
|
+
context 'when the book has chapters' do
|
70
|
+
let(:chapter) { double('chapter', :filename => 'chapter.html', :id => 'chapter1') }
|
71
|
+
before { book.stub! :chapters => [chapter] }
|
72
|
+
it { should have_xpath('/xmlns:package/xmlns:manifest/xmlns:item[@id="chapter1"][@href="chapter.html"][@media-type="application/xhtml+xml"]') }
|
73
|
+
it { should have_xpath('/xmlns:package/xmlns:spine[@toc="ncx"]/xmlns:itemref[@idref="chapter1"]') }
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rpub::Epub::Cover do
|
4
|
+
let(:book) { double('book', :cover_image => 'cover.jpg', :title => 'title') }
|
5
|
+
let(:subject) { described_class.new(book).render }
|
6
|
+
|
7
|
+
it { should have_xpath('/xmlns:html/xmlns:head/xmlns:title[text()="Cover"]') }
|
8
|
+
it { should have_xpath('/xmlns:html/xmlns:body/xmlns:div/xmlns:img[@src="cover.jpg"][@alt="title"]') }
|
9
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rpub::Epub::HtmlToc do
|
4
|
+
let(:outline) { [] }
|
5
|
+
let(:book) { double('book', :outline => outline) }
|
6
|
+
let(:subject) { described_class.new(book).render }
|
7
|
+
|
8
|
+
it { should have_xpath('/div/h1[text()="Table of Contents"]') }
|
9
|
+
it { should have_xpath('/div/div[@class="toc"]') }
|
10
|
+
|
11
|
+
context 'without headings in the outline' do
|
12
|
+
it { should_not have_xpath('//a') }
|
13
|
+
end
|
14
|
+
|
15
|
+
context 'with heading in the outline' do
|
16
|
+
let(:outline) { [['foo.html', double('heading', :text => 'link', :html_id => 'bar', :level => 1)]] }
|
17
|
+
it { should have_xpath('/div/div/div[@class="level-1"]/a[@href="foo.html#bar"][text()="link"]') }
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Rpub::Epub::Toc do
|
4
|
+
let(:chapters) { [] }
|
5
|
+
let(:book) { double('book', :uid => 'foo', :title => 'title', :chapters => chapters) }
|
6
|
+
let(:subject) { described_class.new(book).render }
|
7
|
+
|
8
|
+
it { should have_xpath('/xmlns:ncx') }
|
9
|
+
it { should have_xpath('/xmlns:ncx/xmlns:head/xmlns:meta[@name="dtb:uid"][@content="foo"]') }
|
10
|
+
it { should have_xpath('/xmlns:ncx/xmlns:head/xmlns:meta[@name="dtb:depth"][@content="1"]') }
|
11
|
+
it { should have_xpath('/xmlns:ncx/xmlns:head/xmlns:meta[@name="dtb:totalPageCount"][@content="0"]') }
|
12
|
+
it { should have_xpath('/xmlns:ncx/xmlns:head/xmlns:meta[@name="dtb:maxPageNumber"][@content="0"]') }
|
13
|
+
it { should have_xpath('/xmlns:ncx/xmlns:docTitle/xmlns:text[text()="title"]') }
|
14
|
+
|
15
|
+
context 'without chapters' do
|
16
|
+
it { should_not have_xpath('/xmlns:ncx/xmlns:navMap/xmlns:navPoint') }
|
17
|
+
end
|
18
|
+
|
19
|
+
context 'with chapters' do
|
20
|
+
let(:chapters) { [double('chapter', :title => 'chapter title', :filename => 'filename', :id => 'id')] }
|
21
|
+
it { should have_xpath('/xmlns:ncx/xmlns:navMap/xmlns:navPoint[@id="id"]') }
|
22
|
+
it { should have_xpath('/xmlns:ncx/xmlns:navMap/xmlns:navPoint/xmlns:navLabel/xmlns:text[text()="chapter title"]') }
|
23
|
+
it { should have_xpath('/xmlns:ncx/xmlns:navMap/xmlns:navPoint/xmlns:content[@src="filename"]') }
|
24
|
+
end
|
25
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'rpub'
|
2
|
+
require 'nokogiri'
|
2
3
|
|
3
4
|
FIXTURES_DIRECTORY = File.expand_path('../fixtures', __FILE__)
|
4
5
|
|
@@ -19,3 +20,9 @@ RSpec::Matchers.define :create_file do |filename|
|
|
19
20
|
!before && after
|
20
21
|
end
|
21
22
|
end
|
23
|
+
|
24
|
+
RSpec::Matchers.define :have_xpath do |xpath, *args|
|
25
|
+
match do |xml|
|
26
|
+
Nokogiri::XML(xml).xpath(xpath, *args).any?
|
27
|
+
end
|
28
|
+
end
|
data/support/config.yml
ADDED
@@ -0,0 +1,14 @@
|
|
1
|
+
---
|
2
|
+
title: 'Untitled book'
|
3
|
+
description: 'No description'
|
4
|
+
creator: 'Anonymous'
|
5
|
+
publisher: 'Untitled publisher'
|
6
|
+
subject: 'General'
|
7
|
+
language: 'en'
|
8
|
+
rights: 'public comain'
|
9
|
+
version: '0.0.0'
|
10
|
+
toc: true
|
11
|
+
ignore:
|
12
|
+
- README.md
|
13
|
+
package:
|
14
|
+
- README.md
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rpub
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-04-
|
12
|
+
date: 2012-04-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: typogruby
|
@@ -91,6 +91,22 @@ dependencies:
|
|
91
91
|
- - ! '>='
|
92
92
|
- !ruby/object:Gem::Version
|
93
93
|
version: '0'
|
94
|
+
- !ruby/object:Gem::Dependency
|
95
|
+
name: nokogiri
|
96
|
+
requirement: !ruby/object:Gem::Requirement
|
97
|
+
none: false
|
98
|
+
requirements:
|
99
|
+
- - ! '>='
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '0'
|
102
|
+
type: :development
|
103
|
+
prerelease: false
|
104
|
+
version_requirements: !ruby/object:Gem::Requirement
|
105
|
+
none: false
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
94
110
|
- !ruby/object:Gem::Dependency
|
95
111
|
name: rspec
|
96
112
|
requirement: !ruby/object:Gem::Requirement
|
@@ -224,6 +240,7 @@ files:
|
|
224
240
|
- lib/rpub/commands/base.rb
|
225
241
|
- lib/rpub/commands/clean.rb
|
226
242
|
- lib/rpub/commands/compile.rb
|
243
|
+
- lib/rpub/commands/generate.rb
|
227
244
|
- lib/rpub/commands/help.rb
|
228
245
|
- lib/rpub/commands/main.rb
|
229
246
|
- lib/rpub/commands/package.rb
|
@@ -253,8 +270,14 @@ files:
|
|
253
270
|
- spec/rpub/commands/clean_spec.rb
|
254
271
|
- spec/rpub/commands/main_spec.rb
|
255
272
|
- spec/rpub/commands/preview_spec.rb
|
273
|
+
- spec/rpub/epub/container_spec.rb
|
274
|
+
- spec/rpub/epub/content_spec.rb
|
275
|
+
- spec/rpub/epub/cover_spec.rb
|
276
|
+
- spec/rpub/epub/html_toc_spec.rb
|
277
|
+
- spec/rpub/epub/toc_spec.rb
|
256
278
|
- spec/rpub_spec.rb
|
257
279
|
- spec/spec_helper.rb
|
280
|
+
- support/config.yml
|
258
281
|
- support/layout.html
|
259
282
|
- support/styles.css
|
260
283
|
homepage: http://avdgaag.github.com/rpub
|
@@ -272,7 +295,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
272
295
|
version: '0'
|
273
296
|
segments:
|
274
297
|
- 0
|
275
|
-
hash:
|
298
|
+
hash: 976501169507100538
|
276
299
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
277
300
|
none: false
|
278
301
|
requirements:
|
@@ -281,7 +304,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
281
304
|
version: '0'
|
282
305
|
segments:
|
283
306
|
- 0
|
284
|
-
hash:
|
307
|
+
hash: 976501169507100538
|
285
308
|
requirements: []
|
286
309
|
rubyforge_project:
|
287
310
|
rubygems_version: 1.8.22
|
@@ -290,8 +313,8 @@ specification_version: 3
|
|
290
313
|
summary: ! 'rPub is a command-line tool that generates a collection of plain text
|
291
314
|
input files into an eBook in ePub format. It provides several related functions
|
292
315
|
to make working with ePub files a little easier: * Generation of table of contents
|
293
|
-
*
|
294
|
-
|
316
|
+
* Packaging your eBook in an archive with additional README file * Embedding fonts
|
317
|
+
* Easy previewing as you write'
|
295
318
|
test_files:
|
296
319
|
- spec/fixtures/clean/config.yml
|
297
320
|
- spec/fixtures/clean/example.epub
|
@@ -305,6 +328,11 @@ test_files:
|
|
305
328
|
- spec/rpub/commands/clean_spec.rb
|
306
329
|
- spec/rpub/commands/main_spec.rb
|
307
330
|
- spec/rpub/commands/preview_spec.rb
|
331
|
+
- spec/rpub/epub/container_spec.rb
|
332
|
+
- spec/rpub/epub/content_spec.rb
|
333
|
+
- spec/rpub/epub/cover_spec.rb
|
334
|
+
- spec/rpub/epub/html_toc_spec.rb
|
335
|
+
- spec/rpub/epub/toc_spec.rb
|
308
336
|
- spec/rpub_spec.rb
|
309
337
|
- spec/spec_helper.rb
|
310
338
|
has_rdoc:
|