alb-test 10.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (129) hide show
  1. checksums.yaml +7 -0
  2. data/bookbinder.gemspec +46 -0
  3. data/install_bin/bookbinder +5 -0
  4. data/lib/bookbinder/cli.rb +105 -0
  5. data/lib/bookbinder/code_example_reader.rb +95 -0
  6. data/lib/bookbinder/colorizer.rb +16 -0
  7. data/lib/bookbinder/commands/bind.rb +120 -0
  8. data/lib/bookbinder/commands/collection.rb +181 -0
  9. data/lib/bookbinder/commands/components/bind/directory_preparer.rb +33 -0
  10. data/lib/bookbinder/commands/components/bind/layout_preparer.rb +27 -0
  11. data/lib/bookbinder/commands/components/command_options.rb +45 -0
  12. data/lib/bookbinder/commands/components/imprint/directory_preparer.rb +24 -0
  13. data/lib/bookbinder/commands/generate.rb +92 -0
  14. data/lib/bookbinder/commands/imprint.rb +55 -0
  15. data/lib/bookbinder/commands/punch.rb +33 -0
  16. data/lib/bookbinder/commands/update_local_doc_repos.rb +42 -0
  17. data/lib/bookbinder/commands/watch.rb +100 -0
  18. data/lib/bookbinder/config/checkers/archive_menu_checker.rb +29 -0
  19. data/lib/bookbinder/config/checkers/ditamap_presence_checker.rb +27 -0
  20. data/lib/bookbinder/config/checkers/duplicate_section_name_checker.rb +33 -0
  21. data/lib/bookbinder/config/checkers/products_checker.rb +34 -0
  22. data/lib/bookbinder/config/checkers/repository_name_presence_checker.rb +34 -0
  23. data/lib/bookbinder/config/checkers/required_keys_checker.rb +18 -0
  24. data/lib/bookbinder/config/checkers/section_presence_checker.rb +15 -0
  25. data/lib/bookbinder/config/configuration.rb +119 -0
  26. data/lib/bookbinder/config/configuration_decorator.rb +54 -0
  27. data/lib/bookbinder/config/dita_config_generator.rb +61 -0
  28. data/lib/bookbinder/config/fetcher.rb +64 -0
  29. data/lib/bookbinder/config/imprint/configuration.rb +24 -0
  30. data/lib/bookbinder/config/product_config.rb +34 -0
  31. data/lib/bookbinder/config/section_config.rb +85 -0
  32. data/lib/bookbinder/config/validator.rb +33 -0
  33. data/lib/bookbinder/config/yaml_loader.rb +34 -0
  34. data/lib/bookbinder/css_link_checker.rb +67 -0
  35. data/lib/bookbinder/directory_helpers.rb +15 -0
  36. data/lib/bookbinder/dita_command_creator.rb +95 -0
  37. data/lib/bookbinder/dita_html_for_middleman_formatter.rb +45 -0
  38. data/lib/bookbinder/errors/programmer_mistake.rb +5 -0
  39. data/lib/bookbinder/html_document_manipulator.rb +21 -0
  40. data/lib/bookbinder/ingest/cloner_factory.rb +26 -0
  41. data/lib/bookbinder/ingest/destination_directory.rb +21 -0
  42. data/lib/bookbinder/ingest/git_accessor.rb +102 -0
  43. data/lib/bookbinder/ingest/git_cloner.rb +36 -0
  44. data/lib/bookbinder/ingest/local_filesystem_cloner.rb +66 -0
  45. data/lib/bookbinder/ingest/missing_working_copy.rb +27 -0
  46. data/lib/bookbinder/ingest/repo_identifier.rb +45 -0
  47. data/lib/bookbinder/ingest/section_repository.rb +49 -0
  48. data/lib/bookbinder/ingest/update_failure.rb +15 -0
  49. data/lib/bookbinder/ingest/update_success.rb +12 -0
  50. data/lib/bookbinder/ingest/working_copy.rb +36 -0
  51. data/lib/bookbinder/local_filesystem_accessor.rb +122 -0
  52. data/lib/bookbinder/middleman_runner.rb +50 -0
  53. data/lib/bookbinder/postprocessing/link_checker.rb +125 -0
  54. data/lib/bookbinder/postprocessing/redirection.rb +38 -0
  55. data/lib/bookbinder/preprocessing/dita_html_preprocessor.rb +91 -0
  56. data/lib/bookbinder/preprocessing/dita_pdf_preprocessor.rb +48 -0
  57. data/lib/bookbinder/preprocessing/link_to_site_gen_dir.rb +39 -0
  58. data/lib/bookbinder/preprocessing/preprocessor.rb +26 -0
  59. data/lib/bookbinder/server_director.rb +30 -0
  60. data/lib/bookbinder/sheller.rb +52 -0
  61. data/lib/bookbinder/streams/colorized_stream.rb +25 -0
  62. data/lib/bookbinder/streams/filter_stream.rb +22 -0
  63. data/lib/bookbinder/subnav/navigation_entries_from_html_toc.rb +59 -0
  64. data/lib/bookbinder/subnav/navigation_entries_from_markdown_root.rb +116 -0
  65. data/lib/bookbinder/subnav/pdf_config_creator.rb +50 -0
  66. data/lib/bookbinder/subnav/subnav_generator.rb +28 -0
  67. data/lib/bookbinder/subnav/subnav_generator_factory.rb +29 -0
  68. data/lib/bookbinder/subnav/template_creator.rb +71 -0
  69. data/lib/bookbinder/terminal.rb +19 -0
  70. data/lib/bookbinder/values/output_locations.rb +91 -0
  71. data/lib/bookbinder/values/product_info.rb +11 -0
  72. data/lib/bookbinder/values/section.rb +58 -0
  73. data/lib/bookbinder/values/subnav_template.rb +4 -0
  74. data/lib/bookbinder/values/user_message.rb +15 -0
  75. data/master_middleman/archive_drop_down_menu.rb +50 -0
  76. data/master_middleman/bookbinder_helpers.rb +282 -0
  77. data/master_middleman/compass_runner.rb +0 -0
  78. data/master_middleman/config.rb +45 -0
  79. data/master_middleman/proof.rb +90 -0
  80. data/master_middleman/quicklinks_renderer.rb +80 -0
  81. data/master_middleman/source/javascripts/all.js +2 -0
  82. data/master_middleman/source/javascripts/book.js +1 -0
  83. data/master_middleman/source/javascripts/bookbinder.js +130 -0
  84. data/master_middleman/source/layouts/_additional-scripts.erb +0 -0
  85. data/master_middleman/source/layouts/_book-footer.erb +0 -0
  86. data/master_middleman/source/layouts/_book-search.erb +0 -0
  87. data/master_middleman/source/layouts/_book-title.erb +3 -0
  88. data/master_middleman/source/layouts/_header.erb +34 -0
  89. data/master_middleman/source/layouts/_local-header.erb +0 -0
  90. data/master_middleman/source/layouts/_page-footer.erb +1 -0
  91. data/master_middleman/source/layouts/_title.erb +5 -0
  92. data/master_middleman/source/layouts/layout.erb +70 -0
  93. data/master_middleman/source/stylesheets/all.css.scss +3 -0
  94. data/master_middleman/source/stylesheets/base.scss +324 -0
  95. data/master_middleman/source/stylesheets/book-styles.scss +0 -0
  96. data/master_middleman/source/stylesheets/layout-styles.scss +0 -0
  97. data/master_middleman/source/stylesheets/partials/_book-base-values.scss +0 -0
  98. data/master_middleman/source/stylesheets/partials/_book-vars.scss +0 -0
  99. data/master_middleman/source/stylesheets/partials/_default.scss +302 -0
  100. data/master_middleman/source/stylesheets/partials/_footer.scss +64 -0
  101. data/master_middleman/source/stylesheets/partials/_header.scss +419 -0
  102. data/master_middleman/source/stylesheets/partials/_layout-vars.scss +0 -0
  103. data/master_middleman/source/stylesheets/partials/_mixins.scss +53 -0
  104. data/master_middleman/source/stylesheets/partials/_reset.scss +233 -0
  105. data/master_middleman/source/stylesheets/partials/_search.scss +78 -0
  106. data/master_middleman/source/stylesheets/partials/_sidenav.scss +191 -0
  107. data/master_middleman/source/stylesheets/partials/_syntax-highlight.scss +64 -0
  108. data/master_middleman/source/stylesheets/partials/_vars.scss +64 -0
  109. data/master_middleman/source/stylesheets/print-book-styles.scss +0 -0
  110. data/master_middleman/source/stylesheets/print-layout-styles.scss +0 -0
  111. data/master_middleman/source/stylesheets/print.css.scss +57 -0
  112. data/master_middleman/source/subnavs/_default.erb +0 -0
  113. data/master_middleman/source/subnavs/_nav-links.erb +10 -0
  114. data/master_middleman/source/subnavs/_subnav_template.erb +8 -0
  115. data/master_middleman/subdirectory_aware_assets.rb +36 -0
  116. data/template_app/Gemfile +10 -0
  117. data/template_app/Gemfile.lock +49 -0
  118. data/template_app/config.ru +9 -0
  119. data/template_app/lib/rack_static_if_exists.rb +19 -0
  120. data/template_app/lib/search/handler.rb +73 -0
  121. data/template_app/lib/search/hit.rb +21 -0
  122. data/template_app/lib/search/query.rb +74 -0
  123. data/template_app/lib/search/renderer.rb +30 -0
  124. data/template_app/lib/server.rb +52 -0
  125. data/template_app/mail_sender.rb +61 -0
  126. data/template_app/rack_app.rb +110 -0
  127. data/template_app/search-results.html.erb +81 -0
  128. data/template_app/search.yml +23 -0
  129. metadata +548 -0
@@ -0,0 +1,29 @@
1
+ module Bookbinder
2
+ module Config
3
+ module Checkers
4
+ class ArchiveMenuChecker
5
+ MissingArchiveMenuPartialError = Class.new(RuntimeError)
6
+ ArchiveMenuNotDefinedError = Class.new(RuntimeError)
7
+ EmptyArchiveItemsError = Class.new(RuntimeError)
8
+
9
+ def initialize(file_system_accessor)
10
+ @file_system_accessor = file_system_accessor
11
+ end
12
+
13
+ def check(config)
14
+ partial_location = './master_middleman/source/archive_menus/_default.erb'
15
+ if config.archive_menu && config.archive_menu.include?(nil)
16
+ EmptyArchiveItemsError.new 'Did you forget to add a value to the archive_menu?'
17
+ elsif config.archive_menu && !file_system_accessor.file_exist?(partial_location)
18
+ MissingArchiveMenuPartialError.new "You must provide a template partial named at #{partial_location}"
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ attr_reader :file_system_accessor
25
+
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,27 @@
1
+ module Bookbinder
2
+ module Config
3
+ module Checkers
4
+ class DitamapPresenceChecker
5
+ DitamapLocationError = Class.new(RuntimeError)
6
+
7
+ def check(config)
8
+ if any_sections_missing_ditamaps?(config.sections)
9
+ DitamapLocationError.new(
10
+ "You must have a 'ditamap_location' for each key in dita_sections."
11
+ )
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def any_sections_missing_ditamaps?(sections)
18
+ sections.any? do |s|
19
+ if s.preprocessor_config.has_key?('ditamap_location')
20
+ s.preprocessor_config['ditamap_location'].nil? || s.preprocessor_config['ditamap_location'].empty?
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,33 @@
1
+ require_relative '../../ingest/destination_directory'
2
+
3
+ module Bookbinder
4
+ module Config
5
+ module Checkers
6
+ class DuplicateSectionNameChecker
7
+ DuplicateSectionNameError = Class.new(RuntimeError)
8
+
9
+ def check(config)
10
+ if duplicate_section_names?(config)
11
+ DuplicateSectionNameError.new error_message
12
+ end
13
+ end
14
+
15
+ private
16
+
17
+ def duplicate_section_names?(config)
18
+ directory_names = config.sections.map {|section|
19
+ Ingest::DestinationDirectory.new(section.repo_name,
20
+ section.desired_directory_name)
21
+ }
22
+ directory_names.length != directory_names.uniq.length
23
+ end
24
+
25
+ def error_message
26
+ <<-ERROR
27
+ Duplicate repository names are not allowed.
28
+ ERROR
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,34 @@
1
+ module Bookbinder
2
+ module Config
3
+ module Checkers
4
+ class ProductsChecker
5
+ MissingProductsKeyError = Class.new(RuntimeError)
6
+ MissingProductIdError = Class.new(RuntimeError)
7
+
8
+ def check(config)
9
+ @config = config
10
+
11
+ if section_product_ids.count > 0
12
+ if config.products.empty?
13
+ MissingProductsKeyError.new('You must specify at least one product under the products key in config.yml')
14
+ elsif missing_products.count != 0
15
+ MissingProductIdError.new("Your config.yml is missing required product id under the products key. Required product ids are #{missing_products.join(", ")}.")
16
+ end
17
+ end
18
+ end
19
+
20
+ attr_reader :config
21
+
22
+ private
23
+
24
+ def missing_products
25
+ section_product_ids - config.products.map(&:id)
26
+ end
27
+
28
+ def section_product_ids
29
+ config.sections.map(&:product_id).compact.uniq
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,34 @@
1
+ module Bookbinder
2
+ module Config
3
+ module Checkers
4
+ class RepositoryNamePresenceChecker
5
+ MissingRepositoryNameError = Class.new(RuntimeError)
6
+
7
+ def check(config)
8
+ failures = config.sections.reject do |section|
9
+ section.repo_name
10
+ end
11
+
12
+ if failures.empty?
13
+ nil
14
+ else
15
+ MissingRepositoryNameError.new error_message
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def error_message
22
+ <<-ERROR
23
+ Cannot locate a specific section.
24
+ All sections must provide the section 'name' key under the 'repository' key:
25
+
26
+ sections:
27
+ - repository:
28
+ name: 'your-org/your-repo'
29
+ ERROR
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,18 @@
1
+ require_relative '../configuration'
2
+
3
+ module Bookbinder
4
+ module Config
5
+ module Checkers
6
+ class RequiredKeysChecker
7
+ MissingRequiredKeyError = Class.new(RuntimeError)
8
+
9
+ def check(config)
10
+ missing_keys = Config::Configuration::CONFIG_REQUIRED_KEYS.reject { |key| config.has_option?(key) }
11
+ if missing_keys.any?
12
+ MissingRequiredKeyError.new("Your config.yml is missing required key(s). Required keys are #{missing_keys.join(", ")}.")
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -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
@@ -0,0 +1,119 @@
1
+ require_relative '../../../lib/bookbinder/config/dita_config_generator'
2
+ require_relative '../ingest/destination_directory'
3
+ require_relative '../ingest/repo_identifier'
4
+ require_relative 'section_config'
5
+ require_relative 'product_config'
6
+
7
+ module Bookbinder
8
+ module Config
9
+ class Configuration
10
+ class << self
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)
19
+ new(symbolize_keys(input_config).
20
+ merge(expand_repo_identifiers(input_config)).
21
+ merge(sections: section_configs))
22
+ end
23
+
24
+ def symbolize_keys(h)
25
+ h.reduce({}) {|acc, (k, v)| acc.merge(k.to_sym => v) }
26
+ end
27
+
28
+ def expand_repo_identifiers(input_config)
29
+ input_config.select {|k, _| k.match(/_repo$/)}.
30
+ reduce({}) {|h, (k, v)| h.merge(:"#{k}_url" => Ingest::RepoIdentifier.new(v))}
31
+ end
32
+
33
+ def to_section_configs sections
34
+ sections.map { |section| Config::SectionConfig.new(section) }
35
+ end
36
+
37
+ private
38
+
39
+ def combined_sections(input_config)
40
+ regular_sections(input_config) + dita_sections(input_config)
41
+ end
42
+
43
+ def regular_sections(input_config)
44
+ input_config['sections'] || []
45
+ end
46
+
47
+ def dita_sections(input_config)
48
+ (input_config['dita_sections'] || []).map { |dita_section|
49
+ DitaConfigGenerator.new(dita_section).to_hash
50
+ }
51
+ end
52
+ end
53
+
54
+ def initialize(config)
55
+ @config = config
56
+ @products = assemble_products || []
57
+ end
58
+
59
+ CONFIG_REQUIRED_KEYS = %w(book_repo public_host)
60
+ CONFIG_OPTIONAL_KEYS = %w(archive_menu book_repo_url cred_repo cred_repo_url repo_link_enabled repo_links feedback_enabled layout_repo layout_repo_ref layout_repo_url sections)
61
+
62
+ CONFIG_REQUIRED_KEYS.each do |method_name|
63
+ define_method(method_name) do
64
+ config.fetch(method_name.to_sym)
65
+ end
66
+ end
67
+
68
+ CONFIG_OPTIONAL_KEYS.each do |method_name|
69
+ define_method(method_name) do
70
+ config[method_name.to_sym]
71
+ end
72
+ end
73
+
74
+ def broken_link_exclusions
75
+ config.fetch(:broken_link_exclusions, /(?!.*)/)
76
+ end
77
+
78
+ def template_variables
79
+ config.fetch(:template_variables, {})
80
+ end
81
+
82
+ def merge(other_configuration)
83
+ Configuration.new(config.merge(other_configuration.instance_variable_get(:@config)))
84
+ end
85
+
86
+ def merge_sections(incoming_sections)
87
+ merge(Configuration.new(sections: sections + incoming_sections))
88
+ end
89
+
90
+ def has_option?(key)
91
+ !!config[key.to_sym]
92
+ end
93
+
94
+ def elastic_search?
95
+ config.fetch(:elastic_search, false)
96
+ end
97
+
98
+ def ==(o)
99
+ o.class == self.class && o.instance_variable_get(:@config) == @config
100
+ end
101
+
102
+ alias_method :eql?, :==
103
+
104
+ attr_reader :products
105
+
106
+ private
107
+
108
+ def assemble_products
109
+ if config[:products]
110
+ config[:products].map do |product|
111
+ Config::ProductConfig.new(product)
112
+ end
113
+ end
114
+ end
115
+
116
+ attr_reader :config
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,54 @@
1
+ require_relative 'configuration'
2
+
3
+ module Bookbinder
4
+ module Config
5
+ class ConfigurationDecorator
6
+ def initialize(loader: nil, config_filename: nil)
7
+ @loader = loader
8
+ @config_filename = config_filename
9
+ end
10
+
11
+ def generate(base_config, sections)
12
+ base_config.merge(
13
+ Configuration.new(
14
+ repo_links: repo_link_config(base_config, sections),
15
+ archive_menu: root_config(base_config).merge(section_config(sections)))
16
+ )
17
+ end
18
+
19
+ private
20
+
21
+ attr_reader :loader, :config_filename
22
+
23
+ def repo_link_config(base_config, sections)
24
+ if base_config.repo_link_enabled
25
+ sections.reduce({}) {|config, section|
26
+ config.merge(
27
+ section.destination_directory.to_s => {
28
+ 'repo' => section.repo_name,
29
+ 'ref' => section.source_ref,
30
+ 'at_path' => section.at_repo_path
31
+ }
32
+ )
33
+ }
34
+ end
35
+ end
36
+
37
+ def root_config(base_config)
38
+ { '.' => base_config.archive_menu }
39
+ end
40
+
41
+ def section_config(sections)
42
+ sections.reduce({}) {|config, section|
43
+ config_path = section.path_to_repo_dir.join(config_filename)
44
+ archive_config = loader.load_key(config_path, 'archive_menu')
45
+ if archive_config
46
+ config.merge(section.desired_directory_name => archive_config)
47
+ else
48
+ config
49
+ end
50
+ }
51
+ end
52
+ end
53
+ end
54
+ end
@@ -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
@@ -0,0 +1,64 @@
1
+ require_relative 'configuration'
2
+ require_relative 'yaml_loader'
3
+
4
+ module Bookbinder
5
+ module Config
6
+ class Fetcher
7
+ def initialize(configuration_validator, loader, config_class)
8
+ @loader = loader
9
+ @configuration_validator = configuration_validator
10
+ @config_class = config_class
11
+ end
12
+
13
+ def fetch_config
14
+ @base_config ||= read_config_file
15
+ @optional_configs ||= read_optional_configs
16
+
17
+ @config ||= validate(@base_config, @optional_configs)
18
+ end
19
+
20
+ def set_config_dir_path(config_dir_path)
21
+ @config_dir_path = File.expand_path(config_dir_path)
22
+ end
23
+
24
+ def set_config_file_path(config_file_path)
25
+ @config_file_path = File.expand_path(config_file_path)
26
+ end
27
+
28
+ private
29
+
30
+ attr_reader(:loader, :configuration_validator, :config, :config_file_path, :config_dir_path,
31
+ :config_class)
32
+
33
+ def read_config_file
34
+ loader.load(config_file_path)
35
+
36
+ rescue FileNotFoundError => e
37
+ raise "The configuration file specified does not exist. Please create a config #{e} file at #{config_file_path} and try again."
38
+ rescue InvalidSyntaxError => e
39
+ raise syntax_error(e)
40
+ end
41
+
42
+ def read_optional_configs
43
+ Dir["#{config_dir_path}/*.yml"].map do |config_file|
44
+ loader.load(File.expand_path(config_file)) || {}
45
+ end.reduce({}, :merge)
46
+ rescue InvalidSyntaxError => e
47
+ raise syntax_error(e)
48
+ end
49
+
50
+ def validate(base_hash, optional_hash)
51
+ raise 'Your config.yml appears to be empty. Please check and try again.' unless base_hash
52
+
53
+ config_class.parse(base_hash.merge(optional_hash)).tap do |config|
54
+ errors = configuration_validator.exceptions(config)
55
+ raise errors.first if errors.any?
56
+ end
57
+ end
58
+
59
+ def syntax_error(e)
60
+ "There is a syntax error in your config file: \n #{e}"
61
+ end
62
+ end
63
+ end
64
+ end