bookwatch 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (126) hide show
  1. checksums.yaml +7 -0
  2. data/bookwatch.gemspec +40 -0
  3. data/install_bin/bookwatch +5 -0
  4. data/lib/bookwatch/cli.rb +109 -0
  5. data/lib/bookwatch/code_example_reader.rb +95 -0
  6. data/lib/bookwatch/colorizer.rb +16 -0
  7. data/lib/bookwatch/commands/bind.rb +119 -0
  8. data/lib/bookwatch/commands/collection.rb +181 -0
  9. data/lib/bookwatch/commands/components/bind/directory_preparer.rb +33 -0
  10. data/lib/bookwatch/commands/components/bind/layout_preparer.rb +27 -0
  11. data/lib/bookwatch/commands/components/command_options.rb +45 -0
  12. data/lib/bookwatch/commands/components/imprint/directory_preparer.rb +24 -0
  13. data/lib/bookwatch/commands/generate.rb +92 -0
  14. data/lib/bookwatch/commands/imprint.rb +55 -0
  15. data/lib/bookwatch/commands/punch.rb +33 -0
  16. data/lib/bookwatch/commands/update_local_doc_repos.rb +42 -0
  17. data/lib/bookwatch/commands/watch.rb +100 -0
  18. data/lib/bookwatch/config/checkers/archive_menu_checker.rb +29 -0
  19. data/lib/bookwatch/config/checkers/ditamap_presence_checker.rb +27 -0
  20. data/lib/bookwatch/config/checkers/duplicate_section_name_checker.rb +33 -0
  21. data/lib/bookwatch/config/checkers/products_checker.rb +34 -0
  22. data/lib/bookwatch/config/checkers/repository_name_presence_checker.rb +34 -0
  23. data/lib/bookwatch/config/checkers/required_keys_checker.rb +18 -0
  24. data/lib/bookwatch/config/checkers/section_presence_checker.rb +15 -0
  25. data/lib/bookwatch/config/configuration.rb +119 -0
  26. data/lib/bookwatch/config/configuration_decorator.rb +54 -0
  27. data/lib/bookwatch/config/dita_config_generator.rb +61 -0
  28. data/lib/bookwatch/config/fetcher.rb +64 -0
  29. data/lib/bookwatch/config/imprint/configuration.rb +24 -0
  30. data/lib/bookwatch/config/product_config.rb +34 -0
  31. data/lib/bookwatch/config/section_config.rb +85 -0
  32. data/lib/bookwatch/config/validator.rb +33 -0
  33. data/lib/bookwatch/config/yaml_loader.rb +34 -0
  34. data/lib/bookwatch/css_link_checker.rb +67 -0
  35. data/lib/bookwatch/directory_helpers.rb +15 -0
  36. data/lib/bookwatch/dita_command_creator.rb +95 -0
  37. data/lib/bookwatch/dita_html_for_middleman_formatter.rb +45 -0
  38. data/lib/bookwatch/errors/programmer_mistake.rb +5 -0
  39. data/lib/bookwatch/html_document_manipulator.rb +21 -0
  40. data/lib/bookwatch/ingest/cloner_factory.rb +26 -0
  41. data/lib/bookwatch/ingest/destination_directory.rb +21 -0
  42. data/lib/bookwatch/ingest/git_accessor.rb +102 -0
  43. data/lib/bookwatch/ingest/git_cloner.rb +36 -0
  44. data/lib/bookwatch/ingest/local_filesystem_cloner.rb +66 -0
  45. data/lib/bookwatch/ingest/missing_working_copy.rb +27 -0
  46. data/lib/bookwatch/ingest/repo_identifier.rb +45 -0
  47. data/lib/bookwatch/ingest/section_repository.rb +49 -0
  48. data/lib/bookwatch/ingest/update_failure.rb +15 -0
  49. data/lib/bookwatch/ingest/update_success.rb +12 -0
  50. data/lib/bookwatch/ingest/working_copy.rb +36 -0
  51. data/lib/bookwatch/local_filesystem_accessor.rb +122 -0
  52. data/lib/bookwatch/middleman_runner.rb +48 -0
  53. data/lib/bookwatch/postprocessing/link_checker.rb +125 -0
  54. data/lib/bookwatch/postprocessing/redirection.rb +38 -0
  55. data/lib/bookwatch/preprocessing/dita_html_preprocessor.rb +91 -0
  56. data/lib/bookwatch/preprocessing/dita_pdf_preprocessor.rb +48 -0
  57. data/lib/bookwatch/preprocessing/link_to_site_gen_dir.rb +39 -0
  58. data/lib/bookwatch/preprocessing/preprocessor.rb +26 -0
  59. data/lib/bookwatch/server_director.rb +30 -0
  60. data/lib/bookwatch/sheller.rb +52 -0
  61. data/lib/bookwatch/streams/colorized_stream.rb +25 -0
  62. data/lib/bookwatch/streams/filter_stream.rb +22 -0
  63. data/lib/bookwatch/subnav/navigation_entries_from_html_toc.rb +59 -0
  64. data/lib/bookwatch/subnav/navigation_entries_from_markdown_root.rb +116 -0
  65. data/lib/bookwatch/subnav/pdf_config_creator.rb +50 -0
  66. data/lib/bookwatch/subnav/subnav_generator.rb +28 -0
  67. data/lib/bookwatch/subnav/subnav_generator_factory.rb +29 -0
  68. data/lib/bookwatch/subnav/template_creator.rb +71 -0
  69. data/lib/bookwatch/terminal.rb +19 -0
  70. data/lib/bookwatch/values/output_locations.rb +91 -0
  71. data/lib/bookwatch/values/product_info.rb +11 -0
  72. data/lib/bookwatch/values/section.rb +58 -0
  73. data/lib/bookwatch/values/subnav_template.rb +4 -0
  74. data/lib/bookwatch/values/user_message.rb +15 -0
  75. data/master_middleman/archive_drop_down_menu.rb +50 -0
  76. data/master_middleman/bookwatch_helpers.rb +259 -0
  77. data/master_middleman/compass_runner.rb +0 -0
  78. data/master_middleman/config.rb +34 -0
  79. data/master_middleman/quicklinks_renderer.rb +80 -0
  80. data/master_middleman/source/javascripts/all.js +2 -0
  81. data/master_middleman/source/javascripts/book.js +1 -0
  82. data/master_middleman/source/javascripts/bookwatch.js +103 -0
  83. data/master_middleman/source/layouts/_additional-scripts.erb +0 -0
  84. data/master_middleman/source/layouts/_book-footer.erb +0 -0
  85. data/master_middleman/source/layouts/_book-search.erb +0 -0
  86. data/master_middleman/source/layouts/_book-title.erb +3 -0
  87. data/master_middleman/source/layouts/_header.erb +34 -0
  88. data/master_middleman/source/layouts/_local-header.erb +0 -0
  89. data/master_middleman/source/layouts/_page-footer.erb +1 -0
  90. data/master_middleman/source/layouts/_title.erb +5 -0
  91. data/master_middleman/source/layouts/layout.erb +69 -0
  92. data/master_middleman/source/stylesheets/all.css.scss +3 -0
  93. data/master_middleman/source/stylesheets/base.scss +380 -0
  94. data/master_middleman/source/stylesheets/book-styles.css.scss +0 -0
  95. data/master_middleman/source/stylesheets/layout-styles.scss +0 -0
  96. data/master_middleman/source/stylesheets/partials/_book-base-values.scss +0 -0
  97. data/master_middleman/source/stylesheets/partials/_book-vars.scss +0 -0
  98. data/master_middleman/source/stylesheets/partials/_default.scss +300 -0
  99. data/master_middleman/source/stylesheets/partials/_footer.scss +64 -0
  100. data/master_middleman/source/stylesheets/partials/_header.scss +419 -0
  101. data/master_middleman/source/stylesheets/partials/_layout-vars.scss +0 -0
  102. data/master_middleman/source/stylesheets/partials/_mixins.scss +53 -0
  103. data/master_middleman/source/stylesheets/partials/_reset.scss +233 -0
  104. data/master_middleman/source/stylesheets/partials/_search.scss +78 -0
  105. data/master_middleman/source/stylesheets/partials/_sidenav.scss +191 -0
  106. data/master_middleman/source/stylesheets/partials/_syntax-highlight.scss +64 -0
  107. data/master_middleman/source/stylesheets/partials/_vars.scss +64 -0
  108. data/master_middleman/source/stylesheets/print.css.scss +58 -0
  109. data/master_middleman/source/subnavs/_default.erb +0 -0
  110. data/master_middleman/source/subnavs/_nav-links.erb +10 -0
  111. data/master_middleman/source/subnavs/_subnav_template.erb +8 -0
  112. data/master_middleman/subdirectory_aware_assets.rb +47 -0
  113. data/template_app/Gemfile +10 -0
  114. data/template_app/Gemfile.lock +43 -0
  115. data/template_app/config.ru +9 -0
  116. data/template_app/lib/rack_static_if_exists.rb +19 -0
  117. data/template_app/lib/search/handler.rb +47 -0
  118. data/template_app/lib/search/hit.rb +21 -0
  119. data/template_app/lib/search/query.rb +74 -0
  120. data/template_app/lib/search/renderer.rb +29 -0
  121. data/template_app/lib/server.rb +52 -0
  122. data/template_app/mail_sender.rb +69 -0
  123. data/template_app/rack_app.rb +110 -0
  124. data/template_app/search-results.html.erb +75 -0
  125. data/template_app/search.yml +22 -0
  126. metadata +491 -0
