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,24 @@
1
+ require_relative '../configuration'
2
+
3
+ module Bookwatch
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
@@ -0,0 +1,34 @@
1
+ module Bookwatch
2
+ module Config
3
+ class ProductConfig
4
+ def initialize(config)
5
+ @config = config
6
+ end
7
+
8
+ def id
9
+ config['id']
10
+ end
11
+
12
+ def pdf_config
13
+ config['pdf_config']
14
+ end
15
+
16
+ def subnav_root
17
+ config['subnav_root']
18
+ end
19
+
20
+ def valid?
21
+ (CONFIG_REQUIRED_KEYS - config.keys).empty?
22
+ end
23
+
24
+ CONFIG_REQUIRED_KEYS = %w(id)
25
+
26
+ alias_method :subnav_name, :id
27
+
28
+ private
29
+
30
+ attr_reader :config
31
+ end
32
+ end
33
+ end
34
+
@@ -0,0 +1,85 @@
1
+ require_relative '../ingest/repo_identifier'
2
+
3
+ module Bookwatch
4
+ module Config
5
+ class SectionConfig
6
+ def initialize(config)
7
+ @config = config
8
+ end
9
+
10
+ def subnav_template
11
+ config['subnav_template']
12
+ end
13
+
14
+ def product_id
15
+ config['product_id']
16
+ end
17
+
18
+ def desired_directory_name
19
+ config['directory']
20
+ end
21
+
22
+ def pdf_output_filename
23
+ config['output_filename']
24
+ end
25
+
26
+ def repo_name
27
+ repo['name']
28
+ end
29
+
30
+ def repo_url
31
+ Ingest::RepoIdentifier.new(repo['name'])
32
+ end
33
+
34
+ def repo_ref
35
+ repo['ref'] || 'master'
36
+ end
37
+
38
+ def at_repo_path
39
+ repo['at_path']
40
+ end
41
+
42
+ def no_docs?
43
+ config.fetch('no_docs', false)
44
+ end
45
+
46
+ def dependent_sections
47
+ @sections ||= (config['dependent_sections'] || []).map do |dep_section|
48
+ SectionConfig.new(dep_section)
49
+ end
50
+ end
51
+
52
+ def preprocessor_config
53
+ config.fetch('preprocessor_config', {})
54
+ end
55
+
56
+ def ==(other)
57
+ config == other.instance_variable_get(:@config)
58
+ end
59
+
60
+ def merge(other_section_config)
61
+ SectionConfig.new(config.merge(other_section_config.instance_variable_get(:@config)))
62
+ end
63
+
64
+ def inspect
65
+ config.inspect
66
+ end
67
+
68
+ alias_method :subnav_name, :product_id
69
+
70
+ def product_info
71
+ return {} if config['product_info'].nil?
72
+ config['product_info']
73
+ end
74
+
75
+ private
76
+
77
+ def repo
78
+ config.fetch('repository', {})
79
+ end
80
+
81
+ attr_reader :config
82
+ end
83
+ end
84
+ end
85
+
@@ -0,0 +1,33 @@
1
+ require_relative 'checkers/duplicate_section_name_checker'
2
+ require_relative 'checkers/archive_menu_checker'
3
+ require_relative 'checkers/required_keys_checker'
4
+ require_relative 'checkers/repository_name_presence_checker'
5
+ require_relative 'checkers/ditamap_presence_checker'
6
+ require_relative 'checkers/section_presence_checker'
7
+ require_relative 'checkers/products_checker'
8
+
9
+ module Bookwatch
10
+ module Config
11
+ class Validator
12
+ def initialize(file_system_accessor)
13
+ @file_system_accessor = file_system_accessor
14
+ end
15
+
16
+ def exceptions(config)
17
+ exceptions = [
18
+ Checkers::RequiredKeysChecker.new,
19
+ Checkers::DuplicateSectionNameChecker.new,
20
+ Checkers::RepositoryNamePresenceChecker.new,
21
+ Checkers::SectionPresenceChecker.new,
22
+ Checkers::DitamapPresenceChecker.new,
23
+ Checkers::ArchiveMenuChecker.new(@file_system_accessor),
24
+ Checkers::ProductsChecker.new
25
+ ].map do |checker|
26
+ checker.check(config)
27
+ end
28
+
29
+ exceptions.compact
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ require 'yaml'
2
+
3
+ module Bookwatch
4
+ module Config
5
+ FileNotFoundError = Class.new(RuntimeError)
6
+ InvalidSyntaxError = Class.new(RuntimeError)
7
+
8
+ class YAMLLoader
9
+ def load(path)
10
+ if File.exist?(path)
11
+ config(path)
12
+ else
13
+ raise FileNotFoundError.new, "YAML"
14
+ end
15
+ rescue Psych::SyntaxError => e
16
+ raise InvalidSyntaxError.new e
17
+ end
18
+
19
+ def load_key(path, key)
20
+ if File.exist?(path)
21
+ config(path)[key]
22
+ end
23
+ rescue Psych::SyntaxError => e
24
+ raise InvalidSyntaxError.new e
25
+ end
26
+
27
+ private
28
+
29
+ def config(path)
30
+ YAML.load_file(path) || {}
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,67 @@
1
+ require 'css_parser'
2
+
3
+ module Bookwatch
4
+ class CssLinkChecker
5
+ def broken_links_in_all_stylesheets
6
+ localized_links_in_stylesheets.reject { |link| target_exists?(link) }
7
+ end
8
+
9
+ private
10
+
11
+ def localized_links_in_stylesheets
12
+ links_in_stylesheets = []
13
+ Dir.glob('**/*.css').each { |stylesheet| links_in_stylesheets.concat localized_links_from(stylesheet) }
14
+
15
+ links_in_stylesheets
16
+ end
17
+
18
+ def localized_links_from(stylesheet)
19
+ localized_uris = []
20
+ css = CssParser::Parser.new
21
+ css.load_uri! stylesheet
22
+
23
+ css.each_selector do |s, declaration, sp|
24
+ contents_of_url_value = /url\((.*?)\)/
25
+ file_url = declaration.match contents_of_url_value
26
+ localized_uris << "#{URI(stylesheet).path} => #{file_url[1]}" if file_url
27
+ end
28
+
29
+ localized_uris
30
+ end
31
+
32
+ def target_exists?(localized_identifier)
33
+ dirs, link = strip_location(localized_identifier)
34
+ link.gsub!(/'|"/, '')
35
+
36
+ data_uri = /^['"]?data:image\//
37
+ remote_uri = /^['"]?https?:\/\//
38
+ absolute_uri = /^['"]?\//
39
+ relative_uri = //
40
+
41
+ case link
42
+ when data_uri then true
43
+ when remote_uri then http_reachable?(link)
44
+ when absolute_uri then File.exists?(File.join('.', 'public', path_info(link)))
45
+ when relative_uri then
46
+ dirs = dirs.split('/')[0...-1]
47
+ computed_uri = File.expand_path(File.join dirs, path_info(link))
48
+ File.exists?(computed_uri)
49
+ end
50
+ end
51
+
52
+ def path_info(link)
53
+ link.sub(/[?#].+\z/, '')
54
+ end
55
+
56
+ def strip_location(id)
57
+ dirs, filepath = id.split('=> ')
58
+ [dirs, filepath]
59
+ end
60
+
61
+ def http_reachable?(link)
62
+ Net::HTTP.get_response(URI(link)).code == '200'
63
+ rescue SocketError => e
64
+ return false
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,15 @@
1
+ module Bookwatch
2
+ module DirectoryHelperMethods
3
+
4
+ private
5
+
6
+ def output_dir_name
7
+ 'output'
8
+ end
9
+
10
+ def source_dir_name
11
+ 'source'
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,95 @@
1
+ module Bookwatch
2
+ class DitaCommandCreator
3
+ MissingDitaOTFlagValue = Class.new(RuntimeError)
4
+
5
+ def initialize(path_to_dita_ot_library)
6
+ @path_to_dita_ot_library = path_to_dita_ot_library
7
+ end
8
+
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
+
21
+ def convert_to_html_command(dita_section, dita_flags: nil, write_to: nil)
22
+ "export CLASSPATH=#{classpath}; " +
23
+ "ant -f #{path_to_dita_ot_library} " +
24
+ unduplicated_flags(
25
+ write_to: write_to,
26
+ dita_flags: dita_flags,
27
+ ditamap_path: dita_section.path_to_preprocessor_attribute('ditamap_location'),
28
+ ditaval_path: dita_section.path_to_preprocessor_attribute('ditaval_location'),
29
+ default_transtype: 'tocjs'
30
+ )
31
+ end
32
+
33
+ private
34
+
35
+ def unduplicated_flags(write_to: nil, ditamap_path: nil, ditaval_path: nil, dita_flags: nil, default_transtype: nil)
36
+ arg_flags = {
37
+ 'output.dir' => write_to,
38
+ 'args.input' => ditamap_path,
39
+ }.merge(filter(ditaval_path))
40
+ all_flags = arg_flags.merge(base_flags(default_transtype: default_transtype).merge(optional_flags(dita_flags)))
41
+ format(all_flags)
42
+ end
43
+
44
+ def filter(ditaval_path)
45
+ ditaval_path ? { 'args.filter' => ditaval_path } : {}
46
+ end
47
+
48
+ def base_flags(default_transtype: nil)
49
+ {
50
+ 'basedir' => '/',
51
+ 'transtype' => default_transtype,
52
+ 'dita.temp.dir' => '/tmp/bookwatch_dita',
53
+ 'generate.copy.outer' => '2',
54
+ 'outer.control' => 'warn'
55
+ }
56
+ end
57
+
58
+ def optional_flags(flags_str)
59
+ flags = flags_str ? flags_str.split(" ") : []
60
+ {}.tap do |h|
61
+ flags.each do |f|
62
+ k,v = f.split('=')
63
+ h[k] = v
64
+ raise MissingDitaOTFlagValue.new("The DITA-flag '#{k}' that you passed is missing a value. Please pass your DITA option in the format '#{k}=<value>'.") unless v
65
+ end
66
+ end
67
+ end
68
+
69
+ def format(flags)
70
+ flags.inject("") do |res, f|
71
+ k,v = f
72
+ res + "-D#{k}='#{stripped_flag_value v}' "
73
+ end
74
+ end
75
+
76
+ def stripped_flag_value(v)
77
+ v.to_s.gsub(/['|"]/, "")
78
+ end
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
+
93
+ attr_reader :path_to_dita_ot_library
94
+ end
95
+ end
@@ -0,0 +1,45 @@
1
+ module Bookwatch
2
+
3
+ class DitaHtmlForMiddlemanFormatter
4
+ def initialize(file_system_accessor, html_document_manipulator)
5
+ @file_system_accessor = file_system_accessor
6
+ @html_document_manipulator = html_document_manipulator
7
+ end
8
+
9
+ def format_html(src, dest)
10
+ all_files_with_ext = file_system_accessor.find_files_with_ext('html', src)
11
+
12
+ all_files_with_ext.map do |filepath|
13
+ file_text = file_system_accessor.read(filepath)
14
+
15
+ file_title_text = html_document_manipulator.read_html_in_tag(document: file_text,
16
+ tag: 'title')
17
+
18
+ file_body_text = html_document_manipulator.read_html_in_tag(document: file_text,
19
+ tag: 'body')
20
+
21
+ cleansed_body_text = revert_to_erb(file_body_text)
22
+
23
+ relative_path_to_file = file_system_accessor.relative_path_from(src, filepath)
24
+ new_filepath = File.join dest, "#{relative_path_to_file}.erb"
25
+
26
+ output_text = frontmatter(file_title_text) + cleansed_body_text
27
+
28
+ file_system_accessor.write(to: new_filepath, text: output_text)
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :file_system_accessor, :html_document_manipulator
35
+
36
+ def frontmatter(title)
37
+ sanitized_title = title.gsub('"', '\"')
38
+ "---\ntitle: \"#{sanitized_title}\"\ndita: true\n---\n"
39
+ end
40
+
41
+ def revert_to_erb(text)
42
+ text.gsub('&lt;%', '<%').gsub('%&gt;', '%>').gsub('&lt;\%', '&lt;%')
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,5 @@
1
+ module Bookwatch
2
+ module Errors
3
+ ProgrammerMistake = Class.new(RuntimeError)
4
+ end
5
+ end