jekyll 0.12.1 → 1.0.0.beta1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. data/CONTRIBUTING.md +67 -0
  2. data/Gemfile +1 -1
  3. data/History.txt +50 -6
  4. data/README.textile +10 -6
  5. data/Rakefile +74 -36
  6. data/bin/jekyll +78 -276
  7. data/cucumber.yml +3 -1
  8. data/features/create_sites.feature +1 -1
  9. data/features/drafts.feature +25 -0
  10. data/features/site_configuration.feature +1 -1
  11. data/features/step_definitions/jekyll_steps.rb +13 -3
  12. data/features/support/env.rb +3 -1
  13. data/jekyll.gemspec +73 -17
  14. data/lib/jekyll.rb +31 -21
  15. data/lib/jekyll/command.rb +12 -0
  16. data/lib/jekyll/commands/build.rb +81 -0
  17. data/lib/jekyll/commands/serve.rb +28 -0
  18. data/lib/jekyll/converter.rb +1 -3
  19. data/lib/jekyll/converters/identity.rb +13 -14
  20. data/lib/jekyll/converters/markdown.rb +128 -128
  21. data/lib/jekyll/converters/textile.rb +37 -37
  22. data/lib/jekyll/convertible.rb +6 -4
  23. data/lib/jekyll/core_ext.rb +9 -1
  24. data/lib/jekyll/draft.rb +35 -0
  25. data/lib/jekyll/errors.rb +1 -3
  26. data/lib/jekyll/filters.rb +13 -4
  27. data/lib/jekyll/generator.rb +1 -4
  28. data/lib/jekyll/generators/pagination.rb +46 -46
  29. data/lib/jekyll/layout.rb +0 -2
  30. data/lib/jekyll/mime.types +1588 -0
  31. data/lib/jekyll/page.rb +24 -8
  32. data/lib/jekyll/plugin.rb +0 -2
  33. data/lib/jekyll/post.rb +66 -40
  34. data/lib/jekyll/site.rb +96 -20
  35. data/lib/jekyll/static_file.rb +0 -2
  36. data/lib/jekyll/tags/gist.rb +19 -0
  37. data/lib/jekyll/tags/highlight.rb +63 -62
  38. data/lib/jekyll/tags/include.rb +25 -25
  39. data/lib/jekyll/tags/post_url.rb +30 -25
  40. data/script/bootstrap +2 -0
  41. data/site/.gitignore +4 -0
  42. data/site/CNAME +1 -0
  43. data/site/README +1 -0
  44. data/site/_config.yml +5 -0
  45. data/site/_includes/analytics.html +32 -0
  46. data/site/_includes/docs_contents.html +82 -0
  47. data/site/_includes/footer.html +15 -0
  48. data/site/_includes/header.html +26 -0
  49. data/site/_includes/section_nav.html +22 -0
  50. data/site/_includes/top.html +14 -0
  51. data/site/_layouts/default.html +12 -0
  52. data/site/_layouts/docs.html +21 -0
  53. data/site/_posts/2012-07-01-configuration.md +277 -0
  54. data/site/_posts/2012-07-01-contributing.md +66 -0
  55. data/site/_posts/2012-07-01-deployment-methods.md +108 -0
  56. data/site/_posts/2012-07-01-extras.md +103 -0
  57. data/site/_posts/2012-07-01-frontmatter.md +120 -0
  58. data/site/_posts/2012-07-01-github-pages.md +34 -0
  59. data/site/_posts/2012-07-01-heroku.md +8 -0
  60. data/site/_posts/2012-07-01-home.md +47 -0
  61. data/site/_posts/2012-07-01-installation.md +43 -0
  62. data/site/_posts/2012-07-01-migrations.md +180 -0
  63. data/site/_posts/2012-07-01-pages.md +62 -0
  64. data/site/_posts/2012-07-01-pagination.md +116 -0
  65. data/site/_posts/2012-07-01-permalinks.md +163 -0
  66. data/site/_posts/2012-07-01-plugins.md +384 -0
  67. data/site/_posts/2012-07-01-posts.md +106 -0
  68. data/site/_posts/2012-07-01-resources.md +49 -0
  69. data/site/_posts/2012-07-01-sites.md +28 -0
  70. data/site/_posts/2012-07-01-structure.md +95 -0
  71. data/site/_posts/2012-07-01-templates.md +217 -0
  72. data/site/_posts/2012-07-01-troubleshooting.md +108 -0
  73. data/site/_posts/2012-07-01-usage.md +38 -0
  74. data/site/_posts/2012-07-01-variables.md +166 -0
  75. data/site/css/grid.css +62 -0
  76. data/site/css/normalize.css +504 -0
  77. data/site/css/pygments.css +70 -0
  78. data/site/css/style.css +697 -0
  79. data/site/docs/index.html +11 -0
  80. data/site/favicon.png +0 -0
  81. data/site/img/article-footer.png +0 -0
  82. data/site/img/footer-arrow.png +0 -0
  83. data/site/img/footer-logo.png +0 -0
  84. data/site/img/logo-2x.png +0 -0
  85. data/site/img/octojekyll.png +0 -0
  86. data/site/img/tube.png +0 -0
  87. data/site/img/tube1x.png +0 -0
  88. data/site/index.html +77 -0
  89. data/site/js/modernizr-2.5.3.min.js +4 -0
  90. data/test/fixtures/broken_front_matter2.erb +4 -0
  91. data/test/fixtures/broken_front_matter3.erb +7 -0
  92. data/test/fixtures/exploit_front_matter.erb +4 -0
  93. data/test/helper.rb +16 -0
  94. data/test/source/_posts/2013-01-12-nil-layout.textile +6 -0
  95. data/test/source/_posts/2013-01-12-no-layout.textile +5 -0
  96. data/test/source/contacts/bar.html +5 -0
  97. data/test/source/contacts/index.html +5 -0
  98. data/test/test_configuration.rb +7 -8
  99. data/test/test_convertible.rb +29 -0
  100. data/test/test_core_ext.rb +22 -0
  101. data/test/test_generated_site.rb +1 -1
  102. data/test/test_kramdown.rb +3 -3
  103. data/test/test_page.rb +88 -2
  104. data/test/test_post.rb +42 -6
  105. data/test/test_rdiscount.rb +1 -1
  106. data/test/test_redcarpet.rb +1 -1
  107. data/test/test_redcloth.rb +6 -6
  108. data/test/test_site.rb +73 -8
  109. data/test/test_tags.rb +36 -13
  110. metadata +150 -19
  111. data/lib/jekyll/migrators/csv.rb +0 -26
  112. data/lib/jekyll/migrators/drupal.rb +0 -103
  113. data/lib/jekyll/migrators/enki.rb +0 -49
  114. data/lib/jekyll/migrators/joomla.rb +0 -53
  115. data/lib/jekyll/migrators/marley.rb +0 -52
  116. data/lib/jekyll/migrators/mephisto.rb +0 -84
  117. data/lib/jekyll/migrators/mt.rb +0 -86
  118. data/lib/jekyll/migrators/posterous.rb +0 -67
  119. data/lib/jekyll/migrators/rss.rb +0 -47
  120. data/lib/jekyll/migrators/textpattern.rb +0 -58
  121. data/lib/jekyll/migrators/tumblr.rb +0 -195
  122. data/lib/jekyll/migrators/typo.rb +0 -51
  123. data/lib/jekyll/migrators/wordpress.rb +0 -294
  124. data/lib/jekyll/migrators/wordpressdotcom.rb +0 -70
