jekyll 2.4.0 → 2.5.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.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 45083d9a5aaf050f5b5dc16a09c7d4001db30856
4
- data.tar.gz: 4bfa431a22f2d147963b49e6664584a43f9cdbc3
3
+ metadata.gz: 49712197bbd449a1ccb1593b58772be181e0df1a
4
+ data.tar.gz: 80f61cc61b7309db6e1dcc29ca833e833f72c233
5
5
  SHA512:
6
- metadata.gz: 289e5fd56eccf356d07eca309241ac9ef9a11c020bd218c394d1499d2984d88c49e842697e07b0d576147ee3b1937d7046d290e31a3d49430f4373e168f0de49
7
- data.tar.gz: 11c5a946bbb64cfbb560db1a28be0534131391b3353a1e55e5ae3aa05bccc2cf28386a879bb91b9ce9068173d63747409df80b1694e54ef8779ce12f0697c65f
6
+ metadata.gz: 698b36066f4d75677f9a2e1e33ef18f9609fac1de550b972f73f3eb16fbf1b082d5b51778a9a6d4ef089adee7355a18b259a002d290f8cb2813e7e6391057ff2
7
+ data.tar.gz: 97bbe79b72ac0dafc6f7eb67e7cfaf1ce46eb4dd0e55971834312d8328ca717d2e56563f6fcf3cb9ab34170125f6bd2ff8d4a1ca92a26033f1d31af63d0bbbd7
@@ -4,6 +4,7 @@
4
4
  [![Build Status](https://secure.travis-ci.org/jekyll/jekyll.svg?branch=master)](https://travis-ci.org/jekyll/jekyll)
5
5
  [![Code Climate](http://img.shields.io/codeclimate/github/jekyll/jekyll.svg)](https://codeclimate.com/github/jekyll/jekyll)
6
6
  [![Dependency Status](https://gemnasium.com/jekyll/jekyll.svg)](https://gemnasium.com/jekyll/jekyll)
7
+ [![Security](https://hakiri.io/github/jekyll/jekyll/master.svg)](https://hakiri.io/github/jekyll/jekyll/master)
7
8
 
8
9
  By Tom Preston-Werner, Nick Quaranto, Parker Moore, and many [awesome contributors](https://github.com/jekyll/jekyll/graphs/contributors)!
9
10
 
data/bin/jekyll CHANGED
@@ -13,6 +13,8 @@ require 'mercenary'
13
13
  end
14
14
  end
15
15
 
16
+ Jekyll::PluginManager.require_from_bundler
17
+
16
18
  Jekyll::Deprecator.process(ARGV)
17
19
 
18
20
  Mercenary.program(:jekyll) do |p|
@@ -68,66 +68,88 @@ module Jekyll
68
68
  require 'jekyll/command'
69
69
  require 'jekyll/liquid_extensions'
70
70
 
71
- # Public: Tells you which Jekyll environment you are building in so you can skip tasks
72
- # if you need to. This is useful when doing expensive compression tasks on css and
73
- # images and allows you to skip that when working in development.
71
+ class << self
72
+ # Public: Tells you which Jekyll environment you are building in so you can skip tasks
73
+ # if you need to. This is useful when doing expensive compression tasks on css and
74
+ # images and allows you to skip that when working in development.
74
75
 
75
- def self.env
76
- ENV["JEKYLL_ENV"] || "development"
77
- end
76
+ def env
77
+ ENV["JEKYLL_ENV"] || "development"
78
+ end
78
79
 
79
- # Public: Generate a Jekyll configuration Hash by merging the default
80
- # options with anything in _config.yml, and adding the given options on top.
81
- #
82
- # override - A Hash of config directives that override any options in both
83
- # the defaults and the config file. See Jekyll::Configuration::DEFAULTS for a
84
- # list of option names and their defaults.
85
- #
86
- # Returns the final configuration Hash.
87
- def self.configuration(override)
88
- config = Configuration[Configuration::DEFAULTS]
89
- override = Configuration[override].stringify_keys
90
- config = config.read_config_files(config.config_files(override))
91
-
92
- # Merge DEFAULTS < _config.yml < override
93
- config = Utils.deep_merge_hashes(config, override).stringify_keys
94
- set_timezone(config['timezone']) if config['timezone']
95
-
96
- config
97
- end
80
+ # Public: Generate a Jekyll configuration Hash by merging the default
81
+ # options with anything in _config.yml, and adding the given options on top.
82
+ #
83
+ # override - A Hash of config directives that override any options in both
84
+ # the defaults and the config file. See Jekyll::Configuration::DEFAULTS for a
85
+ # list of option names and their defaults.
86
+ #
87
+ # Returns the final configuration Hash.
88
+ def configuration(override = Hash.new)
89
+ config = Configuration[Configuration::DEFAULTS]
90
+ override = Configuration[override].stringify_keys
91
+ unless override.delete('skip_config_files')
92
+ config = config.read_config_files(config.config_files(override))
93
+ end
94
+
95
+ # Merge DEFAULTS < _config.yml < override
96
+ config = Utils.deep_merge_hashes(config, override).stringify_keys
97
+ set_timezone(config['timezone']) if config['timezone']
98
+
99
+ config
100
+ end
98
101
 
99
- # Static: Set the TZ environment variable to use the timezone specified
100
- #
101
- # timezone - the IANA Time Zone
102
- #
103
- # Returns nothing
104
- def self.set_timezone(timezone)
105
- ENV['TZ'] = timezone
106
- end
102
+ # Public: Set the TZ environment variable to use the timezone specified
103
+ #
104
+ # timezone - the IANA Time Zone
105
+ #
106
+ # Returns nothing
107
+ def set_timezone(timezone)
108
+ ENV['TZ'] = timezone
109
+ end
107
110
 
108
- def self.logger
109
- @logger ||= LogAdapter.new(Stevenson.new)
110
- end
111
+ # Public: Fetch the logger instance for this Jekyll process.
112
+ #
113
+ # Returns the LogAdapter instance.
114
+ def logger
115
+ @logger ||= LogAdapter.new(Stevenson.new, (ENV["JEKYLL_LOG_LEVEL"] || :info).to_sym)
116
+ end
111
117
 
112
- def self.logger=(writer)
113
- @logger = LogAdapter.new(writer)
114
- end
118
+ # Public: Set the log writer.
119
+ # New log writer must respond to the same methods
120
+ # as Ruby's interal Logger.
121
+ #
122
+ # writer - the new Logger-compatible log transport
123
+ #
124
+ # Returns the new logger.
125
+ def logger=(writer)
126
+ @logger = LogAdapter.new(writer)
127
+ end
115
128
 
116
- # Public: File system root
117
- #
118
- # Returns the root of the filesystem as a Pathname
119
- def self.fs_root
120
- @fs_root ||= "/"
121
- end
129
+ # Public: An array of sites
130
+ #
131
+ # Returns the Jekyll sites created.
132
+ def sites
133
+ @sites ||= []
134
+ end
122
135
 
123
- def self.sanitized_path(base_directory, questionable_path)
124
- clean_path = File.expand_path(questionable_path, fs_root)
125
- clean_path.gsub!(/\A\w\:\//, '/')
126
- unless clean_path.start_with?(base_directory)
127
- File.join(base_directory, clean_path)
128
- else
129
- clean_path
136
+ # Public: Ensures the questionable path is prefixed with the base directory
137
+ # and prepends the questionable path with the base directory if false.
138
+ #
139
+ # base_directory - the directory with which to prefix the questionable path
140
+ # questionable_path - the path we're unsure about, and want prefixed
141
+ #
142
+ # Returns the sanitized path.
143
+ def sanitized_path(base_directory, questionable_path)
144
+ clean_path = File.expand_path(questionable_path, "/")
145
+ clean_path.gsub!(/\A\w\:\//, '/')
146
+ unless clean_path.start_with?(base_directory)
147
+ File.join(base_directory, clean_path)
148
+ else
149
+ clean_path
150
+ end
130
151
  end
152
+
131
153
  end
132
154
  end
133
155
 
@@ -29,7 +29,7 @@ module Jekyll
29
29
  # Returns a Set with the file paths
30
30
  def existing_files
31
31
  files = Set.new
32
- Dir.glob(File.join(site.dest, "**", "*"), File::FNM_DOTMATCH) do |file|
32
+ Dir.glob(site.in_dest_dir("**", "*"), File::FNM_DOTMATCH) do |file|
33
33
  files << file unless file =~ /\/\.{1,2}$/ || file =~ keep_file_regex || keep_dirs.include?(file)
34
34
  end
35
35
  files
@@ -76,7 +76,7 @@ module Jekyll
76
76
  #
77
77
  # Returns a Set with the directory paths
78
78
  def keep_dirs
79
- site.keep_files.map{|file| parent_dirs(File.join(site.dest, file))}.flatten.to_set
79
+ site.keep_files.map { |file| parent_dirs(site.in_dest_dir(file)) }.flatten.to_set
80
80
  end
81
81
 
82
82
  # Private: Creates a regular expression from the config's keep_files array
@@ -35,7 +35,8 @@ module Jekyll
35
35
  # Returns the sorted array of docs.
36
36
  def read
37
37
  filtered_entries.each do |file_path|
38
- full_path = Jekyll.sanitized_path(directory, file_path)
38
+ full_path = collection_dir(file_path)
39
+ next if File.directory?(full_path)
39
40
  if Utils.has_yaml_header? full_path
40
41
  doc = Jekyll::Document.new(full_path, { site: site, collection: self })
41
42
  doc.read
@@ -54,9 +55,10 @@ module Jekyll
54
55
  # relative to the collection's directory
55
56
  def entries
56
57
  return Array.new unless exists?
57
- Dir.glob(File.join(directory, "**", "*.*")).map do |entry|
58
- entry[File.join(directory, "")] = ''; entry
59
- end
58
+ @entries ||=
59
+ Dir.glob(collection_dir("**", "*.*")).map do |entry|
60
+ entry["#{collection_dir}/"] = ''; entry
61
+ end
60
62
  end
61
63
 
62
64
  # Filtered version of the entries in this collection.
@@ -65,9 +67,13 @@ module Jekyll
65
67
  # Returns a list of filtered entry paths.
66
68
  def filtered_entries
67
69
  return Array.new unless exists?
68
- Dir.chdir(directory) do
69
- entry_filter.filter(entries).reject { |f| File.directory?(f) }
70
- end
70
+ @filtered_entries ||=
71
+ Dir.chdir(directory) do
72
+ entry_filter.filter(entries).reject do |f|
73
+ path = collection_dir(f)
74
+ File.directory?(path) || (File.symlink?(f) && site.safe)
75
+ end
76
+ end
71
77
  end
72
78
 
73
79
  # The directory for this Collection, relative to the site source.
@@ -75,15 +81,28 @@ module Jekyll
75
81
  # Returns a String containing the directory name where the collection
76
82
  # is stored on the filesystem.
77
83
  def relative_directory
78
- "_#{label}"
84
+ @relative_directory ||= "_#{label}"
79
85
  end
80
86
 
81
- # The full path to the directory containing the
87
+ # The full path to the directory containing the collection.
82
88
  #
83
89
  # Returns a String containing th directory name where the collection
84
90
  # is stored on the filesystem.
85
91
  def directory
86
- Jekyll.sanitized_path(site.source, relative_directory)
92
+ @directory ||= site.in_source_dir(relative_directory)
93
+ end
94
+
95
+ # The full path to the directory containing the collection, with
96
+ # optional subpaths.
97
+ #
98
+ # *files - (optional) any other path pieces relative to the
99
+ # directory to append to the path
100
+ #
101
+ # Returns a String containing th directory name where the collection
102
+ # is stored on the filesystem.
103
+ def collection_dir(*files)
104
+ return directory if files.empty?
105
+ site.in_source_dir(relative_directory, *files)
87
106
  end
88
107
 
89
108
  # Checks whether the directory "exists" for this collection.
@@ -9,6 +9,7 @@ module Jekyll
9
9
  prog.command(:build) do |c|
10
10
  c.syntax 'build [options]'
11
11
  c.description 'Build your site'
12
+ c.alias :b
12
13
 
13
14
  add_build_options(c)
14
15
 
@@ -10,6 +10,7 @@ module Jekyll
10
10
  c.syntax 'serve [options]'
11
11
  c.description 'Serve your site locally'
12
12
  c.alias :server
13
+ c.alias :s
13
14
 
14
15
  add_build_options(c)
15
16
 
@@ -75,16 +76,20 @@ module Jekyll
75
76
 
76
77
  def webrick_options(config)
77
78
  opts = {
78
- :DocumentRoot => config['destination'],
79
- :Port => config['port'],
80
79
  :BindAddress => config['host'],
81
- :MimeTypes => mime_types,
80
+ :DirectoryIndex => %w(index.html index.htm index.cgi index.rhtml index.xml),
81
+ :DocumentRoot => config['destination'],
82
82
  :DoNotReverseLookup => true,
83
- :StartCallback => start_callback(config['detach']),
84
- :DirectoryIndex => %w(index.html index.htm index.cgi index.rhtml index.xml)
83
+ :MimeTypes => mime_types,
84
+ :Port => config['port'],
85
+ :StartCallback => start_callback(config['detach'])
85
86
  }
86
87
 
87
- if !config['verbose']
88
+ if config['verbose']
89
+ opts.merge!({
90
+ :Logger => WEBrick::Log.new($stdout, WEBrick::Log::DEBUG)
91
+ })
92
+ else
88
93
  opts.merge!({
89
94
  :AccessLog => [],
90
95
  :Logger => WEBrick::Log.new([], WEBrick::Log::WARN)
@@ -118,9 +123,10 @@ module Jekyll
118
123
 
119
124
  # recreate NondisclosureName under utf-8 circumstance
120
125
  def file_handler_options
121
- fh_option = WEBrick::Config::FileHandler
122
- fh_option[:NondisclosureName] = ['.ht*','~*']
123
- fh_option
126
+ WEBrick::Config::FileHandler.merge({
127
+ :FancyIndexing => true,
128
+ :NondisclosureName => ['.ht*','~*']
129
+ })
124
130
  end
125
131
 
126
132
  end
@@ -42,7 +42,7 @@ module Jekyll
42
42
  # Serving
43
43
  'detach' => false, # default to not detaching the server
44
44
  'port' => '4000',
45
- 'host' => '0.0.0.0',
45
+ 'host' => '127.0.0.1',
46
46
  'baseurl' => '',
47
47
 
48
48
  # Backwards-compatibility options
@@ -46,9 +46,15 @@ module Jekyll
46
46
  ].map(&:to_sym)
47
47
  end
48
48
 
49
+ def extname_matches_regexp
50
+ @extname_matches_regexp ||= Regexp.new(
51
+ '(' + @config['markdown_ext'].gsub(',','|') +')$',
52
+ Regexp::IGNORECASE
53
+ )
54
+ end
55
+
49
56
  def matches(ext)
50
- rgx = '^\.(' + @config['markdown_ext'].gsub(',','|') +')$'
51
- ext =~ Regexp.new(rgx, Regexp::IGNORECASE)
57
+ ext =~ extname_matches_regexp
52
58
  end
53
59
 
54
60
  def output_ext(ext)
@@ -48,8 +48,8 @@ module Jekyll
48
48
  end
49
49
 
50
50
  protected
51
- def rouge_formatter(opts = {})
52
- Rouge::Formatters::HTML.new(opts.merge(wrap: false))
51
+ def rouge_formatter(lexer)
52
+ Rouge::Formatters::HTML.new(:wrap => false)
53
53
  end
54
54
  end
55
55
 
@@ -16,9 +16,15 @@ module Jekyll
16
16
  raise Errors::FatalException.new("Missing dependency: RedCloth")
17
17
  end
18
18
 
19
+ def extname_matches_regexp
20
+ @extname_matches_regexp ||= Regexp.new(
21
+ '(' + @config['textile_ext'].gsub(',','|') +')$',
22
+ Regexp::IGNORECASE
23
+ )
24
+ end
25
+
19
26
  def matches(ext)
20
- rgx = '(' + @config['textile_ext'].gsub(',','|') +')'
21
- ext =~ Regexp.new(rgx, Regexp::IGNORECASE)
27
+ ext =~ extname_matches_regexp
22
28
  end
23
29
 
24
30
  def output_ext(ext)
@@ -43,7 +43,7 @@ module Jekyll
43
43
  # Returns nothing.
44
44
  def read_yaml(base, name, opts = {})
45
45
  begin
46
- self.content = File.read(Jekyll.sanitized_path(base, name),
46
+ self.content = File.read(site.in_source_dir(base, name),
47
47
  merged_file_read_opts(opts))
48
48
  if content =~ /\A(---\s*\n.*?\n?)^((---|\.\.\.)\s*$\n?)/m
49
49
  self.content = $POSTMATCH
@@ -60,7 +60,7 @@ module Jekyll
60
60
 
61
61
  # Transform the contents based on the content type.
62
62
  #
63
- # Returns nothing.
63
+ # Returns the transformed contents.
64
64
  def transform
65
65
  converters.reduce(content) do |output, converter|
66
66
  begin
@@ -4,7 +4,7 @@ module Jekyll
4
4
  class Document
5
5
  include Comparable
6
6
 
7
- attr_reader :path, :site
7
+ attr_reader :path, :site, :extname
8
8
  attr_accessor :content, :collection, :output
9
9
 
10
10
  # Create a new Document.
@@ -16,6 +16,7 @@ module Jekyll
16
16
  def initialize(path, relations)
17
17
  @site = relations[:site]
18
18
  @path = path
19
+ @extname = File.extname(path)
19
20
  @collection = relations[:collection]
20
21
  @has_yaml_header = nil
21
22
  end
@@ -33,23 +34,21 @@ module Jekyll
33
34
  # Returns a String path which represents the relative path
34
35
  # from the site source to this document
35
36
  def relative_path
36
- Pathname.new(path).relative_path_from(Pathname.new(site.source)).to_s
37
+ @relative_path ||= Pathname.new(path).relative_path_from(Pathname.new(site.source)).to_s
37
38
  end
38
39
 
39
- # The base filename of the document.
40
- #
41
- # suffix - (optional) the suffix to be removed from the end of the filename
40
+ # The base filename of the document, without the file extname.
42
41
  #
43
- # Returns the base filename of the document.
44
- def basename(suffix = "")
45
- File.basename(path, suffix)
42
+ # Returns the basename without the file extname.
43
+ def basename_without_ext
44
+ @basename_without_ext ||= File.basename(path, '.*')
46
45
  end
47
46
 
48
- # The extension name of the document.
47
+ # The base filename of the document.
49
48
  #
50
- # Returns the extension name of the document.
51
- def extname
52
- File.extname(path)
49
+ # Returns the base filename of the document.
50
+ def basename
51
+ @basename ||= File.basename(path)
53
52
  end
54
53
 
55
54
  # Produces a "cleaned" relative path.
@@ -64,7 +63,8 @@ module Jekyll
64
63
  #
65
64
  # Returns the cleaned relative path of the document.
66
65
  def cleaned_relative_path
67
- relative_path[0 .. -extname.length - 1].sub(collection.relative_directory, "")
66
+ @cleaned_relative_path ||=
67
+ relative_path[0 .. -extname.length - 1].sub(collection.relative_directory, "")
68
68
  end
69
69
 
70
70
  # Determine whether the document is a YAML file.
@@ -129,8 +129,8 @@ module Jekyll
129
129
  collection: collection.label,
130
130
  path: cleaned_relative_path,
131
131
  output_ext: Jekyll::Renderer.new(site, self).output_ext,
132
- name: Utils.slugify(basename(".*")),
133
- title: Utils.slugify(data['title']) || Utils.slugify(basename(".*"))
132
+ name: Utils.slugify(basename_without_ext),
133
+ title: Utils.slugify(data['title']) || Utils.slugify(basename_without_ext)
134
134
  }
135
135
  end
136
136
 
@@ -159,7 +159,8 @@ module Jekyll
159
159
  #
160
160
  # Returns the full path to the output file of this document.
161
161
  def destination(base_directory)
162
- path = Jekyll.sanitized_path(base_directory, url)
162
+ dest = site.in_dest_dir(base_directory)
163
+ path = site.in_dest_dir(dest, url)
163
164
  path = File.join(path, "index.html") if url =~ /\/$/
164
165
  path
165
166
  end
@@ -14,8 +14,8 @@ module Jekyll
14
14
  end
15
15
 
16
16
  # Get the full path to the directory containing the draft files
17
- def containing_dir(source, dir)
18
- File.join(source, dir, '_drafts')
17
+ def containing_dir(dir)
18
+ site.in_source_dir(dir, '_drafts')
19
19
  end
20
20
 
21
21
  # The path to the draft source file, relative to the site source
@@ -47,7 +47,7 @@ module Jekyll
47
47
 
48
48
  def excluded?(entry)
49
49
  excluded = glob_include?(site.exclude, relative_to_source(entry))
50
- Jekyll.logger.debug "excluded?(#{relative_to_source(entry)}) ==> #{excluded}"
50
+ Jekyll.logger.debug "EntryFilter:", "excluded?(#{relative_to_source(entry)}) ==> #{excluded}"
51
51
  excluded
52
52
  end
53
53
 
@@ -106,7 +106,7 @@ module Jekyll
106
106
  # Returns excerpt String
107
107
  def extract_excerpt(post_content)
108
108
  separator = site.config['excerpt_separator']
109
- head, _, tail = post_content.partition(separator)
109
+ head, _, tail = post_content.to_s.partition(separator)
110
110
 
111
111
  "" << head << "\n\n" << tail.scan(/^\[[^\]]+\]:.+$/).join("\n")
112
112
  end
@@ -219,7 +219,8 @@ module Jekyll
219
219
  #
220
220
  # Returns the filtered array of objects
221
221
  def where(input, property, value)
222
- return input unless input.is_a?(Array)
222
+ return input unless input.is_a?(Enumerable)
223
+ input = input.values if input.is_a?(Hash)
223
224
  input.select { |object| item_property(object, property) == value }
224
225
  end
225
226
 
@@ -326,7 +327,16 @@ module Jekyll
326
327
  end
327
328
 
328
329
  def as_liquid(item)
329
- item.respond_to?(:to_liquid) ? item.to_liquid : item
330
+ case item
331
+ when String, Numeric, nil
332
+ item.to_liquid
333
+ when Hash
334
+ Hash[item.map { |k, v| [as_liquid(k), as_liquid(v)] }]
335
+ when Array
336
+ item.map{ |i| as_liquid(i) }
337
+ else
338
+ item.respond_to?(:to_liquid) ? as_liquid(item.to_liquid) : item
339
+ end
330
340
  end
331
341
  end
332
342
  end
@@ -38,12 +38,12 @@ module Jekyll
38
38
  end
39
39
 
40
40
  def layout_directory_inside_source
41
- Jekyll.sanitized_path(site.source, site.config['layouts'])
41
+ site.in_source_dir(site.config['layouts'])
42
42
  end
43
43
 
44
44
  def layout_directory_in_cwd
45
45
  dir = Jekyll.sanitized_path(Dir.pwd, site.config['layouts'])
46
- if File.directory?(dir)
46
+ if File.directory?(dir) && !site.safe
47
47
  dir
48
48
  else
49
49
  nil
@@ -126,7 +126,7 @@ module Jekyll
126
126
  #
127
127
  # Returns the path to the source file
128
128
  def path
129
- data.fetch('path', relative_path.sub(/\A\//, ''))
129
+ data.fetch('path') { relative_path.sub(/\A\//, '') }
130
130
  end
131
131
 
132
132
  # The path to the page source file, relative to the site source
@@ -140,7 +140,7 @@ module Jekyll
140
140
  #
141
141
  # Returns the destination file path String.
142
142
  def destination(dest)
143
- path = Jekyll.sanitized_path(dest, URL.unescape_path(url))
143
+ path = site.in_dest_dir(dest, URL.unescape_path(url))
144
144
  path = File.join(path, "index.html") if url =~ /\/$/
145
145
  path
146
146
  end
@@ -17,6 +17,7 @@ module Jekyll
17
17
  def conscientious_require
18
18
  require_plugin_files
19
19
  require_gems
20
+ self.class.require_from_bundler
20
21
  end
21
22
 
22
23
  # Require each of the gem plugins specified.
@@ -25,11 +26,26 @@ module Jekyll
25
26
  def require_gems
26
27
  site.gems.each do |gem|
27
28
  if plugin_allowed?(gem)
29
+ Jekyll.logger.debug("PluginManager:", "Requiring #{gem}")
28
30
  require gem
29
31
  end
30
32
  end
31
33
  end
32
34
 
35
+ def self.require_from_bundler
36
+ if ENV["JEKYLL_NO_BUNDLER_REQUIRE"]
37
+ false
38
+ else
39
+ require "bundler"
40
+ required_gems = Bundler.require(:jekyll_plugins)
41
+ Jekyll.logger.debug("PluginManager:", "Required #{required_gems.map(&:name).join(', ')}")
42
+ ENV["JEKYLL_NO_BUNDLER_REQUIRE"] = "true"
43
+ true
44
+ end
45
+ rescue LoadError, Bundler::GemfileNotFound
46
+ false
47
+ end
48
+
33
49
  # Check whether a gem plugin is allowed to be used during this build.
34
50
  #
35
51
  # gem_name - the name of the gem
@@ -66,7 +82,7 @@ module Jekyll
66
82
  # Returns an Array of plugin search paths
67
83
  def plugins_path
68
84
  if (site.config['plugins'] == Jekyll::Configuration::DEFAULTS['plugins'])
69
- [Jekyll.sanitized_path(site.source, site.config['plugins'])]
85
+ [site.in_source_dir(site.config['plugins'])]
70
86
  else
71
87
  Array(site.config['plugins']).map { |d| File.expand_path(d) }
72
88
  end
@@ -49,7 +49,7 @@ module Jekyll
49
49
  def initialize(site, source, dir, name)
50
50
  @site = site
51
51
  @dir = dir
52
- @base = containing_dir(source, dir)
52
+ @base = containing_dir(dir)
53
53
  @name = name
54
54
 
55
55
  self.categories = dir.downcase.split('/').reject { |x| x.empty? }
@@ -88,8 +88,8 @@ module Jekyll
88
88
  end
89
89
 
90
90
  # Get the full path to the directory containing the post files
91
- def containing_dir(source, dir)
92
- return File.join(source, dir, '_posts')
91
+ def containing_dir(dir)
92
+ site.in_source_dir(dir, '_posts')
93
93
  end
94
94
 
95
95
  # Read the YAML frontmatter.
@@ -108,14 +108,14 @@ module Jekyll
108
108
  #
109
109
  # Returns excerpt string.
110
110
  def excerpt
111
- data.fetch('excerpt', extracted_excerpt.to_s)
111
+ data.fetch('excerpt') { extracted_excerpt.to_s }
112
112
  end
113
113
 
114
114
  # Public: the Post title, from the YAML Front-Matter or from the slug
115
115
  #
116
116
  # Returns the post title
117
117
  def title
118
- data.fetch("title", titleized_slug)
118
+ data.fetch('title') { titleized_slug }
119
119
  end
120
120
 
121
121
  # Turns the post slug into a suitable title
@@ -130,7 +130,7 @@ module Jekyll
130
130
  #
131
131
  # Returns the path to the file relative to the site source
132
132
  def path
133
- data.fetch('path', relative_path.sub(/\A\//, ''))
133
+ data.fetch('path') { relative_path.sub(/\A\//, '') }
134
134
  end
135
135
 
136
136
  # The path to the post source file, relative to the site source
@@ -268,8 +268,8 @@ module Jekyll
268
268
  # Returns destination file path String.
269
269
  def destination(dest)
270
270
  # The url needs to be unescaped in order to preserve the correct filename
271
- path = Jekyll.sanitized_path(dest, URL.unescape_path(url))
272
- path = File.join(path, "index.html") if path[/\.html$/].nil?
271
+ path = site.in_dest_dir(dest, URL.unescape_path(url))
272
+ path = File.join(path, "index.html") if path[/\.html?$/].nil?
273
273
  path
274
274
  end
275
275
 
@@ -46,8 +46,7 @@ module Jekyll
46
46
  end
47
47
 
48
48
  def most_recent_posts
49
- recent_posts = site.posts.reverse - [post]
50
- recent_posts.first(10)
49
+ @most_recent_posts ||= (site.posts.reverse - [post]).first(10)
51
50
  end
52
51
 
53
52
  def display(output)
@@ -3,11 +3,12 @@ require 'csv'
3
3
 
4
4
  module Jekyll
5
5
  class Site
6
- attr_accessor :config, :layouts, :posts, :pages, :static_files,
7
- :exclude, :include, :source, :dest, :lsi, :highlighter,
8
- :permalink_style, :time, :future, :unpublished, :safe, :plugins, :limit_posts,
9
- :show_drafts, :keep_files, :baseurl, :data, :file_read_opts, :gems,
10
- :plugin_manager
6
+ attr_reader :source, :dest, :config
7
+ attr_accessor :layouts, :posts, :pages, :static_files,
8
+ :exclude, :include, :lsi, :highlighter, :permalink_style,
9
+ :time, :future, :unpublished, :safe, :plugins, :limit_posts,
10
+ :show_drafts, :keep_files, :baseurl, :data, :file_read_opts,
11
+ :gems, :plugin_manager
11
12
 
12
13
  attr_accessor :converters, :generators
13
14
 
@@ -15,16 +16,16 @@ module Jekyll
15
16
  #
16
17
  # config - A Hash containing site configuration details.
17
18
  def initialize(config)
18
- self.config = config.clone
19
+ @config = config.clone
19
20
 
20
21
  %w[safe lsi highlighter baseurl exclude include future unpublished
21
22
  show_drafts limit_posts keep_files gems].each do |opt|
22
23
  self.send("#{opt}=", config[opt])
23
24
  end
24
25
 
25
- self.source = File.expand_path(config['source'])
26
- self.dest = File.expand_path(config['destination'])
27
- self.permalink_style = config['permalink'].to_sym
26
+ # Source and destination may not be changed after the site has been created.
27
+ @source = File.expand_path(config['source']).freeze
28
+ @dest = File.expand_path(config['destination']).freeze
28
29
 
29
30
  self.plugin_manager = Jekyll::PluginManager.new(self)
30
31
  self.plugins = plugin_manager.plugins_path
@@ -32,6 +33,10 @@ module Jekyll
32
33
  self.file_read_opts = {}
33
34
  self.file_read_opts[:encoding] = config['encoding'] if config['encoding']
34
35
 
36
+ self.permalink_style = config['permalink'].to_sym
37
+
38
+ Jekyll.sites << self
39
+
35
40
  reset
36
41
  setup
37
42
  end
@@ -88,6 +93,30 @@ module Jekyll
88
93
  end
89
94
  end
90
95
 
96
+ # Public: Prefix a given path with the source directory.
97
+ #
98
+ # paths - (optional) path elements to a file or directory within the
99
+ # source directory
100
+ #
101
+ # Returns a path which is prefixed with the source directory.
102
+ def in_source_dir(*paths)
103
+ paths.reduce(source) do |base, path|
104
+ Jekyll.sanitized_path(base, path)
105
+ end
106
+ end
107
+
108
+ # Public: Prefix a given path with the destination directory.
109
+ #
110
+ # paths - (optional) path elements to a file or directory within the
111
+ # destination directory
112
+ #
113
+ # Returns a path which is prefixed with the destination directory.
114
+ def in_dest_dir(*paths)
115
+ paths.reduce(dest) do |base, path|
116
+ Jekyll.sanitized_path(base, path)
117
+ end
118
+ end
119
+
91
120
  # The list of collections and their corresponding Jekyll::Collection instances.
92
121
  # If config['collections'] is set, a new instance is created for each item in the collection.
93
122
  # If config['collections'] is not set, a new hash is returned.
@@ -132,7 +161,7 @@ module Jekyll
132
161
  #
133
162
  # Returns nothing.
134
163
  def read_directories(dir = '')
135
- base = File.join(source, dir)
164
+ base = in_source_dir(dir)
136
165
  entries = Dir.chdir(base) { filter_entries(Dir.entries('.'), base) }
137
166
 
138
167
  read_posts(dir)
@@ -141,7 +170,7 @@ module Jekyll
141
170
  limit_posts! if limit_posts > 0 # limit the posts if :limit_posts option is set
142
171
 
143
172
  entries.each do |f|
144
- f_abs = File.join(base, f)
173
+ f_abs = in_source_dir(base, f)
145
174
  if File.directory?(f_abs)
146
175
  f_rel = File.join(dir, f)
147
176
  read_directories(f_rel) unless dest.sub(/\/$/, '') == f_abs
@@ -198,7 +227,7 @@ module Jekyll
198
227
  #
199
228
  # Returns nothing
200
229
  def read_data(dir)
201
- base = Jekyll.sanitized_path(source, dir)
230
+ base = in_source_dir(dir)
202
231
  read_data_to(base, self.data)
203
232
  end
204
233
 
@@ -217,7 +246,7 @@ module Jekyll
217
246
  end
218
247
 
219
248
  entries.each do |entry|
220
- path = Jekyll.sanitized_path(dir, entry)
249
+ path = in_source_dir(dir, entry)
221
250
  next if File.symlink?(path) && safe
222
251
 
223
252
  key = sanitize_filename(File.basename(entry, '.*'))
@@ -407,10 +436,10 @@ module Jekyll
407
436
  #
408
437
  # Returns the list of entries to process
409
438
  def get_entries(dir, subfolder)
410
- base = File.join(source, dir, subfolder)
439
+ base = in_source_dir(dir, subfolder)
411
440
  return [] unless File.exist?(base)
412
441
  entries = Dir.chdir(base) { filter_entries(Dir['**/*'], base) }
413
- entries.delete_if { |e| File.directory?(File.join(base, e)) }
442
+ entries.delete_if { |e| File.directory?(in_source_dir(base, e)) }
414
443
  end
415
444
 
416
445
  # Aggregate post information
@@ -37,7 +37,7 @@ module Jekyll
37
37
  #
38
38
  # Returns destination file path.
39
39
  def destination(dest)
40
- File.join(*[dest, destination_rel_dir, @name].compact)
40
+ @site.in_dest_dir(*[dest, destination_rel_dir, @name].compact)
41
41
  end
42
42
 
43
43
  def destination_rel_dir
@@ -13,11 +13,14 @@ module Jekyll
13
13
 
14
14
  class IncludeTag < Liquid::Tag
15
15
 
16
+ attr_reader :includes_dir
17
+
16
18
  VALID_SYNTAX = /([\w-]+)\s*=\s*(?:"([^"\\]*(?:\\.[^"\\]*)*)"|'([^'\\]*(?:\\.[^'\\]*)*)'|([\w\.-]+))/
17
19
  VARIABLE_SYNTAX = /(?<variable>[^{]*\{\{\s*(?<name>[\w\-\.]+)\s*(\|.*)?\}\}[^\s}]*)(?<params>.*)/
18
20
 
19
21
  def initialize(tag_name, markup, tokens)
20
22
  super
23
+ @includes_dir = tag_includes_dir
21
24
  matched = markup.strip.match(VARIABLE_SYNTAX)
22
25
  if matched
23
26
  @file = matched['variable'].strip
@@ -97,7 +100,7 @@ eos
97
100
  end
98
101
  end
99
102
 
100
- def includes_dir
103
+ def tag_includes_dir
101
104
  '_includes'
102
105
  end
103
106
 
@@ -118,12 +121,12 @@ eos
118
121
  partial.render!(context)
119
122
  end
120
123
  rescue => e
121
- raise IncludeTagError.new e.message, File.join(includes_dir, @file)
124
+ raise IncludeTagError.new e.message, File.join(@includes_dir, @file)
122
125
  end
123
126
  end
124
127
 
125
128
  def resolved_includes_dir(context)
126
- File.join(File.realpath(context.registers[:site].source), includes_dir)
129
+ File.join(File.realpath(context.registers[:site].source), @includes_dir)
127
130
  end
128
131
 
129
132
  def validate_path(path, dir, safe)
@@ -135,7 +138,7 @@ eos
135
138
  end
136
139
 
137
140
  def path_relative_to_source(dir, path)
138
- File.join(includes_dir, path.sub(Regexp.new("^#{dir}"), ""))
141
+ File.join(@includes_dir, path.sub(Regexp.new("^#{dir}"), ""))
139
142
  end
140
143
 
141
144
  def realpath_prefixed_with?(path, dir)
@@ -149,13 +152,16 @@ eos
149
152
  end
150
153
 
151
154
  class IncludeRelativeTag < IncludeTag
152
- def includes_dir
155
+ def tag_includes_dir
153
156
  '.'
154
157
  end
155
158
 
159
+ def page_path(context)
160
+ context.registers[:page].nil? ? includes_dir : File.dirname(context.registers[:page]["path"])
161
+ end
162
+
156
163
  def resolved_includes_dir(context)
157
- page_path = context.registers[:page].nil? ? includes_dir : File.dirname(context.registers[:page]["path"])
158
- Jekyll.sanitized_path(context.registers[:site].source, page_path)
164
+ context.registers[:site].in_source_dir(page_path(context))
159
165
  end
160
166
  end
161
167
  end
@@ -37,33 +37,46 @@ module Jekyll
37
37
  #
38
38
  # Returns the String URL
39
39
  def to_s
40
- sanitize_url(@permalink || generate_url)
40
+ sanitize_url(generated_permalink || generated_url)
41
+ end
42
+
43
+ # Generates a URL from the permalink
44
+ #
45
+ # Returns the _unsanitized String URL
46
+ def generated_permalink
47
+ (@generated_permlink ||= generate_url(@permalink)) if @permalink
48
+ end
49
+
50
+ # Generates a URL from the template
51
+ #
52
+ # Returns the _unsanitized String URL
53
+ def generated_url
54
+ @generated_url ||= generate_url(@template)
41
55
  end
42
56
 
43
57
  # Internal: Generate the URL by replacing all placeholders with their
44
- # respective values
58
+ # respective values in the given template
45
59
  #
46
60
  # Returns the _unsanitizied_ String URL
47
- def generate_url
48
- @placeholders.inject(@template) do |result, token|
61
+ def generate_url(template)
62
+ @placeholders.inject(template) do |result, token|
63
+ break result if result.index(':').nil?
49
64
  result.gsub(/:#{token.first}/, self.class.escape_path(token.last))
50
65
  end
51
66
  end
52
67
 
53
68
  # Returns a sanitized String URL
54
69
  def sanitize_url(in_url)
55
-
56
- # Remove all double slashes
57
- url = in_url.gsub(/\/\//, "/")
58
-
59
- # Remove every URL segment that consists solely of dots
60
- url = url.split('/').reject{ |part| part =~ /^\.+$/ }.join('/')
70
+ url = in_url \
71
+ # Remove all double slashes
72
+ .gsub(/\/\//, '/') \
73
+ # Remove every URL segment that consists solely of dots
74
+ .split('/').reject{ |part| part =~ /^\.+$/ }.join('/') \
75
+ # Always add a leading slash
76
+ .gsub(/\A([^\/])/, '/\1')
61
77
 
62
78
  # Append a trailing slash to the URL if the unsanitized URL had one
63
- url += "/" if in_url =~ /\/$/
64
-
65
- # Always add a leading slash
66
- url.gsub!(/\A([^\/])/, '/\1')
79
+ url << "/" if in_url[-1].eql?('/')
67
80
 
68
81
  url
69
82
  end
@@ -79,7 +92,7 @@ module Jekyll
79
92
  #
80
93
  # Returns the escaped path.
81
94
  def self.escape_path(path)
82
- # Because URI.escape doesn't escape '?', '[' and ']' by defaut,
95
+ # Because URI.escape doesn't escape '?', '[' and ']' by default,
83
96
  # specify unsafe string (except unreserved, sub-delims, ":", "@" and "/").
84
97
  #
85
98
  # URI path segment is defined in RFC 3986 as follows:
@@ -14,7 +14,7 @@ module Jekyll
14
14
  def deep_merge_hashes(master_hash, other_hash)
15
15
  target = master_hash.dup
16
16
 
17
- other_hash.keys.each do |key|
17
+ other_hash.each_key do |key|
18
18
  if other_hash[key].is_a? Hash and target[key].is_a? Hash
19
19
  target[key] = Utils.deep_merge_hashes(target[key], other_hash[key])
20
20
  next
@@ -111,11 +111,13 @@ module Jekyll
111
111
  # hyphen.
112
112
  def slugify(string)
113
113
  unless string.nil?
114
- # Replace each non-alphanumeric character sequence with a hyphen
115
- slug = string.gsub(/[^a-z0-9]+/i, '-')
116
- # Remove leading/trailing hyphen
117
- slug.gsub!(/^\-|\-$/i, '')
118
- slug.downcase
114
+ string \
115
+ # Replace each non-alphanumeric character sequence with a hyphen
116
+ .gsub(/[^a-z0-9]+/i, '-') \
117
+ # Remove leading/trailing hyphen
118
+ .gsub(/^\-|\-$/i, '') \
119
+ # Downcase it
120
+ .downcase
119
121
  end
120
122
  end
121
123
 
@@ -1,3 +1,3 @@
1
1
  module Jekyll
2
- VERSION = '2.4.0'
2
+ VERSION = '2.5.0'
3
3
  end
@@ -1,11 +1,12 @@
1
1
  <head>
2
- <meta charset="utf-8">
3
- <meta name="viewport" content="width=device-width initial-scale=1" />
4
- <meta http-equiv="X-UA-Compatible" content="IE=edge">
2
+ <meta charset="utf-8">
3
+ <meta http-equiv="X-UA-Compatible" content="IE=edge">
4
+ <meta name="viewport" content="width=device-width initial-scale=1">
5
5
 
6
- <title>{% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %}</title>
7
- <meta name="description" content="{{ site.description }}">
6
+ <title>{% if page.title %}{{ page.title }}{% else %}{{ site.title }}{% endif %}</title>
7
+ <meta name="description" content="{% if page.excerpt %}{{ page.excerpt | strip_html | strip_newlines | truncate: 160 }}{% else %}{{ site.description }}{% endif %}">
8
8
 
9
- <link rel="stylesheet" href="{{ "/css/main.css" | prepend: site.baseurl }}">
10
- <link rel="canonical" href="{{ page.url | replace:'index.html','' | prepend: site.baseurl | prepend: site.url }}">
9
+ <link rel="stylesheet" href="{{ "/css/main.css" | prepend: site.baseurl }}">
10
+ <link rel="canonical" href="{{ page.url | replace:'index.html','' | prepend: site.baseurl | prepend: site.url }}">
11
+ <link rel="alternate" type="application/atom+xml" title="{{ site.title }}" href="{{ "/feed.xml" | prepend: site.baseurl | prepend: site.url }}" />
11
12
  </head>
@@ -4,7 +4,7 @@ title: "Welcome to Jekyll!"
4
4
  date: <%= Time.now.strftime('%Y-%m-%d %H:%M:%S') %>
5
5
  categories: jekyll update
6
6
  ---
7
- You’ll find this post in your `_posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run `jekyll serve --watch`, which launches a web server and auto-regenerates your site when a file is updated.
7
+ You’ll find this post in your `_posts` directory. Go ahead and edit it and re-build the site to see your changes. You can rebuild the site in many different ways, but the most common way is to run `jekyll serve`, which launches a web server and auto-regenerates your site when a file is updated.
8
8
 
9
9
  To add new posts, simply add a file in the `_posts` directory that follows the convention `YYYY-MM-DD-name-of-post.ext` and includes the necessary front matter. Take a look at the source for this post to get an idea about how it works.
10
10
 
@@ -154,8 +154,8 @@ pre {
154
154
  * Wrapper
155
155
  */
156
156
  .wrapper {
157
- max-width: -webkit-calc(800px - (#{$spacing-unit} * 2));
158
- max-width: calc(800px - (#{$spacing-unit} * 2));
157
+ max-width: -webkit-calc(#{$content-width} - (#{$spacing-unit} * 2));
158
+ max-width: calc(#{$content-width} - (#{$spacing-unit} * 2));
159
159
  margin-right: auto;
160
160
  margin-left: auto;
161
161
  padding-right: $spacing-unit;
@@ -163,8 +163,8 @@ pre {
163
163
  @extend %clearfix;
164
164
 
165
165
  @include media-query($on-laptop) {
166
- max-width: -webkit-calc(800px - (#{$spacing-unit}));
167
- max-width: calc(800px - (#{$spacing-unit}));
166
+ max-width: -webkit-calc(#{$content-width} - (#{$spacing-unit}));
167
+ max-width: calc(#{$content-width} - (#{$spacing-unit}));
168
168
  padding-right: $spacing-unit / 2;
169
169
  padding-left: $spacing-unit / 2;
170
170
  }
@@ -21,13 +21,16 @@ $grey-color: #828282;
21
21
  $grey-color-light: lighten($grey-color, 40%);
22
22
  $grey-color-dark: darken($grey-color, 25%);
23
23
 
24
+ // Width of the content area
25
+ $content-width: 800px;
26
+
24
27
  $on-palm: 600px;
25
28
  $on-laptop: 800px;
26
29
 
27
30
 
28
31
 
29
32
  // Using media queries with like this:
30
- // @include media-query($palm) {
33
+ // @include media-query($on-palm) {
31
34
  // .wrapper {
32
35
  // padding-right: $spacing-unit / 2;
33
36
  // padding-left: $spacing-unit / 2;
@@ -7,7 +7,7 @@ layout: null
7
7
  <title>{{ site.title | xml_escape }}</title>
8
8
  <description>{{ site.description | xml_escape }}</description>
9
9
  <link>{{ site.url }}{{ site.baseurl }}/</link>
10
- <atom:link href="{{ "/feed.xml" | prepend: site.baseurl | prepend: site.url }}" rel="self" type="application/rss+xml" />
10
+ <atom:link href="{{ "/feed.xml" | prepend: site.baseurl | prepend: site.url }}" rel="self" type="application/rss+xml"/>
11
11
  <pubDate>{{ site.time | date_to_rfc822 }}</pubDate>
12
12
  <lastBuildDate>{{ site.time | date_to_rfc822 }}</lastBuildDate>
13
13
  <generator>Jekyll v{{ jekyll.version }}</generator>
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: jekyll
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.4.0
4
+ version: 2.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Tom Preston-Werner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-09-10 00:00:00.000000000 Z
11
+ date: 2014-11-06 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: liquid
@@ -436,14 +436,14 @@ dependencies:
436
436
  requirements:
437
437
  - - "~>"
438
438
  - !ruby/object:Gem::Version
439
- version: '1.3'
439
+ version: '1.7'
440
440
  type: :development
441
441
  prerelease: false
442
442
  version_requirements: !ruby/object:Gem::Requirement
443
443
  requirements:
444
444
  - - "~>"
445
445
  - !ruby/object:Gem::Version
446
- version: '1.3'
446
+ version: '1.7'
447
447
  description: Jekyll is a simple, blog aware, static site generator.
448
448
  email: tom@mojombo.com
449
449
  executables: