bookbindery 7.4.4 → 7.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bookbinder.gemspec +1 -1
- data/lib/bookbinder/cli.rb +1 -1
- data/lib/bookbinder/commands/bind.rb +17 -17
- data/lib/bookbinder/commands/collection.rb +48 -26
- data/lib/bookbinder/commands/components/bind/directory_preparer.rb +33 -0
- data/lib/bookbinder/commands/components/bind/layout_preparer.rb +27 -0
- data/lib/bookbinder/commands/{bind/bind_options.rb → components/command_options.rb} +2 -2
- data/lib/bookbinder/commands/components/imprint/directory_preparer.rb +24 -0
- data/lib/bookbinder/commands/imprint.rb +62 -0
- data/lib/bookbinder/commands/watch.rb +0 -1
- data/lib/bookbinder/config/checkers/ditamap_presence_checker.rb +27 -0
- data/lib/bookbinder/config/checkers/section_presence_checker.rb +15 -0
- data/lib/bookbinder/config/configuration.rb +18 -27
- data/lib/bookbinder/config/dita_config_generator.rb +61 -0
- data/lib/bookbinder/config/fetcher.rb +4 -3
- data/lib/bookbinder/config/imprint/configuration.rb +24 -0
- data/lib/bookbinder/config/section_config.rb +4 -0
- data/lib/bookbinder/config/validator.rb +4 -2
- data/lib/bookbinder/dita_command_creator.rb +31 -16
- data/lib/bookbinder/ingest/section_repository.rb +2 -1
- data/lib/bookbinder/local_filesystem_accessor.rb +9 -2
- data/lib/bookbinder/preprocessing/dita_html_preprocessor.rb +94 -0
- data/lib/bookbinder/preprocessing/dita_pdf_preprocessor.rb +50 -0
- data/lib/bookbinder/preprocessing/dita_preprocessor.rb +2 -86
- data/lib/bookbinder/values/output_locations.rb +12 -0
- data/lib/bookbinder/values/section.rb +3 -1
- data/master_middleman/bookbinder_helpers.rb +7 -6
- metadata +13 -6
- data/lib/bookbinder/commands/bind/directory_preparer.rb +0 -31
- data/lib/bookbinder/commands/bind/layout_preparer.rb +0 -25
- data/lib/bookbinder/config/checkers/dita_section_checker.rb +0 -31
@@ -0,0 +1,15 @@
|
|
1
|
+
module Bookbinder
|
2
|
+
module Config
|
3
|
+
module Checkers
|
4
|
+
class SectionPresenceChecker
|
5
|
+
NoSectionsError = Class.new(RuntimeError)
|
6
|
+
|
7
|
+
def check(config)
|
8
|
+
if config.sections.none?
|
9
|
+
NoSectionsError.new('No sections found in your config.yml. Add sections under the appropriate key(s) and try again.')
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
require_relative '../../../lib/bookbinder/config/dita_config_generator'
|
1
2
|
require_relative '../ingest/destination_directory'
|
2
3
|
require_relative '../ingest/repo_identifier'
|
3
4
|
require_relative 'section_config'
|
@@ -8,13 +9,18 @@ module Bookbinder
|
|
8
9
|
class Configuration
|
9
10
|
class << self
|
10
11
|
def parse(input_config)
|
12
|
+
section_configs = to_section_configs(combined_sections(input_config))
|
13
|
+
parse_sections(input_config, section_configs)
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def parse_sections(input_config, section_configs)
|
11
19
|
new(symbolize_keys(input_config).
|
12
20
|
merge(expand_repo_identifiers(input_config)).
|
13
|
-
merge(sections:
|
21
|
+
merge(sections: section_configs))
|
14
22
|
end
|
15
23
|
|
16
|
-
private
|
17
|
-
|
18
24
|
def symbolize_keys(h)
|
19
25
|
h.reduce({}) {|acc, (k, v)| acc.merge(k.to_sym => v) }
|
20
26
|
end
|
@@ -24,9 +30,14 @@ module Bookbinder
|
|
24
30
|
reduce({}) {|h, (k, v)| h.merge(:"#{k}_url" => Ingest::RepoIdentifier.new(v))}
|
25
31
|
end
|
26
32
|
|
33
|
+
def to_section_configs sections
|
34
|
+
sections.map { |section| Config::SectionConfig.new(section) }
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
27
39
|
def combined_sections(input_config)
|
28
|
-
|
29
|
-
map { |section| Config::SectionConfig.new(section) }
|
40
|
+
regular_sections(input_config) + dita_sections(input_config)
|
30
41
|
end
|
31
42
|
|
32
43
|
def regular_sections(input_config)
|
@@ -34,30 +45,10 @@ module Bookbinder
|
|
34
45
|
end
|
35
46
|
|
36
47
|
def dita_sections(input_config)
|
37
|
-
|
38
|
-
|
39
|
-
dita_section.merge(
|
40
|
-
'preprocessor_config' => {
|
41
|
-
'ditamap_location' => dita_section['ditamap_location'],
|
42
|
-
'ditaval_location' => dita_section['ditaval_location']
|
43
|
-
},
|
44
|
-
'subnav_template' => dita_subnav_template(dita_sections, dita_section)
|
45
|
-
).reject { |k, _|
|
46
|
-
%w(ditamap_location ditaval_location).include?(k)
|
47
|
-
}
|
48
|
+
(input_config['dita_sections'] || []).map { |dita_section|
|
49
|
+
DitaConfigGenerator.new(dita_section).to_hash
|
48
50
|
}
|
49
51
|
end
|
50
|
-
|
51
|
-
def dita_subnav_template(all_sections, current_section)
|
52
|
-
subnav_sections = all_sections.select { |section| section['ditamap_location'] }
|
53
|
-
if subnav_sections.any?
|
54
|
-
subnav_section = subnav_sections.include?(current_section) ? current_section : subnav_sections.first
|
55
|
-
dest_dir = Ingest::DestinationDirectory.new(
|
56
|
-
subnav_section.fetch('repository', {})['name'], subnav_section['directory'])
|
57
|
-
|
58
|
-
"dita_subnav_#{dest_dir}"
|
59
|
-
end
|
60
|
-
end
|
61
52
|
end
|
62
53
|
|
63
54
|
def initialize(config)
|
@@ -0,0 +1,61 @@
|
|
1
|
+
require_relative '../../../lib/bookbinder/ingest/destination_directory'
|
2
|
+
|
3
|
+
module Bookbinder
|
4
|
+
module Config
|
5
|
+
class DitaConfigGenerator
|
6
|
+
|
7
|
+
def initialize(section_hash)
|
8
|
+
@section_hash = section_hash
|
9
|
+
end
|
10
|
+
|
11
|
+
def subnav_template
|
12
|
+
dest_dir = Ingest::DestinationDirectory.new(section_hash.fetch('repository', {})['name'], section_hash['directory'])
|
13
|
+
|
14
|
+
"dita_subnav_#{dest_dir}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def ditamap_location
|
18
|
+
section_hash['ditamap_location'] if section_hash['ditamap_location'] && !section_hash['ditamap_location'].empty?
|
19
|
+
end
|
20
|
+
|
21
|
+
def pdf_output_filename
|
22
|
+
if present?(section_hash['output_filename'])
|
23
|
+
filename = section_hash['output_filename']
|
24
|
+
elsif ditamap_location
|
25
|
+
filename = ditamap_location.gsub(/\.ditamap/, '')
|
26
|
+
else
|
27
|
+
return
|
28
|
+
end
|
29
|
+
|
30
|
+
filename + '.pdf'
|
31
|
+
end
|
32
|
+
|
33
|
+
def preprocessor_config
|
34
|
+
{
|
35
|
+
'preprocessor_config' => {
|
36
|
+
'ditamap_location' => ditamap_location,
|
37
|
+
'ditaval_location' => section_hash['ditaval_location']
|
38
|
+
}
|
39
|
+
}
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_hash
|
43
|
+
section_hash.tap do |hash|
|
44
|
+
hash.merge!(preprocessor_config)
|
45
|
+
.merge!('subnav_template' => subnav_template, 'output_filename' => pdf_output_filename)
|
46
|
+
|
47
|
+
hash.delete('ditaval_location')
|
48
|
+
hash.delete('ditamap_location')
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
attr_reader :section_hash
|
55
|
+
|
56
|
+
def present?(value)
|
57
|
+
value && !value.empty?
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
@@ -6,10 +6,11 @@ require_relative 'yaml_loader'
|
|
6
6
|
module Bookbinder
|
7
7
|
module Config
|
8
8
|
class Fetcher
|
9
|
-
def initialize(configuration_validator, loader, credentials_provider)
|
9
|
+
def initialize(configuration_validator, loader, credentials_provider, config_class)
|
10
10
|
@loader = loader
|
11
11
|
@configuration_validator = configuration_validator
|
12
12
|
@credentials_provider = credentials_provider
|
13
|
+
@config_class = config_class
|
13
14
|
end
|
14
15
|
|
15
16
|
def fetch_config
|
@@ -43,7 +44,7 @@ module Bookbinder
|
|
43
44
|
private
|
44
45
|
|
45
46
|
attr_reader(:loader, :configuration_validator, :config, :config_file_path, :config_dir_path,
|
46
|
-
:credentials_provider)
|
47
|
+
:credentials_provider, :config_class)
|
47
48
|
|
48
49
|
def read_config_file
|
49
50
|
loader.load(config_file_path)
|
@@ -65,7 +66,7 @@ module Bookbinder
|
|
65
66
|
def validate(base_hash, optional_hash)
|
66
67
|
raise 'Your config.yml appears to be empty. Please check and try again.' unless base_hash
|
67
68
|
|
68
|
-
|
69
|
+
config_class.parse(base_hash.merge(optional_hash)).tap do |config|
|
69
70
|
errors = configuration_validator.exceptions(config)
|
70
71
|
raise errors.first if errors.any?
|
71
72
|
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require_relative '../configuration'
|
2
|
+
|
3
|
+
module Bookbinder
|
4
|
+
module Config
|
5
|
+
module Imprint
|
6
|
+
class Configuration < Config::Configuration
|
7
|
+
class << self
|
8
|
+
def parse(input_config)
|
9
|
+
section_configs = to_section_configs(pdf_sections(input_config))
|
10
|
+
parse_sections(input_config, section_configs)
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def pdf_sections(input_config)
|
16
|
+
(input_config['pdf_sections'] || []).map { |pdf_section|
|
17
|
+
DitaConfigGenerator.new(pdf_section).to_hash
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -2,7 +2,8 @@ require_relative 'checkers/duplicate_section_name_checker'
|
|
2
2
|
require_relative 'checkers/archive_menu_checker'
|
3
3
|
require_relative 'checkers/required_keys_checker'
|
4
4
|
require_relative 'checkers/repository_name_presence_checker'
|
5
|
-
require_relative 'checkers/
|
5
|
+
require_relative 'checkers/ditamap_presence_checker'
|
6
|
+
require_relative 'checkers/section_presence_checker'
|
6
7
|
require_relative 'checkers/subnavs_checker'
|
7
8
|
require_relative 'checkers/topics_checker'
|
8
9
|
|
@@ -18,7 +19,8 @@ module Bookbinder
|
|
18
19
|
Checkers::RequiredKeysChecker.new,
|
19
20
|
Checkers::DuplicateSectionNameChecker.new,
|
20
21
|
Checkers::RepositoryNamePresenceChecker.new,
|
21
|
-
Checkers::
|
22
|
+
Checkers::SectionPresenceChecker.new,
|
23
|
+
Checkers::DitamapPresenceChecker.new,
|
22
24
|
Checkers::ArchiveMenuChecker.new(@file_system_accessor),
|
23
25
|
Checkers::SubnavsChecker.new,
|
24
26
|
Checkers::TopicsChecker.new
|
@@ -6,36 +6,38 @@ module Bookbinder
|
|
6
6
|
@path_to_dita_ot_library = path_to_dita_ot_library
|
7
7
|
end
|
8
8
|
|
9
|
-
def
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
9
|
+
def convert_to_pdf_command(dita_section, dita_flags: nil, write_to: nil)
|
10
|
+
"export CLASSPATH=#{classpath}; " +
|
11
|
+
"ant -f #{path_to_dita_ot_library} " +
|
12
|
+
unduplicated_flags(
|
13
|
+
write_to: write_to,
|
14
|
+
dita_flags: dita_flags,
|
15
|
+
ditamap_path: dita_section.path_to_preprocessor_attribute('ditamap_location'),
|
16
|
+
ditaval_path: dita_section.path_to_preprocessor_attribute('ditaval_location'),
|
17
|
+
default_transtype: 'pdf2'
|
18
|
+
)
|
19
|
+
end
|
20
20
|
|
21
|
+
def convert_to_html_command(dita_section, dita_flags: nil, write_to: nil)
|
21
22
|
"export CLASSPATH=#{classpath}; " +
|
22
23
|
"ant -f #{path_to_dita_ot_library} " +
|
23
24
|
unduplicated_flags(
|
24
25
|
write_to: write_to,
|
25
26
|
dita_flags: dita_flags,
|
26
27
|
ditamap_path: dita_section.path_to_preprocessor_attribute('ditamap_location'),
|
27
|
-
ditaval_path: dita_section.path_to_preprocessor_attribute('ditaval_location')
|
28
|
+
ditaval_path: dita_section.path_to_preprocessor_attribute('ditaval_location'),
|
29
|
+
default_transtype: 'tocjs'
|
28
30
|
)
|
29
31
|
end
|
30
32
|
|
31
33
|
private
|
32
34
|
|
33
|
-
def unduplicated_flags(write_to: nil, ditamap_path: nil, ditaval_path: nil, dita_flags: nil)
|
35
|
+
def unduplicated_flags(write_to: nil, ditamap_path: nil, ditaval_path: nil, dita_flags: nil, default_transtype: nil)
|
34
36
|
arg_flags = {
|
35
37
|
'output.dir' => write_to,
|
36
38
|
'args.input' => ditamap_path,
|
37
39
|
}.merge(filter(ditaval_path))
|
38
|
-
all_flags = arg_flags.merge(base_flags.merge(optional_flags
|
40
|
+
all_flags = arg_flags.merge(base_flags(default_transtype: default_transtype).merge(optional_flags(dita_flags)))
|
39
41
|
format(all_flags)
|
40
42
|
end
|
41
43
|
|
@@ -43,10 +45,10 @@ module Bookbinder
|
|
43
45
|
ditaval_path ? { 'args.filter' => ditaval_path } : {}
|
44
46
|
end
|
45
47
|
|
46
|
-
def base_flags
|
48
|
+
def base_flags(default_transtype: nil)
|
47
49
|
{
|
48
50
|
'basedir' => '/',
|
49
|
-
'transtype' =>
|
51
|
+
'transtype' => default_transtype,
|
50
52
|
'dita.temp.dir' => '/tmp/bookbinder_dita',
|
51
53
|
'generate.copy.outer' => '2',
|
52
54
|
'outer.control' => 'warn'
|
@@ -75,6 +77,19 @@ module Bookbinder
|
|
75
77
|
v.to_s.gsub(/['|"]/, "")
|
76
78
|
end
|
77
79
|
|
80
|
+
def classpath
|
81
|
+
"#{path_to_dita_ot_library}/lib/xercesImpl.jar:" +
|
82
|
+
"#{path_to_dita_ot_library}/lib/xml-apis.jar:" +
|
83
|
+
"#{path_to_dita_ot_library}/lib/resolver.jar:" +
|
84
|
+
"#{path_to_dita_ot_library}/lib/commons-codec-1.4.jar:" +
|
85
|
+
"#{path_to_dita_ot_library}/lib/icu4j.jar:" +
|
86
|
+
"#{path_to_dita_ot_library}/lib/saxon/saxon9-dom.jar:" +
|
87
|
+
"#{path_to_dita_ot_library}/lib/saxon/saxon9.jar:target/classes:" +
|
88
|
+
"#{path_to_dita_ot_library}:" +
|
89
|
+
"#{path_to_dita_ot_library}/lib/:" +
|
90
|
+
"#{path_to_dita_ot_library}/lib/dost.jar"
|
91
|
+
end
|
92
|
+
|
78
93
|
attr_reader :path_to_dita_ot_library
|
79
94
|
end
|
80
95
|
end
|
@@ -46,6 +46,11 @@ module Bookbinder
|
|
46
46
|
FileUtils.cp_r src, dest
|
47
47
|
end
|
48
48
|
|
49
|
+
def copy_and_rename(src, dest)
|
50
|
+
make_directory(Pathname(dest).dirname)
|
51
|
+
FileUtils.cp_r src, dest
|
52
|
+
end
|
53
|
+
|
49
54
|
def copy_contents(src, dest)
|
50
55
|
raise Errors::ProgrammerMistake.new("The method copy_contents cannot copy the contents of the directory '#{src}' because it was not found.") unless Dir.exists?(src)
|
51
56
|
copy "#{src}/.", dest
|
@@ -68,7 +73,9 @@ module Bookbinder
|
|
68
73
|
end
|
69
74
|
|
70
75
|
def find_files_with_ext(ext, path)
|
71
|
-
|
76
|
+
all_files = find_files_recursively(path)
|
77
|
+
matching_files = all_files.select {|p| p.to_s.match(/\.#{ext}/) }
|
78
|
+
matching_files.map(&:to_s)
|
72
79
|
end
|
73
80
|
|
74
81
|
def relative_path_from(src, target)
|
@@ -78,7 +85,7 @@ module Bookbinder
|
|
78
85
|
end
|
79
86
|
|
80
87
|
def find_files_recursively(from)
|
81
|
-
`find -L #{from}`.
|
88
|
+
`find -L #{from} -type f`.
|
82
89
|
lines.
|
83
90
|
map(&:chomp).
|
84
91
|
map(&Pathname.method(:new)).
|
@@ -0,0 +1,94 @@
|
|
1
|
+
require_relative '../values/subnav_template'
|
2
|
+
require_relative '../../../lib/bookbinder/subnav/json_from_html_toc'
|
3
|
+
require_relative 'dita_preprocessor'
|
4
|
+
|
5
|
+
module Bookbinder
|
6
|
+
module Preprocessing
|
7
|
+
class DitaHTMLPreprocessor < DitaPreprocessor
|
8
|
+
DitaToHtmlLibraryFailure = Class.new(RuntimeError)
|
9
|
+
|
10
|
+
ACCEPTED_IMAGE_FORMATS = %w(png jpeg jpg svg gif bmp tif tiff eps)
|
11
|
+
|
12
|
+
def initialize(fs, subnav_gen_factory, dita_formatter, command_creator, sheller)
|
13
|
+
@fs = fs
|
14
|
+
@subnav_gen_factory = subnav_gen_factory
|
15
|
+
@dita_formatter = dita_formatter
|
16
|
+
@command_creator = command_creator
|
17
|
+
@sheller = sheller
|
18
|
+
end
|
19
|
+
|
20
|
+
def applicable_to?(section)
|
21
|
+
section.subnav_template.include?('dita_subnav') if section.subnav_template
|
22
|
+
end
|
23
|
+
|
24
|
+
def preprocess(sections, output_locations, options: [], output_streams: nil, **_)
|
25
|
+
@output_locations = output_locations
|
26
|
+
|
27
|
+
dita_options = dita_flags(options)
|
28
|
+
|
29
|
+
sections.each do |section|
|
30
|
+
if section.path_to_preprocessor_attribute('ditamap_location')
|
31
|
+
convert_dita_files(section,
|
32
|
+
command_creator,
|
33
|
+
dita_options,
|
34
|
+
section_html_dir(section),
|
35
|
+
sheller,
|
36
|
+
output_streams)
|
37
|
+
|
38
|
+
subnav_generator.generate(section)
|
39
|
+
end
|
40
|
+
|
41
|
+
dita_formatter.format_html(section_html_dir(section), formatted_dir(section))
|
42
|
+
copy_images(section.path_to_repo_dir, formatted_dir(section))
|
43
|
+
fs.copy_contents(formatted_dir(section), source_for_site_gen_dir(section))
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
attr_reader :fs, :subnav_gen_factory, :dita_formatter, :command_creator, :sheller, :subnav_generator, :output_locations
|
50
|
+
|
51
|
+
def section_html_dir(section)
|
52
|
+
output_locations.html_from_preprocessing_dir.join(section.destination_directory)
|
53
|
+
end
|
54
|
+
|
55
|
+
def formatted_dir(section)
|
56
|
+
output_locations.formatted_dir.join(section.destination_directory)
|
57
|
+
end
|
58
|
+
|
59
|
+
def source_for_site_gen_dir(section)
|
60
|
+
output_locations.source_for_site_generator.join(section.destination_directory)
|
61
|
+
end
|
62
|
+
|
63
|
+
def convert_dita_files(section, command_creator, options, section_html_dir, sheller, output_streams)
|
64
|
+
command = command_creator.convert_to_html_command(
|
65
|
+
section,
|
66
|
+
dita_flags: options,
|
67
|
+
write_to: section_html_dir
|
68
|
+
)
|
69
|
+
status = sheller.run_command(command, output_streams.to_h)
|
70
|
+
unless status.success?
|
71
|
+
raise DitaToHtmlLibraryFailure.new 'The DITA-to-HTML conversion failed. ' +
|
72
|
+
'Please check that you have specified the path to your DITA-OT library in the ENV, ' +
|
73
|
+
'that your DITA-specific keys/values in config.yml are set, ' +
|
74
|
+
'and that your DITA toolkit is correctly configured.'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def copy_images(src, dest)
|
79
|
+
image_paths = ACCEPTED_IMAGE_FORMATS.map do |format|
|
80
|
+
fs.find_files_with_ext(format, src)
|
81
|
+
end.flatten
|
82
|
+
|
83
|
+
image_paths.each do |image_path|
|
84
|
+
fs.copy_including_intermediate_dirs(image_path, src, dest)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
|
89
|
+
def subnav_generator
|
90
|
+
@subnav_generator ||= subnav_gen_factory.produce(Subnav::JsonFromHtmlToc.new(fs))
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|