epub_tools 0.6.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.
Files changed (58) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/ci.yml +3 -8
  3. data/.gitignore +0 -1
  4. data/.tool-versions +1 -1
  5. data/CLAUDE.md +17 -25
  6. data/Gemfile +5 -8
  7. data/Gemfile.lock +60 -54
  8. data/README.md +31 -4
  9. data/bin/epub-tools +1 -3
  10. data/epub_tools.gemspec +3 -4
  11. data/lib/epub_tools/add_chapters.rb +17 -18
  12. data/lib/epub_tools/append_book.rb +8 -15
  13. data/lib/epub_tools/book_builder.rb +28 -22
  14. data/lib/epub_tools/chapter_order.rb +33 -0
  15. data/lib/epub_tools/chapter_validator.rb +6 -3
  16. data/lib/epub_tools/cli.rb +121 -23
  17. data/lib/epub_tools/compile_book.rb +1 -1
  18. data/lib/epub_tools/epub_initializer.rb +93 -99
  19. data/lib/epub_tools/pack_ebook.rb +2 -14
  20. data/lib/epub_tools/pdf_converter.rb +113 -0
  21. data/lib/epub_tools/pdf_layout.rb +116 -0
  22. data/lib/epub_tools/set_cover.rb +116 -0
  23. data/lib/epub_tools/split_chapters.rb +22 -20
  24. data/lib/epub_tools/style_finder.rb +11 -40
  25. data/lib/epub_tools/unpack_ebook.rb +2 -20
  26. data/lib/epub_tools/version.rb +1 -1
  27. data/lib/epub_tools/xhtml_cleaner.rb +4 -7
  28. data/lib/epub_tools/xhtml_extractor.rb +1 -7
  29. data/lib/epub_tools.rb +2 -1
  30. data/test/add_chapters_test.rb +64 -0
  31. data/test/append_book_test.rb +11 -11
  32. data/test/chapter_validator_test.rb +12 -0
  33. data/test/cli_test.rb +40 -3
  34. data/test/compile_book_test.rb +14 -0
  35. data/test/epub_initializer_test.rb +18 -0
  36. data/test/pdf_converter_test.rb +69 -0
  37. data/test/pdf_layout_test.rb +57 -0
  38. data/test/set_cover_test.rb +68 -0
  39. data/test/split_chapters_test.rb +18 -12
  40. data/test/style_finder_test.rb +3 -9
  41. data/test/test_helper.rb +1 -1
  42. data/test/unpack_ebook_test.rb +1 -1
  43. data/test/xhtml_cleaner_test.rb +1 -4
  44. metadata +14 -35
  45. data/lib/epub_tools/chapter_marker_detector.rb +0 -46
  46. data/lib/epub_tools/cli/command_options_configurator.rb +0 -128
  47. data/lib/epub_tools/cli/command_registry.rb +0 -49
  48. data/lib/epub_tools/cli/option_builder.rb +0 -166
  49. data/lib/epub_tools/cli/runner.rb +0 -114
  50. data/lib/epub_tools/compile_workspace.rb +0 -40
  51. data/lib/epub_tools/epub_configuration.rb +0 -33
  52. data/lib/epub_tools/epub_file_writer.rb +0 -57
  53. data/lib/epub_tools/epub_metadata_builder.rb +0 -92
  54. data/lib/epub_tools/xhtml_generator.rb +0 -71
  55. data/test/cli/command_registry_test.rb +0 -68
  56. data/test/cli/option_builder_test.rb +0 -183
  57. data/test/cli/runner_test.rb +0 -91
  58. 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.6.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'
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.18'
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.2'
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.2'
39
+ version: '3.7'
54
40
  email:
55
41
  - rodas@hey.com
56
42
  executables:
@@ -76,42 +62,35 @@ files:
76
62
  - lib/epub_tools/add_chapters.rb
77
63
  - lib/epub_tools/append_book.rb
78
64
  - lib/epub_tools/book_builder.rb
79
- - lib/epub_tools/chapter_marker_detector.rb
65
+ - lib/epub_tools/chapter_order.rb
80
66
  - lib/epub_tools/chapter_validator.rb
81
67
  - lib/epub_tools/cli.rb
82
- - lib/epub_tools/cli/command_options_configurator.rb
83
- - lib/epub_tools/cli/command_registry.rb
84
- - lib/epub_tools/cli/option_builder.rb
85
- - lib/epub_tools/cli/runner.rb
86
68
  - lib/epub_tools/compile_book.rb
87
- - lib/epub_tools/compile_workspace.rb
88
- - lib/epub_tools/epub_configuration.rb
89
- - lib/epub_tools/epub_file_writer.rb
90
69
  - lib/epub_tools/epub_initializer.rb
91
- - lib/epub_tools/epub_metadata_builder.rb
92
70
  - lib/epub_tools/loggable.rb
93
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
94
75
  - lib/epub_tools/split_chapters.rb
95
76
  - lib/epub_tools/style_finder.rb
96
77
  - lib/epub_tools/unpack_ebook.rb
