jekyll 2.0.0.alpha.3 → 2.0.0.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.

Files changed (44) hide show
  1. checksums.yaml +4 -4
  2. data/CONTRIBUTING.markdown +1 -1
  3. data/History.markdown +10 -0
  4. data/features/collections.feature +49 -15
  5. data/features/create_sites.feature +16 -16
  6. data/features/data.feature +4 -4
  7. data/features/drafts.feature +4 -4
  8. data/features/embed_filters.feature +6 -6
  9. data/features/frontmatter_defaults.feature +79 -0
  10. data/features/include_tag.feature +4 -4
  11. data/features/markdown.feature +4 -4
  12. data/features/pagination.feature +3 -3
  13. data/features/permalinks.feature +8 -8
  14. data/features/post_data.feature +18 -18
  15. data/features/post_excerpts.feature +3 -3
  16. data/features/site_configuration.feature +21 -21
  17. data/features/site_data.feature +11 -11
  18. data/features/step_definitions/jekyll_steps.rb +5 -18
  19. data/features/support/env.rb +2 -15
  20. data/lib/jekyll.rb +2 -0
  21. data/lib/jekyll/collection.rb +30 -4
  22. data/lib/jekyll/configuration.rb +2 -1
  23. data/lib/jekyll/convertible.rb +15 -1
  24. data/lib/jekyll/document.rb +11 -7
  25. data/lib/jekyll/filters.rb +1 -1
  26. data/lib/jekyll/frontmatter_defaults.rb +148 -0
  27. data/lib/jekyll/liquid_extensions.rb +22 -0
  28. data/lib/jekyll/page.rb +5 -0
  29. data/lib/jekyll/post.rb +12 -0
  30. data/lib/jekyll/site.rb +42 -27
  31. data/lib/jekyll/version.rb +1 -1
  32. data/script/test +11 -0
  33. data/site/docs/collections.md +78 -5
  34. data/site/docs/configuration.md +49 -0
  35. data/site/docs/frontmatter.md +10 -0
  36. data/site/docs/plugins.md +1 -0
  37. data/site/docs/posts.md +9 -0
  38. data/site/docs/sites.md +2 -2
  39. data/site/docs/templates.md +1 -1
  40. data/test/test_collections.rb +50 -4
  41. data/test/test_filters.rb +4 -0
  42. data/test/test_liquid_extensions.rb +31 -0
  43. data/test/test_utils.rb +1 -0
  44. metadata +9 -2
@@ -5,14 +5,14 @@ Feature: Site data
5
5
 
6
6
  Scenario: Use page variable in a page
7
7
  Given I have an "contact.html" page with title "Contact" that contains "{{ page.title }}: email@example.com"
8
- When I run jekyll
8
+ When I run jekyll build
9
9
  Then the _site directory should exist
10
10
  And I should see "Contact: email@example.com" in "_site/contact.html"
11
11
 
12
12
  Scenario Outline: Use page.path variable in a page
13
13
  Given I have a <dir> directory
14
14
  And I have a "<path>" page that contains "Source path: {{ page.path }}"
15
- When I run jekyll
15
+ When I run jekyll build
16
16
  Then the _site directory should exist
17
17
  And I should see "Source path: <path>" in "_site/<path>"
18
18
 
@@ -24,13 +24,13 @@ Feature: Site data
24
24
 
25
25
  Scenario: Override page.path
26
26
  Given I have an "override.html" page with path "custom-override.html" that contains "Custom path: {{ page.path }}"
27
- When I run jekyll
27
+ When I run jekyll build
28
28
  Then the _site directory should exist
29
29
  And I should see "Custom path: custom-override.html" in "_site/override.html"
30
30
 
31
31
  Scenario: Use site.time variable
32
32
  Given I have an "index.html" page that contains "{{ site.time }}"
33
- When I run jekyll
33
+ When I run jekyll build
34
34
  Then the _site directory should exist
35
35
  And I should see today's time in "_site/index.html"
36
36
 
@@ -42,7 +42,7 @@ Feature: Site data
42
42
  | First Post | 2009-03-25 | My First Post |
43
43
  | Second Post | 2009-03-26 | My Second Post |
44
44
  | Third Post | 2009-03-27 | My Third Post |
45
- When I run jekyll
45
+ When I run jekyll build
46
46
  Then the _site directory should exist
47
47
  And I should see "Third Post: /2009/03/27/third-post.html" in "_site/index.html"
48
48
 
@@ -54,7 +54,7 @@ Feature: Site data
54
54
  | First Post | 2009-03-25 | My First Post |
55
55
  | Second Post | 2009-03-26 | My Second Post |
56
56
  | Third Post | 2009-03-27 | My Third Post |
57
- When I run jekyll
57
+ When I run jekyll build
58
58
  Then the _site directory should exist
59
59
  And I should see "Third Post Second Post First Post" in "_site/index.html"
60
60
 
@@ -65,7 +65,7 @@ Feature: Site data
65
65
  | title | date | category | content |
