jberkel-jekyll 0.5.4 → 0.5.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -26,6 +26,7 @@ require 'jekyll/filters'
26
26
  require 'jekyll/tags/highlight'
27
27
  require 'jekyll/tags/include'
28
28
  require 'jekyll/albino'
29
+ require 'jekyll/static_file'
29
30
 
30
31
  module Jekyll
31
32
  # Default options. Overriden by values in _config.yml or command-line opts.
@@ -68,10 +69,10 @@ module Jekyll
68
69
  begin
69
70
  config = YAML.load_file(config_file)
70
71
  raise "Invalid configuration - #{config_file}" if !config.is_a?(Hash)
71
- STDOUT.puts "Configuration from #{config_file}"
72
+ $stdout.puts "Configuration from #{config_file}"
72
73
  rescue => err
73
- STDERR.puts "WARNING: Could not read configuration. Using defaults (and options)."
74
- STDERR.puts "\t" + err.to_s
74
+ $stderr.puts "WARNING: Could not read configuration. Using defaults (and options)."
75
+ $stderr.puts "\t" + err.to_s
75
76
  config = {}
76
77
  end
77
78
 
@@ -3,6 +3,10 @@
3
3
  #
4
4
  # Requires
5
5
  # self.site -> Jekyll::Site
6
+ # self.content=
7
+ # self.data=
8
+ # self.ext=
9
+ # self.output=
6
10
  module Jekyll
7
11
  module Convertible
8
12
  # Return the contents as a string
@@ -20,7 +24,7 @@ module Jekyll
20
24
 
21
25
  self.data ||= {}
22
26
 
23
- if self.content =~ /^(---\s*\n.*?\n?)(---.*?\n)/m
27
+ if self.content =~ /^(---\s*\n.*?\n?)^(---\s*$\n?)/m
24
28
  self.content = self.content[($1.size + $2.size)..-1]
25
29
  self.data = self.data.deep_merge(YAML.load($1) || {})
26
30
  end
@@ -52,7 +56,7 @@ module Jekyll
52
56
  case self.ext[1..-1]
53
57
  when /textile/i
54
58
  return 'textile'
55
- when /markdown/i, /mkdn/i, /md/i
59
+ when /markdown/i, /mkdn/i, /md/i, /mkd/i
56
60
  return 'markdown'
57
61
  when /haml/i
58
62
  return 'haml'
@@ -19,6 +19,28 @@ class Hash
19
19
 
20
20
  target
21
21
  end
22
+
23
+ # Read array from the supplied hash favouring the singular key
24
+ # and then the plural key, and handling any nil entries.
25
+ # +hash+ the hash to read from
26
+ # +singular_key+ the singular key
27
+ # +plural_key+ the singular key
28
+ #
29
+ # Returns an array
30
+ def pluralized_array(singular_key, plural_key)
31
+ hash = self
32
+ if hash.has_key?(singular_key)
33
+ array = [hash[singular_key]] if hash[singular_key]
34
+ elsif hash.has_key?(plural_key)
35
+ case hash[plural_key]
36
+ when String
37
+ array = hash[plural_key].split
38
+ when Array
39
+ array = hash[plural_key].compact
40
+ end
41
+ end
42
+ array || []
43
+ end
22
44
  end
23
45
 
24
46
 
@@ -4,7 +4,7 @@ module Jekyll
4
4
  include Convertible
5
5
 
6
6
  attr_accessor :site
7
- attr_accessor :name, :ext, :basename
7
+ attr_accessor :name, :ext, :basename, :dir
8
8
  attr_accessor :data, :content, :output
9
9
 
10
10
  # Initialize a new Page.
@@ -101,6 +101,10 @@ module Jekyll
101
101
  end
102
102
  end
103
103
 
104
+ def inspect
105
+ "#<Jekyll:Page @name=#{self.name.inspect}>"
106
+ end
107
+
104
108
  private
105
109
 
106
110
  def index?
@@ -4,7 +4,7 @@ module Jekyll
4
4
 
5
5
  def self.calculate_pages(all_posts, per_page)
6
6
  num_pages = all_posts.size / per_page.to_i
7
- num_pages.abs + 1 if all_posts.size % per_page.to_i != 0
7
+ num_pages = num_pages + 1 if all_posts.size % per_page.to_i != 0
8
8
  num_pages
9
9
  end
10
10
 
@@ -20,12 +20,9 @@ module Jekyll
20
20
  name =~ MATCHER
21
21
  end
22
22
 
23
- attr_accessor :site, :date, :slug, :ext, :published, :data, :content, :output, :tags
24
- attr_writer :categories
25
-
26
- def categories
27
- @categories ||= []
28
- end
23
+ attr_accessor :site
24
+ attr_accessor :data, :content, :output, :ext
25
+ attr_accessor :date, :slug, :published, :tags, :categories
29
26
 
