jekyll 3.0.0.pre.beta10 → 3.0.0.pre.rc1

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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 632b5286e2a7093e76fe77a51c398287edaad2a7
4
- data.tar.gz: 1e21bd762b78a2fc59537a89c6b0776abb656352
3
+ metadata.gz: 46b7d3601d36e13abc6d7b4f491499c22a330892
4
+ data.tar.gz: 8a32e0599dcf8413d2cc9dfe2241d95c1080e20c
5
5
  SHA512:
6
- metadata.gz: f35c3c3690736f43f5afe00e9e4915597e1af52680781bdce7a1590ac22672c6a75634c0e547d7b390b0976c6d6e045613cafcff5a931ce3b7d8016a85f8e2e2
7
- data.tar.gz: 39908d9a537b41c7545698b80c10850eff2dcf132eb0bdcee663cf602126825ea4c960fbee0d08b72d1a4bae8427ccd3a62478de4b436e6f26d0c30f84bed3d4
6
+ metadata.gz: 4df79a6ea3e788be41f0edc6b115cec186cf52f57a4c098ada7da45786e5cd734040065f4bc46b047683f40ab353e35455d10ecea55ae09da615e11140bb28d5
7
+ data.tar.gz: 6397a49e24251b2dbbf59362df048bc241b2c4d2287ee21a29f28bc978041b19be89194f38c0a59c667592591c8eff32d0c91326a8dc22c29233b756f070fa0d
data/lib/jekyll.rb CHANGED
@@ -30,7 +30,6 @@ require 'kramdown'
30
30
  require 'colorator'
31
31
 
32
32
  SafeYAML::OPTIONS[:suppress_warnings] = true
33
- Liquid::Template.error_mode = :strict
34
33
 
35
34
  module Jekyll
36
35
 
@@ -53,7 +52,6 @@ module Jekyll
53
52
  autoload :CollectionReader, 'jekyll/readers/collection_reader'
54
53
  autoload :DataReader, 'jekyll/readers/data_reader'
55
54
  autoload :LayoutReader, 'jekyll/readers/layout_reader'
56
- autoload :DraftReader, 'jekyll/readers/draft_reader'
57
55
  autoload :PostReader, 'jekyll/readers/post_reader'
58
56
  autoload :PageReader, 'jekyll/readers/page_reader'
59
57
  autoload :StaticFileReader, 'jekyll/readers/static_file_reader'
@@ -136,7 +134,7 @@ module Jekyll
136
134
  #
137
135
  # Returns the new logger.
138
136
  def logger=(writer)
139
- @logger = LogAdapter.new(writer)
137
+ @logger = LogAdapter.new(writer, (ENV["JEKYLL_LOG_LEVEL"] || :info).to_sym)
140
138
  end
141
139
 
142
140
  # Public: An array of sites
@@ -3,6 +3,7 @@ require 'set'
3
3
  module Jekyll
4
4
  # Handles the cleanup of a site's destination before it is built.
5
5
  class Cleaner
6
+ HIDDEN_FILE_REGEX = /\/\.{1,2}$/
6
7
  attr_reader :site
7
8
 
8
9
  def initialize(site)
@@ -12,7 +13,7 @@ module Jekyll
12
13
  # Cleans up the site's destination directory
13
14
  def cleanup!
14
15
  FileUtils.rm_rf(obsolete_files)
15
- FileUtils.rm_rf(metadata_file) if @site.full_rebuild?
16
+ FileUtils.rm_rf(metadata_file) if !@site.incremental?
16
17
  end
17
18
 
18
19
  private
@@ -40,7 +41,7 @@ module Jekyll
40
41
  dirs = keep_dirs
41
42
 
42
43
  Dir.glob(site.in_dest_dir("**", "*"), File::FNM_DOTMATCH) do |file|
43
- next if file =~ /\/\.{1,2}$/ || file =~ regex || dirs.include?(file)
44
+ next if file =~ HIDDEN_FILE_REGEX || file =~ regex || dirs.include?(file)
44
45
  files << file
45
46
  end
46
47
 
@@ -1,6 +1,7 @@
1
1
  module Jekyll
2
2
  class Collection
