jekyll 2.1.1 → 2.2.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of jekyll might be problematic. Click here for more details.

Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/History.markdown +36 -3
  3. data/Rakefile +20 -1
  4. data/features/post_data.feature +41 -6
  5. data/jekyll.gemspec +7 -4
  6. data/lib/jekyll.rb +53 -50
  7. data/lib/jekyll/command.rb +1 -1
  8. data/lib/jekyll/commands/build.rb +1 -1
  9. data/lib/jekyll/converters/markdown.rb +23 -4
  10. data/lib/jekyll/converters/markdown/kramdown_parser.rb +1 -1
  11. data/lib/jekyll/converters/markdown/maruku_parser.rb +1 -1
  12. data/lib/jekyll/converters/markdown/rdiscount_parser.rb +1 -5
  13. data/lib/jekyll/converters/markdown/redcarpet_parser.rb +31 -36
  14. data/lib/jekyll/converters/textile.rb +2 -2
  15. data/lib/jekyll/convertible.rb +12 -0
  16. data/lib/jekyll/deprecator.rb +19 -1
  17. data/lib/jekyll/errors.rb +6 -1
  18. data/lib/jekyll/excerpt.rb +0 -1
  19. data/lib/jekyll/frontmatter_defaults.rb +120 -122
  20. data/lib/jekyll/post.rb +3 -1
  21. data/lib/jekyll/renderer.rb +12 -0
  22. data/lib/jekyll/site.rb +2 -2
  23. data/lib/jekyll/tags/highlight.rb +24 -3
  24. data/lib/jekyll/tags/include.rb +0 -4
  25. data/lib/jekyll/version.rb +1 -1
  26. data/script/cibuild +4 -1
  27. data/script/proof +22 -0
  28. data/script/test +8 -2
  29. data/site/_config.yml +0 -1
  30. data/site/_posts/2013-05-06-jekyll-1-0-0-released.markdown +1 -1
  31. data/site/_posts/2013-05-08-jekyll-1-0-1-released.markdown +1 -1
  32. data/site/_posts/2013-05-12-jekyll-1-0-2-released.markdown +1 -1
  33. data/site/_posts/2013-06-07-jekyll-1-0-3-released.markdown +1 -1
  34. data/site/_posts/2014-03-24-jekyll-1-5-0-released.markdown +1 -1
  35. data/site/_posts/2014-05-06-jekyll-turns-2-0-0.markdown +2 -2
  36. data/site/_posts/2014-06-28-jekyll-turns-21-i-mean-2-1-0.markdown +1 -1
  37. data/site/_posts/2014-07-29-jekyll-2-2-0-released.markdown +19 -0
  38. data/site/docs/assets.md +17 -3
  39. data/site/docs/contributing.md +1 -1
  40. data/site/docs/deployment-methods.md +15 -2
  41. data/site/docs/extras.md +3 -75
  42. data/site/docs/github-pages.md +1 -1
  43. data/site/docs/history.md +260 -3
  44. data/site/docs/index.md +1 -1
  45. data/site/docs/installation.md +1 -1
  46. data/site/docs/plugins.md +6 -2
  47. data/site/docs/posts.md +3 -3
  48. data/site/docs/resources.md +3 -6
  49. data/site/docs/troubleshooting.md +0 -10
  50. data/site/index.html +1 -1
  51. data/test/test_command.rb +1 -1
  52. data/test/test_kramdown.rb +1 -1
  53. data/test/test_post.rb +17 -2
  54. data/test/test_site.rb +5 -5
  55. data/test/test_tags.rb +33 -2
  56. metadata +20 -19
  57. data/site/docs/heroku.md +0 -9
@@ -13,7 +13,7 @@ module Jekyll
13
13
  rescue LoadError
14
14
  STDERR.puts 'You are missing a library required for Textile. Please run:'
15
15
  STDERR.puts ' $ [sudo] gem install RedCloth'
16
- raise FatalException.new("Missing dependency: RedCloth")
16
+ raise Errors::FatalException.new("Missing dependency: RedCloth")
17
17
  end
18
18
 
19
19
  def matches(ext)
@@ -32,7 +32,7 @@ module Jekyll
32
32
  return RedCloth.new(content).to_html if @config['redcloth'].nil?
33
33
 
34
34
  # List of attributes defined on RedCloth
35
- # (from http://redcloth.rubyforge.org/classes/RedCloth/TextileDoc.html)
35
+ # (from https://github.com/jgarber/redcloth/blob/master/lib/redcloth/textile_doc.rb)
36
36
  attrs = ['filter_classes', 'filter_html', 'filter_ids', 'filter_styles',
37
37
  'hard_breaks', 'lite_mode', 'no_span_caps', 'sanitize_html']
38
38
 
@@ -153,6 +153,15 @@ module Jekyll
153
153
  !asset_file?
154
154
  end
155
155
 
156
+ # Checks if the layout specified in the document actually exists
157
+ #
158
+ # layout - the layout to check
159
+ #
160
+ # Returns true if the layout is invalid, false if otherwise
161
+ def invalid_layout?(layout)
162
+ !data["layout"].nil? && data["layout"] != "none" && layout.nil? && !(self.is_a? Jekyll::Excerpt)
163
+ end
164
+
156
165
  # Recursively render layouts
157
166
  #
158
167
  # layouts - a list of the layouts
@@ -163,6 +172,9 @@ module Jekyll
163
172
  def render_all_layouts(layouts, payload, info)
164
173
  # recursively render layouts
165
174
  layout = layouts[data["layout"]]
175
+
176
+ Jekyll.logger.warn("Build Warning:", "Layout '#{data["layout"]}' requested in #{path} does not exist.") if invalid_layout? layout
177
+
166
178
  used = Set.new([layout])
167
179
 
168
180
  while layout
@@ -1,5 +1,5 @@
1
1
  module Jekyll
2
- class Deprecator
2
+ module Deprecator
3
3
  def self.process(args)
4
4
  no_subcommand(args)
5
5
  arg_is_present? args, "--server", "The --server command has been replaced by the \
@@ -32,5 +32,23 @@ module Jekyll
32
32
  def self.deprecation_message(message)
33
33
  Jekyll.logger.error "Deprecation:", message
34
34
  end
35
+
36
+ def self.gracefully_require(gem_name)
37
+ Array(gem_name).each do |name|
38
+ begin
39
+ require name
40
+ rescue LoadError => e
41
+ Jekyll.logger.error "Dependency Error:", <<-MSG
42
+ Yikes! It looks like you don't have #{name} or one of its dependencies installed.
43
+ In order to use Jekyll as currently contfigured, you'll need to install this gem.
44
+
45
+ The full error message from Ruby is: '#{e.message}'
46
+
47
+ If you run into trouble, you can find helpful resources at http://jekyllrb.com/help/!
48
+ MSG
49
+ raise Errors::MissingDependencyException.new(name)
50
+ end
51
+ end
52
+ end
35
53
  end
36
54
  end
@@ -1,4 +1,9 @@
1
1
  module Jekyll
2
- class FatalException < StandardError
2
+ module Errors
3
+ class FatalException < RuntimeError
4
+ end
5
+
6
+ class MissingDependencyException < FatalException
7
+ end
3
8
  end
4
9
  end
@@ -1,4 +1,3 @@
1
- require 'jekyll/convertible'
2
1
  require 'forwardable'
3
2
 
4
3
  module Jekyll
@@ -1,147 +1,145 @@
1
1
  module Jekyll
2
- class Configuration
3
- # This class handles custom defaults for YAML frontmatter settings.
4
- # These are set in _config.yml and apply both to internal use (e.g. layout)
5
- # and the data available to liquid.
6
- #
7
- # It is exposed via the frontmatter_defaults method on the site class.
8
- class FrontmatterDefaults
9
- # Initializes a new instance.
10
- def initialize(site)
11
- @site = site
12
- end
2
+ # This class handles custom defaults for YAML frontmatter settings.
3
+ # These are set in _config.yml and apply both to internal use (e.g. layout)
4
+ # and the data available to liquid.
5
+ #
6
+ # It is exposed via the frontmatter_defaults method on the site class.
7
+ class FrontmatterDefaults
8
+ # Initializes a new instance.
9
+ def initialize(site)
10
+ @site = site
11
+ end
13
12
 