97
78
  - lib/epub_tools/version.rb
98
79
  - lib/epub_tools/xhtml_cleaner.rb
99
80
  - lib/epub_tools/xhtml_extractor.rb
100
- - lib/epub_tools/xhtml_generator.rb
101
81
  - style.css
102
82
  - test/add_chapters_test.rb
103
83
  - test/append_book_test.rb
104
84
  - test/chapter_validator_test.rb
105
- - test/cli/command_registry_test.rb
106
- - test/cli/option_builder_test.rb
107
- - test/cli/runner_test.rb
108
85
  - test/cli_commands_test.rb
109
86
  - test/cli_test.rb
110
87
  - test/cli_version_test.rb
111
88
  - test/compile_book_test.rb
112
- - test/compile_workspace_test.rb
113
89
  - test/epub_initializer_test.rb
114
90
  - test/pack_ebook_test.rb
91
+ - test/pdf_converter_test.rb
92
+ - test/pdf_layout_test.rb
93
+ - test/set_cover_test.rb
115
94
  - test/split_chapters_test.rb
116
95
  - test/style_finder_test.rb
117
96
  - test/test_helper.rb
@@ -132,14 +111,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
132
111
  requirements:
133
112
  - - ">="
134
113
  - !ruby/object:Gem::Version
135
- version: '3.2'
114
+ version: '4.0'
136
115
  required_rubygems_version: !ruby/object:Gem::Requirement
137
116
  requirements:
138
117
  - - ">="
139
118
  - !ruby/object:Gem::Version
140
119
  version: '0'
141
120
  requirements: []
142
- rubygems_version: 3.7.0
121
+ rubygems_version: 4.0.23
143
122
  specification_version: 4
144
123
  summary: Tools to extract, split, and compile EPUB books
145
124
  test_files: []
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module EpubTools
4
- # Detects chapter boundary markers in XHTML nodes.
5
- # Recognizes: "Chapter N", "Chapter N (continued)", and "Prologue".
6
- class ChapterMarkerDetector
7
- # Tags that can contain chapter markers
8
- MARKER_TAGS = %w[p span h2 h3 h4].freeze
9
- # Tags that can contain prologue markers
10
- PROLOGUE_TAGS = %w[h3 h4].freeze
11
-
12
- # Detect what type of chapter marker a node represents
13
- # @param node [Nokogiri::XML::Node] The XHTML node to check
14
- # @return [Symbol, nil] :chapter, :continued, :prologue, or nil
15
- def detect(node)
16
- if continued_marker?(node)
17
- :continued
18
- elsif chapter_marker?(node)
19
- :chapter
20
- elsif prologue_marker?(node)
21
- :prologue
22
- end
23
- end
24
-
25
- # Extract the chapter number from a node's text
26
- # @param node [Nokogiri::XML::Node] A node containing "Chapter N" text
27
- # @return [Integer] The chapter number
28
- def extract_chapter_number(node)
29
- node.text.match(/Chapter\s+(\d+)/i)[1].to_i
30
- end
31
-
32
- private
33
-
34
- def continued_marker?(node)
35
- MARKER_TAGS.include?(node.name) && node.text.match?(/Chapter\s+\d+\s*\(continued\)/i)
36
- end
37
-
38
- def chapter_marker?(node)
39
- MARKER_TAGS.include?(node.name) && node.text.match?(/Chapter\s+\d+/i) && !continued_marker?(node)
40
- end
41
-
42
- def prologue_marker?(node)
43
- PROLOGUE_TAGS.include?(node.name) && node.text.strip.match?(/\APrologue\z/i)
44
- end
45
- end
46
- end
@@ -1,128 +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 'append' command
100
- # @param builder [OptionBuilder] Option builder instance
101
- def configure_append_options(builder)
102
- builder.with_custom_options do |opts, options|
103
- opts.on('-s DIR', '--source-dir DIR', 'Directory with EPUBs to append (required)') do |v|
104
- options[:source_dir] = v
105
- end
106
- opts.on('-t FILE', '--target-epub FILE', 'Existing EPUB file to append to (required)') do |v|
107
- options[:target_epub] = v
108
- end
109
- end.with_verbose_option
110
- end
111
-
112
- # Configure options for the 'compile' command
113
- # @param builder [OptionBuilder] Option builder instance
114
- def configure_compile_options(builder)
115
- builder.with_title_option
116
- .with_author_option
117
- .with_custom_options do |opts, options|
118
- opts.on('-s DIR', '--source-dir DIR', 'Directory with EPUBs to extract XHTMLs from (required)') do |v|
119
- options[:source_dir] = v
120
- end
121
- opts.on('-o FILE', '--output FILE', 'EPUB to create (default: book title in source dir)') do |v|
122
- options[:output_file] = v
123
- end
124
- end.with_cover_option.with_verbose_option
125
- end
126
- end
127
- end
128
- 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,114 +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
- puts ' append Extracts and splits EPUBs from a dir and appends them to an existing EPUB.'
111
- end
112
- end
113
- end
114
- 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