epub_tools 0.5.0 → 0.9.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 -8
- data/.gitignore +0 -1
- data/.tool-versions +1 -1
- data/CLAUDE.md +20 -24
- data/Gemfile +5 -8
- data/Gemfile.lock +60 -54
- data/README.md +65 -25
- data/bin/epub-tools +1 -3
- data/epub_tools.gemspec +3 -4
- data/lib/epub_tools/add_chapters.rb +26 -14
- data/lib/epub_tools/append_book.rb +74 -0
- data/lib/epub_tools/book_builder.rb +114 -0
- data/lib/epub_tools/chapter_order.rb +33 -0
- data/lib/epub_tools/chapter_validator.rb +20 -7
- data/lib/epub_tools/cli.rb +121 -22
- data/lib/epub_tools/compile_book.rb +12 -126
- data/lib/epub_tools/epub_initializer.rb +93 -99
- data/lib/epub_tools/pack_ebook.rb +2 -14
- data/lib/epub_tools/pdf_converter.rb +113 -0
- data/lib/epub_tools/pdf_layout.rb +116 -0
- data/lib/epub_tools/set_cover.rb +116 -0
- data/lib/epub_tools/split_chapters.rb +35 -70
- data/lib/epub_tools/style_finder.rb +11 -40
- data/lib/epub_tools/unpack_ebook.rb +2 -20
- data/lib/epub_tools/version.rb +1 -1
- data/lib/epub_tools/xhtml_cleaner.rb +4 -7
- data/lib/epub_tools/xhtml_extractor.rb +1 -7
- data/lib/epub_tools.rb +4 -0
- data/test/add_chapters_test.rb +134 -0
- data/test/append_book_test.rb +127 -0
- data/test/chapter_validator_test.rb +39 -0
- data/test/cli_commands_test.rb +9 -0
- data/test/cli_test.rb +40 -3
- data/test/compile_book_test.rb +15 -2
- data/test/epub_initializer_test.rb +18 -0
- data/test/pdf_converter_test.rb +69 -0
- data/test/pdf_layout_test.rb +57 -0
- data/test/set_cover_test.rb +68 -0
- data/test/split_chapters_test.rb +84 -9
- data/test/style_finder_test.rb +3 -9
- data/test/test_helper.rb +1 -1
- data/test/unpack_ebook_test.rb +1 -1
- data/test/xhtml_cleaner_test.rb +1 -4
- metadata +17 -34
- data/lib/epub_tools/cli/command_options_configurator.rb +0 -115
- data/lib/epub_tools/cli/command_registry.rb +0 -49
- data/lib/epub_tools/cli/option_builder.rb +0 -166
- data/lib/epub_tools/cli/runner.rb +0 -113
- data/lib/epub_tools/compile_workspace.rb +0 -40
- data/lib/epub_tools/epub_configuration.rb +0 -33
- data/lib/epub_tools/epub_file_writer.rb +0 -57
- data/lib/epub_tools/epub_metadata_builder.rb +0 -92
- data/lib/epub_tools/xhtml_generator.rb +0 -71
- data/test/cli/command_registry_test.rb +0 -68
- data/test/cli/option_builder_test.rb +0 -183
- data/test/cli/runner_test.rb +0 -91
- data/test/compile_workspace_test.rb +0 -55
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.9.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jaime Rodas
|
|
@@ -15,42 +15,28 @@ dependencies:
|
|
|
15
15
|
requirements:
|
|
16
16
|
- - "~>"
|
|
17
17
|
- !ruby/object:Gem::Version
|
|
18
|
-
version: '1.
|
|
18
|
+
version: '1.19'
|
|
19
19
|
type: :runtime
|
|
20
20
|
prerelease: false
|
|
21
21
|
version_requirements: !ruby/object:Gem::Requirement
|
|
22
22
|
requirements:
|
|
23
23
|
- - "~>"
|
|
24
24
|
- !ruby/object:Gem::Version
|
|
25
|
-
version: '1.
|
|
26
|
-
- !ruby/object:Gem::Dependency
|
|
27
|
-
name: rake
|
|
28
|
-
requirement: !ruby/object:Gem::Requirement
|
|
29
|
-
requirements:
|
|
30
|
-
- - "~>"
|
|
31
|
-
- !ruby/object:Gem::Version
|
|
32
|
-
version: '13.2'
|
|
33
|
-
type: :runtime
|
|
34
|
-
prerelease: false
|
|
35
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
-
requirements:
|
|
37
|
-
- - "~>"
|
|
38
|
-
- !ruby/object:Gem::Version
|
|
39
|
-
version: '13.2'
|
|
25
|
+
version: '1.19'
|
|
40
26
|
- !ruby/object:Gem::Dependency
|
|
41
27
|
name: rubyzip
|
|
42
28
|
requirement: !ruby/object:Gem::Requirement
|
|
43
29
|
requirements:
|
|
44
30
|
- - "~>"
|
|
45
31
|
- !ruby/object:Gem::Version
|
|
46
|
-
version: '3.
|
|
32
|
+
version: '3.7'
|
|
47
33
|
type: :runtime
|
|
48
34
|
prerelease: false
|
|
49
35
|
version_requirements: !ruby/object:Gem::Requirement
|
|
50
36
|
requirements:
|
|
51
37
|
- - "~>"
|
|
52
38
|
- !ruby/object:Gem::Version
|
|
53
|
-
version: '3.
|
|
39
|
+
version: '3.7'
|
|
54
40
|
email:
|
|
55
41
|
- rodas@hey.com
|
|
56
42
|
executables:
|
|
@@ -74,40 +60,37 @@ files:
|
|
|
74
60
|
- epub_tools.gemspec
|
|
75
61
|
- lib/epub_tools.rb
|
|
76
62
|
- lib/epub_tools/add_chapters.rb
|
|
63
|
+
- lib/epub_tools/append_book.rb
|
|
64
|
+
- lib/epub_tools/book_builder.rb
|
|
65
|
+
- lib/epub_tools/chapter_order.rb
|
|
77
66
|
- lib/epub_tools/chapter_validator.rb
|
|
78
67
|
- lib/epub_tools/cli.rb
|
|
79
|
-
- lib/epub_tools/cli/command_options_configurator.rb
|
|
80
|
-
- lib/epub_tools/cli/command_registry.rb
|
|
81
|
-
- lib/epub_tools/cli/option_builder.rb
|
|
82
|
-
- lib/epub_tools/cli/runner.rb
|
|
83
68
|
- 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
|
|
87
69
|
- lib/epub_tools/epub_initializer.rb
|
|
88
|
-
- lib/epub_tools/epub_metadata_builder.rb
|
|
89
70
|
- lib/epub_tools/loggable.rb
|
|
90
71
|
- lib/epub_tools/pack_ebook.rb
|
|
72
|
+
- lib/epub_tools/pdf_converter.rb
|
|
73
|
+
- lib/epub_tools/pdf_layout.rb
|
|
74
|
+
- lib/epub_tools/set_cover.rb
|
|
91
75
|
- lib/epub_tools/split_chapters.rb
|
|
92
76
|
- lib/epub_tools/style_finder.rb
|
|
93
77
|
- lib/epub_tools/unpack_ebook.rb
|
|
94
78
|
- lib/epub_tools/version.rb
|
|
95
79
|
- lib/epub_tools/xhtml_cleaner.rb
|
|
96
80
|
- lib/epub_tools/xhtml_extractor.rb
|
|
97
|
-
- lib/epub_tools/xhtml_generator.rb
|
|
98
81
|
- style.css
|
|
99
82
|
- test/add_chapters_test.rb
|
|
83
|
+
- test/append_book_test.rb
|
|
100
84
|
- test/chapter_validator_test.rb
|
|
101
|
-
- test/cli/command_registry_test.rb
|
|
102
|
-
- test/cli/option_builder_test.rb
|
|
103
|
-
- test/cli/runner_test.rb
|
|
104
85
|
- test/cli_commands_test.rb
|
|
105
86
|
- test/cli_test.rb
|
|
106
87
|
- test/cli_version_test.rb
|
|
107
88
|
- test/compile_book_test.rb
|
|
108
|
-
- test/compile_workspace_test.rb
|
|
109
89
|
- test/epub_initializer_test.rb
|
|
110
90
|
- test/pack_ebook_test.rb
|
|
91
|
+
- test/pdf_converter_test.rb
|
|
92
|
+
- test/pdf_layout_test.rb
|
|
93
|
+
- test/set_cover_test.rb
|
|
111
94
|
- test/split_chapters_test.rb
|
|
112
95
|
- test/style_finder_test.rb
|
|
113
96
|
- test/test_helper.rb
|
|
@@ -128,14 +111,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
128
111
|
requirements:
|
|
129
112
|
- - ">="
|
|
130
113
|
- !ruby/object:Gem::Version
|
|
131
|
-
version: '
|
|
114
|
+
version: '4.0'
|
|
132
115
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
133
116
|
requirements:
|
|
134
117
|
- - ">="
|
|
135
118
|
- !ruby/object:Gem::Version
|
|
136
119
|
version: '0'
|
|
137
120
|
requirements: []
|
|
138
|
-
rubygems_version:
|
|
121
|
+
rubygems_version: 4.0.23
|
|
139
122
|
specification_version: 4
|
|
140
123
|
summary: Tools to extract, split, and compile EPUB books
|
|
141
124
|
test_files: []
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module EpubTools
|
|
4
|
-
module CLI
|
|
5
|
-
# Handles command-specific option configuration for CLI commands
|
|
6
|
-
class CommandOptionsConfigurator
|
|
7
|
-
# Configure command-specific options using dynamic dispatch
|
|
8
|
-
# @param cmd [String] Command name
|
|
9
|
-
# @param builder [OptionBuilder] Option builder instance
|
|
10
|
-
def configure(cmd, builder)
|
|
11
|
-
method_name = "configure_#{cmd.tr('-', '_')}_options"
|
|
12
|
-
raise ArgumentError, "Unknown command: #{cmd}" unless respond_to?(method_name, true)
|
|
13
|
-
|
|
14
|
-
send(method_name, builder)
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
private
|
|
18
|
-
|
|
19
|
-
# Configure options for the 'add' command
|
|
20
|
-
# @param builder [OptionBuilder] Option builder instance
|
|
21
|
-
def configure_add_options(builder)
|
|
22
|
-
builder.with_custom_options do |opts, options|
|
|
23
|
-
opts.on('-c DIR', '--chapters-dir DIR', 'Chapters directory (required)') { |v| options[:chapters_dir] = v }
|
|
24
|
-
opts.on('-e DIR', '--oebps-dir DIR', 'EPUB OEBPS directory (required)') { |v| options[:oebps_dir] = v }
|
|
25
|
-
end
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Configure options for the 'extract' command
|
|
29
|
-
# @param builder [OptionBuilder] Option builder instance
|
|
30
|
-
def configure_extract_options(builder)
|
|
31
|
-
builder.with_custom_options do |opts, options|
|
|
32
|
-
opts.on('-s DIR', '--source-dir DIR', 'Directory with EPUBs to extract XHTMLs from (required)') do |v|
|
|
33
|
-
options[:source_dir] = v
|
|
34
|
-
end
|
|
35
|
-
opts.on('-t DIR', '--target-dir DIR',
|
|
36
|
-
'Directory where the XHTML files will be extracted to (required)') do |v|
|
|
37
|
-
options[:target_dir] = v
|
|
38
|
-
end
|
|
39
|
-
end.with_verbose_option
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
# Configure options for the 'split' command
|
|
43
|
-
# @param builder [OptionBuilder] Option builder instance
|
|
44
|
-
def configure_split_options(builder)
|
|
45
|
-
builder.with_custom_options do |opts, options|
|
|
46
|
-
add_split_input_options(opts, options)
|
|
47
|
-
add_split_output_options(opts, options)
|
|
48
|
-
end.with_verbose_option
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def add_split_input_options(opts, options)
|
|
52
|
-
opts.on('-i FILE', '--input FILE', 'Source XHTML file (required)') { |v| options[:input_file] = v }
|
|
53
|
-
opts.on('-t TITLE', '--title TITLE', 'Book title for HTML <title> tags (required)') do |v|
|
|
54
|
-
options[:book_title] = v
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
def add_split_output_options(opts, options)
|
|
59
|
-
opts.on('-o DIR', '--output-dir DIR',
|
|
60
|
-
"Output directory for chapter files (default: #{options[:output_dir]})") do |v|
|
|
61
|
-
options[:output_dir] = v
|
|
62
|
-
end
|
|
63
|
-
opts.on('-p PREFIX', '--prefix PREFIX', "Filename prefix for chapters (default: #{options[:prefix]})") do |v|
|
|
64
|
-
options[:prefix] = v
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Configure options for the 'init' command
|
|
69
|
-
# @param builder [OptionBuilder] Option builder instance
|
|
70
|
-
def configure_init_options(builder)
|
|
71
|
-
builder.with_title_option
|
|
72
|
-
.with_author_option
|
|
73
|
-
.with_custom_options do |opts, options|
|
|
74
|
-
opts.on('-o DIR', '--output-dir DIR', 'Destination EPUB directory (required)') do |v|
|
|
75
|
-
options[:destination] = v
|
|
76
|
-
end
|
|
77
|
-
end.with_cover_option
|
|
78
|
-
end
|
|
79
|
-
|
|
80
|
-
# Configure options for the 'pack' command
|
|
81
|
-
# @param builder [OptionBuilder] Option builder instance
|
|
82
|
-
def configure_pack_options(builder)
|
|
83
|
-
builder.with_input_dir('EPUB directory to package')
|
|
84
|
-
.with_output_file('Output EPUB file path')
|
|
85
|
-
.with_verbose_option
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
# Configure options for the 'unpack' command
|
|
89
|
-
# @param builder [OptionBuilder] Option builder instance
|
|
90
|
-
def configure_unpack_options(builder)
|
|
91
|
-
builder.with_custom_options do |opts, options|
|
|
92
|
-
opts.on('-i FILE', '--input-file FILE', 'EPUB file to unpack (required)') { |v| options[:epub_file] = v }
|
|
93
|
-
opts.on('-o DIR', '--output-dir DIR', 'Output directory to extract into (default: basename of epub)') do |v|
|
|
94
|
-
options[:output_dir] = v
|
|
95
|
-
end
|
|
96
|
-
end.with_verbose_option
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
# Configure options for the 'compile' command
|
|
100
|
-
# @param builder [OptionBuilder] Option builder instance
|
|
101
|
-
def configure_compile_options(builder)
|
|
102
|
-
builder.with_title_option
|
|
103
|
-
.with_author_option
|
|
104
|
-
.with_custom_options do |opts, options|
|
|
105
|
-
opts.on('-s DIR', '--source-dir DIR', 'Directory with EPUBs to extract XHTMLs from (required)') do |v|
|
|
106
|
-
options[:source_dir] = v
|
|
107
|
-
end
|
|
108
|
-
opts.on('-o FILE', '--output FILE', 'EPUB to create (default: book title in source dir)') do |v|
|
|
109
|
-
options[:output_file] = v
|
|
110
|
-
end
|
|
111
|
-
end.with_cover_option.with_verbose_option
|
|
112
|
-
end
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
end
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module EpubTools
|
|
4
|
-
module CLI
|
|
5
|
-
# Manages the registration and retrieval of commands
|
|
6
|
-
class CommandRegistry
|
|
7
|
-
attr_reader :commands
|
|
8
|
-
|
|
9
|
-
def initialize
|
|
10
|
-
@commands = {}
|
|
11
|
-
end
|
|
12
|
-
|
|
13
|
-
# Register a new command in the registry
|
|
14
|
-
# @param name [String] the command name
|
|
15
|
-
# @param command_class [Class] the class that implements the command
|
|
16
|
-
# @param required_keys [Array<Symbol>] keys that must be present in options
|
|
17
|
-
# @param default_options [Hash] default options for the command
|
|
18
|
-
# @return [self]
|
|
19
|
-
def register(name, command_class, required_keys = [], default_options = {})
|
|
20
|
-
@commands[name] = {
|
|
21
|
-
class: command_class,
|
|
22
|
-
required_keys: required_keys,
|
|
23
|
-
default_options: default_options
|
|
24
|
-
}
|
|
25
|
-
self
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Get a command by name
|
|
29
|
-
# @param name [String] the command name
|
|
30
|
-
# @return [Hash, nil] the command configuration or nil if not found
|
|
31
|
-
def get(name)
|
|
32
|
-
@commands[name]
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# Get all available command names
|
|
36
|
-
# @return [Array<String>] list of registered command names
|
|
37
|
-
def available_commands
|
|
38
|
-
@commands.keys
|
|
39
|
-
end
|
|
40
|
-
|
|
41
|
-
# Check if a command is registered
|
|
42
|
-
# @param name [String] the command name
|
|
43
|
-
# @return [Boolean] true if command exists
|
|
44
|
-
def command_exists?(name)
|
|
45
|
-
@commands.key?(name)
|
|
46
|
-
end
|
|
47
|
-
end
|
|
48
|
-
end
|
|
49
|
-
end
|
|
@@ -1,166 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'optparse'
|
|
4
|
-
|
|
5
|
-
module EpubTools
|
|
6
|
-
module CLI
|
|
7
|
-
# Builds and manages command line options
|
|
8
|
-
class OptionBuilder
|
|
9
|
-
attr_reader :options, :required_keys, :parser
|
|
10
|
-
|
|
11
|
-
# Initialize a new OptionBuilder
|
|
12
|
-
# @param default_options [Hash] Default options to start with
|
|
13
|
-
# @param required_keys [Array<Symbol>] Keys that must be present in the final options
|
|
14
|
-
def initialize(default_options = {}, required_keys = [])
|
|
15
|
-
@options = default_options.dup
|
|
16
|
-
@required_keys = required_keys
|
|
17
|
-
@parser = OptionParser.new
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
# Add banner to the option parser
|
|
21
|
-
# @param text [String] Banner text
|
|
22
|
-
# @return [self] for method chaining
|
|
23
|
-
def with_banner(text)
|
|
24
|
-
@parser.banner = text
|
|
25
|
-
self
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Add help option to the parser
|
|
29
|
-
# @return [self] for method chaining
|
|
30
|
-
def with_help_option
|
|
31
|
-
@parser.on('-h', '--help', 'Print this help') do
|
|
32
|
-
puts @parser
|
|
33
|
-
exit
|
|
34
|
-
end
|
|
35
|
-
self
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Add verbose option to the parser
|
|
39
|
-
# @return [self] for method chaining
|
|
40
|
-
def with_verbose_option
|
|
41
|
-
@options[:verbose] = true unless @options.key?(:verbose)
|
|
42
|
-
@parser.on('-q', '--quiet', 'Run quietly (default: verbose)') { |v| @options[:verbose] = !v }
|
|
43
|
-
self
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
# Add input file option to the parser
|
|
47
|
-
# @param description [String] Option description
|
|
48
|
-
# @param required [Boolean] Whether this option is required
|
|
49
|
-
# @return [self] for method chaining
|
|
50
|
-
def with_input_file(description = 'Input file', required: true)
|
|
51
|
-
desc = required ? "#{description} (required)" : description
|
|
52
|
-
@parser.on('-i FILE', '--input-file FILE', desc) { |v| @options[:input_file] = v }
|
|
53
|
-
self
|
|
54
|
-
end
|
|
55
|
-
|
|
56
|
-
# Add input directory option to the parser
|
|
57
|
-
# @param description [String] Option description
|
|
58
|
-
# @param required [Boolean] Whether this option is required
|
|
59
|
-
# @return [self] for method chaining
|
|
60
|
-
def with_input_dir(description = 'Input directory', required: true)
|
|
61
|
-
desc = required ? "#{description} (required)" : description
|
|
62
|
-
@parser.on('-i DIR', '--input-dir DIR', desc) { |v| @options[:input_dir] = v }
|
|
63
|
-
self
|
|
64
|
-
end
|
|
65
|
-
|
|
66
|
-
# Add output directory option to the parser
|
|
67
|
-
# @param description [String] Option description
|
|
68
|
-
# @param default [String, nil] Default value
|
|
69
|
-
# @return [self] for method chaining
|
|
70
|
-
def with_output_dir(description = 'Output directory', default = nil)
|
|
71
|
-
if default
|
|
72
|
-
desc = "#{description} (default: #{default})"
|
|
73
|
-
@options[:output_dir] = default unless @options.key?(:output_dir)
|
|
74
|
-
else
|
|
75
|
-
desc = "#{description} (required)"
|
|
76
|
-
end
|
|
77
|
-
@parser.on('-o DIR', '--output-dir DIR', desc) { |v| @options[:output_dir] = v }
|
|
78
|
-
self
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
# Add output file option to the parser
|
|
82
|
-
# @param description [String] Option description
|
|
83
|
-
# @param required [Boolean] Whether this option is required
|
|
84
|
-
# @return [self] for method chaining
|
|
85
|
-
def with_output_file(description = 'Output file', required: true)
|
|
86
|
-
desc = required ? "#{description} (required)" : description
|
|
87
|
-
@parser.on('-o FILE', '--output-file FILE', desc) { |v| @options[:output_file] = v }
|
|
88
|
-
self
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
# Add title option to the parser
|
|
92
|
-
# @return [self] for method chaining
|
|
93
|
-
def with_title_option
|
|
94
|
-
@parser.on('-t TITLE', '--title TITLE', 'Book title (required)') { |v| @options[:title] = v }
|
|
95
|
-
self
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
# Add author option to the parser
|
|
99
|
-
# @return [self] for method chaining
|
|
100
|
-
def with_author_option
|
|
101
|
-
@parser.on('-a AUTHOR', '--author AUTHOR', 'Author name (required)') { |v| @options[:author] = v }
|
|
102
|
-
self
|
|
103
|
-
end
|
|
104
|
-
|
|
105
|
-
# Add cover option to the parser
|
|
106
|
-
# @return [self] for method chaining
|
|
107
|
-
def with_cover_option
|
|
108
|
-
@parser.on('-c PATH', '--cover PATH', 'Cover image file path (optional)') { |v| @options[:cover_image] = v }
|
|
109
|
-
self
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
# Add a custom option to the parser
|
|
113
|
-
# @param short [String] Short option flag
|
|
114
|
-
# @param long [String] Long option flag
|
|
115
|
-
# @param description [String] Option description
|
|
116
|
-
# @param option_key [Symbol] Key in the options hash
|
|
117
|
-
# @param block [Proc] Optional block for custom processing
|
|
118
|
-
# @return [self] for method chaining
|
|
119
|
-
def with_option(short, long, description, option_key)
|
|
120
|
-
@parser.on(short, long, description) do |v|
|
|
121
|
-
@options[option_key] = block_given? ? yield(v) : v
|
|
122
|
-
end
|
|
123
|
-
self
|
|
124
|
-
end
|
|
125
|
-
|
|
126
|
-
# Add a custom block to configure options
|
|
127
|
-
# @yield [OptionParser, Hash] Yields the parser and options hash
|
|
128
|
-
# @return [self] for method chaining
|
|
129
|
-
def with_custom_options
|
|
130
|
-
yield @parser, @options if block_given?
|
|
131
|
-
self
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
# Parse the command line arguments
|
|
135
|
-
# @param args [Array<String>] Command line arguments
|
|
136
|
-
# @return [Hash] Parsed options
|
|
137
|
-
# @raise [SystemExit] If required options are missing
|
|
138
|
-
def parse(args = ARGV)
|
|
139
|
-
begin
|
|
140
|
-
@parser.parse!(args.dup)
|
|
141
|
-
validate_required_keys
|
|
142
|
-
rescue ArgumentError => e
|
|
143
|
-
abort "#{e.message}\n#{@parser}"
|
|
144
|
-
end
|
|
145
|
-
@options
|
|
146
|
-
end
|
|
147
|
-
|
|
148
|
-
private
|
|
149
|
-
|
|
150
|
-
# Validate that all required keys are present in the options
|
|
151
|
-
# @raise [SystemExit] If required options are missing
|
|
152
|
-
def validate_required_keys
|
|
153
|
-
return if @required_keys.empty?
|
|
154
|
-
|
|
155
|
-
missing = @required_keys.select { |k| @options[k].nil? }
|
|
156
|
-
return if missing.empty?
|
|
157
|
-
|
|
158
|
-
raise ArgumentError, "Missing required options: #{missing_keys(missing)}"
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
def missing_keys(keys)
|
|
162
|
-
keys.map { |k| "--#{k.to_s.tr('_', '-')}" }.join(', ')
|
|
163
|
-
end
|
|
164
|
-
end
|
|
165
|
-
end
|
|
166
|
-
end
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require_relative 'command_registry'
|
|
4
|
-
require_relative 'option_builder'
|
|
5
|
-
require_relative 'command_options_configurator'
|
|
6
|
-
|
|
7
|
-
module EpubTools
|
|
8
|
-
module CLI
|
|
9
|
-
# Main runner for the CLI application
|
|
10
|
-
class Runner
|
|
11
|
-
attr_reader :registry, :program_name
|
|
12
|
-
|
|
13
|
-
# Initialize a new CLI Runner
|
|
14
|
-
# @param program_name [String] Name of the program
|
|
15
|
-
def initialize(program_name = nil)
|
|
16
|
-
@registry = CommandRegistry.new
|
|
17
|
-
@program_name = program_name || File.basename($PROGRAM_NAME)
|
|
18
|
-
@options_configurator = CommandOptionsConfigurator.new
|
|
19
|
-
end
|
|
20
|
-
|
|
21
|
-
# Run the CLI application
|
|
22
|
-
# @param args [Array<String>] Command line arguments
|
|
23
|
-
# @return [Boolean] true if the command was run successfully
|
|
24
|
-
def run(args = ARGV)
|
|
25
|
-
handle_version_flag(args)
|
|
26
|
-
validate_command_args(args)
|
|
27
|
-
|
|
28
|
-
cmd = args.shift
|
|
29
|
-
handle_command(cmd, args)
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Handle a specific command
|
|
33
|
-
# @param cmd [String] Command name
|
|
34
|
-
# @param args [Array<String>] Command line arguments
|
|
35
|
-
# @return [Boolean] true if the command was run successfully
|
|
36
|
-
def handle_command(cmd, args = ARGV)
|
|
37
|
-
command_config = registry.get(cmd)
|
|
38
|
-
return false unless command_config
|
|
39
|
-
|
|
40
|
-
builder = build_option_parser(cmd, command_config)
|
|
41
|
-
execute_command(command_config, builder, args)
|
|
42
|
-
end
|
|
43
|
-
|
|
44
|
-
private
|
|
45
|
-
|
|
46
|
-
# Handle version flag and exit if present
|
|
47
|
-
# @param args [Array<String>] Command line arguments
|
|
48
|
-
def handle_version_flag(args)
|
|
49
|
-
return unless ['-v', '--version'].include?(args[0])
|
|
50
|
-
|
|
51
|
-
puts EpubTools::VERSION
|
|
52
|
-
exit 0
|
|
53
|
-
end
|
|
54
|
-
|
|
55
|
-
# Validate command arguments and exit if invalid
|
|
56
|
-
# @param args [Array<String>] Command line arguments
|
|
57
|
-
def validate_command_args(args)
|
|
58
|
-
commands = registry.available_commands
|
|
59
|
-
return unless args.empty? || !commands.include?(args[0])
|
|
60
|
-
|
|
61
|
-
print_usage(commands)
|
|
62
|
-
exit 1
|
|
63
|
-
end
|
|
64
|
-
|
|
65
|
-
# Build option parser for a command
|
|
66
|
-
# @param cmd [String] Command name
|
|
67
|
-
# @param command_config [Hash] Command configuration
|
|
68
|
-
# @return [OptionBuilder] Configured option builder
|
|
69
|
-
def build_option_parser(cmd, command_config)
|
|
70
|
-
options = command_config[:default_options].dup
|
|
71
|
-
required_keys = command_config[:required_keys]
|
|
72
|
-
|
|
73
|
-
builder = OptionBuilder.new(options, required_keys)
|
|
74
|
-
.with_banner("Usage: #{program_name} #{cmd} [options]")
|
|
75
|
-
.with_help_option
|
|
76
|
-
|
|
77
|
-
@options_configurator.configure(cmd, builder)
|
|
78
|
-
builder
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
# Execute a command with parsed options
|
|
82
|
-
# @param command_config [Hash] Command configuration
|
|
83
|
-
# @param builder [OptionBuilder] Option builder instance
|
|
84
|
-
# @param args [Array<String>] Command line arguments
|
|
85
|
-
# @return [Object] Command instance
|
|
86
|
-
def execute_command(command_config, builder, args)
|
|
87
|
-
options = builder.parse(args)
|
|
88
|
-
command_class = command_config[:class]
|
|
89
|
-
command_instance = command_class.new(options)
|
|
90
|
-
command_instance.run
|
|
91
|
-
command_instance
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
# Print usage information
|
|
95
|
-
# @param commands [Array<String>] Available commands
|
|
96
|
-
def print_usage(_commands)
|
|
97
|
-
puts "Usage: #{program_name} COMMAND [options]"
|
|
98
|
-
puts 'Commands:'
|
|
99
|
-
print_command_list
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
def print_command_list
|
|
103
|
-
puts ' init Initialize a bare-bones EPUB'
|
|
104
|
-
puts ' extract Extract XHTML files from EPUBs'
|
|
105
|
-
puts ' split Split XHTML into separate XHTMLs per chapter'
|
|
106
|
-
puts ' add Add chapter XHTML files into an EPUB'
|
|
107
|
-
puts ' pack Package an EPUB directory into a .epub file'
|
|
108
|
-
puts ' unpack Unpack an EPUB file into a directory'
|
|
109
|
-
puts ' compile Takes EPUBs in a dir and splits, cleans, and compiles into a single EPUB.'
|
|
110
|
-
end
|
|
111
|
-
end
|
|
112
|
-
end
|
|
113
|
-
end
|
|
@@ -1,40 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'fileutils'
|
|
4
|
-
|
|
5
|
-
module EpubTools
|
|
6
|
-
# Manages the build workspace for book compilation
|
|
7
|
-
class CompileWorkspace
|
|
8
|
-
attr_reader :build_dir
|
|
9
|
-
|
|
10
|
-
def initialize(build_dir)
|
|
11
|
-
@build_dir = build_dir
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
# Cleans the build directory
|
|
15
|
-
def clean
|
|
16
|
-
FileUtils.rm_rf(@build_dir)
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
# Prepares all necessary directories
|
|
20
|
-
def prepare_directories
|
|
21
|
-
FileUtils.mkdir_p(xhtml_dir)
|
|
22
|
-
FileUtils.mkdir_p(chapters_dir)
|
|
23
|
-
end
|
|
24
|
-
|
|
25
|
-
# Gets the XHTML extraction directory
|
|
26
|
-
def xhtml_dir
|
|
27
|
-
@xhtml_dir ||= File.join(@build_dir, 'xhtml')
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
# Gets the chapters directory
|
|
31
|
-
def chapters_dir
|
|
32
|
-
@chapters_dir ||= File.join(@build_dir, 'chapters')
|
|
33
|
-
end
|
|
34
|
-
|
|
35
|
-
# Gets the EPUB build directory
|
|
36
|
-
def epub_dir
|
|
37
|
-
@epub_dir ||= File.join(@build_dir, 'epub')
|
|
38
|
-
end
|
|
39
|
-
end
|
|
40
|
-
end
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'securerandom'
|
|
4
|
-
require 'time'
|
|
5
|
-
|
|
6
|
-
module EpubTools
|
|
7
|
-
# Handles configuration parsing and setup for EPUB initialization
|
|
8
|
-
class EpubConfiguration
|
|
9
|
-
attr_reader :title, :author, :destination, :uuid, :modified,
|
|
10
|
-
:cover_image_path, :cover_image_fname, :cover_image_media_type, :verbose
|
|
11
|
-
|
|
12
|
-
def initialize(options = {})
|
|
13
|
-
@title = options.fetch(:title)
|
|
14
|
-
@author = options.fetch(:author)
|
|
15
|
-
@destination = File.expand_path(options.fetch(:destination))
|
|
16
|
-
@uuid = "urn:uuid:#{SecureRandom.uuid}"
|
|
17
|
-
@modified = Time.now.utc.iso8601
|
|
18
|
-
@cover_image_path = options[:cover_image]
|
|
19
|
-
@cover_image_fname = nil
|
|
20
|
-
@cover_image_media_type = nil
|
|
21
|
-
@verbose = options[:verbose] || false
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def cover_image?
|
|
25
|
-
!@cover_image_path.nil?
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
def update_cover_info(fname, media_type)
|
|
29
|
-
@cover_image_fname = fname
|
|
30
|
-
@cover_image_media_type = media_type
|
|
31
|
-
end
|
|
32
|
-
end
|
|
33
|
-
end
|