rpub 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -1
  3. data/.rspec +0 -1
  4. data/.travis.yml +3 -4
  5. data/.yardopts +1 -0
  6. data/Gemfile +1 -1
  7. data/Gemfile.lock +70 -46
  8. data/Guardfile +6 -0
  9. data/HISTORY.md +10 -0
  10. data/README.md +1 -1
  11. data/Rakefile +7 -2
  12. data/bin/rpub +64 -1
  13. data/doc/.gitignore +8 -0
  14. data/doc/api/Rpub.html +339 -0
  15. data/doc/api/Rpub/Book.html +983 -0
  16. data/doc/api/Rpub/Chapter.html +737 -0
  17. data/doc/api/Rpub/Command.html +356 -0
  18. data/doc/api/Rpub/Commands.html +117 -0
  19. data/doc/api/Rpub/Commands/Clean.html +216 -0
  20. data/doc/api/Rpub/Commands/Compile.html +218 -0
  21. data/doc/api/Rpub/Commands/Generate.html +242 -0
  22. data/doc/api/Rpub/Commands/Package.html +230 -0
  23. data/doc/api/Rpub/Commands/Preview.html +216 -0
  24. data/doc/api/Rpub/Commands/Stats.html +226 -0
  25. data/doc/api/Rpub/Compressor.html +687 -0
  26. data/doc/api/Rpub/Context.html +553 -0
  27. data/doc/api/Rpub/Document.html +647 -0
  28. data/doc/api/Rpub/Document/OutlineElement.html +388 -0
  29. data/doc/api/Rpub/Epub.html +465 -0
  30. data/doc/api/Rpub/Epub/Container.html +224 -0
  31. data/doc/api/Rpub/Epub/Content.html +344 -0
  32. data/doc/api/Rpub/Epub/Cover.html +236 -0
  33. data/doc/api/Rpub/Epub/HtmlToc.html +240 -0
  34. data/doc/api/Rpub/Epub/Toc.html +244 -0
  35. data/doc/api/Rpub/FilesystemSource.html +544 -0
  36. data/doc/api/Rpub/HashDelegation.html +281 -0
  37. data/doc/api/Rpub/HashDelegation/ClassMethods.html +175 -0
  38. data/doc/api/Rpub/MediaType.html +192 -0
  39. data/doc/api/Rpub/Preview.html +464 -0
  40. data/doc/api/Rpub/XmlFile.html +457 -0
  41. data/doc/api/_index.html +405 -0
  42. data/doc/api/class_list.html +54 -0
  43. data/doc/api/css/common.css +1 -0
  44. data/doc/api/css/full_list.css +57 -0
  45. data/doc/api/css/style.css +339 -0
  46. data/doc/api/file.HISTORY.html +131 -0
  47. data/doc/api/file.LICENSE.html +92 -0
  48. data/doc/api/file.README.html +337 -0
  49. data/doc/api/file_list.html +62 -0
  50. data/doc/api/frames.html +26 -0
  51. data/doc/api/index.html +337 -0
  52. data/doc/api/js/app.js +219 -0
  53. data/doc/api/js/full_list.js +178 -0
  54. data/doc/api/js/jquery.js +4 -0
  55. data/doc/api/method_list.html +533 -0
  56. data/doc/api/top-level-namespace.html +112 -0
  57. data/doc/index.html +312 -0
  58. data/doc/javascripts/scale.fix.js +17 -0
  59. data/doc/params.json +1 -0
  60. data/doc/stylesheets/pygment_trac.css +69 -0
  61. data/doc/stylesheets/styles.css +255 -0
  62. data/features/clean.feature +31 -0
  63. data/features/compile.feature +49 -0
  64. data/features/embedded_assets.feature +51 -0
  65. data/features/generate.feature +63 -0
  66. data/features/layout.feature +41 -0
  67. data/features/package.feature +30 -0
  68. data/features/previews.feature +66 -0
  69. data/features/stats.feature +18 -0
  70. data/features/step_definitions/general_steps.rb +60 -0
  71. data/features/styles.feature +33 -0
  72. data/features/support/env.rb +7 -0
  73. data/features/table_of_contents.feature +35 -0
  74. data/lib/rpub.rb +28 -31
  75. data/lib/rpub/book.rb +9 -16
  76. data/lib/rpub/chapter.rb +4 -54
  77. data/lib/rpub/command.rb +32 -0
  78. data/lib/rpub/commands/clean.rb +4 -49
  79. data/lib/rpub/commands/compile.rb +4 -49
  80. data/lib/rpub/commands/generate.rb +10 -67
  81. data/lib/rpub/commands/package.rb +9 -37
  82. data/lib/rpub/commands/preview.rb +4 -54
  83. data/lib/rpub/commands/stats.rb +6 -10
  84. data/lib/rpub/compressor.rb +3 -3
  85. data/lib/rpub/context.rb +48 -0
  86. data/lib/rpub/document.rb +68 -0
  87. data/lib/rpub/epub.rb +8 -7
  88. data/lib/rpub/epub/content.rb +9 -30
  89. data/lib/rpub/epub/cover.rb +1 -8
  90. data/lib/rpub/epub/html_toc.rb +4 -9
  91. data/lib/rpub/epub/toc.rb +16 -13
  92. data/lib/rpub/filesystem_source.rb +47 -0
  93. data/lib/rpub/media_type.rb +16 -0
  94. data/lib/rpub/preview.rb +29 -0
  95. data/lib/rpub/version.rb +1 -1
  96. data/lib/rpub/xml_file.rb +4 -1
  97. data/rpub.gemspec +6 -7
  98. data/spec/rpub/book_spec.rb +45 -45
  99. data/spec/rpub/chapter_spec.rb +87 -22
  100. data/spec/rpub/epub/container_spec.rb +3 -5
  101. data/spec/rpub/epub/content_spec.rb +62 -41
  102. data/spec/rpub/epub/cover_spec.rb +3 -5
  103. data/spec/rpub/epub/html_toc_spec.rb +8 -8
  104. data/spec/rpub/epub/toc_spec.rb +20 -22
  105. data/spec/rpub_spec.rb +1 -3
  106. data/spec/spec_helper.rb +28 -0
  107. data/support/config.yml +1 -0
  108. data/support/styles.css +3 -3
  109. metadata +131 -120
  110. data/lib/rpub/commander.rb +0 -23
  111. data/lib/rpub/commands/base.rb +0 -33
  112. data/lib/rpub/commands/help.rb +0 -37
  113. data/lib/rpub/commands/main.rb +0 -45
  114. data/lib/rpub/compilation_helpers.rb +0 -73
  115. data/lib/rpub/subclass_tracker.rb +0 -53
  116. data/spec/rpub/commands/clean_spec.rb +0 -46
  117. data/spec/rpub/commands/generate_spec.rb +0 -52
  118. data/spec/rpub/commands/main_spec.rb +0 -26
  119. data/spec/rpub/commands/package_spec.rb +0 -33
  120. 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
