mojombo-jekyll 0.5.1 → 0.5.2

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.
@@ -17,12 +17,14 @@ module Jekyll
17
17
  # Returns nothing
18
18
  def read_yaml(base, name)
19
19
  self.content = File.read(File.join(base, name))
20
-
21
- if self.content =~ /^(---\s*\n.*?)\n---\s*\n/m
22
- self.content = self.content[($1.size + 5)..-1]
23
-
20
+
21
+ if self.content =~ /^(---\s*\n.*?\n?)(---.*?\n)/m
22
+ self.content = self.content[($1.size + $2.size)..-1]
23
+
24
24
  self.data = YAML.load($1)
25
25
  end
26
+
27
+ self.data ||= {}
26
28
  end
27
29
 
28
30
  # Transform the contents based on the file extension.
@@ -19,4 +19,12 @@ class Hash
19
19
 
20
20
  target
21
21
  end
22
- end
22
+ end
23
+
24
+ # Thanks, ActiveSupport!
25
+ class Date
26
+ # Converts datetime to an appropriate format for use in XML
27
+ def xmlschema
28
+ strftime("%Y-%m-%dT%H:%M:%S%Z")
29
+ end if RUBY_VERSION < '1.9'
30
+ end
@@ -4,7 +4,7 @@ module Jekyll
4
4
  include Convertible
5
5
 
6
6
  attr_accessor :site
7
- attr_accessor :ext
7
+ attr_accessor :name, :ext, :basename
8
8
  attr_accessor :data, :content, :output
9
9
 
10
10
  # Initialize a new Page.
@@ -17,14 +17,47 @@ module Jekyll
17
17
  def initialize(site, base, dir, name)
18
18
  @site = site
19
19
  @base = base
20
- @dir = dir
20
+ @dir = dir
21
21
  @name = name
22
22
 
23
- self.data = {}
24
-
25
23
  self.process(name)
26
24
  self.read_yaml(File.join(base, dir), name)
27
- #self.transform
25
+ end
26
+
27
+ # The generated directory into which the page will be placed
28
+ # upon generation. This is derived from the permalink or, if
29
+ # permalink is absent, set to '/'
30
+ #
31
+ # Returns <String>
32
+ def dir
33
+ url[-1, 1] == '/' ? url : File.dirname(url)
34
+ end
35
+
36
+ # The full path and filename of the post.
37
+ # Defined in the YAML of the post body
38
+ # (Optional)
39
+ #
40
+ # Returns <String>
41
+ def permalink
42
+ self.data && self.data['permalink']
43
+ end
44
+
45
+ def template
46
+ if self.site.permalink_style == :pretty && !index?
47
+ "/:name/"
48
+ else
49
+ "/:name.html"
50
+ end
51
+ end
52
+
53
+ # The generated relative url of this page
54
+ # e.g. /about.html
55
+ #
56
+ # Returns <String>
57
+ def url
58
+ return permalink if permalink
59
+
60
+ @url ||= template.gsub(':name', basename)
28
61
  end
29
62
 
30
63
  # Extract information from the page filename
@@ -33,6 +66,7 @@ module Jekyll
33
66
  # Returns nothing
34
67
  def process(name)
35
68
  self.ext = File.extname(name)
69
+ self.basename = name.split('.')[0..-2].first
36
70
  end
37
71
 
38
72
  # Add any necessary layouts to this post
@@ -46,22 +80,33 @@ module Jekyll
46
80
  end
47
81
 
48
82
  # Write the generated page file to the destination directory.
49
- # +dest+ is the String path to the destination dir
83
+ # +dest_prefix+ is the String path to the destination dir
84
+ # +dest_suffix+ is a suffix path to the destination dir
50
85
  #
51
86
  # Returns nothing
52
- def write(dest)
53
- FileUtils.mkdir_p(File.join(dest, @dir))
87
+ def write(dest_prefix, dest_suffix = nil)
88
+ dest = File.join(dest_prefix, @dir)
89
+ dest = File.join(dest, dest_suffix) if dest_suffix
90
+ FileUtils.mkdir_p(dest)
54
91
 
55
- name = @name
56
- if self.ext != ""
57
- name = @name.split(".")[0..-2].join('.') + self.ext
92
+ # The url needs to be unescaped in order to preserve the correct filename
93
+ path = File.join(dest, CGI.unescape(self.url))
94
+ if self.url[/\.html$/].nil?
95
+ FileUtils.mkdir_p(path)
96
+ path = File.join(path, "index.html")
58
97
  end
59
98
 
60
- path = File.join(dest, @dir, name)
61
99
  File.open(path, 'w') do |f|
62
100
  f.write(self.output)
63
101
  end
64
102
  end
103
+
104
+ private
105
+
106
+ def index?
107
+ basename == 'index'
108
+ end
109
+
65
110
  end
66
111
 
67
- end
112
+ end
@@ -0,0 +1,45 @@
1
+ module Jekyll
2
+ class Pager
3
+ attr_reader :page, :per_page, :posts, :total_posts, :total_pages, :previous_page, :next_page
4
+
5
+ def self.calculate_pages(all_posts, per_page)
6
+ num_pages = all_posts.size / per_page.to_i
7
+ num_pages.abs + 1 if all_posts.size % per_page.to_i != 0
8
+ num_pages
9
+ end
10
+
11
+ def self.pagination_enabled?(config, file)
12
+ file == 'index.html' && !config['paginate'].nil?
13
+ end
14
+
15
+ def initialize(config, page, all_posts, num_pages = nil)
16
+ @page = page
17
+ @per_page = config['paginate'].to_i
18
+ @total_pages = num_pages || Pager.calculate_pages(all_posts, @per_page)
19
+
20
+ if @page > @total_pages
21
+ raise RuntimeError, "page number can't be greater than total pages: #{@page} > #{@total_pages}"
22
+ end
23
+
24
+ init = (@page - 1) * @per_page
25
+ offset = (init + @per_page - 1) >= all_posts.size ? all_posts.size : (init + @per_page - 1)
26
+
27
+ @total_posts = all_posts.size
28
+ @posts = all_posts[init..offset]
29
+ @previous_page = @page != 1 ? @page - 1 : nil
30
+ @next_page = @page != @total_pages ? @page + 1 : nil
31
+ end
32
+
33
+ def to_hash
34
+ {
35
+ 'page' => page,
36
+ 'per_page' => per_page,
37
+ 'posts' => posts,
38
+ 'total_posts' => total_posts,
39
+ 'total_pages' => total_pages,
40
+ 'previous_page' => previous_page,
41
+ 'next_page' => next_page
42
+ }
43
+ end
44
+ end
45
+ end
@@ -18,7 +18,7 @@ module Jekyll
18
18
  name =~ MATCHER
19
19
  end
20
20
 
21
- attr_accessor :site, :date, :slug, :ext, :topics, :published, :data, :content, :output
21
+ attr_accessor :site, :date, :slug, :ext, :published, :data, :content, :output, :tags
22
22
  attr_writer :categories
23
23
 
24
24
  def categories
@@ -38,10 +38,6 @@ module Jekyll
38
38
  @name = name
39
39
 
40
40
  self.categories = dir.split('/').reject { |x| x.empty? }
41
-
42
- parts = name.split('/')
43
- self.topics = parts.size > 1 ? parts[0..-2] : []
44
-
45
41
  self.process(name)
46
42
  self.read_yaml(@base, name)
47
43
 
@@ -51,6 +47,14 @@ module Jekyll
51
47
  self.published = true
52
48
  end
53
49
 
50
+ if self.data.has_key?("tag")
51
+ self.tags = [self.data["tag"]]
52
+ elsif self.data.has_key?("tags")
53
+ self.tags = self.data['tags']
54
+ else
55
+ self.tags = []
56
+ end
57
+
54
58
  if self.categories.empty?
55
59
  if self.data.has_key?('category')
56
60
  self.categories << self.data['category']
@@ -66,11 +70,15 @@ module Jekyll
66
70
  end
67
71
  end
68
72
 
69
- # Spaceship is based on Post#date
73
+ # Spaceship is based on Post#date, slug
70
74
  #
71
75
  # Returns -1, 0, 1
72
76
  def <=>(other)
73
- self.date <=> other.date
77
+ cmp = self.date <=> other.date
78
+ if 0 == cmp
79
+ cmp = self.slug <=> other.slug
80
+ end
81
+ return cmp
74
82
  end
75
83
 
76
84
  # Extract information from the post filename
@@ -106,7 +114,7 @@ module Jekyll
106
114
  def template
107
115
  case self.site.permalink_style
108
116
  when :pretty
109
- "/:categories/:year/:month/:day/:title"
117
+ "/:categories/:year/:month/:day/:title/"
110
118
  when :none
111
119
  "/:categories/:title.html"
112
120
  when :date
@@ -205,15 +213,15 @@ module Jekyll
205
213
  #
206
214
  # Returns <Hash>
207
215
  def to_liquid
208
- { "title" => self.data["title"] || self.slug.split('-').select {|w| w.capitalize! || w }.join(' '),
209
- "url" => self.url,
210
- "date" => self.date,
211
- "id" => self.id,
212
- "topics" => self.topics,
216
+ { "title" => self.data["title"] || self.slug.split('-').select {|w| w.capitalize! || w }.join(' '),
217
+ "url" => self.url,
218
+ "date" => self.date,
219
+ "id" => self.id,
213
220
  "categories" => self.categories,
214
- "next" => self.next,
215
- "previous" => self.previous,
216
- "content" => self.content }.deep_merge(self.data)
221
+ "next" => self.next,
222
+ "previous" => self.previous,
223
+ "tags" => self.tags,
224
+ "content" => self.content }.deep_merge(self.data)
217
225
  end
218
226
 
219
227
  def inspect
@@ -1,8 +1,8 @@
1
1
  module Jekyll
2
2
 
3
3
  class Site
4
- attr_accessor :config, :layouts, :posts, :categories, :exclude
5
- attr_accessor :source, :dest, :lsi, :pygments, :permalink_style
4
+ attr_accessor :config, :layouts, :posts, :categories, :exclude,
5
+ :source, :dest, :lsi, :pygments, :permalink_style, :tags
6
6
 
7
7
  # Initialize the site
8
8
  # +config+ is a Hash containing site configurations details
@@ -25,7 +25,8 @@ module Jekyll
25
25
  def reset
26
26
  self.layouts = {}
27
27
  self.posts = []
28
- self.categories = Hash.new { |hash, key| hash[key] = Array.new }
28
+ self.categories = Hash.new { |hash, key| hash[key] = [] }
29
+ self.tags = Hash.new { |hash, key| hash[key] = [] }
29
30
  end
30
31
 
31
32
  def setup
@@ -76,6 +77,8 @@ module Jekyll
76
77
  rescue LoadError
77
78
  puts "The maruku gem is required for markdown support!"
78
79
  end
80
+ else
81
+ raise "Invalid Markdown processor: '#{self.config['markdown']}' -- did you mean 'maruku' or 'rdiscount'?"
79
82
  end
80
83
  end
81
84
 
@@ -126,6 +129,7 @@ module Jekyll
126
129
  if post.published
127
130
  self.posts << post
128
131
  post.categories.each { |c| self.categories[c] << post }
132
+ post.tags.each { |c| self.tags[c] << post }
129
133
  end
130
134
  end
131
135
  end
@@ -137,7 +141,8 @@ module Jekyll
137
141
  post.render(self.layouts, site_payload)
138
142
  end
139
143
 
140
- self.categories.values.map { |cats| cats.sort! { |a, b| b <=> a} }
144
+ self.categories.values.map { |ps| ps.sort! { |a, b| b <=> a} }
145
+ self.tags.values.map { |ps| ps.sort! { |a, b| b <=> a} }
141
146
  rescue Errno::ENOENT => e
142
147
  # ignore missing layout dir
143
148
  end
@@ -172,11 +177,14 @@ module Jekyll
172
177
  directories.delete('_posts')
173
178
  read_posts(dir)
174
179
  end
180
+
175
181
  [directories, files].each do |entries|
176
182
  entries.each do |f|
177
183
  if File.directory?(File.join(base, f))
178
184
  next if self.dest.sub(/\/$/, '') == File.join(base, f)
179
185
  transform_pages(File.join(dir, f))
186
+ elsif Pager.pagination_enabled?(self.config, f)
187
+ paginate_posts(f, dir)
180
188
  else
181
189
  first3 = File.open(File.join(self.source, dir, f)) { |fd| fd.read(3) }
182
190
 
@@ -211,15 +219,13 @@ module Jekyll
211
219
  #
212
220
  # Returns {"site" => {"time" => <Time>,
213
221
  # "posts" => [<Post>],
214
- # "categories" => [<Post>],
215
- # "topics" => [<Post>] }}
222
+ # "categories" => [<Post>]}
216
223
  def site_payload
217
- {"site" => {
218
- "time" => Time.now,
219
- "posts" => self.posts.sort { |a,b| b <=> a },
220
- "categories" => post_attr_hash('categories'),
221
- "topics" => post_attr_hash('topics')
222
- }}
224
+ {"site" => self.config.merge({
225
+ "time" => Time.now,
226
+ "posts" => self.posts.sort { |a,b| b <=> a },
227
+ "categories" => post_attr_hash('categories'),
228
+ "tags" => post_attr_hash('tags')})}
223
229
  end
224
230
 
225
231
  # Filter out any files/directories that are hidden or backup files (start
@@ -233,5 +239,28 @@ module Jekyll
233
239
  end
234
240
  end
235
241
  end
242
+
243
+ # Paginates the blog's posts. Renders the index.html file into paginated directories, ie: page2, page3...
244
+ # and adds more wite-wide data
245
+ #
246
+ # {"paginator" => { "page" => <Number>,
247
+ # "per_page" => <Number>,
248
+ # "posts" => [<Post>],
249
+ # "total_posts" => <Number>,
250
+ # "total_pages" => <Number>,
251
+ # "previous_page" => <Number>,
252
+ # "next_page" => <Number> }}
253
+ def paginate_posts(file, dir)
254
+ all_posts = self.posts.sort { |a,b| b <=> a }
255
+ pages = Pager.calculate_pages(all_posts, self.config['paginate'].to_i)
256
+ pages += 1
257
+ (1..pages).each do |num_page|
258
+ pager = Pager.new(self.config, num_page, all_posts, pages)
259
+ page = Page.new(self, self.source, dir, file)
260
+ page.render(self.layouts, site_payload.merge({'paginator' => pager.to_hash}))
261
+ suffix = "page#{num_page}" if num_page > 1
262
+ page.write(self.dest, suffix)
263
+ end
264
+ end
236
265
  end
237
266
  end
@@ -1,5 +1,5 @@
1
1
  require 'rubygems'
2
- gem 'RedCloth', '= 4.1.0'
2
+ gem 'RedCloth', '= 4.2.1'
3
3
 
4
4
  require File.join(File.dirname(__FILE__), *%w[.. lib jekyll])
5
5
 
@@ -0,0 +1,6 @@
1
+ ---
2
+ title: A Tag
3
+ tag: code
4
+ ---
5
+
6
+ Whoa.
@@ -0,0 +1,9 @@
1
+ ---
2
+ title: Some Tags
3
+ tags:
4
+ - food
5
+ - cooking
6
+ - pizza
7
+ ---
8
+
9
+ Awesome!
@@ -0,0 +1,3 @@
1
+ ---
2
+ ---
3
+ Empty YAML.
@@ -0,0 +1,6 @@
1
+ ---
2
+ title: About
3
+ permalink: /about/
4
+ ---
5
+
6
+ About the site
@@ -0,0 +1,5 @@
1
+ ---
2
+ title: Contact Information
3
+ ---
4
+
5
+ Contact Information
@@ -0,0 +1,7 @@
1
+ ---
2
+ layout: post
3
+ title: "Test title"
4
+ tag: "Ruby"
5
+ ---
6
+
7
+ This is the content
@@ -0,0 +1,29 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ class TestConfiguration < Test::Unit::TestCase
4
+ context "loading configuration" do
5
+ setup do
6
+ @path = './_config.yml'
7
+ end
8
+
9
+ should "fire warning with no _config.yml" do
10
+ mock(YAML).load_file(@path) { raise "No such file or directory - #{@path}" }
11
+ mock(STDERR).puts("WARNING: Could not read configuration. Using defaults (and options).")
12
+ mock(STDERR).puts("\tNo such file or directory - #{@path}")
13
+ assert_equal Jekyll::DEFAULTS, Jekyll.configuration({})
14
+ end
15
+
16
+ should "load configuration as hash" do
17
+ mock(YAML).load_file(@path) { Hash.new }
18
+ mock(STDOUT).puts("Configuration from #{@path}")
19
+ assert_equal Jekyll::DEFAULTS, Jekyll.configuration({})
20
+ end
21
+
22
+ should "fire warning with bad config" do
23
+ mock(YAML).load_file(@path) { Array.new }
24
+ mock(STDERR).puts("WARNING: Could not read configuration. Using defaults (and options).")
25
+ mock(STDERR).puts("\tInvalid configuration - #{@path}")
26
+ assert_equal Jekyll::DEFAULTS, Jekyll.configuration({})
27
+ end
28
+ end
29
+ end