bookbindery 7.4.4 → 7.5.0

Sign up to get free protection for your applications and to get access to all the features.
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