epub_tools 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 +4 -4
- data/.github/workflows/ci.yml +3 -0
- data/.gitignore +1 -1
- data/.rubocop.yml +10 -17
- data/.tool-versions +1 -0
- data/CLAUDE.md +124 -0
- data/Gemfile +4 -4
- data/Gemfile.lock +44 -37
- data/README.md +10 -6
- data/Rakefile +2 -0
- data/bin/epub-tools +2 -0
- data/epub_tools.gemspec +3 -1
- data/lib/epub_tools/add_chapters.rb +48 -30
- data/lib/epub_tools/chapter_validator.rb +40 -0
- data/lib/epub_tools/cli/command_options_configurator.rb +115 -0
- data/lib/epub_tools/cli/command_registry.rb +2 -0
- data/lib/epub_tools/cli/option_builder.rb +5 -3
- data/lib/epub_tools/cli/runner.rb +59 -110
- data/lib/epub_tools/cli.rb +16 -29
- data/lib/epub_tools/compile_book.rb +48 -65
- data/lib/epub_tools/compile_workspace.rb +40 -0
- data/lib/epub_tools/epub_configuration.rb +33 -0
- data/lib/epub_tools/epub_file_writer.rb +57 -0
- data/lib/epub_tools/epub_initializer.rb +83 -162
- data/lib/epub_tools/epub_metadata_builder.rb +92 -0
- data/lib/epub_tools/loggable.rb +2 -0
- data/lib/epub_tools/pack_ebook.rb +28 -14
- data/lib/epub_tools/split_chapters.rb +42 -17
- data/lib/epub_tools/style_finder.rb +17 -6
- data/lib/epub_tools/unpack_ebook.rb +20 -10
- data/lib/epub_tools/version.rb +3 -1
- data/lib/epub_tools/xhtml_cleaner.rb +1 -0
- data/lib/epub_tools/xhtml_extractor.rb +20 -10
- data/lib/epub_tools/xhtml_generator.rb +71 -0
- data/lib/epub_tools.rb +2 -0
- data/test/add_chapters_test.rb +50 -26
- data/test/chapter_validator_test.rb +47 -0
- data/test/cli/command_registry_test.rb +2 -0
- data/test/cli/option_builder_test.rb +24 -14
- data/test/cli/runner_test.rb +15 -15
- data/test/cli_commands_test.rb +3 -1
- data/test/cli_test.rb +2 -0
- data/test/cli_version_test.rb +2 -0
- data/test/compile_book_test.rb +17 -102
- data/test/compile_workspace_test.rb +55 -0
- data/test/epub_initializer_test.rb +55 -27
- data/test/pack_ebook_test.rb +33 -9
- data/test/split_chapters_test.rb +27 -7
- data/test/style_finder_test.rb +2 -0
- data/test/test_helper.rb +2 -0
- data/test/unpack_ebook_test.rb +45 -20
- data/test/xhtml_cleaner_test.rb +2 -0
- data/test/xhtml_extractor_test.rb +3 -1
- metadata +15 -5
- data/.ruby-version +0 -1
data/test/compile_book_test.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative 'test_helper'
|
|
2
4
|
require_relative '../lib/epub_tools/compile_book'
|
|
3
5
|
|
|
@@ -11,7 +13,7 @@ class CompileBookTest < Minitest::Test
|
|
|
11
13
|
end
|
|
12
14
|
|
|
13
15
|
def teardown
|
|
14
|
-
FileUtils.
|
|
16
|
+
FileUtils.rm_rf(@tmp)
|
|
15
17
|
end
|
|
16
18
|
|
|
17
19
|
def test_default_output_file
|
|
@@ -67,39 +69,6 @@ class CompileBookTest < Minitest::Test
|
|
|
67
69
|
refute cb.verbose
|
|
68
70
|
end
|
|
69
71
|
|
|
70
|
-
def test_clean_build_dir_removes_directory
|
|
71
|
-
build = File.join(@tmp, 'build')
|
|
72
|
-
FileUtils.mkdir_p(build)
|
|
73
|
-
File.write(File.join(build, 'foo'), 'bar')
|
|
74
|
-
cb = EpubTools::CompileBook.new(
|
|
75
|
-
title: @title,
|
|
76
|
-
author: @author,
|
|
77
|
-
source_dir: @source,
|
|
78
|
-
build_dir: build
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
assert Dir.exist?(build)
|
|
82
|
-
cb.send(:clean_build_dir)
|
|
83
|
-
|
|
84
|
-
refute Dir.exist?(build)
|
|
85
|
-
end
|
|
86
|
-
|
|
87
|
-
def test_prepare_dirs_creates_xhtml_and_chapters_directories
|
|
88
|
-
build = File.join(@tmp, 'build')
|
|
89
|
-
cb = EpubTools::CompileBook.new(
|
|
90
|
-
title: @title,
|
|
91
|
-
author: @author,
|
|
92
|
-
source_dir: @source,
|
|
93
|
-
build_dir: build
|
|
94
|
-
)
|
|
95
|
-
cb.send(:prepare_dirs)
|
|
96
|
-
xhtml_dir = cb.send(:xhtml_dir)
|
|
97
|
-
chapters_dir = cb.send(:chapters_dir)
|
|
98
|
-
|
|
99
|
-
assert Dir.exist?(xhtml_dir)
|
|
100
|
-
assert Dir.exist?(chapters_dir)
|
|
101
|
-
end
|
|
102
|
-
|
|
103
72
|
def test_log_outputs_when_verbose
|
|
104
73
|
cb = EpubTools::CompileBook.new(
|
|
105
74
|
title: @title,
|
|
@@ -122,81 +91,27 @@ class CompileBookTest < Minitest::Test
|
|
|
122
91
|
assert_silent { cb.send(:log, 'hello') }
|
|
123
92
|
end
|
|
124
93
|
|
|
125
|
-
def
|
|
126
|
-
|
|
127
|
-
title: @title,
|
|
128
|
-
author: @author,
|
|
129
|
-
source_dir: @source,
|
|
130
|
-
build_dir: @tmp
|
|
131
|
-
)
|
|
132
|
-
FileUtils.mkdir_p(cb.send(:chapters_dir))
|
|
133
|
-
err = assert_raises(RuntimeError) { cb.send(:validate_sequence) }
|
|
134
|
-
assert_match(/No chapter files found/, err.message)
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
def test_validate_sequence_raises_on_missing_chapters
|
|
138
|
-
build = File.join(@tmp, 'build')
|
|
94
|
+
def test_run_completes_workflow
|
|
95
|
+
# Test that run method executes the complete workflow
|
|
139
96
|
cb = EpubTools::CompileBook.new(
|
|
140
97
|
title: @title,
|
|
141
98
|
author: @author,
|
|
142
99
|
source_dir: @source,
|
|
143
|
-
build_dir:
|
|
100
|
+
build_dir: @tmp,
|
|
101
|
+
output_file: 'test.epub'
|
|
144
102
|
)
|
|
145
|
-
chapters = cb.send(:chapters_dir)
|
|
146
|
-
FileUtils.mkdir_p(chapters)
|
|
147
|
-
File.write(File.join(chapters, 'chap_1.xhtml'), '')
|
|
148
|
-
File.write(File.join(chapters, 'chap_3.xhtml'), '')
|
|
149
|
-
err = assert_raises(RuntimeError) { cb.send(:validate_sequence) }
|
|
150
|
-
assert_match(/Missing chapter numbers: 2/, err.message)
|
|
151
|
-
end
|
|
152
103
|
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
cb
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
)
|
|
161
|
-
chapters = cb.send(:chapters_dir)
|
|
162
|
-
FileUtils.mkdir_p(chapters)
|
|
163
|
-
File.write(File.join(chapters, 'chap_1.xhtml'), '')
|
|
164
|
-
File.write(File.join(chapters, 'chap_2.xhtml'), '')
|
|
165
|
-
File.write(File.join(chapters, 'chap_3.xhtml'), '')
|
|
104
|
+
# Mock the workflow methods to avoid complex file setup
|
|
105
|
+
def cb.extract_xhtmls; end
|
|
106
|
+
def cb.split_xhtmls; end
|
|
107
|
+
def cb.validate_chapters; end
|
|
108
|
+
def cb.initialize_epub; end
|
|
109
|
+
def cb.add_chapters; end
|
|
110
|
+
def cb.pack_epub; end
|
|
166
111
|
|
|
167
|
-
|
|
168
|
-
|
|
112
|
+
# Should complete without error
|
|
113
|
+
result = cb.run
|
|
169
114
|
|
|
170
|
-
|
|
171
|
-
cb = EpubTools::CompileBook.new(
|
|
172
|
-
title: @title,
|
|
173
|
-
author: @author,
|
|
174
|
-
source_dir: @source,
|
|
175
|
-
build_dir: @tmp,
|
|
176
|
-
output_file: 'o.epub'
|
|
177
|
-
)
|
|
178
|
-
seq = []
|
|
179
|
-
cb.define_singleton_method(:clean_build_dir) { seq << :clean }
|
|
180
|
-
cb.define_singleton_method(:prepare_dirs) { seq << :prepare }
|
|
181
|
-
cb.define_singleton_method(:extract_xhtmls) { seq << :extract }
|
|
182
|
-
cb.define_singleton_method(:split_xhtmls) { seq << :split }
|
|
183
|
-
cb.define_singleton_method(:validate_sequence) { seq << :validate }
|
|
184
|
-
cb.define_singleton_method(:initialize_epub) { seq << :init }
|
|
185
|
-
cb.define_singleton_method(:add_chapters) { seq << :add }
|
|
186
|
-
cb.define_singleton_method(:pack_epub) { seq << :pack }
|
|
187
|
-
cb.define_singleton_method(:log) { |msg| seq << [:log, msg] }
|
|
188
|
-
cb.run
|
|
189
|
-
expected = [
|
|
190
|
-
:clean, :prepare, :extract, :split,
|
|
191
|
-
:validate, :init, :add, :pack,
|
|
192
|
-
[:log, /Done\. Output EPUB: .*o\.epub/],
|
|
193
|
-
:clean
|
|
194
|
-
]
|
|
195
|
-
|
|
196
|
-
assert_equal expected[0..7], seq[0..7]
|
|
197
|
-
assert_kind_of Array, seq[8]
|
|
198
|
-
assert_equal :log, seq[8][0]
|
|
199
|
-
assert_match expected[8][1], seq[8][1]
|
|
200
|
-
assert_equal expected[9], seq[9]
|
|
115
|
+
assert_equal 'test.epub', result
|
|
201
116
|
end
|
|
202
117
|
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'test_helper'
|
|
4
|
+
require_relative '../lib/epub_tools/compile_workspace'
|
|
5
|
+
|
|
6
|
+
class CompileWorkspaceTest < Minitest::Test
|
|
7
|
+
def setup
|
|
8
|
+
@tmp = Dir.mktmpdir
|
|
9
|
+
@workspace = EpubTools::CompileWorkspace.new(@tmp)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def teardown
|
|
13
|
+
FileUtils.rm_rf(@tmp)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def test_initialize_sets_build_dir
|
|
17
|
+
assert_equal @tmp, @workspace.build_dir
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def test_prepare_directories_creates_required_dirs
|
|
21
|
+
@workspace.prepare_directories
|
|
22
|
+
|
|
23
|
+
assert Dir.exist?(@workspace.xhtml_dir)
|
|
24
|
+
assert Dir.exist?(@workspace.chapters_dir)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_directory_paths_are_correct
|
|
28
|
+
assert_equal File.join(@tmp, 'xhtml'), @workspace.xhtml_dir
|
|
29
|
+
assert_equal File.join(@tmp, 'chapters'), @workspace.chapters_dir
|
|
30
|
+
assert_equal File.join(@tmp, 'epub'), @workspace.epub_dir
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_directory_paths_are_memoized
|
|
34
|
+
# First call creates the path
|
|
35
|
+
path1 = @workspace.xhtml_dir
|
|
36
|
+
# Second call should return the same object
|
|
37
|
+
path2 = @workspace.xhtml_dir
|
|
38
|
+
|
|
39
|
+
assert_equal path1, path2
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def test_clean_removes_build_directory
|
|
43
|
+
# Create the directory and some content
|
|
44
|
+
FileUtils.mkdir_p(@tmp)
|
|
45
|
+
test_file = File.join(@tmp, 'test.txt')
|
|
46
|
+
File.write(test_file, 'test content')
|
|
47
|
+
|
|
48
|
+
assert Dir.exist?(@tmp)
|
|
49
|
+
assert_path_exists test_file
|
|
50
|
+
|
|
51
|
+
@workspace.clean
|
|
52
|
+
|
|
53
|
+
refute Dir.exist?(@tmp)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative 'test_helper'
|
|
2
4
|
require_relative '../lib/epub_tools/epub_initializer'
|
|
3
5
|
|
|
@@ -16,34 +18,10 @@ class EpubInitializerTest < Minitest::Test
|
|
|
16
18
|
def test_run_creates_basic_structure
|
|
17
19
|
result = EpubTools::EpubInitializer.new(title: @title, author: @author, destination: @dest).run
|
|
18
20
|
|
|
19
|
-
# Check return value is the destination directory
|
|
20
21
|
assert_equal @dest, result
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
assert File.directory?(File.join(@dest, 'META-INF'))
|
|
25
|
-
assert File.directory?(File.join(@dest, 'OEBPS'))
|
|
26
|
-
# Check files
|
|
27
|
-
mimetype = File.join(@dest, 'mimetype')
|
|
28
|
-
|
|
29
|
-
assert_path_exists mimetype
|
|
30
|
-
assert_equal 'application/epub+zip', File.read(mimetype)
|
|
31
|
-
assert_path_exists File.join(@dest, 'META-INF', 'container.xml')
|
|
32
|
-
assert_path_exists File.join(@dest, 'OEBPS', 'title.xhtml')
|
|
33
|
-
assert_path_exists File.join(@dest, 'OEBPS', 'nav.xhtml')
|
|
34
|
-
assert_path_exists File.join(@dest, 'OEBPS', 'package.opf')
|
|
35
|
-
assert_path_exists File.join(@dest, 'OEBPS', 'style.css')
|
|
36
|
-
# Check content of title.xhtml
|
|
37
|
-
title_page = File.read(File.join(@dest, 'OEBPS', 'title.xhtml'))
|
|
38
|
-
|
|
39
|
-
assert_includes title_page, "<h1 class=\"title\">#{@title}</h1>"
|
|
40
|
-
assert_includes title_page, "<p class=\"author\">by #{@author}</p>"
|
|
41
|
-
# Check package.opf metadata
|
|
42
|
-
opf = File.read(File.join(@dest, 'OEBPS', 'package.opf'))
|
|
43
|
-
|
|
44
|
-
assert_includes opf, "<dc:title>#{@title}</dc:title>"
|
|
45
|
-
assert_includes opf, "<dc:creator>#{@author}</dc:creator>"
|
|
46
|
-
refute_includes opf, 'cover.xhtml'
|
|
22
|
+
verify_directory_structure
|
|
23
|
+
verify_required_files
|
|
24
|
+
verify_file_contents
|
|
47
25
|
end
|
|
48
26
|
|
|
49
27
|
def test_run_with_cover_image
|
|
@@ -136,4 +114,54 @@ class EpubInitializerTest < Minitest::Test
|
|
|
136
114
|
|
|
137
115
|
refute_includes opf, 'cover-image'
|
|
138
116
|
end
|
|
117
|
+
|
|
118
|
+
private
|
|
119
|
+
|
|
120
|
+
def verify_directory_structure
|
|
121
|
+
assert Dir.exist?(@dest)
|
|
122
|
+
assert File.directory?(File.join(@dest, 'META-INF'))
|
|
123
|
+
assert File.directory?(File.join(@dest, 'OEBPS'))
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def verify_required_files
|
|
127
|
+
required_files = [
|
|
128
|
+
'mimetype',
|
|
129
|
+
File.join('META-INF', 'container.xml'),
|
|
130
|
+
File.join('OEBPS', 'title.xhtml'),
|
|
131
|
+
File.join('OEBPS', 'nav.xhtml'),
|
|
132
|
+
File.join('OEBPS', 'package.opf'),
|
|
133
|
+
File.join('OEBPS', 'style.css')
|
|
134
|
+
]
|
|
135
|
+
|
|
136
|
+
required_files.each do |file|
|
|
137
|
+
assert_path_exists File.join(@dest, file)
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def verify_file_contents
|
|
142
|
+
verify_mimetype_content
|
|
143
|
+
verify_title_page_content
|
|
144
|
+
verify_opf_metadata
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def verify_mimetype_content
|
|
148
|
+
mimetype = File.read(File.join(@dest, 'mimetype'))
|
|
149
|
+
|
|
150
|
+
assert_equal 'application/epub+zip', mimetype
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def verify_title_page_content
|
|
154
|
+
title_page = File.read(File.join(@dest, 'OEBPS', 'title.xhtml'))
|
|
155
|
+
|
|
156
|
+
assert_includes title_page, "<h1 class=\"title\">#{@title}</h1>"
|
|
157
|
+
assert_includes title_page, "<p class=\"author\">by #{@author}</p>"
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
def verify_opf_metadata
|
|
161
|
+
opf = File.read(File.join(@dest, 'OEBPS', 'package.opf'))
|
|
162
|
+
|
|
163
|
+
assert_includes opf, "<dc:title>#{@title}</dc:title>"
|
|
164
|
+
assert_includes opf, "<dc:creator>#{@author}</dc:creator>"
|
|
165
|
+
refute_includes opf, 'cover.xhtml'
|
|
166
|
+
end
|
|
139
167
|
end
|
data/test/pack_ebook_test.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative 'test_helper'
|
|
2
4
|
require_relative '../lib/epub_tools/pack_ebook'
|
|
3
5
|
require 'zip'
|
|
@@ -14,37 +16,59 @@ class PackEbookTest < Minitest::Test
|
|
|
14
16
|
end
|
|
15
17
|
|
|
16
18
|
def test_run_creates_epub_with_expected_entries
|
|
17
|
-
|
|
19
|
+
create_minimal_epub_structure
|
|
20
|
+
output = File.join(@tmp, 'out.epub')
|
|
21
|
+
result = EpubTools::PackEbook.new(input_dir: @epub_dir, output_file: output).run
|
|
22
|
+
|
|
23
|
+
verify_return_value(output, result)
|
|
24
|
+
verify_epub_entries(output)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def create_minimal_epub_structure
|
|
18
30
|
File.write(File.join(@epub_dir, 'mimetype'), 'application/epub+zip')
|
|
19
31
|
FileUtils.mkdir_p(File.join(@epub_dir, 'META-INF'))
|
|
20
32
|
File.write(File.join(@epub_dir, 'META-INF', 'container.xml'), '<container/>')
|
|
21
33
|
FileUtils.mkdir_p(File.join(@epub_dir, 'OEBPS'))
|
|
22
34
|
File.write(File.join(@epub_dir, 'OEBPS', 'title.xhtml'), '<html/>')
|
|
35
|
+
end
|
|
23
36
|
|
|
24
|
-
|
|
25
|
-
|
|
37
|
+
def verify_return_value(expected_output, result)
|
|
38
|
+
assert_equal expected_output, result
|
|
39
|
+
assert_path_exists expected_output, 'Expected output EPUB to exist'
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def verify_epub_entries(output_file)
|
|
43
|
+
entries = extract_zip_entries(output_file)
|
|
44
|
+
verify_mimetype_entry(entries)
|
|
45
|
+
verify_expected_files(entries)
|
|
46
|
+
end
|
|
26
47
|
|
|
27
|
-
|
|
28
|
-
assert_equal output, result
|
|
29
|
-
assert_path_exists output, 'Expected output EPUB to exist'
|
|
48
|
+
def extract_zip_entries(output_file)
|
|
30
49
|
entries = []
|
|
31
|
-
Zip::File.open(
|
|
50
|
+
Zip::File.open(output_file) do |zip|
|
|
32
51
|
zip.each do |entry|
|
|
33
52
|
entries << { name: entry.name, compression: entry.compression_method }
|
|
34
53
|
end
|
|
35
54
|
end
|
|
55
|
+
entries
|
|
56
|
+
end
|
|
36
57
|
|
|
37
|
-
|
|
58
|
+
def verify_mimetype_entry(entries)
|
|
38
59
|
assert_equal 'mimetype', entries.first[:name]
|
|
39
60
|
assert_equal 0, entries.first[:compression]
|
|
61
|
+
end
|
|
40
62
|
|
|
41
|
-
|
|
63
|
+
def verify_expected_files(entries)
|
|
42
64
|
names = entries.map { |e| e[:name] }
|
|
43
65
|
|
|
44
66
|
assert_includes names, 'META-INF/container.xml'
|
|
45
67
|
assert_includes names, 'OEBPS/title.xhtml'
|
|
46
68
|
end
|
|
47
69
|
|
|
70
|
+
public
|
|
71
|
+
|
|
48
72
|
def test_missing_input_dir_raises_error
|
|
49
73
|
assert_raises(ArgumentError) do
|
|
50
74
|
EpubTools::PackEbook.new(input_dir: File.join(@tmp, 'nonexistent'), output_file: 'out.epub').run
|
data/test/split_chapters_test.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative 'test_helper'
|
|
2
4
|
require_relative '../lib/epub_tools/split_chapters'
|
|
3
5
|
|
|
@@ -30,10 +32,19 @@ class SplitChaptersTest < Minitest::Test
|
|
|
30
32
|
result = EpubTools::SplitChapters.new(input_file: @input, book_title: 'BookTitle', output_dir: @out,
|
|
31
33
|
output_prefix: 'chap').run
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
verify_return_value(result)
|
|
36
|
+
verify_generated_files_exist(result)
|
|
37
|
+
verify_chapter_contents
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def verify_return_value(result)
|
|
34
43
|
assert_instance_of Array, result
|
|
35
44
|
assert_equal 3, result.size
|
|
45
|
+
end
|
|
36
46
|
|
|
47
|
+
def verify_generated_files_exist(result)
|
|
37
48
|
expected_paths = [
|
|
38
49
|
File.join(@out, 'chap_0.xhtml'),
|
|
39
50
|
File.join(@out, 'chap_1.xhtml'),
|
|
@@ -47,25 +58,34 @@ class SplitChaptersTest < Minitest::Test
|
|
|
47
58
|
|
|
48
59
|
files = Dir.children(@out)
|
|
49
60
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
61
|
+
['chap_0.xhtml', 'chap_1.xhtml', 'chap_2.xhtml'].each do |file|
|
|
62
|
+
assert_includes files, file
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def verify_chapter_contents
|
|
67
|
+
verify_prologue_content
|
|
68
|
+
verify_chapter_one_content
|
|
69
|
+
verify_chapter_two_content
|
|
70
|
+
end
|
|
53
71
|
|
|
54
|
-
|
|
72
|
+
def verify_prologue_content
|
|
55
73
|
prologue = File.read(File.join(@out, 'chap_0.xhtml'))
|
|
56
74
|
|
|
57
75
|
assert_includes prologue, '<h1>Prologue</h1>'
|
|
58
76
|
assert_includes prologue, 'Intro text'
|
|
59
77
|
refute_includes prologue, 'Chapter 1'
|
|
78
|
+
end
|
|
60
79
|
|
|
61
|
-
|
|
80
|
+
def verify_chapter_one_content
|
|
62
81
|
ch1 = File.read(File.join(@out, 'chap_1.xhtml'))
|
|
63
82
|
|
|
64
83
|
assert_includes ch1, '<h1>Chapter 1</h1>'
|
|
65
84
|
assert_includes ch1, 'First paragraph'
|
|
66
85
|
refute_includes ch1, 'Chapter 2'
|
|
86
|
+
end
|
|
67
87
|
|
|
68
|
-
|
|
88
|
+
def verify_chapter_two_content
|
|
69
89
|
ch2 = File.read(File.join(@out, 'chap_2.xhtml'))
|
|
70
90
|
|
|
71
91
|
assert_includes ch2, '<h1>Chapter 2</h1>'
|
data/test/style_finder_test.rb
CHANGED
data/test/test_helper.rb
CHANGED
data/test/unpack_ebook_test.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative 'test_helper'
|
|
2
4
|
require_relative '../lib/epub_tools/unpack_ebook'
|
|
3
5
|
require 'zip'
|
|
@@ -5,37 +7,60 @@ require 'zip'
|
|
|
5
7
|
class UnpackEbookTest < Minitest::Test
|
|
6
8
|
def setup
|
|
7
9
|
@tmp = Dir.mktmpdir
|
|
8
|
-
# Build a minimal EPUB directory for zipping
|
|
9
10
|
@build_dir = File.join(@tmp, 'build')
|
|
11
|
+
@epub_file = File.join(@tmp, 'test.epub')
|
|
12
|
+
@dest_dir = File.join(@tmp, 'output')
|
|
13
|
+
|
|
14
|
+
create_epub_build_directory
|
|
15
|
+
create_zip_file_from_build_dir
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
private
|
|
19
|
+
|
|
20
|
+
def create_epub_build_directory
|
|
10
21
|
FileUtils.mkdir_p(File.join(@build_dir, 'META-INF'))
|
|
11
22
|
FileUtils.mkdir_p(File.join(@build_dir, 'OEBPS'))
|
|
23
|
+
|
|
24
|
+
write_epub_files
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def write_epub_files
|
|
12
28
|
File.write(File.join(@build_dir, 'mimetype'), 'application/epub+zip')
|
|
13
29
|
File.write(File.join(@build_dir, 'META-INF', 'container.xml'), '<container/>')
|
|
14
30
|
File.write(File.join(@build_dir, 'OEBPS', 'title.xhtml'), '<html/>')
|
|
31
|
+
end
|
|
15
32
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
# Add mimetype first, uncompressed
|
|
21
|
-
mime_src = File.join(@build_dir, 'mimetype')
|
|
22
|
-
zip.add_stored('mimetype', mime_src)
|
|
23
|
-
# Add directories and files
|
|
24
|
-
Dir.glob(File.join(@build_dir, '**', '*'), File::FNM_DOTMATCH).sort.each do |src_path|
|
|
25
|
-
rel_path = src_path.sub(%r{^#{Regexp.escape(@build_dir)}/?}, '')
|
|
26
|
-
next if rel_path.empty? || rel_path == 'mimetype'
|
|
27
|
-
|
|
28
|
-
if File.directory?(src_path)
|
|
29
|
-
zip.mkdir(rel_path)
|
|
30
|
-
else
|
|
31
|
-
zip.add(rel_path, src_path)
|
|
32
|
-
end
|
|
33
|
-
end
|
|
33
|
+
def create_zip_file_from_build_dir
|
|
34
|
+
Zip::File.open(@epub_file, create: true) do |zip|
|
|
35
|
+
add_mimetype_to_zip(zip)
|
|
36
|
+
add_remaining_files_to_zip(zip)
|
|
34
37
|
end
|
|
38
|
+
end
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
def add_mimetype_to_zip(zip)
|
|
41
|
+
mime_src = File.join(@build_dir, 'mimetype')
|
|
42
|
+
zip.add_stored('mimetype', mime_src)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def add_remaining_files_to_zip(zip)
|
|
46
|
+
Dir.glob(File.join(@build_dir, '**', '*'), File::FNM_DOTMATCH).sort.each do |src_path|
|
|
47
|
+
add_file_or_directory_to_zip(zip, src_path)
|
|
48
|
+
end
|
|
37
49
|
end
|
|
38
50
|
|
|
51
|
+
def add_file_or_directory_to_zip(zip, src_path)
|
|
52
|
+
rel_path = src_path.sub(%r{^#{Regexp.escape(@build_dir)}/?}, '')
|
|
53
|
+
return if rel_path.empty? || rel_path == 'mimetype'
|
|
54
|
+
|
|
55
|
+
if File.directory?(src_path)
|
|
56
|
+
zip.mkdir(rel_path)
|
|
57
|
+
else
|
|
58
|
+
zip.add(rel_path, src_path)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
public
|
|
63
|
+
|
|
39
64
|
def teardown
|
|
40
65
|
FileUtils.remove_entry(@tmp)
|
|
41
66
|
end
|
data/test/xhtml_cleaner_test.rb
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
1
3
|
require_relative 'test_helper'
|
|
2
4
|
require 'zip'
|
|
3
5
|
require_relative '../lib/epub_tools/xhtml_extractor'
|
|
@@ -9,7 +11,7 @@ class XHTMLExtractorTest < Minitest::Test
|
|
|
9
11
|
@tgt = File.join(@tmp, 'tgt')
|
|
10
12
|
Dir.mkdir(@src)
|
|
11
13
|
@file = File.join(@src, 'sample.epub')
|
|
12
|
-
Zip::File.open(@file,
|
|
14
|
+
Zip::File.open(@file, create: true) do |zip|
|
|
13
15
|
zip.get_output_stream('chapter1.xhtml') { |f| f.write '<html><body><p>One</p></body></html>' }
|
|
14
16
|
zip.get_output_stream('nav.xhtml') { |f| f.write '<html><body>Nav</body></html>' }
|
|
15
17
|
zip.get_output_stream('folder/ch2.xhtml') { |f| f.write '<html><body><p>Two</p></body></html>' }
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: epub_tools
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.5.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jaime Rodas
|
|
@@ -43,14 +43,14 @@ dependencies:
|
|
|
43
43
|
requirements:
|
|
44
44
|
- - "~>"
|
|
45
45
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '2
|
|
46
|
+
version: '3.2'
|
|
47
47
|
type: :runtime
|
|
48
48
|
prerelease: false
|
|
49
49
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
50
|
requirements:
|
|
51
51
|
- - "~>"
|
|
52
52
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '2
|
|
53
|
+
version: '3.2'
|
|
54
54
|
email:
|
|
55
55
|
- rodas@hey.com
|
|
56
56
|
executables:
|
|
@@ -63,7 +63,8 @@ files:
|
|
|
63
63
|
- ".gitignore"
|
|
64
64
|
- ".nova/Configuration.json"
|
|
65
65
|
- ".rubocop.yml"
|
|
66
|
-
- ".
|
|
66
|
+
- ".tool-versions"
|
|
67
|
+
- CLAUDE.md
|
|
67
68
|
- Gemfile
|
|
68
69
|
- Gemfile.lock
|
|
69
70
|
- LICENSE
|
|
@@ -73,12 +74,18 @@ files:
|
|
|
73
74
|
- epub_tools.gemspec
|
|
74
75
|
- lib/epub_tools.rb
|
|
75
76
|
- lib/epub_tools/add_chapters.rb
|
|
77
|
+
- lib/epub_tools/chapter_validator.rb
|
|
76
78
|
- lib/epub_tools/cli.rb
|
|
79
|
+
- lib/epub_tools/cli/command_options_configurator.rb
|
|
77
80
|
- lib/epub_tools/cli/command_registry.rb
|
|
78
81
|
- lib/epub_tools/cli/option_builder.rb
|
|
79
82
|
- lib/epub_tools/cli/runner.rb
|
|
80
83
|
- lib/epub_tools/compile_book.rb
|
|
84
|
+
- lib/epub_tools/compile_workspace.rb
|
|
85
|
+
- lib/epub_tools/epub_configuration.rb
|
|
86
|
+
- lib/epub_tools/epub_file_writer.rb
|
|
81
87
|
- lib/epub_tools/epub_initializer.rb
|
|
88
|
+
- lib/epub_tools/epub_metadata_builder.rb
|
|
82
89
|
- lib/epub_tools/loggable.rb
|
|
83
90
|
- lib/epub_tools/pack_ebook.rb
|
|
84
91
|
- lib/epub_tools/split_chapters.rb
|
|
@@ -87,8 +94,10 @@ files:
|
|
|
87
94
|
- lib/epub_tools/version.rb
|
|
88
95
|
- lib/epub_tools/xhtml_cleaner.rb
|
|
89
96
|
- lib/epub_tools/xhtml_extractor.rb
|
|
97
|
+
- lib/epub_tools/xhtml_generator.rb
|
|
90
98
|
- style.css
|
|
91
99
|
- test/add_chapters_test.rb
|
|
100
|
+
- test/chapter_validator_test.rb
|
|
92
101
|
- test/cli/command_registry_test.rb
|
|
93
102
|
- test/cli/option_builder_test.rb
|
|
94
103
|
- test/cli/runner_test.rb
|
|
@@ -96,6 +105,7 @@ files:
|
|
|
96
105
|
- test/cli_test.rb
|
|
97
106
|
- test/cli_version_test.rb
|
|
98
107
|
- test/compile_book_test.rb
|
|
108
|
+
- test/compile_workspace_test.rb
|
|
99
109
|
- test/epub_initializer_test.rb
|
|
100
110
|
- test/pack_ebook_test.rb
|
|
101
111
|
- test/split_chapters_test.rb
|
|
@@ -125,7 +135,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
125
135
|
- !ruby/object:Gem::Version
|
|
126
136
|
version: '0'
|
|
127
137
|
requirements: []
|
|
128
|
-
rubygems_version: 3.
|
|
138
|
+
rubygems_version: 3.7.0
|
|
129
139
|
specification_version: 4
|
|
130
140
|
summary: Tools to extract, split, and compile EPUB books
|
|
131
141
|
test_files: []
|
data/.ruby-version
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
3.4.3
|