30
27
  # Initialize this Post instance.
31
28
  # +site+ is the Site
@@ -46,6 +43,13 @@ module Jekyll
46
43
  self.read_yaml(@base, name)
47
44
 
48
45
  extract_title_from_first_header_or_slug
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.
49
+ if self.data.has_key?('date')
50
+ # ensure Time via to_s and reparse
51
+ self.date = Time.parse(self.data["date"].to_s)
52
+ end
49
53
 
50
54
  if self.data.has_key?('published') && self.data['published'] == false
51
55
  self.published = false
@@ -53,26 +57,10 @@ module Jekyll
53
57
  self.published = true
54
58
  end
55
59
 
56
- if self.data.has_key?("tag")
57
- self.tags = [self.data["tag"]]
58
- elsif self.data.has_key?("tags")
59
- self.tags = self.data['tags']
60
- else
61
- self.tags = []
62
- end
60
+ self.tags = self.data.pluralized_array("tag", "tags")
63
61
 
64
62
  if self.categories.empty?
65
- if self.data.has_key?('category')
66
- self.categories << self.data['category']
67
- elsif self.data.has_key?('categories')
68
- # Look for categories in the YAML-header, either specified as
69
- # an array or a string.
70
- if self.data['categories'].kind_of? String
71
- self.categories = self.data['categories'].split
72
- else
73
- self.categories = self.data['categories']
74
- end
75
- end
63
+ self.categories = self.data.pluralized_array('category', 'categories')
76
64
  end
77
65
  end
78
66
 
@@ -143,7 +131,7 @@ module Jekyll
143
131
  "month" => date.strftime("%m"),
144
132
  "day" => date.strftime("%d"),
145
133
  "title" => CGI.escape(slug),
146
- "categories" => categories.sort.join('/')
134
+ "categories" => categories.join('/')
147
135
  }.inject(template) { |result, token|
148
136
  result.gsub(/:#{token.first}/, token.last)
149
137
  }.gsub("//", "/")
@@ -1,9 +1,9 @@
1
1
  module Jekyll
2
2
 
3
3
  class Site
4
- attr_accessor :config, :layouts, :posts, :categories, :exclude,
4
+ attr_accessor :config, :layouts, :posts, :pages, :static_files, :categories, :exclude,
5
5
  :source, :dest, :lsi, :pygments, :permalink_style, :tags
6
-
6
+
7
7
  attr_accessor :sass, :post_defaults, :collated_posts
8
8
 
9
9
 
@@ -14,7 +14,7 @@ module Jekyll
14
14
  def initialize(config)
15
15
  self.config = config.clone
16
16
 
17
- self.source = config['source']
17
+ self.source = File.expand_path(config['source'])
18
18
  self.dest = config['destination']
19
19
  self.lsi = config['lsi']
20
20
  self.pygments = config['pygments']
@@ -30,6 +30,8 @@ module Jekyll
30
30
  self.layouts = {}
31
31
  self.posts = []
32
32
  self.collated_posts = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = Hash.new {|h,k| h[k] = [] } } }
33
+ self.pages = []
34
+ self.static_files = []
33
35
  self.categories = Hash.new { |hash, key| hash[key] = [] }
34
36
  self.tags = Hash.new { |hash, key| hash[key] = [] }
35
37
  end
@@ -37,7 +39,7 @@ module Jekyll
37
39
  def setup
38
40
  # Check to see if LSI is enabled.
39
41
  require 'classifier' if self.lsi
40
-
42
+
41
43
  if self.config['sass']
42
44
  begin
43
45
  require 'sass'
@@ -47,7 +49,7 @@ module Jekyll
47
49
  puts 'You must have the haml gem installed first'
48
50
  end
49
51
  end
50
-
52
+
51
53
  if self.config['haml']
52
54
  begin
53
55
  require 'haml'
@@ -55,11 +57,11 @@ module Jekyll
55
57
  helpers = File.join(source, '_helpers.rb')
56
58
  require helpers if File.exist?(helpers)
57
59
  puts 'Enabled Haml'
58
- rescue LoadError => e
59
- raise "You must have the haml gem installed first: #{e}"
60
+ rescue LoadError
61
+ puts 'You must have the haml gem installed first'
60
62
  end
61
63
  end
62
-
64
+
63
65
  # Set the Markdown interpreter (and Maruku self.config, if necessary)
64
66
  case self.config['markdown']
65
67
  when 'rdiscount'
@@ -114,22 +116,30 @@ module Jekyll
114
116
  end
115
117
 
116
118
  # Do the actual work of processing the site and generating the
