From 4cc6a0f2631ec9d37832a9c7ae242c3b48f6b35e Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Sat, 12 Jul 2014 13:08:35 -0700 Subject: [PATCH 1/3] Mark certain gems as eventually-non-core. --- jekyll.gemspec | 11 +++++++---- lib/jekyll.rb | 12 +++++++++--- 2 files changed, 16 insertions(+), 7 deletions(-) diff --git a/jekyll.gemspec b/jekyll.gemspec index 765626f5..4a43cf1d 100644 --- a/jekyll.gemspec +++ b/jekyll.gemspec @@ -28,13 +28,16 @@ Gem::Specification.new do |s| s.rdoc_options = ["--charset=UTF-8"] s.extra_rdoc_files = %w[README.markdown LICENSE] - s.add_runtime_dependency('liquid', "~> 2.6.1") - s.add_runtime_dependency('classifier', "~> 1.3") - s.add_runtime_dependency('kramdown', "~> 1.3") - s.add_runtime_dependency('pygments.rb', "~> 0.6.0") + s.add_runtime_dependency('liquid', "~> 2.6.1") + s.add_runtime_dependency('kramdown', "~> 1.3") s.add_runtime_dependency('mercenary', "~> 0.3.3") s.add_runtime_dependency('safe_yaml', "~> 1.0") s.add_runtime_dependency('colorator', "~> 0.1") + + # Before 3.0 drops, phase the following gems out as dev dependencies + # and gracefully handle their absence. + s.add_runtime_dependency('classifier', "~> 1.3") + s.add_runtime_dependency('pygments.rb', "~> 0.6.0") s.add_runtime_dependency('redcarpet', "~> 3.1") s.add_runtime_dependency('toml', '~> 0.1.0') s.add_runtime_dependency('jekyll-paginate', '~> 1.0') diff --git a/lib/jekyll.rb b/lib/jekyll.rb index b36569b3..bf2f566f 100644 --- a/lib/jekyll.rb +++ b/lib/jekyll.rb @@ -27,7 +27,6 @@ require 'logger' require 'liquid' require 'kramdown' require 'colorator' -require 'toml' # internal requires require 'jekyll/version' @@ -74,8 +73,15 @@ require_all 'jekyll/tags' # plugins require 'jekyll-coffeescript' require 'jekyll-sass-converter' -require 'jekyll-paginate' -require 'jekyll-gist' + +# Eventually remove these for 3.0 as non-core +require "classifier" +require "pygments.rb" +require "toml" +require "jekyll-paginate" +require "jekyll-gist" +require "jekyll-coffeescript" +require "jekyll-sass-converter" SafeYAML::OPTIONS[:suppress_warnings] = true From a97ae67552f82cb309e3cb951947fa082733f77a Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Sat, 12 Jul 2014 13:46:49 -0700 Subject: [PATCH 2/3] Gracefully require --- lib/jekyll.rb | 109 +++++++++--------- lib/jekyll/commands/build.rb | 2 +- .../converters/markdown/rdiscount_parser.rb | 6 +- .../converters/markdown/redcarpet_parser.rb | 63 +++++----- lib/jekyll/deprecator.rb | 20 +++- lib/jekyll/errors.rb | 2 + 6 files changed, 105 insertions(+), 97 deletions(-) diff --git a/lib/jekyll.rb b/lib/jekyll.rb index bf2f566f..ba892e18 100644 --- a/lib/jekyll.rb +++ b/lib/jekyll.rb @@ -18,75 +18,56 @@ require 'rubygems' # stdlib require 'fileutils' require 'time' -require 'safe_yaml/load' require 'English' require 'pathname' require 'logger' # 3rd party +require 'safe_yaml/load' require 'liquid' require 'kramdown' require 'colorator' -# internal requires -require 'jekyll/version' -require 'jekyll/utils' -require 'jekyll/log_adapter' -require 'jekyll/stevenson' -require 'jekyll/deprecator' -require 'jekyll/configuration' -require 'jekyll/document' -require 'jekyll/collection' -require 'jekyll/plugin_manager' -require 'jekyll/frontmatter_defaults' -require 'jekyll/site' -require 'jekyll/convertible' -require 'jekyll/url' -require 'jekyll/layout' -require 'jekyll/page' -require 'jekyll/post' -require 'jekyll/excerpt' -require 'jekyll/draft' -require 'jekyll/filters' -require 'jekyll/static_file' -require 'jekyll/errors' -require 'jekyll/related_posts' -require 'jekyll/cleaner' -require 'jekyll/entry_filter' -require 'jekyll/layout_reader' -require 'jekyll/publisher' -require 'jekyll/renderer' - -# extensions -require 'jekyll/plugin' -require 'jekyll/converter' -require 'jekyll/generator' -require 'jekyll/command' -require 'jekyll/liquid_extensions' - -require_all 'jekyll/commands' -require_all 'jekyll/converters' -require_all 'jekyll/converters/markdown' -require_all 'jekyll/generators' -require_all 'jekyll/tags' - -# plugins -require 'jekyll-coffeescript' -require 'jekyll-sass-converter' - -# Eventually remove these for 3.0 as non-core -require "classifier" -require "pygments.rb" -require "toml" -require "jekyll-paginate" -require "jekyll-gist" -require "jekyll-coffeescript" -require "jekyll-sass-converter" - SafeYAML::OPTIONS[:suppress_warnings] = true module Jekyll + # internal requires + autoload :Cleaner, 'jekyll/cleaner' + autoload :Collection, 'jekyll/collection' + autoload :Configuration, 'jekyll/configuration' + autoload :Convertible, 'jekyll/convertible' + autoload :Deprecator, 'jekyll/deprecator' + autoload :Document, 'jekyll/document' + autoload :Draft, 'jekyll/draft' + autoload :EntryFilter, 'jekyll/entry_filter' + autoload :Errors, 'jekyll/errors' + autoload :Excerpt, 'jekyll/excerpt' + autoload :Filters, 'jekyll/filters' + autoload :FrontmatterDefaults, 'jekyll/frontmatter_defaults' + autoload :Layout, 'jekyll/layout' + autoload :LayoutReader, 'jekyll/layout_reader' + autoload :LogAdapter, 'jekyll/log_adapter' + autoload :Page, 'jekyll/page' + autoload :PluginManager, 'jekyll/plugin_manager' + autoload :Post, 'jekyll/post' + autoload :Publisher, 'jekyll/publisher' + autoload :RelatedPosts, 'jekyll/related_posts' + autoload :Renderer, 'jekyll/renderer' + autoload :Site, 'jekyll/site' + autoload :StaticFile, 'jekyll/static_file' + autoload :Stevenson, 'jekyll/stevenson' + autoload :URL, 'jekyll/url' + autoload :Utils, 'jekyll/utils' + autoload :VERSION, 'jekyll/version' + + # extensions + require 'jekyll/plugin' + require 'jekyll/converter' + require 'jekyll/generator' + require 'jekyll/command' + require 'jekyll/liquid_extensions' + # Public: Tells you which Jekyll environment you are building in so you can skip tasks # if you need to. This is useful when doing expensive compression tasks on css and # images and allows you to skip that when working in development. @@ -149,3 +130,19 @@ module Jekyll end end end + +require_all 'jekyll/commands' +require_all 'jekyll/converters' +require_all 'jekyll/converters/markdown' +require_all 'jekyll/generators' +require_all 'jekyll/tags' + +# Eventually remove these for 3.0 as non-core +Jekyll::Deprecator.gracefully_require(%w[ + classifier + toml + jekyll-paginate + jekyll-gist + jekyll-coffeescript + jekyll-sass-converter +]) diff --git a/lib/jekyll/commands/build.rb b/lib/jekyll/commands/build.rb index 3f187601..140a7ae3 100644 --- a/lib/jekyll/commands/build.rb +++ b/lib/jekyll/commands/build.rb @@ -58,7 +58,7 @@ module Jekyll # # Returns nothing. def watch(site, options) - require 'jekyll-watch' + Deprecator.gracefully_require 'jekyll-watch' Jekyll::Commands::Watch.watch(site, options) end diff --git a/lib/jekyll/converters/markdown/rdiscount_parser.rb b/lib/jekyll/converters/markdown/rdiscount_parser.rb index bfe1a7ca..0c8634e4 100644 --- a/lib/jekyll/converters/markdown/rdiscount_parser.rb +++ b/lib/jekyll/converters/markdown/rdiscount_parser.rb @@ -3,13 +3,9 @@ module Jekyll class Markdown class RDiscountParser def initialize(config) - require 'rdiscount' + Jekyll::Deprecator.gracefully_require "rdiscount" @config = config @rdiscount_extensions = @config['rdiscount']['extensions'].map { |e| e.to_sym } - rescue LoadError - STDERR.puts 'You are missing a library required for Markdown. Please run:' - STDERR.puts ' $ [sudo] gem install rdiscount' - raise FatalException.new("Missing dependency: rdiscount") end def convert(content) diff --git a/lib/jekyll/converters/markdown/redcarpet_parser.rb b/lib/jekyll/converters/markdown/redcarpet_parser.rb index 2c5bb2f8..b69df9b8 100644 --- a/lib/jekyll/converters/markdown/redcarpet_parser.rb +++ b/lib/jekyll/converters/markdown/redcarpet_parser.rb @@ -14,7 +14,7 @@ module Jekyll module WithPygments include CommonMethods def block_code(code, lang) - require 'pygments' + Jekyll::Deprecator.gracefully_require("pygments") lang = lang && lang.split.first || "text" add_code_tags( Pygments.highlight(code, :lexer => lang, :options => { :encoding => 'utf-8' }), @@ -55,45 +55,40 @@ module Jekyll def initialize(config) - require 'redcarpet' + Deprecator.gracefully_require("redcarpet") @config = config @redcarpet_extensions = {} @config['redcarpet']['extensions'].each { |e| @redcarpet_extensions[e.to_sym] = true } - @renderer ||= case @config['highlighter'] - when 'pygments' - Class.new(Redcarpet::Render::HTML) do - include WithPygments - end - when 'rouge' - Class.new(Redcarpet::Render::HTML) do - begin - require 'rouge' - require 'rouge/plugins/redcarpet' - rescue LoadError => e - Jekyll.logger.error "You are missing the 'rouge' gem. Please run:" - Jekyll.logger.error " $ [sudo] gem install rouge" - Jekyll.logger.error "Or add 'rouge' to your Gemfile." - raise FatalException.new("Missing dependency: rouge") - end + @renderer ||= class_with_proper_highlighter(@config['highlighter']) + end - if Rouge.version < '1.3.0' - abort "Please install Rouge 1.3.0 or greater and try running Jekyll again." - end + def class_with_proper_highlighter(highlighter) + case highlighter + when "pygments" + Class.new(Redcarpet::Render::HTML) do + include WithPygments + end + when "rouge" + Class.new(Redcarpet::Render::HTML) do + Jekyll::Deprecator.gracefully_require(%w[ + rouge + rouge/plugins/redcarpet + ]) - include Rouge::Plugins::Redcarpet - include CommonMethods - include WithRouge - end - else - Class.new(Redcarpet::Render::HTML) do - include WithoutHighlighting - end - end - rescue LoadError - STDERR.puts 'You are missing a library required for Markdown. Please run:' - STDERR.puts ' $ [sudo] gem install redcarpet' - raise FatalException.new("Missing dependency: redcarpet") + if Rouge.version < '1.3.0' + abort "Please install Rouge 1.3.0 or greater and try running Jekyll again." + end + + include Rouge::Plugins::Redcarpet + include CommonMethods + include WithRouge + end + else + Class.new(Redcarpet::Render::HTML) do + include WithoutHighlighting + end + end end def convert(content) diff --git a/lib/jekyll/deprecator.rb b/lib/jekyll/deprecator.rb index 47f03b70..6fd94fb2 100644 --- a/lib/jekyll/deprecator.rb +++ b/lib/jekyll/deprecator.rb @@ -1,5 +1,5 @@ module Jekyll - class Deprecator + module Deprecator def self.process(args) no_subcommand(args) arg_is_present? args, "--server", "The --server command has been replaced by the \ @@ -32,5 +32,23 @@ module Jekyll def self.deprecation_message(message) Jekyll.logger.error "Deprecation:", message end + + def self.gracefully_require(gem_name) + Array(gem_name).each do |name| + begin + require name + rescue LoadError => e + Jekyll.logger.error "Dependency Error:", <<-MSG + Yikes! It looks like you don't have #{name} or one of its dependencies installed. + In order to use Jekyll as currently contfigured, you'll need to install this gem. + + The full error message from Ruby is: '#{e.message}' + + If you run into trouble, you can find helpful resources at http://jekyllrb.com/help/! + MSG + raise Errors::MissingDependencyException.new(name) + end + end + end end end diff --git a/lib/jekyll/errors.rb b/lib/jekyll/errors.rb index af03ad84..6801f9b5 100644 --- a/lib/jekyll/errors.rb +++ b/lib/jekyll/errors.rb @@ -1,4 +1,6 @@ module Jekyll class FatalException < StandardError + class MissingDependencyException < FatalException + end end end From c7cc36abdb59c161037cc36b6816cc53952a3841 Mon Sep 17 00:00:00 2001 From: Parker Moore Date: Sat, 12 Jul 2014 13:47:59 -0700 Subject: [PATCH 3/3] Put errors/exceptions into Jekyll::Errors module --- lib/jekyll/command.rb | 2 +- lib/jekyll/converters/markdown.rb | 27 +- .../converters/markdown/kramdown_parser.rb | 2 +- .../converters/markdown/maruku_parser.rb | 2 +- lib/jekyll/converters/textile.rb | 2 +- lib/jekyll/errors.rb | 5 +- lib/jekyll/excerpt.rb | 1 - lib/jekyll/frontmatter_defaults.rb | 252 +++++++++--------- lib/jekyll/post.rb | 2 +- lib/jekyll/site.rb | 4 +- test/test_command.rb | 2 +- test/test_post.rb | 2 +- test/test_site.rb | 8 +- 13 files changed, 165 insertions(+), 146 deletions(-) diff --git a/lib/jekyll/command.rb b/lib/jekyll/command.rb index 654f0ae3..d11631d8 100644 --- a/lib/jekyll/command.rb +++ b/lib/jekyll/command.rb @@ -51,7 +51,7 @@ module Jekyll # Returns nothing def process_site(site) site.process - rescue Jekyll::FatalException => e + rescue Jekyll::Errors::FatalException => e Jekyll.logger.error "ERROR:", "YOUR SITE COULD NOT BE BUILT:" Jekyll.logger.error "", "------------------------------------" Jekyll.logger.error "", e.message diff --git a/lib/jekyll/converters/markdown.rb b/lib/jekyll/converters/markdown.rb index f68af673..687b0eca 100644 --- a/lib/jekyll/converters/markdown.rb +++ b/lib/jekyll/converters/markdown.rb @@ -11,22 +11,41 @@ module Jekyll @parser = case @config['markdown'].downcase when 'redcarpet' then RedcarpetParser.new(@config) - when 'kramdown' then KramdownParser.new(@config) + when 'kramdown' then KramdownParser.new(@config) when 'rdiscount' then RDiscountParser.new(@config) - when 'maruku' then MarukuParser.new(@config) + when 'maruku' then MarukuParser.new(@config) else # So they can't try some tricky bullshit or go down the ancestor chain, I hope. if allowed_custom_class?(@config['markdown']) self.class.const_get(@config['markdown']).new(@config) else Jekyll.logger.error "Invalid Markdown Processor:", "#{@config['markdown']}" - Jekyll.logger.error "", "Valid options are [ maruku | rdiscount | kramdown | redcarpet ]" - raise FatalException, "Invalid Markdown Processor: #{@config['markdown']}" + Jekyll.logger.error "", "Valid options are [ #{valid_processors.join(" | ")} ]" + raise Errors::FatalException, "Invalid Markdown Processor: #{@config['markdown']}" end end @setup = true end + def valid_processors + %w[ + maruku + rdiscount + kramdown + redcarpet + ] + third_party_processors + end + + def third_party_processors + self.class.constants - %w[ + KramdownParser + MarukuParser + RDiscountParser + RedcarpetParser + PRIORITIES + ].map(&:to_sym) + end + def matches(ext) rgx = '^\.(' + @config['markdown_ext'].gsub(',','|') +')$' ext =~ Regexp.new(rgx, Regexp::IGNORECASE) diff --git a/lib/jekyll/converters/markdown/kramdown_parser.rb b/lib/jekyll/converters/markdown/kramdown_parser.rb index cd1fdf9a..9dd086e4 100644 --- a/lib/jekyll/converters/markdown/kramdown_parser.rb +++ b/lib/jekyll/converters/markdown/kramdown_parser.rb @@ -8,7 +8,7 @@ module Jekyll rescue LoadError STDERR.puts 'You are missing a library required for Markdown. Please run:' STDERR.puts ' $ [sudo] gem install kramdown' - raise FatalException.new("Missing dependency: kramdown") + raise Errors::FatalException.new("Missing dependency: kramdown") end def convert(content) diff --git a/lib/jekyll/converters/markdown/maruku_parser.rb b/lib/jekyll/converters/markdown/maruku_parser.rb index 7d14c8a5..7be577c8 100644 --- a/lib/jekyll/converters/markdown/maruku_parser.rb +++ b/lib/jekyll/converters/markdown/maruku_parser.rb @@ -15,7 +15,7 @@ module Jekyll rescue LoadError STDERR.puts 'You are missing a library required for Markdown. Please run:' STDERR.puts ' $ [sudo] gem install maruku' - raise FatalException.new("Missing dependency: maruku") + raise Errors::FatalException.new("Missing dependency: maruku") end def load_divs_library diff --git a/lib/jekyll/converters/textile.rb b/lib/jekyll/converters/textile.rb index d05b2169..85e0e355 100644 --- a/lib/jekyll/converters/textile.rb +++ b/lib/jekyll/converters/textile.rb @@ -13,7 +13,7 @@ module Jekyll rescue LoadError STDERR.puts 'You are missing a library required for Textile. Please run:' STDERR.puts ' $ [sudo] gem install RedCloth' - raise FatalException.new("Missing dependency: RedCloth") + raise Errors::FatalException.new("Missing dependency: RedCloth") end def matches(ext) diff --git a/lib/jekyll/errors.rb b/lib/jekyll/errors.rb index 6801f9b5..dc5238a0 100644 --- a/lib/jekyll/errors.rb +++ b/lib/jekyll/errors.rb @@ -1,5 +1,8 @@ module Jekyll - class FatalException < StandardError + module Errors + class FatalException < RuntimeError + end + class MissingDependencyException < FatalException end end diff --git a/lib/jekyll/excerpt.rb b/lib/jekyll/excerpt.rb index 130b2880..958e3f3c 100644 --- a/lib/jekyll/excerpt.rb +++ b/lib/jekyll/excerpt.rb @@ -1,4 +1,3 @@ -require 'jekyll/convertible' require 'forwardable' module Jekyll diff --git a/lib/jekyll/frontmatter_defaults.rb b/lib/jekyll/frontmatter_defaults.rb index 42b85ed5..e747143b 100644 --- a/lib/jekyll/frontmatter_defaults.rb +++ b/lib/jekyll/frontmatter_defaults.rb @@ -1,147 +1,145 @@ module Jekyll - class Configuration - # This class handles custom defaults for YAML frontmatter settings. - # These are set in _config.yml and apply both to internal use (e.g. layout) - # and the data available to liquid. + # This class handles custom defaults for YAML frontmatter settings. + # These are set in _config.yml and apply both to internal use (e.g. layout) + # and the data available to liquid. + # + # It is exposed via the frontmatter_defaults method on the site class. + class FrontmatterDefaults + # Initializes a new instance. + def initialize(site) + @site = site + end + + # Finds a default value for a given setting, filtered by path and type # - # It is exposed via the frontmatter_defaults method on the site class. - class FrontmatterDefaults - # Initializes a new instance. - def initialize(site) - @site = site - end + # path - the path (relative to the source) of the page, post or :draft the default is used in + # type - a symbol indicating whether a :page, a :post or a :draft calls this method + # + # Returns the default value or nil if none was found + def find(path, type, setting) + value = nil + old_scope = nil - # Finds a default value for a given setting, filtered by path and type - # - # path - the path (relative to the source) of the page, post or :draft the default is used in - # type - a symbol indicating whether a :page, a :post or a :draft calls this method - # - # Returns the default value or nil if none was found - def find(path, type, setting) - value = nil - old_scope = nil - - matching_sets(path, type).each do |set| - if set['values'].has_key?(setting) && has_precedence?(old_scope, set['scope']) - value = set['values'][setting] - old_scope = set['scope'] - end - end - value - end - - # Collects a hash with all default values for a page or post - # - # path - the relative path of the page or post - # type - a symbol indicating the type (:post, :page or :draft) - # - # Returns a hash with all default values (an empty hash if there are none) - def all(path, type) - defaults = {} - old_scope = nil - matching_sets(path, type).each do |set| - if has_precedence?(old_scope, set['scope']) - defaults = Utils.deep_merge_hashes(defaults, set['values']) - old_scope = set['scope'] - else - defaults = Utils.deep_merge_hashes(set['values'], defaults) - end - end - defaults - end - - private - - # Checks if a given default setting scope matches the given path and type - # - # scope - the hash indicating the scope, as defined in _config.yml - # path - the path to check for - # type - the type (:post, :page or :draft) to check for - # - # Returns true if the scope applies to the given path and type - def applies?(scope, path, type) - applies_path?(scope, path) && applies_type?(scope, type) - end - - def applies_path?(scope, path) - return true if scope['path'].empty? - - scope_path = Pathname.new(scope['path']) - Pathname.new(sanitize_path(path)).ascend do |path| - if path == scope_path - return true - end + matching_sets(path, type).each do |set| + if set['values'].has_key?(setting) && has_precedence?(old_scope, set['scope']) + value = set['values'][setting] + old_scope = set['scope'] end end + value + end - def applies_type?(scope, type) - !scope.has_key?('type') || scope['type'] == type.to_s - end - - # Checks if a given set of default values is valid - # - # set - the default value hash, as defined in _config.yml - # - # Returns true if the set is valid and can be used in this class - def valid?(set) - set.is_a?(Hash) && set['scope'].is_a?(Hash) && set['scope']['path'].is_a?(String) && set['values'].is_a?(Hash) - end - - # Determines if a new scope has precedence over an old one - # - # old_scope - the old scope hash, or nil if there's none - # new_scope - the new scope hash - # - # Returns true if the new scope has precedence over the older - def has_precedence?(old_scope, new_scope) - return true if old_scope.nil? - - new_path = sanitize_path(new_scope['path']) - old_path = sanitize_path(old_scope['path']) - - if new_path.length != old_path.length - new_path.length >= old_path.length - elsif new_scope.has_key? 'type' - true + # Collects a hash with all default values for a page or post + # + # path - the relative path of the page or post + # type - a symbol indicating the type (:post, :page or :draft) + # + # Returns a hash with all default values (an empty hash if there are none) + def all(path, type) + defaults = {} + old_scope = nil + matching_sets(path, type).each do |set| + if has_precedence?(old_scope, set['scope']) + defaults = Utils.deep_merge_hashes(defaults, set['values']) + old_scope = set['scope'] else - !old_scope.has_key? 'type' + defaults = Utils.deep_merge_hashes(set['values'], defaults) end end + defaults + end - # Collects a list of sets that match the given path and type - # - # Returns an array of hashes - def matching_sets(path, type) - valid_sets.select do |set| - applies?(set['scope'], path, type) + private + + # Checks if a given default setting scope matches the given path and type + # + # scope - the hash indicating the scope, as defined in _config.yml + # path - the path to check for + # type - the type (:post, :page or :draft) to check for + # + # Returns true if the scope applies to the given path and type + def applies?(scope, path, type) + applies_path?(scope, path) && applies_type?(scope, type) + end + + def applies_path?(scope, path) + return true if scope['path'].empty? + + scope_path = Pathname.new(scope['path']) + Pathname.new(sanitize_path(path)).ascend do |path| + if path == scope_path + return true end end + end - # Returns a list of valid sets - # - # This is not cached to allow plugins to modify the configuration - # and have their changes take effect - # - # Returns an array of hashes - def valid_sets - sets = @site.config['defaults'] - return [] unless sets.is_a?(Array) + def applies_type?(scope, type) + !scope.has_key?('type') || scope['type'] == type.to_s + end - sets.select do |set| - unless valid?(set) - Jekyll.logger.warn "Default:", "An invalid default set was found" - end - valid?(set) - end + # Checks if a given set of default values is valid + # + # set - the default value hash, as defined in _config.yml + # + # Returns true if the set is valid and can be used in this class + def valid?(set) + set.is_a?(Hash) && set['scope'].is_a?(Hash) && set['scope']['path'].is_a?(String) && set['values'].is_a?(Hash) + end + + # Determines if a new scope has precedence over an old one + # + # old_scope - the old scope hash, or nil if there's none + # new_scope - the new scope hash + # + # Returns true if the new scope has precedence over the older + def has_precedence?(old_scope, new_scope) + return true if old_scope.nil? + + new_path = sanitize_path(new_scope['path']) + old_path = sanitize_path(old_scope['path']) + + if new_path.length != old_path.length + new_path.length >= old_path.length + elsif new_scope.has_key? 'type' + true + else + !old_scope.has_key? 'type' end + end - # Sanitizes the given path by removing a leading and addding a trailing slash - def sanitize_path(path) - if path.nil? || path.empty? - "" - else - path.gsub(/\A\//, '').gsub(/([^\/])\z/, '\1/') + # Collects a list of sets that match the given path and type + # + # Returns an array of hashes + def matching_sets(path, type) + valid_sets.select do |set| + applies?(set['scope'], path, type) + end + end + + # Returns a list of valid sets + # + # This is not cached to allow plugins to modify the configuration + # and have their changes take effect + # + # Returns an array of hashes + def valid_sets + sets = @site.config['defaults'] + return [] unless sets.is_a?(Array) + + sets.select do |set| + unless valid?(set) + Jekyll.logger.warn "Default:", "An invalid default set was found" end + valid?(set) + end + end + + # Sanitizes the given path by removing a leading and addding a trailing slash + def sanitize_path(path) + if path.nil? || path.empty? + "" + else + path.gsub(/\A\//, '').gsub(/([^\/])\z/, '\1/') end end end diff --git a/lib/jekyll/post.rb b/lib/jekyll/post.rb index f13d8b2c..82ed696f 100644 --- a/lib/jekyll/post.rb +++ b/lib/jekyll/post.rb @@ -166,7 +166,7 @@ module Jekyll path = File.join(@dir || "", name) msg = "Post '#{path}' does not have a valid date.\n" msg << "Fix the date, or exclude the file or directory from being processed" - raise FatalException.new(msg) + raise Errors::FatalException.new(msg) end # The generated directory into which the post will be placed diff --git a/lib/jekyll/site.rb b/lib/jekyll/site.rb index 1015d5d9..15bacb14 100644 --- a/lib/jekyll/site.rb +++ b/lib/jekyll/site.rb @@ -80,7 +80,7 @@ module Jekyll dest_pathname = Pathname.new(dest) Pathname.new(source).ascend do |path| if path == dest_pathname - raise FatalException.new "Destination directory cannot be or contain the Source directory." + raise Errors::FatalException.new "Destination directory cannot be or contain the Source directory." end end end @@ -443,7 +443,7 @@ module Jekyll end def frontmatter_defaults - @frontmatter_defaults ||= Configuration::FrontmatterDefaults.new(self) + @frontmatter_defaults ||= FrontmatterDefaults.new(self) end private diff --git a/test/test_command.rb b/test/test_command.rb index 23f23379..8d1a751d 100644 --- a/test/test_command.rb +++ b/test/test_command.rb @@ -44,7 +44,7 @@ class TestCommand < Test::Unit::TestCase context "when fatal error occurs" do should "exit with non-zero error code" do site = Object.new - stub(site).process { raise Jekyll::FatalException } + stub(site).process { raise Jekyll::Errors::FatalException } error = assert_raise(SystemExit) { Command.process_site(site) } assert_not_equal 0, error.status end diff --git a/test/test_post.rb b/test/test_post.rb index 5e264ea7..28c6366b 100644 --- a/test/test_post.rb +++ b/test/test_post.rb @@ -83,7 +83,7 @@ class TestPost < Test::Unit::TestCase end should "raise a good error on invalid post date" do - assert_raise Jekyll::FatalException do + assert_raise Jekyll::Errors::FatalException do @post.process("2009-27-03-foo-bar.textile") end end diff --git a/test/test_site.rb b/test/test_site.rb index 48290d45..a343b1c9 100644 --- a/test/test_site.rb +++ b/test/test_site.rb @@ -225,7 +225,7 @@ class TestSite < Test::Unit::TestCase Jekyll::Configuration::DEFAULTS.merge({'source' => source_dir, 'destination' => source_dir}) end - assert_raise Jekyll::FatalException do + assert_raise Jekyll::Errors::FatalException do site = Site.new(Jekyll.configuration) end end @@ -235,7 +235,7 @@ class TestSite < Test::Unit::TestCase Jekyll::Configuration::DEFAULTS.merge({'source' => source_dir, 'destination' => File.join(source_dir, "..")}) end - assert_raise Jekyll::FatalException do + assert_raise Jekyll::Errors::FatalException do site = Site.new(Jekyll.configuration) end end @@ -332,7 +332,7 @@ class TestSite < Test::Unit::TestCase bad_processor = "Custom::Markdown" s = Site.new(Jekyll.configuration.merge({ 'markdown' => bad_processor })) - assert_raise Jekyll::FatalException do + assert_raise Jekyll::Errors::FatalException do s.process end @@ -352,7 +352,7 @@ class TestSite < Test::Unit::TestCase should 'throw FatalException at process time' do bad_processor = 'not a processor name' s = Site.new(Jekyll.configuration.merge({ 'markdown' => bad_processor })) - assert_raise Jekyll::FatalException do + assert_raise Jekyll::Errors::FatalException do s.process end end