@@ -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', Content.new(book)
13
- target.compress_file 'OEBPS/toc.ncx', Toc.new(book)
14
- target.compress_file 'OEBPS/styles.css', styles
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), File.read(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), File.read(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), File.read(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(File.read(book.layout)).result(binding)
40
+ ERB.new(read(book.layout)).result(binding)
40
41
  end
41
42
  end
42
43
  end
@@ -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
@@ -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
@@ -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
- xml.div :class => "level-#{heading.level}" do
18
- xml.a heading.text, :href => [filename, heading.html_id].join('#')
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
@@ -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 => @max_level
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
- @play_order += 1
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] <= @max_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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Rpub
2
- VERSION = '0.4.0'
2
+ VERSION = '0.5.0'
3
3
  end
@@ -3,8 +3,11 @@ module Rpub
3
3
  # @return [Builder::XmlMarkup]
4
4
  attr_reader :xml
5
5
 
6
- def initialize
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
@@ -1,5 +1,5 @@
1
1
  # -*- encoding: utf-8 -*-
2
- require './lib/rpub/version'
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 'typogruby'
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
 
@@ -1,115 +1,115 @@
1
- require 'spec_helper'
2
-
3
1
  describe Rpub::Book do
4
- let(:subject) { described_class.new('some_file', 'title' => 'My "Awesome" Title!', 'version' => '1.0.3') }
5
-
6
- it 'should start with an empty configuration' do
7
- described_class.new('some_file').config.should == {}
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
- before { subject.add_chapter 'foo' }
16
-
17
- it { should have(1).chapters }
8
+ it 'has 1 chapter' do
9
+ expect(subject.chapters.size).to eq(1)
10
+ end
18
11
 
19
- it { should be_kind_of(Enumerable) }
12
+ it { is_expected.to be_kind_of(Enumerable) }
20
13
 
21
- it 'should start with no chapters' do
22
- described_class.new('foo').should have(0).chapters
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.should have(3).chapters
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.should be_true
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
- described_class.new(nil, {}).should_not have_fonts
40
- end
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
- described_class.new(nil, nil, ['foo']).should have_fonts
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
- Rpub::Book.new('bar').add_chapter('foo').should_not == subject.uid
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
- Rpub::Book.new('bar', 'baz' => 'qux').should_not == subject.uid
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.should be_empty
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.should have(2).elements
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
- described_class.new(nil, {}).should_not have_cover
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
- described_class.new(nil, { 'cover_image' => false }).should_not have_cover
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
- described_class.new(nil, { 'cover_image' => true}).should have_cover
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
- described_class.new(nil, {}).should_not have_toc
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
- described_class.new(nil, { 'toc' => false }).should_not have_toc
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
- described_class.new(nil, { 'toc' => true }).should have_toc
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 << '![foo](bar)' << '![baz](qux)' << '![bla](qux)' }
102
- it { should have(2).images }
103
- its(:images) { should include('bar') }
104
- its(:images) { should include('qux') }
105
- end
106
106
 
107
- describe '#outline' do
108
- before { subject << '# foo' << '## bar' }
109
- its(:outline) { should have(2).elements }
110
- it 'should combine chapter outlines' do
111
- subject.outline.first[0].should == 'chapter-0-foo.html'
112
- subject.outline.first[1][0].text.should == 'foo'
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