117
- # real deal.
119
+ # real deal. Now has 4 phases; reset, read, render, write. This allows
120
+ # rendering to have full site payload available.
118
121
  #
119
122
  # Returns nothing
120
123
  def process
121
124
  self.reset
122
- self.read_layouts
123
- self.transform_pages
125
+ self.read
126
+ self.render
127
+ self.write
124
128
  self.transform_sass if self.sass
125
- self.write_posts
126
129
  end
127
130
 
128
- # Read all the files in <source>/_layouts into memory for later use.
131
+ def read
132
+ self.read_layouts # existing implementation did this at top level only so preserved that
133
+ self.read_directories
134
+ end
135
+
136
+ # Read all the files in <source>/<dir>/_layouts and create a new Layout
137
+ # object with each one.
129
138
  #
130
139
  # Returns nothing
131
- def read_layouts
132
- base = File.join(self.source, "_layouts")
140
+ def read_layouts(dir = '')
141
+ base = File.join(self.source, dir, "_layouts")
142
+ return unless File.exists?(base)
133
143
  entries = []
134
144
  Dir.chdir(base) { entries = filter_entries(Dir['*.*']) }
135
145
 
@@ -137,17 +147,16 @@ module Jekyll
137
147
  name = f.split(".")[0..-2].join(".")
138
148
  self.layouts[name] = Layout.new(self, base, f)
139
149
  end
140
- rescue Errno::ENOENT => e
141
- # ignore missing layout dir
142
150
  end
143
151
 
144
- # Read all the files in <base>/_posts and create a new Post object with each one.
152
+ # Read all the files in <source>/<dir>/_posts and create a new Post
153
+ # object with each one.
145
154
  #
146
155
  # Returns nothing
147
156
  def read_posts(dir)
148
157
  base = File.join(self.source, dir, '_posts')
149
- entries = []
150
- Dir.chdir(base) { entries = filter_entries(Dir['**/*']) }
158
+ return unless File.exists?(base)
159
+ entries = Dir.chdir(base) { filter_entries(Dir['**/*']) }
151
160
 
152
161
  # first pass processes, but does not yet render post content
153
162
  entries.each do |f|
@@ -163,69 +172,69 @@ module Jekyll
163
172
  end
164
173
 
165
174
  self.posts.sort!
175
+ end
166
176
 
167
- # second pass renders each post now that full site payload is available
177
+ def render
168
178
  self.posts.each do |post|
169
179
  post.render(self.layouts, site_payload)
170
180
  self.collated_posts[post.date.year][post.date.month][post.date.day].unshift(post)
171
181
  end
172
182
 
183
+ self.pages.dup.each do |page|
184
+ if Pager.pagination_enabled?(self.config, page.name)
185
+ paginate(page)
186
+ else
187
+ page.render(self.layouts, site_payload)
188
+ end
189
+ end
190
+
173
191
  self.categories.values.map { |ps| ps.sort! { |a, b| b <=> a} }
174
192
  self.tags.values.map { |ps| ps.sort! { |a, b| b <=> a} }
175
193
  rescue Errno::ENOENT => e
176
194
  # ignore missing layout dir
177
195
  end
178
196
 
179
- # Write each post to <dest>/<year>/<month>/<day>/<slug>
197
+ # Write static files, pages and posts
180
198
  #
181
199
  # Returns nothing
182
- def write_posts
200
+ def write
183
201
  self.posts.each do |post|
184
202
  post.write(self.dest)
185
203
  end
204
+ self.pages.each do |page|
205
+ page.write(self.dest)
206
+ end
207
+ self.static_files.each do |sf|
208
+ sf.write(self.dest)
209
+ end
186
210
  end
187
211
 
188
- # Copy all regular files from <source> to <dest>/ ignoring
189
- # any files/directories that are hidden or backup files (start
190
- # with "." or "#" or end with "~") or contain site content (start with "_")
191
- # unless they are "_posts" directories or web server files such as
192
- # '.htaccess'
212
+ # Reads the directories and finds posts, pages and static files that will
213
+ # become part of the valid site according to the rules in +filter_entries+.
193
214
  # The +dir+ String is a relative path used to call this method
194
215
  # recursively as it descends through directories
195
216
  #
196
217
  # Returns nothing
197
- def transform_pages(dir = '')
218
+ def read_directories(dir = '')
198
219
  base = File.join(self.source, dir)
199
220
  entries = filter_entries(Dir.entries(base))
200
- directories = entries.select { |e| File.directory?(File.join(base, e)) }
201
- files = entries.reject { |e| File.directory?(File.join(base, e)) || File.symlink?(File.join(base, e)) }
202
221
 
