bookbindery 7.4.4 → 7.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.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/bookbinder.gemspec +1 -1
  3. data/lib/bookbinder/cli.rb +1 -1
  4. data/lib/bookbinder/commands/bind.rb +17 -17
  5. data/lib/bookbinder/commands/collection.rb +48 -26
  6. data/lib/bookbinder/commands/components/bind/directory_preparer.rb +33 -0
  7. data/lib/bookbinder/commands/components/bind/layout_preparer.rb +27 -0
  8. data/lib/bookbinder/commands/{bind/bind_options.rb → components/command_options.rb} +2 -2
  9. data/lib/bookbinder/commands/components/imprint/directory_preparer.rb +24 -0
  10. data/lib/bookbinder/commands/imprint.rb +62 -0
  11. data/lib/bookbinder/commands/watch.rb +0 -1
  12. data/lib/bookbinder/config/checkers/ditamap_presence_checker.rb +27 -0
  13. data/lib/bookbinder/config/checkers/section_presence_checker.rb +15 -0
  14. data/lib/bookbinder/config/configuration.rb +18 -27
  15. data/lib/bookbinder/config/dita_config_generator.rb +61 -0
  16. data/lib/bookbinder/config/fetcher.rb +4 -3
  17. data/lib/bookbinder/config/imprint/configuration.rb +24 -0
  18. data/lib/bookbinder/config/section_config.rb +4 -0
  19. data/lib/bookbinder/config/validator.rb +4 -2
  20. data/lib/bookbinder/dita_command_creator.rb +31 -16
  21. data/lib/bookbinder/ingest/section_repository.rb +2 -1
  22. data/lib/bookbinder/local_filesystem_accessor.rb +9 -2
  23. data/lib/bookbinder/preprocessing/dita_html_preprocessor.rb +94 -0
  24. data/lib/bookbinder/preprocessing/dita_pdf_preprocessor.rb +50 -0
  25. data/lib/bookbinder/preprocessing/dita_preprocessor.rb +2 -86
  26. data/lib/bookbinder/values/output_locations.rb +12 -0
  27. data/lib/bookbinder/values/section.rb +3 -1
  28. data/master_middleman/bookbinder_helpers.rb +7 -6
  29. metadata +13 -6
  30. data/lib/bookbinder/commands/bind/directory_preparer.rb +0 -31
  31. data/lib/bookbinder/commands/bind/layout_preparer.rb +0 -25
  32. 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: combined_sections(input_config)))
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
- (regular_sections(input_config) + dita_sections(input_config)).
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
- dita_sections = input_config['dita_sections']
38
- (dita_sections || []).map { |dita_section|
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
- Configuration.parse(base_hash.merge(optional_hash)).tap do |config|
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
@@ -19,6 +19,10 @@ module Bookbinder
19
19
  config['directory']
20
20
  end
21
21
 
22
+ def pdf_output_filename
23
+ config['output_filename']
24
+ end
25
+
22
26
  def repo_name
23
27
  repo['name']
24
28
  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/dita_section_checker'
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::DitaSectionChecker.new,
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 convert_to_html_command(dita_section, dita_flags: nil, write_to: nil)
10
- classpath = "#{path_to_dita_ot_library}/lib/xercesImpl.jar:" +
11
- "#{path_to_dita_ot_library}/lib/xml-apis.jar:" +
12
- "#{path_to_dita_ot_library}/lib/resolver.jar:" +
13
- "#{path_to_dita_ot_library}/lib/commons-codec-1.4.jar:" +
14
- "#{path_to_dita_ot_library}/lib/icu4j.jar:" +
15
- "#{path_to_dita_ot_library}/lib/saxon/saxon9-dom.jar:" +
16
- "#{path_to_dita_ot_library}/lib/saxon/saxon9.jar:target/classes:" +
17
- "#{path_to_dita_ot_library}:" +
18
- "#{path_to_dita_ot_library}/lib/:" +
19
- "#{path_to_dita_ot_library}/lib/dost.jar"
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 dita_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' => 'tocjs',
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
@@ -34,7 +34,8 @@ module Bookbinder
34
34
  section_config.preprocessor_config,
35
35
  section_config.at_repo_path,
36
36
  section_config.repo_name,
37
- working_copy.ref
37
+ working_copy.ref,
38
+ section_config.pdf_output_filename
38
39
  )
39
40
  end
40
41
  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
- Dir[File.join path, "**/*.#{ext}"]
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