@@ -0,0 +1,122 @@
1
+ require 'find'
2
+ require 'pathname'
3
+ require_relative 'errors/programmer_mistake'
4
+
5
+ module Bookwatch
6
+
7
+ class LocalFilesystemAccessor
8
+ def file_exist?(path)
9
+ File.exist?(path)
10
+ end
11
+
12
+ def is_file?(path)
13
+ File.file?(path)
14
+ end
15
+
16
+ def is_dir?(path)
17
+ Dir.exists?(path)
18
+ end
19
+
20
+ def write(to: nil, text: nil)
21
+ make_directory(File.dirname to)
22
+
23
+ File.open(to, 'a') do |f|
24
+ f.write(text)
25
+ end
26
+
27
+ to
28
+ end
29
+
30
+ def overwrite(to: nil, text: nil)
31
+ File.delete(to) if file_exist?(to)
32
+ write(to: to, text: text)
33
+ end
34
+
35
+ def read(path)
36
+ File.read(path)
37
+ end
38
+
39
+ def empty_directory(path)
40
+ FileUtils.rm_rf(File.join(path, '.'))
41
+ end
42
+
43
+ def remove_directory(path)
44
+ FileUtils.rm_rf(path)
45
+ end
46
+
47
+ def make_directory(path)
48
+ FileUtils.mkdir_p(path)
49
+ end
50
+
51
+ def copy(src, dest)
52
+ make_directory(dest)
53
+ FileUtils.cp_r src, dest
54
+ end
55
+
56
+ def copy_and_rename(src, dest)
57
+ make_directory(Pathname(dest).dirname)
58
+ FileUtils.cp_r src, dest
59
+ end
60
+
61
+ def copy_contents(src, dest)
62
+ 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)
63
+ copy "#{src}/.", dest
64
+ end
65
+
66
+ def copy_including_intermediate_dirs(file, root, dest)
67
+ path_within_destination = relative_path_from(root, file)
68
+ extended_dest = File.dirname(File.join dest, path_within_destination)
69
+ copy file, extended_dest
70
+ end
71
+
72
+ def link_creating_intermediate_dirs(src, dst)
73
+ FileUtils.mkdir_p(File.dirname(dst))
74
+ File.symlink(src, dst)
75
+ end
76
+
77
+ def rename_file(path, new_name)
78
+ new_path = File.expand_path File.join path, '..', new_name
79
+ File.rename(path, new_path)
80
+ end
81
+
82
+ def find_files_with_ext(ext, path)
83
+ all_files = find_files_recursively(path)
84
+ matching_files = all_files.select {|p| p.to_s.match(/\.#{ext}/) }
85
+ matching_files.map(&:to_s)
86
+ end
87
+
88
+ def relative_path_from(src, target)
89
+ target_path = Pathname(File.absolute_path target)
90
+ relative_path = target_path.relative_path_from(Pathname(File.absolute_path src))
91
+ relative_path.to_s
92
+ end
93
+
94
+ def find_files_recursively(from)
95
+ `find -L #{from} -type f`.
96
+ lines.
97
+ map(&:chomp).
98
+ map(&Pathname.method(:new)).
99
+ reject {|p| p.to_s.match %r{/\.}}.
100
+ reject(&:directory?)
101
+ end
102
+
103
+ def find_files_extension_agnostically(pattern, directory='.')
104
+ extensionless_pattern = File.join(File.dirname(pattern), File.basename(pattern).split('.').first)
105
+
106
+ `find -L #{directory} -path '*/#{extensionless_pattern}.*' -type f`.
107
+ lines.
108
+ map(&:chomp).
109
+ map(&Pathname.method(:new))
110
+ end
111
+
112
+ def source_file_exists?(directory, path_to_file)
113
+ path = Pathname(path_to_file.split('/').last)
114
+ source_file_found = false
115
+
116
+ Pathname(directory).ascend do |dir|
117
+ source_file_found = true if dir.entries.any? { |entry| entry == path }
118
+ end
119
+ source_file_found
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,48 @@
1
+ require 'middleman-core'
2
+ require 'middleman-core/cli'
3
+ require 'middleman-core/profiling'
4
+ require 'yaml'
5
+
6
+ module Bookwatch
7
+ class MiddlemanRunner
8
+ def initialize(fs, sheller)
9
+ @fs = fs
10
+ @sheller = sheller
11
+ end
12
+
13
+ def run(command,
14
+ streams: nil,
15
+ output_locations: nil,
16
+ config: nil,
17
+ local_repo_dir: nil,
18
+ subnavs: nil,
19
+ product_info: nil)
20
+ streams[:out].puts "\nRunning middleman...\n\n"
21
+ Dir.chdir(output_locations.master_dir) do
22
+ config = {
23
+ archive_menu: config.archive_menu,
24
+ production_host: config.public_host,
25
+ subnav_templates: subnavs,
26
+ template_variables: config.template_variables,
27
+ local_repo_dir: local_repo_dir,
28
+ workspace: output_locations.workspace_dir,
29
+ feedback_enabled: config.feedback_enabled,
30
+ repo_link_enabled: config.repo_link_enabled,
31
+ repo_links: config.repo_links,
32
+ product_info: product_info,
33
+ elastic_search: config.elastic_search?
34
+ }
35
+
36
+ fs.write(to: "bookwatch_config.yml", text: YAML.dump(config))
37
+ sheller.run_command({'MM_ROOT' => output_locations.master_dir.to_s},
38
+ "middleman #{command}",
39
+ streams)
40
+ end
41
+ end
42
+
43
+ private
44
+
45
+ attr_reader :streams, :fs, :sheller
46
+
47
+ end
48
+ end
@@ -0,0 +1,125 @@
1
+ require 'nokogiri'
2
+ require_relative '../css_link_checker'
3
+ require_relative 'redirection'
4
+
5
+ module Bookwatch
6
+ module Postprocessing
7
+ class LinkChecker
8
+ def initialize(fs, root_path, output_streams)
9
+ @fs = fs
10
+ @root_path = root_path
11
+ @output_streams = output_streams
12
+ @broken_link_count = 0
13
+
14
+ @convert_to_relative = %r{\A.*#{root_path.to_s}/public}
15
+ @default_link_exclusions = %r{\A(?:https?://|javascript:|mailto:)}
16
+ @excluded_pages = %r{\A/(?:404\.html|subnavs|javascripts|stylesheets|style_guide)}
17
+ end
18
+
19
+ def check!(link_exclusions = /(?!.*)/)
20
+ @output_streams[:out].puts "\nChecking for broken links..."
21
+ @redirects = Redirection.new(@fs, File.join(@root_path, 'redirects.rb'))
22
+ load_page_links
23
+
24
+ report_broken_links!(link_exclusions)
25
+ report_orphaned_pages!
26
+
27
+ if has_errors?
28
+ err "\nFound #{@broken_link_count} broken links!"
29
+ else
30
+ out "\nNo broken links!"
31
+ end
32
+ end
33
+
34
+ def has_errors?
35
+ @broken_link_count > 0
36
+ end
37
+
38
+ private
39
+
40
+ def report_broken_links!(link_exclusions)
41
+ @page_links.each do |page, links|
42
+ links.each do |link|
43
+ next if skip?(link, link_exclusions)
44
+
45
+ absolute_link, fragment = normalize_link(link, page)
46
+
47
+ if !page_exists?(absolute_link) && !file_exists?(absolute_link)
48
+ @broken_link_count += 1
49
+ err "#{page} => #{absolute_link}#{fragment ? "##{fragment}" : ''}"
50
+ end
51
+ end
52
+ end
53
+
54
+ broken_css_links = Dir.chdir(@root_path) { CssLinkChecker.new.broken_links_in_all_stylesheets }
55
+ broken_css_links.reject! { |link| link_exclusions.match(link) }
56
+
57
+ @broken_link_count += broken_css_links.size
58
+ broken_css_links.each do |link|
59
+ err link
60
+ end
61
+ end
62
+
63
+ def report_orphaned_pages!
64
+ linked_pages = @page_links.map do |page, links|
65
+ links.map do |link|
66
+ normalize_link(link, page)[0]
67
+ end
68
+ end.flatten.uniq
69
+
70
+ orphaned_pages = @page_links.keys.reject { |page| page == '/index.html' || linked_pages.include?(page) }
71
+ if orphaned_pages.size > 0
72
+ err "\nOrphaned pages"
73
+ orphaned_pages.each do |page|
74
+ err "No links to => #{page}"
75
+ end
76
+ end
77
+ end
78
+
79
+ def skip?(link_path, link_exclusions)
80
+ @default_link_exclusions.match(link_path) || link_path.match(link_exclusions)
81
+ end
82
+
83
+ def page_exists?(link)
84
+ @page_links.has_key?(link) || @redirects.redirected?(link)
85
+ end
86
+
87
+ def normalize_link(link, page)
88
+ return [page, link.sub(/\A#/, '')] if link[0] == '#'
89
+
90
+ absolute_link = link[0] == '/' ? link : File.expand_path(link, File.dirname(page))
91
+ absolute_link.split('#')
92
+ end
93
+
94
+ def file_exists?(link)
95
+ full_path = File.join(@root_path, 'public', link)
96
+ @fs.is_file?(full_path) || (@fs.is_dir?(full_path) && @fs.is_file?(File.join(full_path, 'index.html')))
97
+ end
98
+
99
+ def load_page_links
100
+ files = @fs.find_files_with_ext('html', File.join(@root_path, 'public'))
101
+
102
+ @page_links = files.each.with_object({}) do |file_path, links|
103
+ public_path = file_path.sub(@convert_to_relative, '')
104
+
105
+ if !@excluded_pages.match(public_path)
106
+ html = Nokogiri::HTML(@fs.read(file_path))
107
+
108
+ links[public_path] = [
109
+ html.css('a[href]').map { |link| link['href'] },
110
+ html.css('img').map { |image| image['src'] }
111
+ ].flatten
112
+ end
113
+ end
114
+ end
115
+
116
+ def out(str)
117
+ @output_streams[:out].puts(str)
118
+ end
119
+
120
+ def err(str)
121
+ @output_streams[:err].puts(str)
122
+ end
123
+ end
124
+ end
125
+ end
@@ -0,0 +1,38 @@
1
+ module Bookwatch
2
+ module Postprocessing
3
+ class Redirection
4
+ def initialize(fs, file_path)
5
+ @redirect_regexes = {}
6
+ @redirect_strings = {}
7
+ load!(fs, file_path)
8
+ end
9
+
10
+ def redirected?(url)
11
+ @redirect_strings.has_key?(url) ||
12
+ @redirect_regexes.keys.detect {|regex| regex.match(url)}
13
+ end
14
+
15
+ private
16
+
17
+ def load!(fs, file_path)
18
+ if fs.is_file?(file_path)
19
+ contents = fs.read(file_path)
20
+ instance_eval contents
21
+ end
22
+ end
23
+
24
+ def r301(source, dest, options={})
25
+ return if options.has_key?(:if)
26
+
27
+ case source
28
+ when Regexp
29
+ @redirect_regexes[source] = dest
30
+ when String
31
+ @redirect_strings[source] = dest
32
+ end
33
+ end
34
+ alias r302 r301
35
+ alias rewrite r301
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,91 @@
1
+ require_relative '../values/subnav_template'
2
+ require_relative '../../../lib/bookwatch/subnav/navigation_entries_from_html_toc'
3
+
4
+ module Bookwatch
5
+ module Preprocessing
6
+ class DitaHTMLPreprocessor
7
+ DitaToHtmlLibraryFailure = Class.new(RuntimeError)
8
+
9
+ ACCEPTED_IMAGE_FORMATS = %w(png jpeg jpg svg gif bmp tif tiff eps)
10
+
11
+ def initialize(fs, subnav_gen_factory, dita_formatter, command_creator, sheller)
12
+ @fs = fs
13
+ @subnav_gen_factory = subnav_gen_factory
14
+ @dita_formatter = dita_formatter
15
+ @command_creator = command_creator
16
+ @sheller = sheller
17
+ end
18
+
19
+ def applicable_to?(section)
20
+ section.subnav_template.include?('dita_subnav') if section.subnav_template
21
+ end
22
+
23
+ def preprocess(sections, output_locations, options: {}, output_streams: nil, **_)
24
+ @output_locations = output_locations
25
+
26
+ sections.each do |section|
27
+ if section.path_to_preprocessor_attribute('ditamap_location')
28
+ convert_dita_files(section,
29
+ command_creator,
30
+ options[:dita_flags],
31
+ section_html_dir(section),
32
+ sheller,
33
+ output_streams)
34
+
35
+ subnav_generator.generate(section)
36
+ end
37
+
38
+ dita_formatter.format_html(section_html_dir(section), formatted_dir(section))
39
+ copy_images(section.path_to_repo_dir, formatted_dir(section))
40
+ fs.copy_contents(formatted_dir(section), source_for_site_gen_dir(section))
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ attr_reader :fs, :subnav_gen_factory, :dita_formatter, :command_creator, :sheller, :output_locations
47
+
48
+ def section_html_dir(section)
49
+ output_locations.html_from_preprocessing_dir.join(section.destination_directory)
50
+ end
51
+
52
+ def formatted_dir(section)
53
+ output_locations.formatted_dir.join(section.destination_directory)
54
+ end
55
+
56
+ def source_for_site_gen_dir(section)
57
+ output_locations.source_for_site_generator.join(section.destination_directory)
58
+ end
59
+
60
+ def convert_dita_files(section, command_creator, options, section_html_dir, sheller, output_streams)
61
+ command = command_creator.convert_to_html_command(
62
+ section,
63
+ dita_flags: options,
64
+ write_to: section_html_dir
65
+ )
66
+ status = sheller.run_command(command, output_streams.to_h)
67
+ unless status.success?
68
+ raise DitaToHtmlLibraryFailure.new 'The DITA-to-HTML conversion failed. ' +
69
+ 'Please check that you have specified the path to your DITA-OT library in the ENV, ' +
70
+ 'that your DITA-specific keys/values in config.yml are set, ' +
71
+ 'and that your DITA toolkit is correctly configured.'
72
+ end
73
+ end
74
+
75
+ def copy_images(src, dest)
76
+ image_paths = ACCEPTED_IMAGE_FORMATS.map do |format|
77
+ fs.find_files_with_ext(format, src)
78
+ end.flatten
79
+
80
+ image_paths.each do |image_path|
81
+ fs.copy_including_intermediate_dirs(image_path, src, dest)
82
+ end
83
+ end
84
+
85
+
86
+ def subnav_generator
87
+ @subnav_generator ||= subnav_gen_factory.produce(Subnav::NavigationEntriesFromHtmlToc.new(fs))
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,48 @@
1
+ module Bookwatch
2
+ module Preprocessing
3
+ class DitaPDFPreprocessor
4
+ DitaToPDFLibraryFailure = Class.new(RuntimeError)
5
+
6
+ def initialize(fs, command_creator, sheller)
7
+ @fs = fs
8
+ @command_creator = command_creator
9
+ @sheller = sheller
10
+ end
11
+
12
+ def applicable_to?(section)
13
+ !!section.pdf_output_filename
14
+ end
15
+
16
+ def preprocess(sections, output_locations, options: {}, output_streams: nil, **_)
17
+ sections.each do |section|
18
+ command = command_creator.convert_to_pdf_command(
19
+ section,
20
+ dita_flags: options[:dita_flags],
21
+ write_to: output_locations.pdf_from_preprocessing_dir
22
+ )
23
+ status = sheller.run_command(command, output_streams.to_h)
24
+
25
+ if status.success?
26
+ pdf_path = most_recent_pdf(output_locations.pdf_from_preprocessing_dir)
27
+ pdf_destination = output_locations.pdf_artifact_dir.join(section.pdf_output_filename)
28
+ fs.copy_and_rename(pdf_path, pdf_destination)
29
+ else
30
+ raise DitaToPDFLibraryFailure.new 'The DITA-to-PDF conversion failed. ' +
31
+ 'Please check that you have specified the path to your DITA-OT library in the ENV, ' +
32
+ 'that your DITA-specific keys/values in config.yml are set, ' +
33
+ 'and that your DITA toolkit is correctly configured.'
34
+ end
35
+ end
36
+ end
37
+
38
+ def most_recent_pdf(dir_path)
39
+ pdfs_by_modified_date = Dir.glob(dir_path + '**/*.pdf').sort_by{ |f| File.mtime(f) }
40
+ pdfs_by_modified_date.last
41
+ end
42
+
43
+ private
44
+
45
+ attr_reader :command_creator, :sheller, :fs
46
+ end
47
+ end
48
+ end