bookbindery 6.0.0 → 7.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/bookbinder.gemspec +2 -2
  3. data/lib/bookbinder/commands/bind.rb +2 -1
  4. data/lib/bookbinder/commands/bind/directory_preparer.rb +3 -12
  5. data/lib/bookbinder/commands/bind/layout_preparer.rb +23 -0
  6. data/lib/bookbinder/commands/collection.rb +22 -8
  7. data/lib/bookbinder/commands/watch.rb +2 -1
  8. data/lib/bookbinder/config/checkers/subnavs_checker.rb +49 -0
  9. data/lib/bookbinder/config/checkers/topics_checker.rb +37 -0
  10. data/lib/bookbinder/config/configuration.rb +14 -1
  11. data/lib/bookbinder/config/fetcher.rb +27 -8
  12. data/lib/bookbinder/config/section_config.rb +4 -0
  13. data/lib/bookbinder/config/subnav_config.rb +41 -0
  14. data/lib/bookbinder/config/topic_config.rb +29 -0
  15. data/lib/bookbinder/config/validator.rb +5 -1
  16. data/lib/bookbinder/dita_command_creator.rb +2 -2
  17. data/lib/bookbinder/dita_html_to_middleman_formatter.rb +2 -2
  18. data/lib/bookbinder/ingest/section_repository.rb +1 -2
  19. data/lib/bookbinder/local_filesystem_accessor.rb +14 -0
  20. data/lib/bookbinder/preprocessing/dita_preprocessor.rb +2 -2
  21. data/lib/bookbinder/preprocessing/link_to_site_gen_dir.rb +15 -3
  22. data/lib/bookbinder/server_director.rb +1 -1
  23. data/lib/bookbinder/subnav/json_from_config.rb +77 -0
  24. data/lib/bookbinder/subnav/json_from_html.rb +38 -0
  25. data/lib/bookbinder/subnav/json_props_creator.rb +35 -0
  26. data/lib/bookbinder/subnav/pdf_config_creator.rb +52 -0
  27. data/lib/bookbinder/subnav/subnav_generator.rb +19 -0
  28. data/lib/bookbinder/subnav/subnav_generator_factory.rb +35 -0
  29. data/lib/bookbinder/subnav/template_creator.rb +41 -0
  30. data/lib/bookbinder/values/output_locations.rb +4 -0
  31. data/lib/bookbinder/values/section.rb +2 -1
  32. data/lib/bookbinder/values/{subnav.rb → subnav_template.rb} +2 -2
  33. data/master_middleman/archive_drop_down_menu.rb +4 -0
  34. data/master_middleman/bookbinder_helpers.rb +4 -3
  35. data/master_middleman/compass_runner.rb +0 -0
  36. metadata +17 -5
  37. data/lib/bookbinder/subnav_formatter.rb +0 -37
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: c73a7a9a4f263f7730ce38c6e09b98ae457a9571
4
- data.tar.gz: bbd94c2710ce6052e2ad401525b9e024a584245b
3
+ metadata.gz: 0fb5495aa9aaa1b4337177fd9659ff1770edb7d1
4
+ data.tar.gz: 6aaa6711faed7f97fb649b8f0a7d76b259fdbc6d
5
5
  SHA512:
6
- metadata.gz: e6dbef842e82feb7bb9e4ab4dd273cc25bee08d6fb0ce4119f041c304e461ecca957269b2a96b96677e8ab03e8bfb05579654a9b47f1ff4cebe3e82723d76e5a
7
- data.tar.gz: c6331e3c72ee6a989f26b879d1234f2e9ae1d809b10c50bc6e2e8e633157ee155d6ffa97b7243e1d9f7944b18d532d0ed1eaa517fb7876f3fae6377db8883e2f
6
+ metadata.gz: d9d3278fb5effbab9c2591a5700685a78af34d9095a28d16dd1eb95144188fa498607a920cbc30224347ae972ec7e870a6a88be060e7091e7961fd609762865b
7
+ data.tar.gz: ae8ebb4008aedf58c261ce94ab0569ea8bec4caacf367ce9c2b5f611523b5793cc9650b9f987579cb95b507fd64f20d5a5ed5b1d5b688e345c75a656ee6cd98b
data/bookbinder.gemspec CHANGED
@@ -2,7 +2,7 @@ require 'base64'
2
2
 
3
3
  Gem::Specification.new do |s|
4
4
  s.name = 'bookbindery'
5
- s.version = '6.0.0'
5
+ s.version = '7.0.0'
6
6
  s.summary = 'Markdown to Rackup application documentation generator'
7
7
  s.description = 'A command line utility to be run in Book repositories to stitch together their constituent Markdown repos into a static-HTML-serving application'
8
8
  s.authors = ['Mike Grafton', 'Lucas Marks', 'Gavin Morgan', 'Nikhil Gajwani', 'Dan Wendorf', 'Brenda Chan', 'Matthew Boedicker', 'Frank Kotsianas', 'Elena Sharma', 'Christa Hartsock']
@@ -14,7 +14,7 @@ Gem::Specification.new do |s|
14
14
  s.bindir = 'install_bin'
15
15
  s.executable = 'bookbinder'
16
16
 
17
- s.required_ruby_version = '~> 2.0'
17
+ s.required_ruby_version = '>= 2.0'
18
18
  s.add_runtime_dependency 'fog-aws', ['~> 0.7.1']
19
19
  s.add_runtime_dependency 'ansi', ['~> 1.4']
20
20
  s.add_runtime_dependency 'unf', ['~> 0.1']
@@ -68,7 +68,8 @@ module Bookbinder
68
68
  sections,
69
69
  output_locations,
70
70
  options: bind_options.options,
71
- output_streams: bind_options.streams
71
+ output_streams: bind_options.streams,
72
+ config: bind_config
72
73
  )