203
- # we need to make sure to process _posts *first* otherwise they
204
- # might not be available yet to other templates as {{ site.posts }}
205
- if directories.include?('_posts')
206
- directories.delete('_posts')
207
- read_posts(dir)
208
- end
222
+ self.read_posts(dir)
209
223
 
210
- [directories, files].each do |entries|
211
- entries.each do |f|
212
- if File.directory?(File.join(base, f))
213
- next if self.dest.sub(/\/$/, '') == File.join(base, f)
214
- transform_pages(File.join(dir, f))
215
- elsif Pager.pagination_enabled?(self.config, f)
216
- paginate_posts(f, dir)
224
+ entries.each do |f|
225
+ f_abs = File.join(base, f)
226
+ f_rel = File.join(dir, f)
227
+ if File.directory?(f_abs)
228
+ next if self.dest.sub(/\/$/, '') == f_abs
229
+ read_directories(f_rel)
230
+ elsif !File.symlink?(f_abs)
231
+ first3 = File.open(f_abs) { |fd| fd.read(3) }
232
+ if first3 == "---"
233
+ # file appears to have a YAML header so process it as a page
234
+ pages << Page.new(self, self.source, dir, f)
217
235
  else
218
- first3 = File.open(File.join(self.source, dir, f)) { |fd| fd.read(3) }
219
- if first3 == "---"
220
- # file appears to have a YAML header so process it as a page
221
- page = Page.new(self, self.source, dir, f)
222
- page.render(self.layouts, site_payload)
223
- page.write(self.dest)
224
- else
225
- # otherwise copy the file without transforming it
226
- FileUtils.mkdir_p(File.join(self.dest, dir))
227
- FileUtils.cp(File.join(self.source, dir, f), File.join(self.dest, dir, f))
228
- end
236
+ # otherwise treat it as a static file
237
+ static_files << StaticFile.new(self, self.source, dir, f)
229
238
  end
230
239
  end
231
240
  end
@@ -279,19 +288,21 @@ module Jekyll
279
288
  end
280
289
 
281
290
  # Filter out any files/directories that are hidden or backup files (start
282
- # with "." or "#" or end with "~") or contain site content (start with "_")
283
- # unless they are "_posts" directories or web server files such as
284
- # '.htaccess'
291
+ # with "." or "#" or end with "~"), or contain site content (start with "_"),
292
+ # or are excluded in the site configuration, unless they are web server
293
+ # files such as '.htaccess'
285
294
  def filter_entries(entries)
286
295
  entries = entries.reject do |e|
287
- unless ['_posts', '.htaccess'].include?(e)
296
+ unless ['.htaccess'].include?(e)
288
297
  ['.', '_', '#'].include?(e[0..0]) || e[-1..-1] == '~' || self.exclude.include?(e)
289
298
  end
290
299
  end
291
300
  end
292
301
 
293
- # Paginates the blog's posts. Renders the index.html file into paginated directories, ie: page2, page3...
294
- # and adds more wite-wide data
302
+ # Paginates the blog's posts. Renders the index.html file into paginated
303
+ # directories, ie: page2/index.html, page3/index.html, etc and adds more
304
+ # site-wide data.
305
+ # +page+ is the index.html Page that requires pagination
295
306
  #
296
307
  # {"paginator" => { "page" => <Number>,
297
308
  # "per_page" => <Number>,
@@ -300,16 +311,19 @@ module Jekyll
300
311
  # "total_pages" => <Number>,
301
312
  # "previous_page" => <Number>,
302
313
  # "next_page" => <Number> }}
303
- def paginate_posts(file, dir)
304
- all_posts = self.posts.sort { |a,b| b <=> a }
314
+ def paginate(page)
315
+ all_posts = site_payload['site']['posts']
305
316
  pages = Pager.calculate_pages(all_posts, self.config['paginate'].to_i)
306
- pages += 1
307
317
  (1..pages).each do |num_page|
308
318
  pager = Pager.new(self.config, num_page, all_posts, pages)
309
- page = Page.new(self, self.source, dir, file)
310
- page.render(self.layouts, site_payload.merge({'paginator' => pager.to_hash}))
311
- suffix = "page#{num_page}" if num_page > 1
312
- page.write(self.dest, suffix)
319
+ if num_page > 1
320
+ newpage = Page.new(self, self.source, page.dir, page.name)
321
+ newpage.render(self.layouts, site_payload.merge({'paginator' => pager.to_hash}))
322
+ newpage.dir = File.join(page.dir, "page#{num_page}")
323
+ self.pages << newpage
324
+ else
325
+ page.render(self.layouts, site_payload.merge({'paginator' => pager.to_hash}))
326
+ end
313
327
  end
314
328
  end
315
329
  end