66
66
  | Awesome Hack | 2009-03-26 | code | puts 'Hello World' |
67
67
  | Delicious Beer | 2009-03-26 | food | 1) Yuengling |
68
- When I run jekyll
68
+ When I run jekyll build
69
69
  Then the _site directory should exist
70
70
  And I should see "Awesome Hack" in "_site/index.html"
71
71
 
@@ -75,7 +75,7 @@ Feature: Site data
75
75
  And I have the following posts:
76
76
  | title | date | tag | content |
77
77
  | Delicious Beer | 2009-03-26 | beer | 1) Yuengling |
78
- When I run jekyll
78
+ When I run jekyll build
79
79
  Then the _site directory should exist
80
80
  And I should see "Yuengling" in "_site/index.html"
81
81
 
@@ -89,19 +89,19 @@ Feature: Site data
89
89
  | B | 2009-03-26 | B |
90
90
  | C | 2009-03-26 | C |
91
91
  | last | 2009-04-26 | last |
92
- When I run jekyll
92
+ When I run jekyll build
93
93
  Then the _site directory should exist
94
94
  And I should see "last:C, C:B,last B:A,C A:first,B first:,A" in "_site/index.html"
95
95
 
96
96
  Scenario: Use configuration date in site payload
97
97
  Given I have an "index.html" page that contains "{{ site.url }}"
98
98
  And I have a configuration file with "url" set to "http://example.com"
99
- When I run jekyll
99
+ When I run jekyll build
100
100
  Then the _site directory should exist
101
101
  And I should see "http://example.com" in "_site/index.html"
102
102
 
103
103
  Scenario: Access Jekyll version via jekyll.version
104
104
  Given I have an "index.html" page that contains "{{ jekyll.version }}"
105
- When I run jekyll
105
+ When I run jekyll build
106
106
  Then the _site directory should exist
107
107
  And I should see "\d+\.\d+\.\d+" in "_site/index.html"
@@ -139,24 +139,11 @@ end
139
139
  #
140
140
  ##################
141
141
 
142
- When /^I run jekyll(?: with "(.+)")?$/ do |opt|
143
- run_jekyll_build(opt)
144
- end
145
-
146
- When /^I run jekyll in safe mode$/ do
147
- run_jekyll_build("--safe")
148
- end
149
-
150
- When /^I run jekyll with drafts$/ do
151
- run_jekyll_build("--drafts")
152
- end
153
-
154
- When /^I call jekyll new with test_blank --blank$/ do
155
- run_jekyll_new("test_blank --blank")
156
- end
157
-
158
- When /^I debug jekyll$/ do
159
- run_jekyll_build("--verbose")
142
+ When /^I run jekyll(.*)$/ do |args|
143
+ status = run_jekyll(args)
144
+ if !status || args.include?("--verbose")
145
+ puts jekyll_run_output
146
+ end
160
147
  end
161
148
 
162
149
  When /^I change "(.*)" to contain "(.*)"$/ do |file, text|
@@ -23,21 +23,8 @@ def jekyll_run_output
23
23
  File.read(jekyll_output_file)
24
24
  end
25
25
 
26
- def run_jekyll(args, output_file)
27
- command = "#{JEKYLL_PATH} #{args} --trace > #{jekyll_output_file} 2>&1"
28
- system command
29
- end
30
-
31
- def run_jekyll_build(build_args = "")
32
- if !run_jekyll("build #{build_args}", jekyll_output_file) || build_args.eql?("--verbose")
33
- puts jekyll_run_output
34
- end
35
- end
36
-
37
- def run_jekyll_new(new_args = "")
38
- unless run_jekyll("new #{new_args}", jekyll_output_file)
39
- puts jekyll_run_output
40
- end
26
+ def run_jekyll(args)
27
+ system "#{JEKYLL_PATH} #{args} --trace > #{jekyll_output_file} 2>&1"
41
28
  end
42
29
 
43
30
  def slug(title)
@@ -37,6 +37,7 @@ require 'jekyll/configuration'
37
37
  require 'jekyll/document'
38
38
  require 'jekyll/collection'
39
39
  require 'jekyll/plugin_manager'
40
+ require 'jekyll/frontmatter_defaults'
40
41
  require 'jekyll/site'
41
42
  require 'jekyll/convertible'
42
43
  require 'jekyll/url'
@@ -60,6 +61,7 @@ require 'jekyll/plugin'
60
61
  require 'jekyll/converter'
61
62
  require 'jekyll/generator'
62
63
  require 'jekyll/command'
64
+ require 'jekyll/liquid_extensions'
63
65
 
64
66
  require_all 'jekyll/commands'
65
67
  require_all 'jekyll/converters'
@@ -1,6 +1,6 @@
1
1
  module Jekyll
2
2
  class Collection
3
- attr_reader :site, :label
3
+ attr_reader :site, :label, :metadata
4
4
 
5
5
  # Create a new Collection.
6
6
  #