73
74
  if file_system_accessor.file_exist?('redirects.rb')
74
75
  file_system_accessor.copy('redirects.rb', output_locations.final_app_dir)
@@ -1,3 +1,5 @@
1
+ require_relative 'layout_preparer'
2
+
1
3
  module Bookbinder
2
4
  module Commands
3
5
  module BindComponents
@@ -13,8 +15,7 @@ module Bookbinder
13
15
  copy_directory_from_gem(gem_root, 'template_app', output_locations.final_app_dir)
14
16
  copy_directory_from_gem(gem_root, 'master_middleman', output_locations.site_generator_home)
15
17
 
16
- layout_repo_path = fetch_layout_repo(config, cloner)
17
- fs.copy_contents(layout_repo_path, output_locations.site_generator_home)
18
+ LayoutPreparer.new(fs).prepare(output_locations, cloner, config)
18
19
  end
19
20
 
20
21
  private
@@ -24,16 +25,6 @@ module Bookbinder
24
25
  def copy_directory_from_gem(gem_root, dir, output_dir)
25
26
  fs.copy_contents(File.join(gem_root, dir), output_dir)
26
27
  end
27
-
28
- def fetch_layout_repo(config, cloner)
29
- if config.has_option?('layout_repo')
30
- cloned_repo = cloner.call(source_repo_name: config.layout_repo,
31
- destination_parent_dir: Dir.mktmpdir)
32
- cloned_repo.path
33
- else
34
- File.absolute_path('master_middleman')
35
- end
36
- end
37
28
  end
38
29
  end
39
30
  end
@@ -0,0 +1,23 @@
1
+ module Bookbinder
2
+ module Commands
3
+ module BindComponents
4
+ class LayoutPreparer
5
+ def initialize(fs)
6
+ @fs = fs
7
+ end
8
+
9
+ attr_reader :fs
10
+
11
+ def prepare(output_locations, cloner, config)
12
+ if config.has_option?('layout_repo')
13
+ cloned_repo = cloner.call(source_repo_name: config.layout_repo,
14
+ destination_parent_dir: Dir.mktmpdir)
15
+
16
+ fs.copy_contents(cloned_repo.path, output_locations.site_generator_home)
17
+ end
18
+ fs.copy_contents(File.absolute_path('master_middleman'), output_locations.site_generator_home)
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -19,8 +19,9 @@ require_relative '../postprocessing/sitemap_writer'
19
19
  require_relative '../preprocessing/dita_preprocessor'
20
20
  require_relative '../preprocessing/link_to_site_gen_dir'
21
21
  require_relative '../preprocessing/preprocessor'
22
+ require_relative '../subnav/subnav_generator_factory'
22
23
  require_relative '../sheller'
23
- require_relative '../subnav_formatter'
24
+ require_relative '../subnav/json_from_html'
24
25
  require_relative '../values/output_locations'
25
26
 
26
27
  module Bookbinder
@@ -87,7 +88,7 @@ module Bookbinder
87
88
  def bind
88
89
  @bind ||= Commands::Bind.new(
89
90
  streams,
90
- OutputLocations.new(final_app_dir: final_app_directory, context_dir: File.absolute_path('.')),
91
+ output_locations,
91
92
  configuration_fetcher,
92
93
  Config::ArchiveMenuConfiguration.new(loader: config_loader, config_filename: 'bookbinder.yml'),
93
94
  local_filesystem_accessor,
@@ -95,12 +96,12 @@ module Bookbinder
95
96
  Postprocessing::SitemapWriter.build(logger, final_app_directory, sitemap_port),
96
97
  Preprocessing::Preprocessor.new(
97
98
  Preprocessing::DitaPreprocessor.new(
98
- DitaHtmlToMiddlemanFormatter.new(local_filesystem_accessor, subnav_formatter, html_document_manipulator),
99
+ DitaHtmlToMiddlemanFormatter.new(local_filesystem_accessor, dita_json_generator, html_document_manipulator),
99
100
  local_filesystem_accessor,
100
101
  DitaCommandCreator.new(ENV['PATH_TO_DITA_OT_LIBRARY']),
101
102
  sheller
102
103
  ),
103
- Preprocessing::LinkToSiteGenDir.new(local_filesystem_accessor),
104
+ Preprocessing::LinkToSiteGenDir.new(local_filesystem_accessor, subnav_generator_factory)
104
105
  ),
105
106
  Ingest::ClonerFactory.new(streams, local_filesystem_accessor, version_control_system),
106
107
  Ingest::SectionRepository.new,
@@ -112,11 +113,11 @@ module Bookbinder
112
113
  @watch ||= Commands::Watch.new(
113
114
  streams,
114
115
  middleman_runner: runner,
115
- output_locations: OutputLocations.new(final_app_dir: final_app_directory, context_dir: File.absolute_path('.')),
116
+ output_locations: output_locations,
116
117
  config_fetcher: configuration_fetcher,
117
118
  config_decorator: Config::ArchiveMenuConfiguration.new(loader: config_loader, config_filename: 'bookbinder.yml'),
118
119
  file_system_accessor: local_filesystem_accessor,
119
- preprocessor: Preprocessing::Preprocessor.new(Preprocessing::LinkToSiteGenDir.new(local_filesystem_accessor)),
120
+ preprocessor: Preprocessing::Preprocessor.new(Preprocessing::LinkToSiteGenDir.new(local_filesystem_accessor, subnav_generator_factory)),
120
121
  cloner: local_file_system_cloner,
121
122
  section_repository: Ingest::SectionRepository.new,
122
123
  directory_preparer: directory_preparer
@@ -146,6 +147,7 @@ module Bookbinder
146
147
  Config::RemoteYamlCredentialProvider.new(logger, version_control_system)
147
148
  ).tap do |fetcher|
148
149
  fetcher.set_config_file_path './config.yml'
150
+ fetcher.set_config_dir_path './config/'
149
151
  end
150
152
  end
151
153
 
@@ -153,16 +155,28 @@ module Bookbinder
153
155
  @config_loader ||= Config::YAMLLoader.new
154
156
  end
155
157
 
158
+ def subnav_generator_factory
159
+ Subnav::SubnavGeneratorFactory.new(local_filesystem_accessor, output_locations)
160
+ end
161
+
162
+ def json_generator
163
+ Subnav::JsonFromConfig.new
164
+ end
165
+
156
166
  def directory_preparer
157
167
  Commands::BindComponents::DirectoryPreparer.new(local_filesystem_accessor)
158
168
  end
159
169
 
170
+ def output_locations
171
+ OutputLocations.new(final_app_dir: final_app_directory, context_dir: File.absolute_path('.'))
172
+ end
173
+
160
174
  def final_app_directory
161
175
  @final_app_directory ||= File.absolute_path('final_app')
162
176
  end
163
177
 
164
- def subnav_formatter
165
- @subnav_formatter ||= SubnavFormatter.new
178
+ def dita_json_generator
179
+ Subnav::JsonFromHtml.new
166
180
  end
167
181
 
168
182
  def html_document_manipulator
@@ -51,7 +51,8 @@ module Bookbinder
51
51
  preprocessor.preprocess(
52
52
  sections,
53
53
  output_locations,
54
- output_streams: streams
54
+ output_streams: streams,
55
+ config: watch_config
55
56
  )
56
57
  if file_system_accessor.file_exist?('redirects.rb')
57
58
  file_system_accessor.copy('redirects.rb', output_locations.final_app_dir)
@@ -0,0 +1,49 @@
1
+ module Bookbinder
2
+ module Config
3
+ module Checkers
4
+ class SubnavsChecker
5
+ MissingRequiredKeyError = Class.new(RuntimeError)
6
+ MissingSubnavsKeyError = Class.new(RuntimeError)
7
+ MissingSubnavNameError = Class.new(RuntimeError)
8
+
9
+ def check(config)
10
+ @config = config
11
+
12
+ if section_subnav_names.count > 0
13
+ if config.subnavs.empty?
14
+ MissingSubnavsKeyError.new("You must specify at least one subnav under the subnavs key in config.yml")
15
+ elsif missing_subnavs.count != 0
16
+ MissingSubnavNameError.new("Your config.yml is missing required subnav names under the subnavs key. Required subnav names are #{missing_subnavs.join(", ")}.")
17
+ elsif invalid_subnavs.any?
18
+ MissingRequiredKeyError.new("Your config.yml is missing required key(s) for subnavs #{invalid_subnav_names}. Required keys are #{required_subnav_keys.join(", ")}.")
19
+ end
20
+ end
21
+ end
22
+
23
+ attr_reader :config
24
+
25
+ private
26
+
27
+ def invalid_subnavs
28
+ config.subnavs.map {|subnav_config| subnav_config unless subnav_config.valid? }
29
+ end
30
+
31
+ def invalid_subnav_names
32
+ invalid_subnavs.map(&:name).join(", ")
33
+ end
34
+
35
+ def missing_subnavs
36
+ section_subnav_names - config.subnavs.map(&:name)
37
+ end
38
+
39
+ def required_subnav_keys
40
+ Bookbinder::Config::SubnavConfig::CONFIG_REQUIRED_KEYS
41
+ end
42
+
43
+ def section_subnav_names
44
+ config.sections.map(&:subnav_name).compact.uniq
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,37 @@
1
+ module Bookbinder
2
+ module Config
3
+ module Checkers
4
+ class TopicsChecker
5
+ MissingRequiredKeyError = Class.new(RuntimeError)
6
+
7
+ def check(config)
8
+ @config = config
9
+
10
+ if invalid_subnavs.any?
11
+ MissingRequiredKeyError.new("Your config.yml is missing required key(s) for subnav(s) #{invalid_subnav_names}. Required keys are #{required_topic_keys.join(", ")}.")
12
+ end
13
+ end
14
+
15
+ attr_reader :config
16
+
17
+ private
18
+
19
+ def invalid_subnavs
20
+ config.subnavs.select { |subnav_config| invalid_topics(subnav_config.topics).any? }
21
+ end
22
+
23
+ def invalid_topics(topics)
24
+ topics.map {|topic| topic unless topic.valid? }
25
+ end
26
+
27
+ def invalid_subnav_names
28
+ invalid_subnavs.map(&:name).join(', ')
29
+ end
30
+
31
+ def required_topic_keys
32
+ Bookbinder::Config::TopicConfig::CONFIG_REQUIRED_KEYS
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
@@ -1,6 +1,7 @@
1
1
  require_relative '../ingest/destination_directory'
2
2
  require_relative '../ingest/repo_identifier'
3
3
  require_relative 'section_config'
4
+ require_relative 'subnav_config'
4
5
 
5
6
  module Bookbinder
6
7
  module Config
@@ -65,10 +66,11 @@ module Bookbinder
65
66
 
66
67
  def initialize(config)
67
68
  @config = config
69
+ @subnavs = assemble_subnavs || []
68
70
  end
69
71
 
70
72
  CONFIG_REQUIRED_KEYS = %w(book_repo public_host)
71
- CONFIG_OPTIONAL_KEYS = %w(archive_menu book_repo_url cred_repo cred_repo_url layout_repo layout_repo_url sections)
73
+ CONFIG_OPTIONAL_KEYS = %w(archive_menu book_repo_url cred_repo cred_repo_url layout_repo layout_repo_url sections subnav_exclusions)
72
74
 
73
75
  CONFIG_REQUIRED_KEYS.each do |method_name|
74
76
  define_method(method_name) do
@@ -108,8 +110,19 @@ module Bookbinder
108
110
 
109
111
  alias_method :eql?, :==
110
112
 
113
+ attr_reader :subnavs
114
+
111
115
  private
112
116
 
117
+ def assemble_subnavs
118
+ if config[:subnavs]
119
+ config[:subnavs].map do |subnav|
120
+ subnav.merge!({'subnav_exclusions' => subnav_exclusions})
121
+ Config::SubnavConfig.new(subnav)
122
+ end
123
+ end
124
+ end
125
+
113
126
  attr_reader :config
114
127
  end
115
128
  end
@@ -13,7 +13,10 @@ module Bookbinder
13
13
  end
14
14
 
15
15
  def fetch_config
16
- @config ||= validate(read_config_file)
16
+ @base_config ||= read_config_file
17
+ @optional_configs ||= read_optional_configs
18
+
19
+ @config ||= validate(@base_config, @optional_configs)
17
20
  end
18
21
 
19
22
  def fetch_credentials(environment = 'null-environment')
@@ -29,13 +32,17 @@ module Bookbinder
29
32
  }
30
33
  end
31
34
 
32
- def set_config_file_path config_file_path
33
- @config_file_path = File.expand_path config_file_path
35
+ def set_config_dir_path(config_dir_path)
36
+ @config_dir_path = File.expand_path(config_dir_path)
37
+ end
38
+
39
+ def set_config_file_path(config_file_path)
40
+ @config_file_path = File.expand_path(config_file_path)
34
41
  end
35
42
 
36
43
  private
37
44
 
38
- attr_reader(:loader, :configuration_validator, :config, :config_file_path,
45
+ attr_reader(:loader, :configuration_validator, :config, :config_file_path, :config_dir_path,
39
46
  :credentials_provider)
40
47
 
41
48
  def read_config_file
@@ -44,17 +51,29 @@ module Bookbinder
44
51
  rescue FileNotFoundError => e
45
52
  raise "The configuration file specified does not exist. Please create a config #{e} file at #{config_file_path} and try again."
46
53
  rescue InvalidSyntaxError => e
47
- raise "There is a syntax error in your config file: \n #{e}"
54
+ raise syntax_error(e)
55
+ end
56
+
57
+ def read_optional_configs
58
+ Dir["#{config_dir_path}/*.yml"].map do |config_file|
59
+ loader.load(File.expand_path(config_file)) || {}
60
+ end.reduce({}, :merge)
61
+ rescue InvalidSyntaxError => e
62
+ raise syntax_error(e)
48
63
  end
49
64
 
50
- def validate(config_hash)
51
- raise 'Your config.yml appears to be empty. Please check and try again.' unless config_hash
65
+ def validate(base_hash, optional_hash)
66
+ raise 'Your config.yml appears to be empty. Please check and try again.' unless base_hash
52
67
 
53
- Configuration.parse(config_hash).tap do |config|
68
+ Configuration.parse(base_hash.merge(optional_hash)).tap do |config|
54
69
  errors = configuration_validator.exceptions(config)
55
70
  raise errors.first if errors.any?
56
71
  end
57
72
  end
73
+
74
+ def syntax_error(e)
75
+ "There is a syntax error in your config file: \n #{e}"
76
+ end
58
77
  end
59
78
  end
60
79
  end
@@ -11,6 +11,10 @@ module Bookbinder
11
11
  config['subnav_template']
12
12
  end
13
13
 
14
+ def subnav_name
15
+ config['subnav_name']
16
+ end
17
+
14
18
  def desired_directory_name
15
19
  config['directory']
16
20
  end
@@ -0,0 +1,41 @@
1
+ require_relative '../config/topic_config'
2
+
3
+ module Bookbinder
4
+ module Config
5
+ class SubnavConfig
6
+ def initialize(config)
7
+ @config = config
8
+ @topics = assemble_topics || []
9
+ end
10
+
11
+ def name
12
+ config['name']
13
+ end
14
+
15
+ def pdf_config
16
+ config['pdf_config']
17
+ end
18
+
19
+ def subnav_exclusions
20
+ config['subnav_exclusions'] || []
21
+ end
22
+
23
+ def valid?
24
+ (CONFIG_REQUIRED_KEYS - config.keys).empty?
25
+ end
26
+
27
+ CONFIG_REQUIRED_KEYS = %w(name topics)
28
+
29
+ attr_reader :topics
30
+
31
+ private
32
+
33
+ attr_reader :config
34
+
35
+ def assemble_topics
36
+ config['topics'].map{|topic| Config::TopicConfig.new(topic)} if config['topics']
37
+ end
38
+ end
39
+ end
40
+ end
41
+
@@ -0,0 +1,29 @@
1
+ module Bookbinder
2
+ module Config
3
+ class TopicConfig
4
+ def initialize(config)
5
+ @config = config
6
+ end
7
+
8
+ def title
9
+ config['title']
10
+ end
11
+
12
+ def toc_path
13
+ Pathname(config['toc_path'])
14
+ end
15
+
16
+ def toc_nav_name
17
+ config['toc_nav_name'] || title
18
+ end
19
+
20
+ def valid?
21
+ (CONFIG_REQUIRED_KEYS - config.keys).empty?
22
+ end
23
+
24
+ CONFIG_REQUIRED_KEYS = %w(title toc_path)
25
+
26
+ attr_reader :config
27
+ end
28
+ end
29
+ end
@@ -3,6 +3,8 @@ require_relative 'checkers/archive_menu_checker'
3
3
  require_relative 'checkers/required_keys_checker'
4
4
  require_relative 'checkers/repository_name_presence_checker'
5
5
  require_relative 'checkers/dita_section_checker'
6
+ require_relative 'checkers/subnavs_checker'
7
+ require_relative 'checkers/topics_checker'
6
8
 
7
9
  module Bookbinder
8
10
  module Config
@@ -17,7 +19,9 @@ module Bookbinder
17
19
  Checkers::DuplicateSectionNameChecker.new,
18
20
  Checkers::RepositoryNamePresenceChecker.new,
19
21
  Checkers::DitaSectionChecker.new,
20
- Checkers::ArchiveMenuChecker.new(@file_system_accessor)
22
+ Checkers::ArchiveMenuChecker.new(@file_system_accessor),
23
+ Checkers::SubnavsChecker.new,
24
+ Checkers::TopicsChecker.new
21
25
  ].map do |checker|