3
3
  attr_reader :site, :label, :metadata
4
+ attr_writer :docs
4
5
 
5
6
  # Create a new Collection.
6
7
  #
@@ -22,6 +23,23 @@ module Jekyll
22
23
  @docs ||= []
23
24
  end
24
25
 
26
+ # Override of normal respond_to? to match method_missing's logic for
27
+ # looking in @data.
28
+ def respond_to?(method, include_private = false)
29
+ docs.respond_to?(method.to_sym, include_private) || super
30
+ end
31
+
32
+ # Override of method_missing to check in @data for the key.
33
+ def method_missing(method, *args, &blck)
34
+ if docs.respond_to?(method.to_sym)
35
+ Jekyll.logger.warn "Deprecation:", "Collection##{method} should be called on the #docs array directly."
36
+ Jekyll.logger.warn "", "Called by #{caller.first}."
37
+ docs.public_send(method.to_sym, *args, &blck)
38
+ else
39
+ super
40
+ end
41
+ end
42
+
25
43
  # Fetch the static files in this collection.
26
44
  # Defaults to an empty array if no static files have been read in.
27
45
  #
@@ -40,7 +58,7 @@ module Jekyll
40
58
  if Utils.has_yaml_header? full_path
41
59
  doc = Jekyll::Document.new(full_path, { site: site, collection: self })
42
60
  doc.read
43
- docs << doc if site.publisher.publish?(doc)
61
+ docs << doc if site.publisher.publish?(doc) || !write?
44
62
  else
45
63
  relative_dir = Jekyll.sanitized_path(relative_directory, File.dirname(file_path)).chomp("/.")
46
64
  files << StaticFile.new(site, site.source, relative_dir, File.basename(full_path), self)
@@ -163,7 +181,7 @@ module Jekyll
163
181
  #
164
182
  # Returns true if the 'write' metadata is true, false otherwise.
165
183
  def write?
166
- !!metadata['output']
184
+ !!metadata.fetch('output', false)
167
185
  end
168
186
 
169
187
  # The URL template to render collection's documents at.
@@ -60,7 +60,7 @@ module Jekyll
60
60
  c.option 'unpublished', '--unpublished', 'Render posts that were marked as unpublished'
61
61
  c.option 'quiet', '-q', '--quiet', 'Silence output.'
62
62
  c.option 'verbose', '-V', '--verbose', 'Print verbose output.'
63
- c.option 'full_rebuild', '-f', '--full-rebuild', 'Disable incremental rebuild.'
63
+ c.option 'incremental', '-I', '--incremental', 'Enable incremental rebuild.'
64
64
  end
65
65
 
66
66
  end
@@ -52,10 +52,10 @@ module Jekyll
52
52
  t = Time.now
53
53
  source = options['source']
54
54
  destination = options['destination']
55
- full_build = options['full_rebuild']
55
+ incremental = options['incremental']
56
56
  Jekyll.logger.info "Source:", source
57
57
  Jekyll.logger.info "Destination:", destination
58
- Jekyll.logger.info "Incremental build:", (full_build ? "disabled" : "enabled")
58
+ Jekyll.logger.info "Incremental build:", (incremental ? "enabled" : "disabled. Enable with --incremental")
59
59
  Jekyll.logger.info "Generating..."
60
60
  process_site(site)
61
61
  Jekyll.logger.info "", "done in #{(Time.now - t).round(3)} seconds."
@@ -5,7 +5,7 @@ module Jekyll
5
5
 
6
6
  # Default options. Overridden by values in _config.yml.
7
7
  # Strings rather than symbols are used for compatibility with YAML.
8
- DEFAULTS = {
8
+ DEFAULTS = Configuration[{
9
9
  # Where things are
10
10
  'source' => Dir.pwd,
11
11
  'destination' => File.join(Dir.pwd, '_site'),
@@ -13,7 +13,7 @@ module Jekyll
13
13
  'layouts_dir' => '_layouts',
14
14
  'data_dir' => '_data',
15
15
  'includes_dir' => '_includes',
16
- 'collections' => nil,
16
+ 'collections' => {},
17
17
 
18
18
  # Handling Reading
19
19
  'safe' => false,
@@ -22,7 +22,6 @@ module Jekyll
22
22
  'keep_files' => ['.git','.svn'],
23
23
  'encoding' => 'utf-8',
24
24
  'markdown_ext' => 'markdown,mkdown,mkdn,mkd,md',
25
- 'full_rebuild' => false,
26
25
 
27
26
  # Filtering Content
28
27
  'show_drafts' => nil,
@@ -39,6 +38,7 @@ module Jekyll
39
38
  'highlighter' => 'rouge',
40
39
  'lsi' => false,
41
40
  'excerpt_separator' => "\n\n",
41
+ 'incremental' => false,
42
42
 
43
43
  # Serving
44
44
  'detach' => false, # default to not detaching the server
@@ -80,7 +80,7 @@ module Jekyll
80
80
  'coderay_css' => 'style'
81
81
  }
82
82
  }
83
- }
83
+ }]
84
84
 
85
85
  # Public: Turn all keys into string
86
86
  #
@@ -186,7 +186,7 @@ module Jekyll
186
186
  $stderr.puts "#{err}"
187
187
  end
188
188
 
189
- configuration.fix_common_issues.backwards_compatibilize
189
+ configuration.fix_common_issues.backwards_compatibilize.add_default_collections
190
190
  end
191
191
 
192
192
  # Public: Split a CSV string into an array containing its values
@@ -275,6 +275,21 @@ module Jekyll
275
275
  config
276
276
  end
277
277
 
278
+ def add_default_collections
279
+ config = clone
280
+
281
+ return config if config['collections'].nil?
282
+
283
+ if config['collections'].is_a?(Array)
284
+ config['collections'] = Hash[config['collections'].map{|c| [c, {}]}]
285
+ end
286
+ config['collections']['posts'] ||= {}
287
+ config['collections']['posts']['output'] = true
288
+ config['collections']['posts']['permalink'] = style_to_permalink(config['permalink'])
289
+
290
+ config
291
+ end
292
+
278
293
  def renamed_key(old, new, config, allowed_values = nil)
279
294
  if config.key?(old)
280
295
  Jekyll::Deprecator.deprecation_message "The '#{old}' configuration" +
@@ -283,5 +298,21 @@ module Jekyll
283
298
  config[new] = config.delete(old)
284
299
  end
285
300
  end
301
+
302
+ private
303
+ def style_to_permalink(permalink_style)
304
+ case permalink_style.to_sym
305
+ when :pretty
306
+ "/:categories/:year/:month/:day/:title/"
307
+ when :none
308
+ "/:categories/:title.html"
309
+ when :date
310
+ "/:categories/:year/:month/:day/:title.html"
311
+ when :ordinal
312
+ "/:categories/:year/:y_day/:title.html"
313
+ else
314
+ permalink_style.to_s
315
+ end
316
+ end
286
317
  end
287
318
  end
@@ -135,21 +135,15 @@ module Jekyll
135
135
  #
136
136
  # Returns the type of self.
137
137
  def type
138
- if is_a?(Draft)
139
- :drafts
140
- elsif is_a?(Post)
141
- :posts
142
- elsif is_a?(Page)
138
+ if is_a?(Page)
143
139
  :pages
144
140
  end
145
141
  end
146
142
 
147
143
  # returns the owner symbol for hook triggering
148
144
  def hook_owner
149
- if is_a?(Post)
150
- :post
151
- elsif is_a?(Page)
152
- :page
145
+ if is_a?(Page)
146
+ :pages
153
147
  end
154
148
  end
155
149
 
@@ -215,6 +209,7 @@ module Jekyll
215
209
  used = Set.new([layout])
216
210
 
217
211
  while layout
212
+ Jekyll.logger.debug "Rendering Layout:", path
218
213
  payload = Utils.deep_merge_hashes(payload, {"content" => output, "page" => layout.data})
219
214
 
220
215
  self.output = render_liquid(layout.content,
@@ -245,6 +240,9 @@ module Jekyll
245
240
  #
246
241
  # Returns nothing.
247
242
  def do_layout(payload, layouts)
243
+ Jekyll.logger.debug "Rendering:", self.relative_path
244
+
245
+ Jekyll.logger.debug "Pre-Render Hooks:", self.relative_path
248
246
  Jekyll::Hooks.trigger hook_owner, :pre_render, self, payload
249
247
  info = { :filters => [Jekyll::Filters], :registers => { :site => site, :page => payload['page'] } }
250
248
 
@@ -252,13 +250,18 @@ module Jekyll
252
250
  payload["highlighter_prefix"] = converters.first.highlighter_prefix
253
251
  payload["highlighter_suffix"] = converters.first.highlighter_suffix
254
252
 
255
- self.content = render_liquid(content, payload, info, path) if render_with_liquid?
253
+ if render_with_liquid?
254
+ Jekyll.logger.debug "Rendering Liquid:", self.relative_path
255
+ self.content = render_liquid(content, payload, info, path)
256
+ end
257
+ Jekyll.logger.debug "Rendering Markup:", self.relative_path
256
258
  self.content = transform
257
259
 
258
260
  # output keeps track of what will finally be written
259
261
  self.output = content
260
262
 
261
263
  render_all_layouts(layouts, payload, info) if place_in_layout?
264
+ Jekyll.logger.debug "Post-Render Hooks:", self.relative_path
262
265
  Jekyll::Hooks.trigger hook_owner, :post_render, self
263
266
  end
264
267
 
@@ -7,6 +7,8 @@ module Jekyll
7
7
  attr_reader :path, :site, :extname, :output_ext, :content, :output, :collection
8
8
 
9
9
  YAML_FRONT_MATTER_REGEXP = /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m
10
+ DATELESS_FILENAME_MATCHER = /^(.*)(\.[^.]+)$/
11
+ DATE_FILENAME_MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
10
12
 
11
13
  # Create a new Document.
12
14
  #
@@ -21,6 +23,17 @@ module Jekyll
21
23
  @output_ext = Jekyll::Renderer.new(site, self).output_ext
22
24
  @collection = relations[:collection]
23
25
  @has_yaml_header = nil
26
+
27
+ subdirs = relative_path.split(File::SEPARATOR).reject do |c|
28
+ c.empty? || c.eql?(collection.relative_directory) || c.eql?("_drafts") || c.eql?(basename)
29
+ end
30
+ merge_data!({'categories' => subdirs })
31
+
32
+ data.default_proc = proc do |hash, key|
33
+ site.frontmatter_defaults.find(relative_path, collection.label, key)
34
+ end
35
+
36
+ trigger_hooks(:post_init)
24
37
  end
25
38
 
26
39
  def output=(output)
@@ -41,6 +54,27 @@ module Jekyll
41
54
  @data ||= Hash.new
42
55
  end
43
56
 
57
+ # Merge some data in with this document's data.
58
+ #
59
+ # Returns the merged data.
60
+ def merge_data!(other)
61
+ if other.key?('categories') && !other['categories'].nil?
62
+ if other['categories'].is_a?(String)
63
+ other['categories'] = other['categories'].split(" ").map(&:strip)
64
+ end
65
+ other['categories'] = (data['categories'] || []) | other['categories']
66
+ end
67
+ Utils.deep_merge_hashes!(data, other)
68
+ if data.key?('date') && !data['date'].is_a?(Time)
69
+ data['date'] = Utils.parse_date(data['date'].to_s, "Document '#{relative_path}' does not have a valid date in the YAML front matter.")
70
+ end
71
+ data
72
+ end
73
+
74
+ def date
75
+ data['date'] ||= site.time
76
+ end
77
+
44
78
  # The path to the document, relative to the site source.
45
79
  #
46
80
  # Returns a String path which represents the relative path
@@ -138,11 +172,23 @@ module Jekyll
138
172
  # Returns the Hash of key-value pairs for replacement in the URL.
139
173
  def url_placeholders
140
174
  {
141
- collection: collection.label,
142
- path: cleaned_relative_path,
143
- output_ext: output_ext,
144
- name: Utils.slugify(basename_without_ext),
145
- title: Utils.slugify(data['slug']) || Utils.slugify(basename_without_ext)
175
+ collection: collection.label,
176
+ path: cleaned_relative_path,
177
+ output_ext: output_ext,
178
+ name: Utils.slugify(basename_without_ext),
179
+ title: Utils.slugify(data['slug']) || Utils.slugify(basename_without_ext),
180
+ year: date.strftime("%Y"),
181
+ month: date.strftime("%m"),
182
+ day: date.strftime("%d"),
183
+ hour: date.strftime("%H"),
184
+ minute: date.strftime("%M"),
185
+ second: date.strftime("%S"),
186
+ i_day: date.strftime("%-d"),
187
+ i_month: date.strftime("%-m"),
188
+ categories: (data['categories'] || []).map { |c| c.to_s.downcase }.uniq.join('/'),
189
+ short_month: date.strftime("%b"),
190
+ short_year: date.strftime("%y"),
191
+ y_day: date.strftime("%j"),
146
192
  }
147
193
  end
148
194
 
@@ -165,6 +211,10 @@ module Jekyll
165
211
  }).to_s
166
212
  end
167
213
 
214
+ def [](key)
215
+ data[key]
216
+ end
217
+
168
218
  # The full path to the output file.
169
219
  #
170
220
  # base_directory - the base path of the output directory
@@ -190,7 +240,7 @@ module Jekyll
190
240
  f.write(output)
191
241
  end
192
242
 
193
- Jekyll::Hooks.trigger :document, :post_write, self
243
+ trigger_hooks(:post_write)
194
244
  end
195
245
 
196
246
  # Returns merged option hash for File.read of self.site (if exists)
@@ -218,22 +268,23 @@ module Jekyll
218
268
  def read(opts = {})
219
269
  @to_liquid = nil
220
270
 
271
+ Jekyll.logger.debug "Reading:", relative_path
272
+
221
273
  if yaml_file?
222
274
  @data = SafeYAML.load_file(path)
223
275
  else
224
276
  begin
225
277
  defaults = @site.frontmatter_defaults.all(url, collection.label.to_sym)
226
- unless defaults.empty?
227
- @data = defaults
228
- end
278
+ merge_data!(defaults) unless defaults.empty?
279
+
229
280
  self.content = File.read(path, merged_file_read_opts(opts))
230
281
  if content =~ YAML_FRONT_MATTER_REGEXP
231
282
  self.content = $POSTMATCH
232
283
  data_file = SafeYAML.load($1)
233
- unless data_file.nil?
234
- @data = Utils.deep_merge_hashes(defaults, data_file)
235
- end
284
+ merge_data!(data_file) if data_file
236
285
  end
286
+
287
+ post_read
237
288
  rescue SyntaxError => e
238
289
  puts "YAML Exception reading #{path}: #{e.message}"
239
290
  rescue Exception => e
@@ -242,19 +293,54 @@ module Jekyll
242
293
  end
243
294
  end
244
295
 
296
+ def post_read
297
+ if DATE_FILENAME_MATCHER =~ relative_path
298
+ m, cats, date, slug, ext = *relative_path.match(DATE_FILENAME_MATCHER)
299
+ merge_data!({
300
+ "slug" => slug,
301
+ "ext" => ext
302
+ })
303
+ merge_data!({"date" => date}) if data['date'].nil? || data['date'].to_i == site.time.to_i
304
+ data['title'] ||= slug.split('-').select {|w| w.capitalize! || w }.join(' ')
305
+ end
306
+ populate_categories
307
+ populate_tags
308
+
309
+ if generate_excerpt?
310
+ data['excerpt'] = Jekyll::Excerpt.new(self)
311
+ end
312
+ end
313
+
314
+ def populate_categories
315
+ merge_data!({
316
+ "categories" => (
317
+ Array(data['categories']) + Utils.pluralized_array_from_hash(data, 'category', 'categories')
318
+ ).map { |c| c.to_s }.flatten.uniq
319
+ })
320
+ end
321
+
322
+ def populate_tags
323
+ merge_data!({
324
+ "tags" => Utils.pluralized_array_from_hash(data, "tag", "tags").flatten
325
+ })
326
+ end
327
+
245
328
  # Create a Liquid-understandable version of this Document.