@@ -1,5 +1,4 @@
1
1
  module Jekyll
2
-
3
2
  class Page
4
3
  include Convertible
5
4
 
@@ -24,6 +23,18 @@ module Jekyll
24
23
  self.read_yaml(File.join(base, dir), name)
25
24
  end
26
25
 
26
+ # Read the YAML frontmatter.
27
+ #
28
+ # base - The String path to the dir containing the file.
29
+ # name - The String filename of the file.
30
+ #
31
+ # Returns nothing.
32
+ def read_yaml(base, name)
33
+ super(base, name)
34
+ self.data['layout'] = 'page' unless self.data.has_key?('layout')
35
+ self.data
36
+ end
37
+
27
38
  # The generated directory into which the page will be placed
28
39
  # upon generation. This is derived from the permalink or, if
29
40
  # permalink is absent, we be '/'
@@ -45,10 +56,16 @@ module Jekyll
45
56
  #
46
57
  # Returns the template String.
47
58
  def template
48
- if self.site.permalink_style == :pretty && !index? && html?
49
- "/:basename/"
59
+ if self.site.permalink_style == :pretty
60
+ if index? && html?
61
+ "/:path/"
62
+ elsif html?
63
+ "/:path/:basename/"
64
+ else
65
+ "/:path/:basename:output_ext"
66
+ end
50
67
  else
51
- "/:basename:output_ext"
68
+ "/:path/:basename:output_ext"
52
69
  end
53
70
  end
54
71
 
@@ -62,6 +79,7 @@ module Jekyll
62
79
  permalink
63
80
  else
64
81
  {
82
+ "path" => @dir,
65
83
  "basename" => self.basename,
66
84
  "output_ext" => self.output_ext,
67
85
  }.inject(template) { |result, token|
@@ -105,7 +123,7 @@ module Jekyll
105
123
  # Returns the Hash representation of this Page.
106
124
  def to_liquid
107
125
  self.data.deep_merge({
108
- "url" => File.join(@dir, self.url),
126
+ "url" => self.url,
109
127
  "content" => self.content })
110
128
  end
111
129
 
@@ -117,7 +135,7 @@ module Jekyll
117
135
  def destination(dest)
118
136
  # The url needs to be unescaped in order to preserve the correct
119
137
  # filename.
120
- path = File.join(dest, @dir, CGI.unescape(self.url))
138
+ path = File.join(dest, CGI.unescape(self.url))
121
139
  path = File.join(path, "index.html") if self.url =~ /\/$/
122
140
  path
123
141
  end
@@ -149,7 +167,5 @@ module Jekyll
149
167
  def index?
150
168
  basename == 'index'
151
169
  end
152
-
153
170
  end
154
-
155
171
  end
@@ -1,5 +1,4 @@
1
1
  module Jekyll
2
-
3
2
  class Plugin
4
3
  PRIORITIES = { :lowest => -100,
5
4
  :low => -10,
@@ -73,5 +72,4 @@ module Jekyll
73
72
  # no-op for default
74
73
  end
75
74
  end
76
-
77
75
  end
@@ -1,5 +1,4 @@
1
1
  module Jekyll
2
-
3
2
  class Post
4
3
  include Comparable
5
4
  include Convertible
@@ -8,12 +7,13 @@ module Jekyll
8
7
  attr_accessor :lsi
9
8
  end
10
9
 
10
+ # Valid post name regex.
11
11
  MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
12
12
 
13
13
  # Post name validator. Post filenames must be like:
14
- # 2008-11-05-my-awesome-post.textile
14
+ # 2008-11-05-my-awesome-post.textile
15
15
  #
16
- # Returns <Bool>
16
+ # Returns true if valid, false if not.
17
17
  def self.valid?(name)
18
18
  name =~ MATCHER
19
19
  end
@@ -25,15 +25,15 @@ module Jekyll
25
25
  attr_reader :name
26
26
 
27
27
  # Initialize this Post instance.
28
- # +site+ is the Site
29
- # +base+ is the String path to the dir containing the post file
30
- # +name+ is the String filename of the post file
31
- # +categories+ is an Array of Strings for the categories for this post
32
28
  #
33
- # Returns <Post>
29
+ # site - The Site.
30
+ # base - The String path to the dir containing the post file.
31
+ # name - The String filename of the post file.
32
+ #
33
+ # Returns the new Post.
34
34
  def initialize(site, source, dir, name)
35
35
  @site = site
36
- @base = File.join(source, dir, '_posts')
36
+ @base = self.containing_dir(source, dir)
37
37
  @name = name
38
38
 
39
39
  self.categories = dir.split('/').reject { |x| x.empty? }
@@ -41,11 +41,11 @@ module Jekyll
41
41
  begin
42
42
  self.read_yaml(@base, name)
43
43
  rescue Exception => msg
44
- raise FatalException.new("#{msg} in #{@base}/#{name}")
44
+ raise FatalException.new("#{msg} in #{@base}/#{name}")
45
45
  end
46
46
 
47
- #If we've added a date and time to the yaml, use that instead of the filename date
48
- #Means we'll sort correctly.
47
+ # If we've added a date and time to the YAML, use that instead of the
48
+ # filename date. Means we'll sort correctly.
49
49
  if self.data.has_key?('date')
50
50
  # ensure Time via to_s and reparse
51
51
  self.date = Time.parse(self.data["date"].to_s)
@@ -64,7 +64,27 @@ module Jekyll
64
64
  end
65
65
  end
66
66
 
67
- # Spaceship is based on Post#date, slug
67
+ # Get the full path to the directory containing the post files
68
+ def containing_dir(source, dir)
69
+ return File.join(source, dir, '_posts')
70
+ end
71
+
72
+ # Read the YAML frontmatter.
73
+ #
74
+ # base - The String path to the dir containing the file.
75
+ # name - The String filename of the file.
76
+ #
77
+ # Returns nothing.
78
+ def read_yaml(base, name)
79
+ super(base, name)
80
+ self.data['layout'] = 'post' unless self.data.has_key?('layout')
81
+ self.data
82
+ end
83
+
84
+ # Compares Post objects. First compares the Post date. If the dates are
85
+ # equal, it compares the Post slugs.
86
+ #
87
+ # other - The other Post we are comparing to.
68
88
  #
69
89
  # Returns -1, 0, 1
70
90
  def <=>(other)
@@ -75,10 +95,11 @@ module Jekyll
75
95
  return cmp
76
96
  end
77
97
 
78
- # Extract information from the post filename
79
- # +name+ is the String filename of the post file
98
+ # Extract information from the post filename.
80
99
  #
81
- # Returns nothing
100
+ # name - The String filename of the post file.
101
+ #
102
+ # Returns nothing.
82
103
  def process(name)
83
104
  m, cats, date, slug, ext = *name.match(MATCHER)
84
105
  self.date = Time.parse(date)
@@ -91,18 +112,17 @@ module Jekyll
91
112
  # The generated directory into which the post will be placed
92
113
  # upon generation. This is derived from the permalink or, if
93
114
  # permalink is absent, set to the default date
94
- # e.g. "/2008/11/05/" if the permalink style is :date, otherwise nothing
115
+ # e.g. "/2008/11/05/" if the permalink style is :date, otherwise nothing.
95
116
  #
96
- # Returns <String>
117
+ # Returns the String directory.
97
118
  def dir
98
119
  File.dirname(url)
99
120
  end
100
121
 
101
- # The full path and filename of the post.
102
- # Defined in the YAML of the post body
103
- # (Optional)
122
+ # The full path and filename of the post. Defined in the YAML of the post
123
+ # body (optional).
104
124
  #
105
- # Returns <String>
125
+ # Returns the String permalink.
106
126
  def permalink
107
127
  self.data && self.data['permalink']
108
128
  end
@@ -120,10 +140,10 @@ module Jekyll
120
140
  end
121
141
  end
122
142
 
123
- # The generated relative url of this post
143
+ # The generated relative url of this post.
124
144
  # e.g. /2008/11/05/my-awesome-post.html
125
145
  #
126
- # Returns <String>
146
+ # Returns the String URL.
127
147
  def url
128
148
  return @url if @url
129
149
 
@@ -137,7 +157,7 @@ module Jekyll
137
157
  "title" => CGI.escape(slug),
138
158
  "i_day" => date.strftime("%d").to_i.to_s,
139
159
  "i_month" => date.strftime("%m").to_i.to_s,
140
- "categories" => categories.map { |c| URI.escape(c) }.join('/'),
160
+ "categories" => categories.map { |c| URI.escape(c.to_s) }.join('/'),
141
161
  "output_ext" => self.output_ext
142
162
  }.inject(template) { |result, token|
143
163
  result.gsub(/:#{Regexp.escape token.first}/, token.last)
@@ -150,25 +170,28 @@ module Jekyll
150
170
  @url
151
171
  end
152
172
 
153
- # The UID for this post (useful in feeds)
173
+ # The UID for this post (useful in feeds).
154
174
  # e.g. /2008/11/05/my-awesome-post
155
175
  #
156
- # Returns <String>
176
+ # Returns the String UID.
157
177
  def id
158
178
  File.join(self.dir, self.slug)
159
179
  end
160
180
 
161
181
  # Calculate related posts.
162
182
  #
163
- # Returns [<Post>]
183
+ # Returns an Array of related Posts.
164
184
  def related_posts(posts)
165
185
  return [] unless posts.size > 1
166
186
 
167
187
  if self.site.lsi
168
188
  self.class.lsi ||= begin
169
- puts "Running the classifier... this could take a while."
170
- lsi = Classifier::LSI.new
189
+ puts "Starting the classifier..."
190
+ lsi = Classifier::LSI.new(:auto_rebuild => false)
191
+ $stdout.print(" Populating LSI... ");$stdout.flush
171
192
  posts.each { |x| $stdout.print(".");$stdout.flush;lsi.add_item(x) }
193
+ $stdout.print("\n Rebuilding LSI index... ")
194
+ lsi.build_index
172
195
  puts ""
173
196
  lsi
174
197
  end
@@ -180,11 +203,12 @@ module Jekyll
180
203
  end
181
204
  end
182
205
 
183
- # Add any necessary layouts to this post
184
- # +layouts+ is a Hash of {"name" => "layout"}
185
- # +site_payload+ is the site payload hash
206
+ # Add any necessary layouts to this post.
207
+ #
208
+ # layouts - A Hash of {"name" => "layout"}.
209
+ # site_payload - The site payload hash.
186
210
  #
187
- # Returns nothing
211
+ # Returns nothing.
188
212
  def render(layouts, site_payload)
189
213
  # construct payload
190
214
  payload = {
@@ -196,9 +220,10 @@ module Jekyll
196
220
  end
197
221
 
198
222
  # Obtain destination path.
199
- # +dest+ is the String path to the destination dir
200
223
  #
201
- # Returns destination file path.
224
+ # dest - The String path to the destination dir.
225
+ #
226
+ # Returns destination file path String.
202
227
  def destination(dest)
203
228
  # The url needs to be unescaped in order to preserve the correct filename
204
229
  path = File.join(dest, CGI.unescape(self.url))
@@ -207,9 +232,10 @@ module Jekyll
207
232
  end
208
233
 
209
234
  # Write the generated post file to the destination directory.
210
- # +dest+ is the String path to the destination dir
211
235
  #
212
- # Returns nothing
236
+ # dest - The String path to the destination dir.
237
+ #
238
+ # Returns nothing.
213
239
  def write(dest)
214
240
  path = destination(dest)
215
241
  FileUtils.mkdir_p(File.dirname(path))
@@ -220,7 +246,7 @@ module Jekyll
220
246
 
221
247
  # Convert this post into a Hash for use in Liquid templates.
222
248
  #
223
- # Returns <Hash>
249
+ # Returns the representative Hash.
224
250
  def to_liquid
225
251
  self.data.deep_merge({
226
252
  "title" => self.data["title"] || self.slug.split('-').select {|w| w.capitalize! || w }.join(' '),
@@ -234,6 +260,7 @@ module Jekyll
234
260
  "content" => self.content })
235
261
  end
236
262
 
263
+ # Returns the shorthand String identifier of this Post.
237
264
  def inspect
238
265
  "<Post: #{self.id}>"
239
266
  end
@@ -257,5 +284,4 @@ module Jekyll
257
284
  end
258
285
  end
259
286
  end
260
-
261
287
  end
@@ -1,11 +1,11 @@
1
1
  require 'set'
2
2
 
3
3
  module Jekyll
4
-
5
4
  class Site
6
5
  attr_accessor :config, :layouts, :posts, :pages, :static_files,
7
6
  :categories, :exclude, :include, :source, :dest, :lsi, :pygments,
8
- :permalink_style, :tags, :time, :future, :safe, :plugins, :limit_posts
7
+ :permalink_style, :tags, :time, :future, :safe, :plugins, :limit_posts,
8
+ :show_drafts, :keep_files
9
9
 
10
10
  attr_accessor :converters, :generators
11
11
 
@@ -18,14 +18,16 @@ module Jekyll
18
18
  self.safe = config['safe']
19
19
  self.source = File.expand_path(config['source'])
20
20
  self.dest = File.expand_path(config['destination'])
21
- self.plugins = Array(config['plugins']).map { |d| File.expand_path(d) }
21
+ self.plugins = plugins_path
22
22
  self.lsi = config['lsi']
23
23
  self.pygments = config['pygments']
24
24
  self.permalink_style = config['permalink'].to_sym
25
25
  self.exclude = config['exclude'] || []
26
26
  self.include = config['include'] || []
27
27
  self.future = config['future']
28
+ self.show_drafts = config['show_drafts'] || nil
28
29
  self.limit_posts = config['limit_posts'] || nil
30
+ self.keep_files = config['keep_files'] || []
29
31
 
30
32
  self.reset
31
33
  self.setup
@@ -70,6 +72,12 @@ module Jekyll
70
72
  def setup
71
73
  require 'classifier' if self.lsi
72
74
 
75
+ # Check that the destination dir isn't the source dir or a directory
76
+ # parent to the source dir.
77
+ if self.source =~ /^#{self.dest}/
78
+ raise FatalException.new "Destination directory cannot be or contain the Source directory."
79
+ end
80
+
73
81
  # If safe mode is off, load in any Ruby files under the plugins
74
82
  # directory.
75
83
  unless self.safe
@@ -93,6 +101,17 @@ module Jekyll
93
101
  end
94
102
  end
95
103
 
104
+ # Internal: Setup the plugin search path
105
+ #
106
+ # Returns an Array of plugin search paths
107
+ def plugins_path
108
+ if (config['plugins'] == Jekyll::DEFAULTS['plugins'])
109
+ [File.join(self.source, config['plugins'])]
110
+ else
111
+ Array(config['plugins']).map { |d| File.expand_path(d) }
112
+ end
113
+ end
114
+
96
115
  # Read Site data from disk and load it into internal data structures.
97
116
  #
98
117
  # Returns nothing.
@@ -130,6 +149,18 @@ module Jekyll
130
149
 
131
150
  self.read_posts(dir)
132
151
 
152
+ if self.show_drafts
153
+ self.read_drafts(dir)
154
+ end
155
+
156
+ self.posts.sort!
157
+
158
+ # limit the posts if :limit_posts option is set
159
+ if limit_posts
160
+ limit = self.posts.length < limit_posts ? self.posts.length : limit_posts
161
+ self.posts = self.posts[-limit, limit]
162
+ end
163
+
133
164
  entries.each do |f|
134
165
  f_abs = File.join(base, f)
135
166
  f_rel = File.join(dir, f)
@@ -156,9 +187,7 @@ module Jekyll
156
187
  #
157
188
  # Returns nothing.
158
189
  def read_posts(dir)
159
- base = File.join(self.source, dir, '_posts')
160
- return unless File.exists?(base)
161
- entries = Dir.chdir(base) { filter_entries(Dir['**/*']) }
190
+ entries = get_entries(dir, '_posts')
162
191
 
163
192
  # first pass processes, but does not yet render post content
164
193
  entries.each do |f|
@@ -166,19 +195,28 @@ module Jekyll
166
195
  post = Post.new(self, self.source, dir, f)
167
196
 
168
197
  if post.published && (self.future || post.date <= self.time)
169
- self.posts << post
170
- post.categories.each { |c| self.categories[c] << post }
171
- post.tags.each { |c| self.tags[c] << post }
198
+ aggregate_post_info(post)
172
199
  end
173
200
  end
174
201
  end
202
+ end
175
203
 
176
- self.posts.sort!
204
+ # Read all the files in <source>/<dir>/_drafts and create a new Post
205
+ # object with each one.
206
+ #
207
+ # dir - The String relative path of the directory to read.
208
+ #
209
+ # Returns nothing.
210
+ def read_drafts(dir)
211
+ entries = get_entries(dir, '_drafts')
177
212
 
178
- # limit the posts if :limit_posts option is set
179
- if limit_posts
180
- limit = self.posts.length < limit_posts ? self.posts.length : limit_posts
181
- self.posts = self.posts[-limit, limit]
213
+ # first pass processes, but does not yet render draft content
214
+ entries.each do |f|
215
+ if Draft.valid?(f)
216
+ draft = Draft.new(self, self.source, dir, f)
217
+
218
+ aggregate_post_info(draft)
219
+ end
182
220
  end
183
221
  end
184
222
 
@@ -217,7 +255,11 @@ module Jekyll
217
255
  # all files and directories in destination, including hidden ones
218
256
  dest_files = Set.new
219
257
  Dir.glob(File.join(self.dest, "**", "*"), File::FNM_DOTMATCH) do |file|
220
- dest_files << file unless file =~ /\/\.{1,2}$/
258
+ if self.keep_files.length > 0
259
+ dest_files << file unless file =~ /\/\.{1,2}$/ || file =~ keep_file_regex
260
+ else
261
+ dest_files << file unless file =~ /\/\.{1,2}$/
262
+ end
221
263
  end
222
264
 
223
265
  # files to be written
@@ -238,10 +280,21 @@ module Jekyll
238
280
  files.merge(dirs)
239
281
 
240
282
  obsolete_files = dest_files - files
241
-
242
283
  FileUtils.rm_rf(obsolete_files.to_a)
243
284
  end
244
285
 
286
+ # Private: creates a regular expression from the keep_files array
287
+ #
288
+ # Examples
289
+ # ['.git','.svn'] creates the following regex: /\/(\.git|\/.svn)/
290
+ #
291
+ # Returns the regular expression
292
+ def keep_file_regex
293
+ or_list = self.keep_files.join("|")
294
+ pattern = "\/(#{or_list.gsub(".", "\.")})"
295
+ Regexp.new pattern
296
+ end
297
+
245
298
  # Write static files, pages, and posts.
246
299
  #
247
300
  # Returns nothing.
@@ -311,12 +364,12 @@ module Jekyll
311
364
  #
312
365
  # Returns the Array of filtered entries.
313
366
  def filter_entries(entries)
314
- entries = entries.reject do |e|
315
- unless self.include.include?(e)
367
+ entries.reject do |e|
368
+ unless self.include.glob_include?(e)
316
369
  ['.', '_', '#'].include?(e[0..0]) ||
317
370
  e[-1..-1] == '~' ||
318
- self.exclude.include?(e) ||
319
- File.symlink?(e)
371
+ self.exclude.glob_include?(e) ||
372
+ (File.symlink?(e) && self.safe)
320
373
  end
321
374
  end
322
375
  end
@@ -334,5 +387,28 @@ module Jekyll
334
387
  raise "Converter implementation not found for #{klass}"
335
388
  end
336
389
  end
390
+
391
+ # Read the entries from a particular directory for processing
392
+ #
393
+ # dir - The String relative path of the directory to read
394
+ # subfolder - The String directory to read
395
+ #
396
+ # Returns the list of entries to process
397
+ def get_entries(dir, subfolder)
398
+ base = File.join(self.source, dir, subfolder)
399
+ return [] unless File.exists?(base)
400
+ entries = Dir.chdir(base) { filter_entries(Dir['**/*']) }
401
+ end
402
+
403
+ # Aggregate post information
404
+ #
405
+ # post - The Post object to aggregate information for
406
+ #
407
+ # Returns nothing
408
+ def aggregate_post_info(post)
409
+ self.posts << post
410
+ post.categories.each { |c| self.categories[c] << post }
411
+ post.tags.each { |c| self.tags[c] << post }
412
+ end
337
413
  end
338
414
  end