22
26
  checker.check(config)
23
27
  end
@@ -24,13 +24,13 @@ module Bookbinder
24
24
  write_to: write_to,
25
25
  dita_flags: dita_flags,
26
26
  ditamap_path: dita_section.path_to_preprocessor_attribute('ditamap_location'),
27
- ditaval_path: dita_section.path_to_preprocessor_attribute('ditaval_location'),
27
+ ditaval_path: dita_section.path_to_preprocessor_attribute('ditaval_location')
28
28
  )
29
29
  end
30
30
 
31
31
  private
32
32
 
33
- def unduplicated_flags(write_to: nil, ditamap_path: nil, ditaval_path: nil, dita_flags: dita_flags)
33
+ def unduplicated_flags(write_to: nil, ditamap_path: nil, ditaval_path: nil, dita_flags: nil)
34
34
  arg_flags = {
35
35
  'output.dir' => write_to,
36
36
  'args.input' => ditamap_path,
@@ -1,4 +1,4 @@
1
- require_relative 'values/subnav'
1
+ require_relative 'values/subnav_template'
2
2
 
3
3
  module Bookbinder
4
4
 
@@ -40,7 +40,7 @@ module Bookbinder
40
40
  selector: 'div.nav-content',
41
41
  attribute: 'data-props-location',
42
42
  value: json_props_location)
43
- Subnav.new(formatted_json_links, nav_text)
43
+ SubnavTemplate.new(formatted_json_links, nav_text)
44
44
  end
45
45
 
46
46
  private
@@ -1,5 +1,3 @@
1
- require 'tmpdir'
2
- require_relative '../deprecated_logger'
3
1
  require_relative '../values/section'
4
2
 
5
3
  module Bookbinder
@@ -21,6 +19,7 @@ module Bookbinder
21
19
  working_copy.full_name,
22
20
  section_config.desired_directory_name,
23
21
  section_config.subnav_template,
22
+ section_config.subnav_name,
24
23
  section_config.preprocessor_config
25
24
  )
26
25
  end
@@ -20,6 +20,11 @@ module Bookbinder
20
20
  to
21
21
  end
22
22
 
23
+ def overwrite(to: nil, text: nil)
24
+ File.delete(to) if file_exist?(to)
25
+ write(to: to, text: text)
26
+ end
27
+
23
28
  def read(path)
24
29
  File.read(path)
25
30
  end
@@ -80,5 +85,14 @@ module Bookbinder
80
85
  reject {|p| p.to_s.match %r{/\.}}.
81
86
  reject(&:directory?)
82
87
  end
88
+
89
+ def find_files_extension_agnostically(pattern, directory='.')
90
+ extensionless_pattern = pattern.to_s.split('.').first
91
+
92
+ `find -L #{directory} -path '*/#{extensionless_pattern}.*'`.
93
+ lines.
94
+ map(&:chomp).
95
+ map(&Pathname.method(:new))
96
+ end
83
97
  end
84
98
  end
@@ -1,4 +1,4 @@
1
- require_relative '../values/subnav'
1
+ require_relative '../values/subnav_template'
2
2
 
3
3
  module Bookbinder
4
4
  module Preprocessing
@@ -18,7 +18,7 @@ module Bookbinder
18
18
  section.subnav_template.include?('dita_subnav') if section.subnav_template
19
19
  end
20
20
 
21
- def preprocess(dita_sections, output_locations, options: [], output_streams: nil)
21
+ def preprocess(dita_sections, output_locations, options: [], output_streams: nil, **_)
22
22
  dita_options = dita_flags(options)
23
23
  dita_sections.each do |dita_section|
24
24
  if dita_section.path_to_preprocessor_attribute('ditamap_location')
@@ -1,26 +1,38 @@
1
+ require_relative '../subnav/subnav_generator'
2
+ require_relative '../subnav/json_from_config'
3
+
1
4
  module Bookbinder
2
5
  module Preprocessing
3
6
  class LinkToSiteGenDir
4
- def initialize(filesystem)
7
+ def initialize(filesystem, subnav_generator_factory)
5
8
  @filesystem = filesystem
9
+ @subnav_generator_factory = subnav_generator_factory
6
10
  end
7
11
 
8
12
  def applicable_to?(section)
9
13
  filesystem.file_exist?(section.path_to_repository)
10
14
  end
11
15
 
12
- def preprocess(sections, output_locations, *_)
16
+ def preprocess(sections, output_locations, config: nil, **_)
13
17
  sections.each do |section|
14
18
  filesystem.link_creating_intermediate_dirs(
15
19
  section.path_to_repository,
16
20
  output_locations.source_for_site_generator.join(section.destination_directory)
17
21
  )
18
22
  end
23
+
24
+ config.subnavs.each do |subnav|
25
+ subnav_generator.generate(subnav)
26
+ end
19
27
  end
20
28
 
21
29
  private
22
30
 
23
- attr_reader :filesystem
31
+ def subnav_generator
32
+ @subnav_generator ||= subnav_generator_factory.produce(Subnav::JsonFromConfig.new(filesystem))
33
+ end
34
+
35
+ attr_reader :filesystem, :subnav_generator_factory
24
36
  end
25
37
  end
26
38
  end
@@ -2,7 +2,7 @@ require 'puma'
2
2
 
3
3
  module Bookbinder
4
4
  class ServerDirector
5
- def initialize(app: app, directory: nil, port: 41722)
5
+ def initialize(app: nil, directory: nil, port: 41722)
6
6
  @app = app
7
7
  @directory = directory
8
8
  @port = port
@@ -0,0 +1,77 @@
1
+ require 'json'
2
+ require 'nokogiri'
3
+ require 'redcarpet'
4
+
5
+ module Bookbinder
6
+ module Subnav
7
+ class JsonFromConfig
8
+ def initialize(fs)
9
+ @fs = fs
10
+ @renderer = Redcarpet::Markdown.new(Redcarpet::Render::HTML.new)
11
+ end
12
+
13
+ def get_links(subnav_config, source_for_site_gen)
14
+ @source_for_site_gen = source_for_site_gen
15
+ @config = subnav_config
16
+
17
+ { links: get_links_and_headers }.to_json
18
+ end
19
+
20
+ attr_reader :fs, :source_for_site_gen, :renderer, :config
21
+
22
+ private
23
+
24
+ def get_links_and_headers
25
+ menu_items = []
26
+
27
+ config.topics.map do |topic|
28
+ menu_items << { text: topic.title, title: true }
29
+ menu_items << { url: "/#{topic.toc_path}.html", text: topic.toc_nav_name }
30
+
31
+ links_from_toc_page = parse_toc_file(topic)
32
+ links_from_toc_page.each {|link| menu_items << link}
33
+ end
34
+
35
+ menu_items
36
+ end
37
+
38
+ def parse_toc_file(topic)
39
+ toc_files = fs.find_files_extension_agnostically(topic.toc_path, source_for_site_gen)
40
+ toc_md = fs.read(toc_files.first)
41
+
42
+ toc_html = get_html(toc_md)
43
+
44
+ gather_urls_and_texts(toc_html.css('html'), topic)
45
+ end
46
+
47
+ def get_html(md)
48
+ Nokogiri::HTML(renderer.render(md))
49
+ end
50
+
51
+ def gather_urls_and_texts(base_node, topic)
52
+ nav_items(base_node).map do |element|
53
+ if element.name == 'h2' && !frontmatter_header?(element)
54
+ {text: element.inner_html}
55
+ else
56
+ list_elements = element.css('li > a', 'li > p > a')
57
+ list_elements.map do |li|
58
+ { url: "/#{topic.toc_path.dirname.join(li['href'])}", text: li.inner_text }
59
+ end
60
+ end
61
+ end.flatten
62
+ end
63
+
64
+ def nav_items(base_node)
65
+ base_node.css('h2, ul') - base_node.css(*exclusions)
66
+ end
67
+
68
+ def exclusions
69
+ @exclusions ||= config.subnav_exclusions << '.nav-exclude'
70
+ end
71
+
72
+ def frontmatter_header?(element)
73
+ element.inner_html.include?('title: ')
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,38 @@
1
+ require 'nokogiri'
2
+ require 'active_support/all'
3
+
4
+ module Bookbinder
5
+ module Subnav
6
+ class JsonFromHtml
7
+ def get_links_as_json(raw_subnav_text, base_dirname)
8
+ doc = Nokogiri::XML(raw_subnav_text)
9
+
10
+ all_anchors = doc.css('a')
11
+ all_anchors.each do |anchor|
12
+ anchor['href'] = "/#{base_dirname}/#{anchor['href']}"
13
+ end
14
+
15
+ {
16
+ links: gather_urls_and_texts(doc.css('body > ul'))
17
+ }.to_json
18
+ end
19
+
20
+ private
21
+
22
+ def gather_urls_and_texts(base_node)
23
+ top_level_li = base_node.css("> li")
24
+ top_level_li.map do |li|
25
+ anchor = li.css('a')[0]
26
+ href = anchor['href']
27
+ text = anchor.inner_text
28
+ ul = li.css('> ul')
29
+ if ul.size > 0
30
+ {url: href, text: text, nestedLinks: gather_urls_and_texts(ul)}
31
+ else
32
+ {url: href, text: text}
33
+ end
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,35 @@
1
+ module Bookbinder
2
+ module Subnav
3
+ class JsonPropsCreator
4
+ def initialize(fs, output_locations, json_generator)
5
+ @fs = fs
6
+ @output_locations = output_locations
7
+ @json_generator = json_generator
8
+ end
9
+
10
+ def create(subnav_config)
11
+ json_links = json_generator.get_links(subnav_config, output_locations.source_for_site_generator)
12
+
13
+ fs.write(text: json_links, to: props_path(subnav_config.name))
14
+
15
+ filename(subnav_config.name)
16
+ end
17
+
18
+ attr_reader :fs, :output_locations, :json_generator
19
+
20
+ private
21
+
22
+ def filename(name)
23
+ "#{name}-props.json"
24
+ end
25
+
26
+ def subnavs_path
27
+ output_locations.subnavs_for_layout_dir
28
+ end
29
+
30
+ def props_path(name)
31
+ subnavs_path.join(filename(name))
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,52 @@
1
+ require 'yaml'
2
+ require "json"
3
+
4
+ module Bookbinder
5
+ module Subnav
6
+ class PdfConfigCreator
7
+ def initialize(fs, output_locations)
8
+ @fs = fs
9
+ @output_locations = output_locations
10
+ end
11
+
12
+ def create(props_filename, subnav_config)
13
+ json = JSON.parse(fs.read(props_location(props_filename)))
14
+ @links = format_links(json['links'])
15
+
16
+ fs.overwrite(to: output_locations.pdf_config_dir.join(subnav_config.pdf_config),
17
+ text: config_content)
18
+ end
19
+
20
+ attr_reader :fs, :output_locations
21
+
22
+ private
23
+
24
+ def props_location(filename)
25
+ output_locations.subnavs_for_layout_dir.join(filename)
26
+ end
27
+
28
+ def format_links(links)
29
+ links.map{|item| item['url'] }.compact.map{|link| link.sub(/^\//, '')}
30
+ end
31
+
32
+ def config_content
33
+ config_keys.inject({}) do |hash, key|
34
+ hash[key] = content_for(key)
35
+ hash
36
+ end.to_yaml
37
+ end
38
+
39
+ def config_keys
40
+ %w{copyright_notice header executable pages}
41
+ end
42
+
43
+ def content_for(key)
44
+ key == 'pages' ? @links : default_content
45
+ end
46
+
47
+ def default_content
48
+ 'REPLACE ME'
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,19 @@
1
+ module Bookbinder
2
+ module Subnav
3
+ class SubnavGenerator
4
+ def initialize(json_props_creator, template_creator, pdf_config_creator)
5
+ @json_props_creator = json_props_creator
6
+ @template_creator = template_creator
7
+ @pdf_config_creator = pdf_config_creator
8
+ end
9
+
10
+ def generate(subnav_config)
11
+ filename = json_props_creator.create(subnav_config)
12
+ template_creator.create(filename, subnav_config)
13
+ pdf_config_creator.create(filename, subnav_config) if subnav_config.pdf_config
14
+ end
15
+
16
+ attr_reader :json_props_creator, :template_creator, :pdf_config_creator
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,35 @@
1
+ require_relative 'json_props_creator'
2
+ require_relative 'template_creator'
3
+ require_relative 'pdf_config_creator'
4
+ require_relative '../html_document_manipulator'
5
+
6
+ module Bookbinder
7
+ module Subnav
8
+ class SubnavGeneratorFactory
9
+ def initialize(fs, output_locations)
10
+ @fs = fs
11
+ @output_locations = output_locations
12
+ end
13
+
14
+ def produce(json_generator)
15
+ SubnavGenerator.new(json_props_creator(json_generator), template_creator, pdf_config_creator)
16
+ end
17
+
18
+ attr_reader :fs, :output_locations
19
+
20
+ private
21
+
22
+ def json_props_creator(json_generator)
23
+ @json_props_creator ||= JsonPropsCreator.new(fs, output_locations, json_generator)
24
+ end
25
+
26
+ def template_creator
27
+ @template_creator ||= TemplateCreator.new(fs, output_locations, HtmlDocumentManipulator.new)
28
+ end
29
+
30
+ def pdf_config_creator
31
+ @pdf_config_creator ||= PdfConfigCreator.new(fs, output_locations)
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,41 @@
1
+ module Bookbinder
2
+ module Subnav
3
+ class TemplateCreator
4
+ def initialize(fs, output_locations, html_doc_manipulator)
5
+ @fs = fs
6
+ @output_locations = output_locations
7
+ @html_doc_manipulator = html_doc_manipulator
8
+ end
9
+
10
+ def create(props_filename, subnav_config)
11
+ template_content = fs.read(template_path)
12
+ nav_content = html_doc_manipulator.set_attribute(document: template_content,
13
+ selector: 'div.nav-content',
14
+ attribute: 'data-props-location',
15
+ value: props_filename)
16
+
17
+ fs.write(text: nav_content, to: subnav_destination(subnav_config.name))
18
+ end
19
+
20
+ attr_reader :fs, :output_locations, :html_doc_manipulator
21
+
22
+ private
23
+
24
+ def subnavs_path
25
+ output_locations.subnavs_for_layout_dir
26
+ end
27
+
28
+ def filename(name)
29
+ "#{name}.erb"
30
+ end
31
+
32
+ def template_path
33
+ subnavs_path.join('subnav_template.erb')
34
+ end
35
+
36
+ def subnav_destination(name)
37
+ subnavs_path.join(filename(name))
38
+ end
39
+ end
40
+ end
41
+ end
@@ -62,6 +62,10 @@ module Bookbinder
62
62
  source_for_site_generator.join('subnavs')
63
63
  end
64
64
 
65
+ def pdf_config_dir
66
+ context_dir
67
+ end
68
+
65
69
  private
66
70
 
67
71
  def context_dir
@@ -6,6 +6,7 @@ module Bookbinder
6
6
  :full_name,
7
7
  :desired_directory_name,
8
8
  :subnav_templ,
9
+ :subnav_name,
9
10
  :preprocessor_config) do
10
11
  def path_to_repository
11
12
  Pathname(self[:path_to_repository].to_s)
@@ -21,7 +22,7 @@ module Bookbinder
21
22
 
22
23
  def subnav
23
24
  namespace = destination_directory.to_s.gsub('/', '_')
24
- template = subnav_template || 'default'
25
+ template = subnav_template || subnav_name || 'default'
25
26
  {namespace => template}
26
27
  end
27
28
 
@@ -1,4 +1,4 @@
1
1
  module Bookbinder
2
- Subnav = Struct.new(:json_links,
2
+ SubnavTemplate = Struct.new(:json_links,
3
3
  :text)
4
- end
4
+ end
@@ -15,6 +15,10 @@ module Bookbinder
15
15
  }
16
16
  end
17
17
 
18
+ def empty?
19
+ title.nil? && dropdown_links.empty?
20
+ end
21
+
18
22
  private
19
23
 
20
24
  attr_reader :config, :current_path
@@ -48,9 +48,10 @@ module Bookbinder
48
48
  config[:archive_menu],
49
49
  current_path: current_page.path
50
50
  )
51
-
52
- partial 'archive_menus/default', locals: { menu_title: menu.title,
53
- dropdown_links: menu.dropdown_links }
51
+ unless menu.empty?
52
+ partial 'archive_menus/default', locals: { menu_title: menu.title,
53
+ dropdown_links: menu.dropdown_links }
54
+ end
54
55
  end
55
56
 
56
57
  def modified_date(format="%B %-d, %Y")
File without changes
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: bookbindery
3
3
  version: !ruby/object:Gem::Version
4
- version: 6.0.0
4
+ version: 7.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mike Grafton
@@ -17,7 +17,7 @@ authors:
17
17
  autorequire:
18
18
  bindir: install_bin
19
19
  cert_chain: []
20
- date: 2015-09-08 00:00:00.000000000 Z
20
+ date: 2015-10-10 00:00:00.000000000 Z
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency
23
23
  name: fog-aws
@@ -301,6 +301,7 @@ files:
301
301
  - lib/bookbinder/command_validator.rb
302
302
  - lib/bookbinder/commands/bind/bind_options.rb
303
303
  - lib/bookbinder/commands/bind/directory_preparer.rb
304
+ - lib/bookbinder/commands/bind/layout_preparer.rb
304
305
  - lib/bookbinder/commands/bind.rb
305
306
  - lib/bookbinder/commands/build_and_push_tarball.rb