246
329
  #
247
330
  # Returns a Hash representing this Document's data.
248
331
  def to_liquid
249
332
  @to_liquid ||= if data.is_a?(Hash)
250
- Utils.deep_merge_hashes data, {
333
+ Utils.deep_merge_hashes Utils.deep_merge_hashes({
251
334
  "output" => output,
252
335
  "content" => content,
253
336
  "relative_path" => relative_path,
254
337
  "path" => relative_path,
255
338
  "url" => url,
256
- "collection" => collection.label
257
- }
339
+ "collection" => collection.label,
340
+ "next" => next_doc,
341
+ "previous" => previous_doc,
342
+ "id" => id,
343
+ }, data), { 'excerpt' => data['excerpt'].to_s }
258
344
  else
259
345
  data
260
346
  end
@@ -272,7 +358,7 @@ module Jekyll
272
358
  #
273
359
  # Returns the content of the document
274
360
  def to_s
275
- content || ''
361
+ output || content || 'NO CONTENT'
276
362
  end
277
363
 
278
364
  # Compare this document against another document.
@@ -281,7 +367,11 @@ module Jekyll
281
367
  # Returns -1, 0, +1 or nil depending on whether this doc's path is less than,
282
368
  # equal or greater than the other doc's path. See String#<=> for more details.
283
369
  def <=>(anotherDocument)
284
- path <=> anotherDocument.path
370
+ cmp = data['date'] <=> anotherDocument.data['date']
371
+ if 0 == cmp
372
+ cmp = path <=> anotherDocument.path
373
+ end
374
+ cmp
285
375
  end
286
376
 
287
377
  # Determine whether this document should be written.
@@ -292,5 +382,71 @@ module Jekyll
292
382
  def write?
293
383
  collection && collection.write?
294
384
  end
385
+
386
+ # The Document excerpt_separator, from the YAML Front-Matter or site
387
+ # default excerpt_separator value
388
+ #
389
+ # Returns the document excerpt_separator
390
+ def excerpt_separator
391
+ (data['excerpt_separator'] || site.config['excerpt_separator']).to_s
392
+ end
393
+
394
+ # Whether to generate an excerpt
395
+ #
396
+ # Returns true if the excerpt separator is configured.
397
+ def generate_excerpt?
398
+ !excerpt_separator.empty?
399
+ end
400
+
401
+ def next_doc
402
+ pos = collection.docs.index {|post| post.equal?(self) }
403
+ if pos && pos < collection.docs.length - 1
404
+ collection.docs[pos + 1]
405
+ else
406
+ nil
407
+ end
408
+ end
409
+
410
+ def previous_doc
411
+ pos = collection.docs.index {|post| post.equal?(self) }
412
+ if pos && pos > 0
413
+ collection.docs[pos - 1]
414
+ else
415
+ nil
416
+ end
417
+ end
418
+
419
+ def trigger_hooks(hook_name, *args)
420
+ Jekyll::Hooks.trigger collection.label.to_sym, hook_name, self, *args if collection
421
+ Jekyll::Hooks.trigger :documents, hook_name, self, *args
422
+ end
423
+
424
+ def id
425
+ @id ||= File.join(File.dirname(url), (data['slug'] || basename_without_ext).to_s)
426
+ end
427
+
428
+ # Calculate related posts.
429
+ #
430
+ # Returns an Array of related Posts.
431
+ def related_posts
432
+ Jekyll::RelatedPosts.new(self).build
433
+ end
434
+
435
+ # Override of normal respond_to? to match method_missing's logic for
436
+ # looking in @data.
437
+ def respond_to?(method, include_private = false)
438
+ data.key?(method.to_s) || super
439
+ end
440
+
441
+ # Override of method_missing to check in @data for the key.
442
+ def method_missing(method, *args, &blck)
443
+ if data.key?(method.to_s)
444
+ Jekyll.logger.warn "Deprecation:", "Document##{method} is now a key in the #data hash."
445
+ Jekyll.logger.warn "", "Called by #{caller.first}."
446
+ data[method.to_s]
447
+ else
448
+ super
449
+ end
450
+ end
295
451
  end
296
452
  end