14
- # Finds a default value for a given setting, filtered by path and type
15
- #
16
- # path - the path (relative to the source) of the page, post or :draft the default is used in
17
- # type - a symbol indicating whether a :page, a :post or a :draft calls this method
18
- #
19
- # Returns the default value or nil if none was found
20
- def find(path, type, setting)
21
- value = nil
22
- old_scope = nil
23
-
24
- matching_sets(path, type).each do |set|
25
- if set['values'].has_key?(setting) && has_precedence?(old_scope, set['scope'])
26
- value = set['values'][setting]
27
- old_scope = set['scope']
28
- end
13
+ # Finds a default value for a given setting, filtered by path and type
14
+ #
15
+ # path - the path (relative to the source) of the page, post or :draft the default is used in
16
+ # type - a symbol indicating whether a :page, a :post or a :draft calls this method
17
+ #
18
+ # Returns the default value or nil if none was found
19
+ def find(path, type, setting)
20
+ value = nil
21
+ old_scope = nil
22
+
23
+ matching_sets(path, type).each do |set|
24
+ if set['values'].has_key?(setting) && has_precedence?(old_scope, set['scope'])
25
+ value = set['values'][setting]
26
+ old_scope = set['scope']
29
27
  end
30
- value
31
28
  end
29
+ value
30
+ end
32
31
 
33
- # Collects a hash with all default values for a page or post
34
- #
35
- # path - the relative path of the page or post
36
- # type - a symbol indicating the type (:post, :page or :draft)
37
- #
38
- # Returns a hash with all default values (an empty hash if there are none)
39
- def all(path, type)
40
- defaults = {}
41
- old_scope = nil
42
- matching_sets(path, type).each do |set|
43
- if has_precedence?(old_scope, set['scope'])
44
- defaults = Utils.deep_merge_hashes(defaults, set['values'])
45
- old_scope = set['scope']
46
- else
47
- defaults = Utils.deep_merge_hashes(set['values'], defaults)
48
- end
32
+ # Collects a hash with all default values for a page or post
33
+ #
34
+ # path - the relative path of the page or post
35
+ # type - a symbol indicating the type (:post, :page or :draft)
36
+ #
37
+ # Returns a hash with all default values (an empty hash if there are none)
38
+ def all(path, type)
39
+ defaults = {}
40
+ old_scope = nil
41
+ matching_sets(path, type).each do |set|
42
+ if has_precedence?(old_scope, set['scope'])
43
+ defaults = Utils.deep_merge_hashes(defaults, set['values'])
44
+ old_scope = set['scope']
45
+ else
46
+ defaults = Utils.deep_merge_hashes(set['values'], defaults)
49
47
  end
50
- defaults
51
48
  end
49
+ defaults
50
+ end
52
51
 
53
- private
54
-
55
- # Checks if a given default setting scope matches the given path and type
56
- #
57
- # scope - the hash indicating the scope, as defined in _config.yml
58
- # path - the path to check for
59
- # type - the type (:post, :page or :draft) to check for
60
- #
61
- # Returns true if the scope applies to the given path and type
62
- def applies?(scope, path, type)
63
- applies_path?(scope, path) && applies_type?(scope, type)
64
- end
52
+ private
65
53
 
66
- def applies_path?(scope, path)
67
- return true if scope['path'].empty?
54
+ # Checks if a given default setting scope matches the given path and type
55
+ #
56
+ # scope - the hash indicating the scope, as defined in _config.yml
57
+ # path - the path to check for
58
+ # type - the type (:post, :page or :draft) to check for
59
+ #
60
+ # Returns true if the scope applies to the given path and type
61
+ def applies?(scope, path, type)
62
+ applies_path?(scope, path) && applies_type?(scope, type)
63
+ end
68
64
 
69
- scope_path = Pathname.new(scope['path'])
70
- Pathname.new(sanitize_path(path)).ascend do |path|
71
- if path == scope_path
72
- return true
73
- end
65
+ def applies_path?(scope, path)
66
+ return true if scope['path'].empty?
67
+
68
+ scope_path = Pathname.new(scope['path'])
69
+ Pathname.new(sanitize_path(path)).ascend do |path|
70
+ if path == scope_path
71
+ return true
74
72
  end
75
73
  end
74
+ end
76
75
 
77
- def applies_type?(scope, type)
78
- !scope.has_key?('type') || scope['type'] == type.to_s
79
- end
76
+ def applies_type?(scope, type)
77
+ !scope.has_key?('type') || scope['type'] == type.to_s
78
+ end
80
79
 
81
- # Checks if a given set of default values is valid
82
- #
83
- # set - the default value hash, as defined in _config.yml
84
- #
85
- # Returns true if the set is valid and can be used in this class
86
- def valid?(set)
87
- set.is_a?(Hash) && set['scope'].is_a?(Hash) && set['scope']['path'].is_a?(String) && set['values'].is_a?(Hash)
88
- end
80
+ # Checks if a given set of default values is valid
81
+ #
82
+ # set - the default value hash, as defined in _config.yml
83
+ #
84
+ # Returns true if the set is valid and can be used in this class
85
+ def valid?(set)
86
+ set.is_a?(Hash) && set['scope'].is_a?(Hash) && set['scope']['path'].is_a?(String) && set['values'].is_a?(Hash)
87
+ end
89
88
 
90
- # Determines if a new scope has precedence over an old one
91
- #
92
- # old_scope - the old scope hash, or nil if there's none
93
- # new_scope - the new scope hash
94
- #
95
- # Returns true if the new scope has precedence over the older
96
- def has_precedence?(old_scope, new_scope)
97
- return true if old_scope.nil?
98
-
99
- new_path = sanitize_path(new_scope['path'])
100
- old_path = sanitize_path(old_scope['path'])
101
-
102
- if new_path.length != old_path.length
103
- new_path.length >= old_path.length
104
- elsif new_scope.has_key? 'type'
105
- true
106
- else
107
- !old_scope.has_key? 'type'
108
- end
89
+ # Determines if a new scope has precedence over an old one
90
+ #
91
+ # old_scope - the old scope hash, or nil if there's none
92
+ # new_scope - the new scope hash
93
+ #
94
+ # Returns true if the new scope has precedence over the older
95
+ def has_precedence?(old_scope, new_scope)
96
+ return true if old_scope.nil?
97
+
98
+ new_path = sanitize_path(new_scope['path'])
99
+ old_path = sanitize_path(old_scope['path'])
100
+
101
+ if new_path.length != old_path.length
102
+ new_path.length >= old_path.length
103
+ elsif new_scope.has_key? 'type'
104
+ true
105
+ else
106
+ !old_scope.has_key? 'type'
109
107
  end
108
+ end
110
109
 
111
- # Collects a list of sets that match the given path and type
112
- #
113
- # Returns an array of hashes
114
- def matching_sets(path, type)
115
- valid_sets.select do |set|
116
- applies?(set['scope'], path, type)
117
- end
110
+ # Collects a list of sets that match the given path and type
111
+ #
112
+ # Returns an array of hashes
113
+ def matching_sets(path, type)
114
+ valid_sets.select do |set|
115
+ applies?(set['scope'], path, type)
118
116
  end
117
+ end
119
118
 
120
- # Returns a list of valid sets
121
- #
122
- # This is not cached to allow plugins to modify the configuration
123
- # and have their changes take effect
124
- #
125
- # Returns an array of hashes
126
- def valid_sets
127
- sets = @site.config['defaults']
128
- return [] unless sets.is_a?(Array)
129
-
130
- sets.select do |set|
131
- unless valid?(set)
132
- Jekyll.logger.warn "Default:", "An invalid default set was found"
133
- end
134
- valid?(set)
119
+ # Returns a list of valid sets
120
+ #
121
+ # This is not cached to allow plugins to modify the configuration
122
+ # and have their changes take effect
123
+ #
124
+ # Returns an array of hashes
125
+ def valid_sets
126
+ sets = @site.config['defaults']
127
+ return [] unless sets.is_a?(Array)
128
+
129
+ sets.select do |set|
130
+ unless valid?(set)
131
+ Jekyll.logger.warn "Default:", "An invalid default set was found"
135
132
  end
133
+ valid?(set)
136
134
  end
135
+ end
137
136
 
138
- # Sanitizes the given path by removing a leading and addding a trailing slash
139
- def sanitize_path(path)
140
- if path.nil? || path.empty?
141
- ""
142
- else
143
- path.gsub(/\A\//, '').gsub(/([^\/])\z/, '\1/')
144
- end
137
+ # Sanitizes the given path by removing a leading and addding a trailing slash
138
+ def sanitize_path(path)
139
+ if path.nil? || path.empty?
140
+ ""
141
+ else
142
+ path.gsub(/\A\//, '').gsub(/([^\/])\z/, '\1/')
145
143
  end
146
144
  end
147
145
  end
@@ -159,6 +159,8 @@ module Jekyll
159
159
  # Returns nothing.
160
160
  def process(name)
161
161
  m, cats, date, slug, ext = *name.match(MATCHER)
162
+ self.categories ||= []
163
+ self.categories += (cats || '').downcase.split('/')
162
164
  self.date = Time.parse(date)
163
165
  self.slug = slug
164
166
  self.ext = ext
@@ -166,7 +168,7 @@ module Jekyll
166
168
  path = File.join(@dir || "", name)
167
169
  msg = "Post '#{path}' does not have a valid date.\n"
168
170
  msg << "Fix the date, or exclude the file or directory from being processed"
169
- raise FatalException.new(msg)
171
+ raise Errors::FatalException.new(msg)
170
172
  end
171
173
 
172
174
  # The generated directory into which the post will be placed
@@ -92,6 +92,15 @@ module Jekyll
92
92
  raise e
93
93
  end
94
94
 
95
+ # Checks if the layout specified in the document actually exists
96
+ #
97
+ # layout - the layout to check
98
+ #
99
+ # Returns true if the layout is invalid, false if otherwise
100
+ def invalid_layout?(layout)
101
+ !document.data["layout"].nil? && layout.nil?
102
+ end
103
+
95
104
  # Render layouts and place given content inside.
96
105
  #
97
106
  # content - the content to be placed in the layout
@@ -101,6 +110,9 @@ module Jekyll
101
110
  def place_in_layouts(content, payload, info)
102
111
  output = content.dup
103
112
  layout = site.layouts[document.data["layout"]]
113
+
114
+ Jekyll.logger.warn("Build Warning:", "Layout '#{document.data["layout"]}' requested in #{document.relative_path} does not exist.") if invalid_layout? layout
115
+
104
116
  used = Set.new([layout])
105
117
 
106
118
  while layout
@@ -80,7 +80,7 @@ module Jekyll
80
80
  dest_pathname = Pathname.new(dest)
81
81
  Pathname.new(source).ascend do |path|
82
82
  if path == dest_pathname
83
- raise FatalException.new "Destination directory cannot be or contain the Source directory."
83
+ raise Errors::FatalException.new "Destination directory cannot be or contain the Source directory."
84
84
  end
85
85
  end
86
86
  end
@@ -443,7 +443,7 @@ module Jekyll
443
443
  end
444
444
 
445
445
  def frontmatter_defaults
446
- @frontmatter_defaults ||= Configuration::FrontmatterDefaults.new(self)
446
+ @frontmatter_defaults ||= FrontmatterDefaults.new(self)
447
447
  end
448
448
 
449
449
  private
@@ -44,9 +44,11 @@ eos
44
44
  suffix = context["highlighter_suffix"] || ""
45
45
  code = super.to_s.strip
46
46
 
47
+ is_safe = !!context.registers[:site].safe
48
+
47
49
  output = case context.registers[:site].highlighter
48
50
  when 'pygments'
49
- render_pygments(code)
51
+ render_pygments(code, is_safe)
50
52
  when 'rouge'
51
53
  render_rouge(code)
52
54
  else
@@ -57,11 +59,30 @@ eos
57
59
  prefix + rendered_output + suffix
58
60
  end
59
61
 
60
- def render_pygments(code)
62
+ def sanitized_opts(opts, is_safe)
63
+ if is_safe
64
+ Hash[[
65
+ [:startinline, opts.fetch(:startinline, nil)],
66
+ [:hl_linenos, opts.fetch(:hl_linenos, nil)],
67
+ [:linenos, opts.fetch(:linenos, nil)],
68
+ [:encoding, opts.fetch(:encoding, 'utf-8')],
69
+ [:cssclass, opts.fetch(:cssclass, nil)]
70
+ ].reject {|f| f.last.nil? }]
71
+ else
72
+ opts
73
+ end
74
+ end
75
+
76
+ def render_pygments(code, is_safe)
61
77
  require 'pygments'
78
+
62
79
  @options[:encoding] = 'utf-8'
63
80
 
64
- highlighted_code = Pygments.highlight(code, :lexer => @lang, :options => @options)
81
+ highlighted_code = Pygments.highlight(
82
+ code,
83
+ :lexer => @lang,
84
+ :options => sanitized_opts(@options, is_safe)
85
+ )
65
86
 
66
87
  if highlighted_code.nil?
67
88
  Jekyll.logger.error "There was an error highlighting your code:"