306
307
  - lib/bookbinder/commands/chain.rb
@@ -322,10 +323,14 @@ files:
322
323
  - lib/bookbinder/config/checkers/duplicate_section_name_checker.rb
323
324
  - lib/bookbinder/config/checkers/repository_name_presence_checker.rb
324
325
  - lib/bookbinder/config/checkers/required_keys_checker.rb
326
+ - lib/bookbinder/config/checkers/subnavs_checker.rb
327
+ - lib/bookbinder/config/checkers/topics_checker.rb
325
328
  - lib/bookbinder/config/configuration.rb
326
329
  - lib/bookbinder/config/fetcher.rb
327
330
  - lib/bookbinder/config/remote_yaml_credential_provider.rb
328
331
  - lib/bookbinder/config/section_config.rb
332
+ - lib/bookbinder/config/subnav_config.rb
333
+ - lib/bookbinder/config/topic_config.rb
329
334
  - lib/bookbinder/config/validator.rb
330
335
  - lib/bookbinder/config/yaml_loader.rb
331
336
  - lib/bookbinder/css_link_checker.rb
@@ -370,11 +375,17 @@ files:
370
375
  - lib/bookbinder/spider.rb
371
376
  - lib/bookbinder/stabilimentum.rb
372
377
  - lib/bookbinder/streams/colorized_stream.rb
373
- - lib/bookbinder/subnav_formatter.rb
378
+ - lib/bookbinder/subnav/json_from_config.rb
379
+ - lib/bookbinder/subnav/json_from_html.rb
380
+ - lib/bookbinder/subnav/json_props_creator.rb
381
+ - lib/bookbinder/subnav/pdf_config_creator.rb
382
+ - lib/bookbinder/subnav/subnav_generator.rb
383
+ - lib/bookbinder/subnav/subnav_generator_factory.rb
384
+ - lib/bookbinder/subnav/template_creator.rb
374
385
  - lib/bookbinder/terminal.rb
375
386
  - lib/bookbinder/values/output_locations.rb
376
387
  - lib/bookbinder/values/section.rb
377
- - lib/bookbinder/values/subnav.rb
388
+ - lib/bookbinder/values/subnav_template.rb
378
389
  - lib/bookbinder/values/user_message.rb
379
390
  - template_app/app.rb
380
391
  - template_app/config.ru
@@ -385,6 +396,7 @@ files:
385
396
  - template_app/rack_app.rb
386
397
  - master_middleman/archive_drop_down_menu.rb
387
398
  - master_middleman/bookbinder_helpers.rb
399
+ - master_middleman/compass_runner.rb
388
400
  - master_middleman/config.rb
389
401
  - master_middleman/quicklinks_renderer.rb
390
402
  - master_middleman/subdirectory_aware_assets.rb
@@ -400,7 +412,7 @@ require_paths:
400
412
  - lib
401
413
  required_ruby_version: !ruby/object:Gem::Requirement
402
414
  requirements:
403
- - - ~>
415
+ - - '>='
404
416
  - !ruby/object:Gem::Version
405
417
  version: '2.0'
406
418
  required_rubygems_version: !ruby/object:Gem::Requirement
@@ -1,37 +0,0 @@
1
- require 'nokogiri'
2
- require 'active_support/all'
3
-
4
- module Bookbinder
5
- class SubnavFormatter
6
-
7
- def get_links_as_json(raw_subnav_text, base_dirname)
8
- doc = Nokogiri::XML(raw_subnav_text)
9
-
10
- all_anchors = doc.css('a')
11
- all_anchors.each do |anchor|
12
- anchor['href'] = "/#{base_dirname}/#{anchor['href']}"
13
- end
14
-
15
- {
16
- links: gather_urls_and_texts(doc.css('body > ul'))
17
- }.to_json
18
- end
19
-
20
- private
21
-
22
- def gather_urls_and_texts(base_node)
23
- top_level_li = base_node.css("> li")
24
- top_level_li.map do |li|
25
- anchor = li.css('a')[0]
26
- href = anchor['href']
27
- text = anchor.inner_text
28
- ul = li.css('> ul')
29
- if ul.size > 0
30
- {url: href, text: text, nestedLinks: gather_urls_and_texts(ul)}
31
- else
32
- {url: href, text: text}
33
- end
34
- end
35
- end
36
- end
37
- end