jekyll 2.0.0.alpha.2 → 2.0.0.alpha.3

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 (79) hide show
  1. checksums.yaml +4 -4
  2. data/History.markdown +35 -0
  3. data/Rakefile +1 -1
  4. data/features/collections.feature +38 -0
  5. data/features/create_sites.feature +17 -0
  6. data/features/step_definitions/jekyll_steps.rb +15 -7
  7. data/features/support/env.rb +6 -1
  8. data/lib/jekyll.rb +6 -1
  9. data/lib/jekyll/cleaner.rb +5 -3
  10. data/lib/jekyll/collection.rb +121 -0
  11. data/lib/jekyll/command.rb +2 -0
  12. data/lib/jekyll/commands/build.rb +5 -1
  13. data/lib/jekyll/commands/serve.rb +1 -1
  14. data/lib/jekyll/configuration.rb +2 -0
  15. data/lib/jekyll/converters/markdown/redcarpet_parser.rb +11 -12
  16. data/lib/jekyll/convertible.rb +1 -1
  17. data/lib/jekyll/document.rb +228 -0
  18. data/lib/jekyll/entry_filter.rb +4 -1
  19. data/lib/jekyll/layout_reader.rb +1 -1
  20. data/lib/jekyll/page.rb +1 -1
  21. data/lib/jekyll/plugin_manager.rb +76 -0
  22. data/lib/jekyll/post.rb +3 -3
  23. data/lib/jekyll/publisher.rb +21 -0
  24. data/lib/jekyll/renderer.rb +132 -0
  25. data/lib/jekyll/site.rb +84 -68
  26. data/lib/jekyll/tags/highlight.rb +8 -6
  27. data/lib/jekyll/url.rb +43 -3
  28. data/lib/jekyll/version.rb +1 -1
  29. data/lib/site_template/_includes/head.html +3 -3
  30. data/script/console +38 -0
  31. data/site/_config.yml +1 -0
  32. data/site/_data/docs.yml +1 -0
  33. data/site/_includes/css/style.css +12 -12
  34. data/site/_includes/news_item.html +3 -3
  35. data/site/_includes/primary-nav-items.html +4 -1
  36. data/site/_includes/top.html +7 -3
  37. data/site/_layouts/news_item.html +3 -3
  38. data/site/_posts/2014-03-27-jekyll-1-5-1-released.markdown +26 -0
  39. data/site/docs/collections.md +126 -0
  40. data/site/docs/configuration.md +3 -2
  41. data/site/docs/datafiles.md +1 -1
  42. data/site/docs/history.md +6 -0
  43. data/site/docs/plugins.md +1 -1
  44. data/site/docs/troubleshooting.md +7 -3
  45. data/site/docs/variables.md +1 -1
  46. data/site/favicon.ico +0 -0
  47. data/site/feed.xml +27 -14
  48. data/site/img/logo-rss.png +0 -0
  49. data/site/js/html5shiv.js +8 -0
  50. data/site/js/respond.min.js +5 -0
  51. data/test/source/+/%# +.md +6 -0
  52. data/test/source/_methods/_do_not_read_me.md +5 -0
  53. data/test/source/_methods/configuration.md +8 -0
  54. data/test/source/_methods/sanitized_path.md +5 -0
  55. data/test/source/_methods/site/_dont_include_me_either.md +5 -0
  56. data/test/source/_methods/site/generate.md +6 -0
  57. data/test/source/_methods/site/initialize.md +5 -0
  58. data/test/source/_methods/um_hi.md +6 -0
  59. data/test/source/_posts/2014-03-03-yaml-with-dots.md +5 -0
  60. data/test/source/_posts/2014-03-22-escape-+ %20[].markdown +6 -0
  61. data/test/source/pgp.key +2 -0
  62. data/test/test_coffeescript.rb +1 -1
  63. data/test/test_collections.rb +129 -0
  64. data/test/test_command.rb +17 -0
  65. data/test/test_document.rb +48 -0
  66. data/test/test_filters.rb +1 -1
  67. data/test/test_generated_site.rb +5 -4
  68. data/test/test_new_command.rb +4 -4
  69. data/test/test_page.rb +22 -8
  70. data/test/test_path_sanitization.rb +4 -0
  71. data/test/test_post.rb +42 -13
  72. data/test/test_site.rb +11 -17
  73. data/test/test_tags.rb +10 -6
  74. metadata +42 -7
  75. data/lib/site_template/_posts/0000-00-00-this-post-demonstrates-post-content-styles.md +0 -88
  76. data/site/favicon.png +0 -0
  77. data/site/img/tube.png +0 -0
  78. data/site/img/tube1x.png +0 -0
  79. data/site/js/modernizr-2.7.1.min.js +0 -4
@@ -13,6 +13,7 @@ module Jekyll
13
13
  'data_source' => '_data',
14
14
  'keep_files' => ['.git','.svn'],
15
15
  'gems' => [],
16
+ 'collections' => nil,
16
17
 
17
18
  'timezone' => nil, # use the local timezone
18
19
 
@@ -24,6 +25,7 @@ module Jekyll
24
25
  'limit_posts' => 0,
25
26
  'lsi' => false,
26
27
  'future' => true, # remove and make true just default
28
+ 'unpublished' => false,
27
29
 
28
30
  'relative_permalinks' => true, # backwards-compatibility with < 1.0
29
31
  # will be set to false once 2.0 hits
@@ -16,7 +16,7 @@ module Jekyll
16
16
  def block_code(code, lang)
17
17
  require 'pygments'
18
18
  lang = lang && lang.split.first || "text"
19
- output = add_code_tags(
19
+ add_code_tags(
20
20
  Pygments.highlight(code, :lexer => lang, :options => { :encoding => 'utf-8' }),
21
21
  lang
22
22
  )
@@ -34,21 +34,11 @@ module Jekyll
34
34
 
35
35
  def block_code(code, lang)
36
36
  lang = lang && lang.split.first || "text"
37
- output = add_code_tags(code_wrap(code), lang)
37
+ add_code_tags(code_wrap(code), lang)
38
38
  end
39
39
  end
40
40
 
41
41
  module WithRouge
42
- require 'rouge'
43
- require 'rouge/plugins/redcarpet'
44
-
45
- if Rouge.version < '1.3.0'
46
- abort "Please install Rouge 1.3.0 or greater and try running Jekyll again."
47
- end
48
-
49
- include Rouge::Plugins::Redcarpet
50
- include CommonMethods
51
-
52
42
  def block_code(code, lang)
53
43
  code = "<pre>#{super}</pre>"
54
44
 
@@ -77,6 +67,15 @@ module Jekyll
77
67
  end
78
68
  when 'rouge'
79
69
  Class.new(Redcarpet::Render::HTML) do
70
+ require 'rouge'
71
+ require 'rouge/plugins/redcarpet'
72
+
73
+ if Rouge.version < '1.3.0'
74
+ abort "Please install Rouge 1.3.0 or greater and try running Jekyll again."
75
+ end
76
+
77
+ include Rouge::Plugins::Redcarpet
78
+ include CommonMethods
80
79
  include WithRouge
81
80
  end
82
81
  else
@@ -43,7 +43,7 @@ module Jekyll
43
43
  begin
44
44
  self.content = File.read(File.join(base, name),
45
45
  merged_file_read_opts(opts))
46
- if content =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
46
+ if content =~ /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m
47
47
  self.content = $POSTMATCH
48
48
  self.data = SafeYAML.load($1)
49
49
  end
@@ -0,0 +1,228 @@
1
+ module Jekyll
2
+ class Document
3
+ include Comparable
4
+
5
+ attr_reader :path, :site
6
+ attr_accessor :content, :collection, :output
7
+
8
+ # Create a new Document.
9
+ #
10
+ # site - the Jekyll::Site instance to which this Document belongs
11
+ # path - the path to the file
12
+ #
13
+ # Returns nothing.
14
+ def initialize(path, relations)
15
+ @site = relations[:site]
16
+ @path = path
17
+ @collection = relations[:collection]
18
+ end
19
+
20
+ # Fetch the Document's data.
21
+ #
22
+ # Returns a Hash containing the data. An empty hash is returned if
23
+ # no data was read.
24
+ def data
25
+ @data ||= Hash.new
26
+ end
27
+
28
+ # The path to the document, relative to the site source.
29
+ #
30
+ # Returns a String path which represents the relative path
31
+ # from the site source to this document
32
+ def relative_path
33
+ Pathname.new(path).relative_path_from(Pathname.new(site.source)).to_s
34
+ end
35
+
36
+ # The base filename of the document.
37
+ #
38
+ # suffix - (optional) the suffix to be removed from the end of the filename
39
+ #
40
+ # Returns the base filename of the document.
41
+ def basename(suffix = "")
42
+ File.basename(path, suffix)
43
+ end
44
+
45
+ # The extension name of the document.
46
+ #
47
+ # Returns the extension name of the document.
48
+ def extname
49
+ File.extname(path)
50
+ end
51
+
52
+ # Produces a "cleaned" relative path.
53
+ # The "cleaned" relative path is the relative path without the extname
54
+ # and with the collection's directory removed as well.
55
+ # This method is useful when building the URL of the document.
56
+ #
57
+ # Examples:
58
+ # When relative_path is "_methods/site/generate.md":
59
+ # cleaned_relative_path
60
+ # # => "/site/generate"
61
+ #
62
+ # Returns the cleaned relative path of the document.
63
+ def cleaned_relative_path
64
+ relative_path[0 .. -extname.length - 1].sub(collection.relative_directory, "")
65
+ end
66
+
67
+ # Determine whether the document is a YAML file.
68
+ #
69
+ # Returns true if the extname is either .yml or .yaml, false otherwise.
70
+ def yaml_file?
71
+ %w[.yaml .yml].include?(extname)
72
+ end
73
+
74
+ # Determine whether the document is an asset file.
75
+ # Asset files include CoffeeScript files and Sass/SCSS files.
76
+ #
77
+ # Returns true if the extname belongs to the set of extensions
78
+ # that asset files use.
79
+ def asset_file?
80
+ %w[.sass .scss .coffee].include?(extname)
81
+ end
82
+
83
+ # Determine whether the file should be rendered with Liquid.
84
+ #
85
+ # Returns false if the document is either an asset file or a yaml file,
86
+ # true otherwise.
87
+ def render_with_liquid?
88
+ !(asset_file? || yaml_file?)
89
+ end
90
+
91
+ # The URL template where the document would be accessible.
92
+ #
93
+ # Returns the URL template for the document.
94
+ def url_template
95
+ "/:collection/:path:output_ext"
96
+ end
97
+
98
+ # Construct a Hash of key-value pairs which contain a mapping between
99
+ # a key in the URL template and the corresponding value for this document.
100
+ #
101
+ # Returns the Hash of key-value pairs for replacement in the URL.
102
+ def url_placeholders
103
+ {
104
+ collection: collection.label,
105
+ path: cleaned_relative_path,
106
+ output_ext: Jekyll::Renderer.new(site, self).output_ext
107
+ }
108
+ end
109
+
110
+ # The permalink for this Document.
111
+ # Permalink is set via the data Hash.
112
+ #
113
+ # Returns the permalink or nil if no permalink was set in the data.
114
+ def permalink
115
+ data && data['permalink']
116
+ end
117
+
118
+ # The computed URL for the document. See `Jekyll::URL#to_s` for more details.
119
+ #
120
+ # Returns the computed URL for the document.
121
+ def url
122
+ @url ||= URL.new({
123
+ template: url_template,
124
+ placeholders: url_placeholders,
125
+ permalink: permalink
126
+ }).to_s
127
+ end
128
+
129
+ # The full path to the output file.
130
+ #
131
+ # base_directory - the base path of the output directory
132
+ #
133
+ # Returns the full path to the output file of this document.
134
+ def destination(base_directory)
135
+ path = Jekyll.sanitized_path(base_directory, url)
136
+ path = File.join(path, "index.html") if url =~ /\/$/
137
+ path
138
+ end
139
+
140
+ # Write the generated Document file to the destination directory.
141
+ #
142
+ # dest - The String path to the destination dir.
143
+ #
144
+ # Returns nothing.
145
+ def write(dest)
146
+ path = destination(dest)
147
+ FileUtils.mkdir_p(File.dirname(path))
148
+ File.open(path, 'wb') do |f|
149
+ f.write(output)
150
+ end
151
+ end
152
+
153
+ # Returns merged option hash for File.read of self.site (if exists)
154
+ # and a given param
155
+ #
156
+ # opts - override options
157
+ #
158
+ # Return the file read options hash.
159
+ def merged_file_read_opts(opts)
160
+ site ? site.file_read_opts.merge(opts) : opts
161
+ end
162
+
163
+ # Whether the file is published or not, as indicated in YAML front-matter
164
+ #
165
+ # Returns true if the 'published' key is specified in the YAML front-matter and not `false`.
166
+ def published?
167
+ !(data.has_key?('published') && data['published'] == false)
168
+ end
169
+
170
+ # Read in the file and assign the content and data based on the file contents.
171
+ #
172
+ # Returns nothing.
173
+ def read(opts = {})
174
+ if yaml_file?
175
+ @data = SafeYAML.load_file(path)
176
+ else
177
+ begin
178
+ @content = File.read(path, merged_file_read_opts(opts))
179
+ if content =~ /\A(---\s*\n.*?\n?)^(---\s*$\n?)/m
180
+ @content = $POSTMATCH
181
+ @data = SafeYAML.load($1)
182
+ end
183
+ rescue SyntaxError => e
184
+ puts "YAML Exception reading #{path}: #{e.message}"
185
+ rescue Exception => e
186
+ puts "Error reading file #{path}: #{e.message}"
187
+ end
188
+ end
189
+ end
190
+
191
+ # Create a Liquid-understandable version of this Document.
192
+ #
193
+ # Returns a Hash representing this Document's data.
194
+ def to_liquid
195
+ Utils.deep_merge_hashes data, {
196
+ "content" => content,
197
+ "path" => path,
198
+ "relative_path" => relative_path,
199
+ "url" => url
200
+ }
201
+ end
202
+
203
+ # The inspect string for this document.
204
+ # Includes the relative path and the collection label.
205
+ #
206
+ # Returns the inspect string for this document.
207
+ def inspect
208
+ "#<Jekyll::Document #{relative_path} collection=#{collection.label}>"
209
+ end
210
+
211
+ # The string representation for this document.
212
+ #
213
+ # Returns the content of the document
214
+ def to_s
215
+ output || content
216
+ end
217
+
218
+ # Compare this document against another document.
219
+ # Comparison is a comparison between the 2 paths of the documents.
220
+ #
221
+ # Returns -1, 0, +1 or nil depending on whether this doc's path is less than,
222
+ # equal or greater than the other doc's path. See String#<=> for more details.
223
+ def <=>(anotherDocument)
224
+ path <=> anotherDocument.path
225
+ end
226
+
227
+ end
228
+ end
@@ -1,5 +1,7 @@
1
1
  module Jekyll
2
2
  class EntryFilter
3
+ SPECIAL_LEADING_CHARACTERS = ['.', '_', '#'].freeze
4
+
3
5
  attr_reader :site
4
6
 
5
7
  def initialize(site, base_directory = nil)
@@ -35,7 +37,8 @@ module Jekyll
35
37
  end
36
38
 
37
39
  def special?(entry)
38
- ['.', '_', '#'].include?(entry[0..0])
40
+ SPECIAL_LEADING_CHARACTERS.include?(entry[0..0]) ||
41
+ SPECIAL_LEADING_CHARACTERS.include?(File.basename(entry)[0..0])
39
42
  end
40
43
 
41
44
  def backup?(entry)
@@ -33,7 +33,7 @@ module Jekyll
33
33
  end
34
34
 
35
35
  def within(directory)
36
- return unless File.exists?(directory)
36
+ return unless File.exist?(directory)
37
37
  Dir.chdir(directory) { yield }
38
38
  end
39
39
 
@@ -135,7 +135,7 @@ module Jekyll
135
135
  #
136
136
  # Returns the destination file path String.
137
137
  def destination(dest)
138
- path = Jekyll.sanitized_path(dest, url)
138
+ path = Jekyll.sanitized_path(dest, URL.unescape_path(url))
139
139
  path = File.join(path, "index.html") if url =~ /\/$/
140
140
  path
141
141
  end
@@ -0,0 +1,76 @@
1
+ module Jekyll
2
+ class PluginManager
3
+ attr_reader :site
4
+
5
+ # Create an instance of this class.
6
+ #
7
+ # site - the instance of Jekyll::Site we're concerned with
8
+ #
9
+ # Returns nothing
10
+ def initialize(site)
11
+ @site = site
12
+ end
13
+
14
+ # Require all the plugins which are allowed.
15
+ #
16
+ # Returns nothing
17
+ def conscientious_require
18
+ require_plugin_files
19
+ require_gems
20
+ end
21
+
22
+ # Require each of the gem plugins specified.
23
+ #
24
+ # Returns nothing.
25
+ def require_gems
26
+ site.gems.each do |gem|
27
+ if plugin_allowed?(gem)
28
+ require gem
29
+ end
30
+ end
31
+ end
32
+
33
+ # Check whether a gem plugin is allowed to be used during this build.
34
+ #
35
+ # gem_name - the name of the gem
36
+ #
37
+ # Returns true if the gem name is in the whitelist or if the site is not
38
+ # in safe mode.
39
+ def plugin_allowed?(gem_name)
40
+ !site.safe || whitelist.include?(gem_name)
41
+ end
42
+
43
+ # Build an array of allowed plugin gem names.
44
+ #
45
+ # Returns an array of strings, each string being the name of a gem name
46
+ # that is allowed to be used.
47
+ def whitelist
48
+ @whitelist ||= Array[site.config['whitelist']].flatten
49
+ end
50
+
51
+ # Require all .rb files if safe mode is off
52
+ #
53
+ # Returns nothing.
54
+ def require_plugin_files
55
+ unless site.safe
56
+ plugins_path.each do |plugins|
57
+ Dir[File.join(plugins, "**", "*.rb")].sort.each do |f|
58
+ require f
59
+ end
60
+ end
61
+ end
62
+ end
63
+
64
+ # Public: Setup the plugin search path
65
+ #
66
+ # Returns an Array of plugin search paths
67
+ def plugins_path
68
+ if (site.config['plugins'] == Jekyll::Configuration::DEFAULTS['plugins'])
69
+ [Jekyll.sanitized_path(site.source, site.config['plugins'])]
70
+ else
71
+ Array(site.config['plugins']).map { |d| File.expand_path(d) }
72
+ end
73
+ end
74
+
75
+ end
76
+ end
@@ -208,10 +208,10 @@ module Jekyll
208
208
  :year => date.strftime("%Y"),
209
209
  :month => date.strftime("%m"),
210
210
  :day => date.strftime("%d"),
211
- :title => CGI.escape(slug),
211
+ :title => slug,
212
212
  :i_day => date.strftime("%d").to_i.to_s,
213
213
  :i_month => date.strftime("%m").to_i.to_s,
214
- :categories => (categories || []).map { |c| URI.escape(c.to_s) }.join('/'),
214
+ :categories => (categories || []).map { |c| c.to_s }.join('/'),
215
215
  :short_month => date.strftime("%b"),
216
216
  :y_day => date.strftime("%j"),
217
217
  :output_ext => output_ext
@@ -260,7 +260,7 @@ module Jekyll
260
260
  # Returns destination file path String.
261
261
  def destination(dest)
262
262
  # The url needs to be unescaped in order to preserve the correct filename
263
- path = Jekyll.sanitized_path(dest, CGI.unescape(url))
263
+ path = Jekyll.sanitized_path(dest, URL.unescape_path(url))
264
264
  path = File.join(path, "index.html") if path[/\.html$/].nil?
265
265
  path
266
266
  end
@@ -0,0 +1,21 @@
1
+ module Jekyll
2
+ class Publisher
3
+ def initialize(site)
4
+ @site = site
5
+ end
6
+
7
+ def publish?(thing)
8
+ can_be_published?(thing) && !hidden_in_the_future?(thing)
9
+ end
10
+
11
+ private
12
+
13
+ def can_be_published?(thing)
14
+ thing.data.fetch('published', true) || @site.unpublished
15
+ end
16
+
17
+ def hidden_in_the_future?(thing)
18
+ thing.is_a?(Post) && !@site.future && thing.date > @site.time
19
+ end
20
+ end
21
+ end