jberkel-jekyll 0.5.4 → 0.5.7

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.
@@ -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