@@ -9,8 +9,9 @@ module Jekyll
9
9
  #
10
10
  # Returns nothing.
11
11
  def initialize(site, label)
12
- @site = site
13
- @label = sanitize_label(label)
12
+ @site = site
13
+ @label = sanitize_label(label)
14
+ @metadata = extract_metadata
14
15
  end
15
16
 
16
17
  # Fetch the Documents in this collection.
@@ -114,7 +115,32 @@ module Jekyll
114
115
  #
115
116
  # Returns a representation of this collection for use in Liquid.
116
117
  def to_liquid
117
- docs
118
+ metadata.merge({
119
+ "label" => label,
120
+ "docs" => docs,
121
+ "directory" => directory,
122
+ "output" => write?,
123
+ "relative_directory" => relative_directory
124
+ })
125
+ end
126
+
127
+ # Whether the collection's documents ought to be written as individual
128
+ # files in the output.
129
+ #
130
+ # Returns true if the 'write' metadata is true, false otherwise.
131
+ def write?
132
+ !!metadata['output']
133
+ end
134
+
135
+ # Extract options for this collection from the site configuration.
136
+ #
137
+ # Returns the metadata for this collection
138
+ def extract_metadata
139
+ if site.config['collections'].is_a?(Hash)
140
+ site.config['collections'][label] || Hash.new
141
+ else
142
+ {}
143
+ end
118
144
  end
119
145
 
120
146
  end
@@ -46,6 +46,8 @@ module Jekyll
46
46
 
47
47
  'excerpt_separator' => "\n\n",
48
48
 
49
+ 'defaults' => [],
50
+
49
51
  'maruku' => {
50
52
  'use_tex' => false,
51
53
  'use_divs' => false,
@@ -251,6 +253,5 @@ module Jekyll
251
253
 
252
254
  config
253
255
  end
254
-
255
256
  end
256
257
  end
@@ -14,6 +14,8 @@ require 'set'
14
14
  # self.output=
15
15
  # self.name
16
16
  # self.path
17
+ # self.type -> :page, :post or :draft
18
+
17
19
  module Jekyll
18
20
  module Convertible
19
21
  # Returns the contents as a String.
@@ -107,7 +109,19 @@ module Jekyll
107
109
  further_data = Hash[(attrs || self.class::ATTRIBUTES_FOR_LIQUID).map { |attribute|
108
110
  [attribute, send(attribute)]
109
111
  }]
110
- Utils.deep_merge_hashes(data, further_data)
112
+
113
+ defaults = site.frontmatter_defaults.all(relative_path, type)
114
+ Utils.deep_merge_hashes defaults, Utils.deep_merge_hashes(data, further_data)
115
+ end
116
+
117
+ def type
118
+ if is_a?(Post)
119
+ :post
120
+ elsif is_a?(Page)
121
+ :page
122
+ elsif is_a?(Draft)
123
+ :draft
124
+ end
111
125
  end
112
126
 
113
127
  # Recursively render layouts
@@ -112,7 +112,7 @@ module Jekyll
112
112
  #
113
113
  # Returns the permalink or nil if no permalink was set in the data.
114
114
  def permalink
115
- data && data['permalink']
115
+ data && data.is_a?(Hash) && data['permalink']
116
116
  end
117
117
 
118
118
  # The computed URL for the document. See `Jekyll::URL#to_s` for more details.
@@ -192,12 +192,16 @@ module Jekyll
192
192
  #
193
193
  # Returns a Hash representing this Document's data.
194
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
- }
195
+ if data.is_a?(Hash)
196
+ Utils.deep_merge_hashes data, {
197
+ "content" => content,
198
+ "path" => path,
199
+ "relative_path" => relative_path,
200
+ "url" => url
201
+ }
202
+ else
203
+ data
204
+ end
201
205
  end
202
206
 
203
207
  # The inspect string for this document.
@@ -83,7 +83,7 @@ module Jekyll
83
83
  #
84
84
  # Returns the escaped String.
85
85
  def xml_escape(input)
86
- CGI.escapeHTML(input)
86
+ CGI.escapeHTML(input.to_s)
87
87
  end
88
88
 
89
89
  # CGI escape a string for use in a URL. Replaces any special characters
@@ -0,0 +1,148 @@
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
13
+
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
29
+ end
30
+ value
31
+ end
32
+
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.merge! set['values']
45
+ old_scope = set['scope']
46
+ else
47
+ defaults = set['values'].merge(defaults)
48
+ end
49
+ end
50
+ defaults
51
+ end
52
+
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
65
+
66
+ def applies_path?(scope, path)
67
+ return true if scope['path'].empty?
68
+
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
74
+ end
75
+ end
76
+
77
+ def applies_type?(scope, type)
78
+ !scope.has_key?('type') || scope['type'] == type.to_s
79
+ end
80
+
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
89
+
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
109
+ end
110
+
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
118
+ end
119
+
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)
135
+ end
136
+ end
137
+
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
145
+ end
146
+ end
147
+ end
148
+ end