jekyll 0.4.1 → 0.5.1

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 (51) hide show
  1. data/History.txt +31 -1
  2. data/README.textile +23 -457
  3. data/Rakefile +91 -0
  4. data/VERSION.yml +2 -2
  5. data/bin/jekyll +61 -57
  6. data/lib/jekyll.rb +48 -32
  7. data/lib/jekyll/albino.rb +13 -7
  8. data/lib/jekyll/converters/mephisto.rb +8 -8
  9. data/lib/jekyll/converters/mt.rb +8 -8
  10. data/lib/jekyll/converters/textpattern.rb +4 -4
  11. data/lib/jekyll/converters/typo.rb +8 -8
  12. data/lib/jekyll/converters/wordpress.rb +1 -2
  13. data/lib/jekyll/convertible.rb +33 -22
  14. data/lib/jekyll/core_ext.rb +5 -5
  15. data/lib/jekyll/filters.rb +15 -7
  16. data/lib/jekyll/layout.rb +9 -6
  17. data/lib/jekyll/page.rb +13 -10
  18. data/lib/jekyll/post.rb +108 -39
  19. data/lib/jekyll/site.rb +121 -51
  20. data/lib/jekyll/tags/highlight.rb +12 -9
  21. data/lib/jekyll/tags/include.rb +5 -5
  22. data/test/helper.rb +20 -6
  23. data/test/source/_posts/2008-02-02-not-published.textile +8 -0
  24. data/test/source/_posts/2008-02-02-published.textile +8 -0
  25. data/test/source/_posts/2009-01-27-array-categories.textile +10 -0
  26. data/test/source/_posts/2009-01-27-categories.textile +7 -0
  27. data/test/source/_posts/2009-01-27-category.textile +7 -0
  28. data/test/source/_posts/2009-03-12-hash-#1.markdown +6 -0
  29. data/test/test_filters.rb +39 -27
  30. data/test/test_generated_site.rb +32 -16
  31. data/test/test_post.rb +258 -102
  32. data/test/test_site.rb +60 -28
  33. data/test/test_tags.rb +103 -18
  34. metadata +25 -70
  35. data/test/dest/2008/10/18/foo-bar.html +0 -28
  36. data/test/dest/2008/11/21/complex.html +0 -29
  37. data/test/dest/2008/12/13/include.html +0 -30
  38. data/test/dest/_posts/2008-10-18-foo-bar.html +0 -28
  39. data/test/dest/_posts/2008-11-21-complex.html +0 -29
  40. data/test/dest/_posts/2008-12-03-permalinked-post.html +0 -2
  41. data/test/dest/_posts/2008-12-13-include.html +0 -30
  42. data/test/dest/category/2008/09/23/categories.html +0 -27
  43. data/test/dest/category/_posts/2008-9-23-categories.html +0 -27
  44. data/test/dest/css/screen.css +0 -76
  45. data/test/dest/foo/2008/12/12/topical-post.html +0 -28
  46. data/test/dest/foo/_posts/bar/2008-12-12-topical-post.html +0 -28
  47. data/test/dest/index.html +0 -60
  48. data/test/dest/my_category/permalinked-post +0 -2
  49. data/test/dest/z_category/2008/09/23/categories.html +0 -27
  50. data/test/dest/z_category/_posts/2008-9-23-categories.html +0 -27
  51. data/test/test_jekyll.rb +0 -0
@@ -11,31 +11,31 @@ require 'fileutils'
11
11
  # installed, running the following commands should work:
12
12
  # $ sudo gem install sequel
13
13
  # $ sudo gem install mysql -- --with-mysql-config=/usr/local/mysql/bin/mysql_config
14
-
14
+
15
15
  module Jekyll
16
16
  module MT
17
17
  # This query will pull blog posts from all entries across all blogs. If
18
18
  # you've got unpublished, deleted or otherwise hidden posts please sift
19
19
  # through the created posts to make sure nothing is accidently published.
20
20
  QUERY = "SELECT entry_id, entry_basename, entry_text, entry_text_more, entry_created_on, entry_title FROM mt_entry"
21
-
21
+
22
22
  def self.process(dbname, user, pass, host = 'localhost')
23
23
  db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host)
24
-
24
+
25
25
  FileUtils.mkdir_p "_posts"
26
-
26
+
27
27
  db[QUERY].each do |post|
28
28
  title = post[:entry_title]
29
29
  slug = post[:entry_basename]
30
30
  date = post[:entry_created_on]
31
31
  content = post[:entry_text]
32
32
  more_content = post[:entry_text_more]
33
-
33
+
34
34
  # Be sure to include the body and extended body.
35
35
  if more_content != nil
36
36
  content = content + " \n" + more_content
37
37
  end
38
-
38
+
39
39
  # Ideally, this script would determine the post format (markdown, html
40
40
  # , etc) and create files with proper extensions. At this point it
41
41
  # just assumes that markdown will be acceptable.
@@ -46,14 +46,14 @@ module Jekyll
46
46
  'title' => title.to_s,
47
47
  'mt_id' => post[:entry_id],
48
48
  }.delete_if { |k,v| v.nil? || v == ''}.to_yaml
49
-
49
+
50
50
  File.open("_posts/#{name}", "w") do |f|
51
51
  f.puts data
52
52
  f.puts "---"
53
53
  f.puts content
54
54
  end
55
55
  end
56
-
56
+
57
57
  end
58
58
  end
59
59
  end
@@ -18,20 +18,20 @@ module Jekyll
18
18
 
19
19
  def self.process(dbname, user, pass, host = 'localhost')
20
20
  db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host)
21
-
21
+
22
22
  FileUtils.mkdir_p "_posts"
23
-
23
+
24
24
  db[QUERY].each do |post|
25
25
  # Get required fields and construct Jekyll compatible name
26
26
  title = post[:Title]
27
27
  slug = post[:url_title]
28
28
  date = post[:Posted]
29
29
  content = post[:Body]
30
-
30
+
31
31
  name = [date.strftime("%Y-%m-%d"), slug].join('-') + ".textile"
32
32
 
33
33
  # Get the relevant fields as a hash, delete empty fields and convert
34
- # to YAML for the header
34
+ # to YAML for the header
35
35
  data = {
36
36
  'layout' => 'post',
37
37
  'title' => title.to_s,
@@ -2,22 +2,22 @@
2
2
  require 'fileutils'
3
3
  require 'rubygems'
4
4
  require 'sequel'
5
-
5
+
6
6
  module Jekyll
7
7
  module Typo
8
- # this SQL *should* work for both MySQL and PostgreSQL, but I haven't
8
+ # this SQL *should* work for both MySQL and PostgreSQL, but I haven't
9
9
  # tested PostgreSQL yet (as of 2008-12-16)
10
10
  SQL = <<-EOS
11
11
  SELECT c.id id,
12
- c.title title,
13
- c.permalink slug,
12
+ c.title title,
13
+ c.permalink slug,
14
14
  c.body body,
15
- c.published_at date,
15
+ c.published_at date,
16
16
  c.state state,
17
17
  COALESCE(tf.name, 'html') filter
18
- FROM contents c
18
+ FROM contents c
19
19
  LEFT OUTER JOIN text_filters tf
20
- ON c.text_filter_id = tf.id
20
+ ON c.text_filter_id = tf.id
21
21
  EOS
22
22
 
23
23
  def self.process dbname, user, pass, host='localhost'
@@ -30,7 +30,7 @@ module Jekyll
30
30
  sprintf("%.02d", post[:date].month),
31
31
  sprintf("%.02d", post[:date].day),
32
32
  post[:slug].strip ].join('-')
33
- # Can have more than one text filter in this field, but we just want
33
+ # Can have more than one text filter in this field, but we just want
34
34
  # the first one for this
35
35
  name += '.' + post[:filter].split(' ')[0]
36
36
 
@@ -15,7 +15,7 @@ module Jekyll
15
15
  # post in wp_posts that has post_status = 'publish'.
16
16
  # This restriction is made because 'draft' posts are not guaranteed to
17
17
  # have valid dates.
18
- QUERY = "select * from wp_posts where post_status = 'publish' and post_type = 'post'"
18
+ QUERY = "select post_title, post_name, post_date, post_content, post_excerpt, ID, guid from wp_posts where post_status = 'publish' and post_type = 'post'"
19
19
 
20
20
  def self.process(dbname, user, pass, host = 'localhost')
21
21
  db = Sequel.mysql(dbname, :user => user, :password => pass, :host => host)
@@ -28,7 +28,6 @@ module Jekyll
28
28
  slug = post[:post_name]
29
29
  date = post[:post_date]
30
30
  content = post[:post_content]
31
-
32
31
  name = "%02d-%02d-%02d-%s.markdown" % [date.year, date.month, date.day,
33
32
  slug]
34
33
 
@@ -1,10 +1,15 @@
1
+ # Convertible provides methods for converting a pagelike item
2
+ # from a certain type of markup into actual content
3
+ #
4
+ # Requires
5
+ # self.site -> Jekyll::Site
1
6
  module Jekyll
2
7
  module Convertible
3
8
  # Return the contents as a string
4
9
  def to_s
5
10
  self.content || ''
6
11
  end
7
-
12
+
8
13
  # Read the YAML frontmatter
9
14
  # +base+ is the String path to the dir containing the file
10
15
  # +name+ is the String filename of the file
@@ -12,58 +17,64 @@ module Jekyll
12
17
  # Returns nothing
13
18
  def read_yaml(base, name)
14
19
  self.content = File.read(File.join(base, name))
15
-
20
+
16
21
  if self.content =~ /^(---\s*\n.*?)\n---\s*\n/m
17
22
  self.content = self.content[($1.size + 5)..-1]
18
-
23
+
19
24
  self.data = YAML.load($1)
20
25
  end
21
26
  end
22
-
27
+
23
28
  # Transform the contents based on the file extension.
24
29
  #
25
30
  # Returns nothing
26
31
  def transform
27
- case Jekyll.content_type
28
- when :textile
32
+ case self.content_type
33
+ when 'textile'
29
34
  self.ext = ".html"
30
- self.content = RedCloth.new(self.content).to_html
31
- when :markdown
35
+ self.content = self.site.textile(self.content)
36
+ when 'markdown'
32
37
  self.ext = ".html"
33
- self.content = Jekyll.markdown_proc.call(self.content)
38
+ self.content = self.site.markdown(self.content)
34
39
  end
35
40
  end
36
-
37
- def determine_content_type
41
+
42
+ # Determine which formatting engine to use based on this convertible's
43
+ # extension
44
+ #
45
+ # Returns one of :textile, :markdown or :unknown
46
+ def content_type
38
47
  case self.ext[1..-1]
39
48
  when /textile/i
40
- return :textile
49
+ return 'textile'
41
50
  when /markdown/i, /mkdn/i, /md/i
42
- return :markdown
43
- end
44
- return :unknown
51
+ return 'markdown'
52
+ end
53
+ return 'unknown'
45
54
  end
46
-
55
+
47
56
  # Add any necessary layouts to this convertible document
48
57
  # +layouts+ is a Hash of {"name" => "layout"}
49
58
  # +site_payload+ is the site payload hash
50
59
  #
51
60
  # Returns nothing
52
61
  def do_layout(payload, layouts)
62
+ info = { :filters => [Jekyll::Filters], :registers => { :site => self.site } }
63
+
53
64
  # render and transform content (this becomes the final content of the object)
54
- Jekyll.content_type = self.determine_content_type
55
- self.content = Liquid::Template.parse(self.content).render(payload, [Jekyll::Filters])
65
+ payload["content_type"] = self.content_type
66
+ self.content = Liquid::Template.parse(self.content).render(payload, info)
56
67
  self.transform
57
-
68
+
58
69
  # output keeps track of what will finally be written
59
70
  self.output = self.content
60
-
71
+
61
72
  # recursively render layouts
62
73
  layout = layouts[self.data["layout"]]
63
74
  while layout
64
75
  payload = payload.deep_merge({"content" => self.output, "page" => layout.data})
65
- self.output = Liquid::Template.parse(layout.content).render(payload, [Jekyll::Filters])
66
-
76
+ self.output = Liquid::Template.parse(layout.content).render(payload, info)
77
+
67
78
  layout = layouts[layout.data["layout"]]
68
79
  end
69
80
  end
@@ -1,22 +1,22 @@
1
1
  class Hash
2
2
  # Merges self with another hash, recursively.
3
- #
3
+ #
4
4
  # This code was lovingly stolen from some random gem:
5
5
  # http://gemjack.com/gems/tartan-0.1.1/classes/Hash.html
6
- #
6
+ #
7
7
  # Thanks to whoever made it.
8
8
  def deep_merge(hash)
9
9
  target = dup
10
-
10
+
11
11
  hash.keys.each do |key|
12
12
  if hash[key].is_a? Hash and self[key].is_a? Hash
13
13
  target[key] = target[key].deep_merge(hash[key])
14
14
  next
15
15
  end
16
-
16
+
17
17
  target[key] = hash[key]
18
18
  end
19
-
19
+
20
20
  target
21
21
  end
22
22
  end
@@ -1,6 +1,10 @@
1
1
  module Jekyll
2
-
2
+
3
3
  module Filters
4
+ def textilize(input)
5
+ RedCloth.new(input).to_html
6
+ end
7
+
4
8
  def date_to_string(date)
5
9
  date.strftime("%d %b %Y")
6
10
  end
@@ -8,19 +12,23 @@ module Jekyll
8
12
  def date_to_long_string(date)
9
13
  date.strftime("%d %B %Y")
10
14
  end
11
-
15
+
12
16
  def date_to_xmlschema(date)
13
17
  date.xmlschema
14
18
  end
15
-
19
+
16
20
  def xml_escape(input)
17
- input.gsub("&", "&amp;").gsub("<", "&lt;").gsub(">", "&gt;")
21
+ CGI.escapeHTML(input)
18
22
  end
19
-
23
+
24
+ def cgi_escape(input)
25
+ CGI::escape(input)
26
+ end
27
+
20
28
  def number_of_words(input)
21
29
  input.split.length
22
30
  end
23
-
31
+
24
32
  def array_to_sentence_string(array)
25
33
  connector = "and"
26
34
  case array.length
@@ -35,5 +43,5 @@ module Jekyll
35
43
  end
36
44
  end
37
45
 
38
- end
46
+ end
39
47
  end
@@ -2,25 +2,28 @@ module Jekyll
2
2
 
3
3
  class Layout
4
4
  include Convertible
5
-
5
+
6
+ attr_accessor :site
6
7
  attr_accessor :ext
7
8
  attr_accessor :data, :content
8
-
9
+
9
10
  # Initialize a new Layout.
11
+ # +site+ is the Site
10
12
  # +base+ is the String path to the <source>
11
13
  # +name+ is the String filename of the post file
12
14
  #
13
15
  # Returns <Page>
14
- def initialize(base, name)
16
+ def initialize(site, base, name)
17
+ @site = site
15
18
  @base = base
16
19
  @name = name
17
-
20
+
18
21
  self.data = {}
19
-
22
+
20
23
  self.process(name)
21
24
  self.read_yaml(base, name)
22
25
  end
23
-
26
+
24
27
  # Extract information from the layout filename
25
28
  # +name+ is the String filename of the layout file
26
29
  #
@@ -2,28 +2,31 @@ module Jekyll
2
2
 
3
3
  class Page
4
4
  include Convertible
5
-
5
+
6
+ attr_accessor :site
6
7
  attr_accessor :ext
7
8
  attr_accessor :data, :content, :output
8
-
9
+
9
10
  # Initialize a new Page.
11
+ # +site+ is the Site
10
12
  # +base+ is the String path to the <source>
11
13
  # +dir+ is the String path between <source> and the file
12
14
  # +name+ is the String filename of the file
13
15
  #
14
16
  # Returns <Page>
15
- def initialize(base, dir, name)
17
+ def initialize(site, base, dir, name)
18
+ @site = site
16
19
  @base = base
17
20
  @dir = dir
18
21
  @name = name
19
-
22
+
20
23
  self.data = {}
21
-
24
+
22
25
  self.process(name)
23
26
  self.read_yaml(File.join(base, dir), name)
24
27
  #self.transform
25
28
  end
26
-
29
+
27
30
  # Extract information from the page filename
28
31
  # +name+ is the String filename of the page file
29
32
  #
@@ -31,7 +34,7 @@ module Jekyll
31
34
  def process(name)
32
35
  self.ext = File.extname(name)
33
36
  end
34
-
37
+
35
38
  # Add any necessary layouts to this post
36
39
  # +layouts+ is a Hash of {"name" => "layout"}
37
40
  # +site_payload+ is the site payload hash
@@ -41,19 +44,19 @@ module Jekyll
41
44
  payload = {"page" => self.data}.deep_merge(site_payload)
42
45
  do_layout(payload, layouts)
43
46
  end
44
-
47
+
45
48
  # Write the generated page file to the destination directory.
46
49
  # +dest+ is the String path to the destination dir
47
50
  #
48
51
  # Returns nothing
49
52
  def write(dest)
50
53
  FileUtils.mkdir_p(File.join(dest, @dir))
51
-
54
+
52
55
  name = @name
53
56
  if self.ext != ""
54
57
  name = @name.split(".")[0..-2].join('.') + self.ext
55
58
  end
56
-
59
+
57
60
  path = File.join(dest, @dir, name)
58
61
  File.open(path, 'w') do |f|
59
62
  f.write(self.output)
@@ -3,13 +3,13 @@ module Jekyll
3
3
  class Post
4
4
  include Comparable
5
5
  include Convertible
6
-
6
+
7
7
  class << self
8
8
  attr_accessor :lsi
9
9
  end
10
-
10
+
11
11
  MATCHER = /^(.+\/)*(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
12
-
12
+
13
13
  # Post name validator. Post filenames must be like:
14
14
  # 2008-11-05-my-awesome-post.textile
15
15
  #
@@ -17,36 +17,62 @@ module Jekyll
17
17
  def self.valid?(name)
18
18
  name =~ MATCHER
19
19
  end
20
-
21
- attr_accessor :date, :slug, :ext, :categories, :topics
22
- attr_accessor :data, :content, :output
23
-
20
+
21
+ attr_accessor :site, :date, :slug, :ext, :topics, :published, :data, :content, :output
22
+ attr_writer :categories
23
+
24
+ def categories
25
+ @categories ||= []
26
+ end
27
+
24
28
  # Initialize this Post instance.
29
+ # +site+ is the Site
25
30
  # +base+ is the String path to the dir containing the post file
26
31
  # +name+ is the String filename of the post file
27
32
  # +categories+ is an Array of Strings for the categories for this post
28
33
  #
29
34
  # Returns <Post>
30
- def initialize(source, dir, name)
35
+ def initialize(site, source, dir, name)
36
+ @site = site
31
37
  @base = File.join(source, dir, '_posts')
32
38
  @name = name
33
-
39
+
34
40
  self.categories = dir.split('/').reject { |x| x.empty? }
35
-
41
+
36
42
  parts = name.split('/')
37
43
  self.topics = parts.size > 1 ? parts[0..-2] : []
38
-
44
+
39
45
  self.process(name)
40
46
  self.read_yaml(@base, name)
47
+
48
+ if self.data.has_key?('published') && self.data['published'] == false
49
+ self.published = false
50
+ else
51
+ self.published = true
52
+ end
53
+
54
+ if self.categories.empty?
55
+ if self.data.has_key?('category')
56
+ self.categories << self.data['category']
57
+ elsif self.data.has_key?('categories')
58
+ # Look for categories in the YAML-header, either specified as
59
+ # an array or a string.
60
+ if self.data['categories'].kind_of? String
61
+ self.categories = self.data['categories'].split
62
+ else
63
+ self.categories = self.data['categories']
64
+ end
65
+ end
66
+ end
41
67
  end
42
-
68
+
43
69
  # Spaceship is based on Post#date
44
70
  #
45
71
  # Returns -1, 0, 1
46
72
  def <=>(other)
47
73
  self.date <=> other.date
48
74
  end
49
-
75
+
50
76
  # Extract information from the post filename
51
77
  # +name+ is the String filename of the post file
52
78
  #
@@ -57,7 +83,7 @@ module Jekyll
57
83
  self.slug = slug
58
84
  self.ext = ext
59
85
  end
60
-
86
+
61
87
  # The generated directory into which the post will be placed
62
88
  # upon generation. This is derived from the permalink or, if
63
89
  # permalink is absent, set to the default date
@@ -65,18 +91,9 @@ module Jekyll
65
91
  #
66
92
  # Returns <String>
67
93
  def dir
68
- if permalink
69
- permalink.to_s.split("/")[0..-2].join("/") + '/'
70
- else
71
- prefix = self.categories.empty? ? '' : '/' + self.categories.join('/')
72
- if Jekyll.permalink_style == :date
73
- prefix + date.strftime("/%Y/%m/%d/")
74
- else
75
- prefix + '/'
76
- end
77
- end
94
+ File.dirname(url)
78
95
  end
79
-
96
+
80
97
  # The full path and filename of the post.
81
98
  # Defined in the YAML of the post body
82
99
  # (Optional)
@@ -85,30 +102,53 @@ module Jekyll
85
102
  def permalink
86
103
  self.data && self.data['permalink']
87
104
  end
88
-
105
+
106
+ def template
107
+ case self.site.permalink_style
108
+ when :pretty
109
+ "/:categories/:year/:month/:day/:title"
110
+ when :none
111
+ "/:categories/:title.html"
112
+ when :date
113
+ "/:categories/:year/:month/:day/:title.html"
114
+ else
115
+ self.site.permalink_style.to_s
116
+ end
117
+ end
118
+
89
119
  # The generated relative url of this post
90
120
  # e.g. /2008/11/05/my-awesome-post.html
91
121
  #
92
122
  # Returns <String>
93
123
  def url
94
- permalink || self.dir + self.slug + ".html"
124
+ return permalink if permalink
125
+
126
+ @url ||= {
127
+ "year" => date.strftime("%Y"),
128
+ "month" => date.strftime("%m"),
129
+ "day" => date.strftime("%d"),
130
+ "title" => CGI.escape(slug),
131
+ "categories" => categories.sort.join('/')
132
+ }.inject(template) { |result, token|
133
+ result.gsub(/:#{token.first}/, token.last)
134
+ }.gsub(/\/\//, "/")
95
135
  end
96
-
136
+
97
137
  # The UID for this post (useful in feeds)
98
138
  # e.g. /2008/11/05/my-awesome-post
99
139
  #
100
140
  # Returns <String>
101
141
  def id
102
- self.dir + self.slug
142
+ File.join(self.dir, self.slug)
103
143
  end
104
-
144
+
105
145
  # Calculate related posts.
106
146
  #
107
147
  # Returns [<Post>]
108
148
  def related_posts(posts)
109
149
  return [] unless posts.size > 1
110
-
111
- if Jekyll.lsi
150
+
151
+ if self.site.lsi
112
152
  self.class.lsi ||= begin
113
153
  puts "Running the classifier... this could take a while."
114
154
  lsi = Classifier::LSI.new
@@ -123,7 +163,7 @@ module Jekyll
123
163
  (posts - [self])[0..9]
124
164
  end
125
165
  end
126
-
166
+
127
167
  # Add any necessary layouts to this post
128
168
  # +layouts+ is a Hash of {"name" => "layout"}
129
169
  # +site_payload+ is the site payload hash
@@ -137,23 +177,30 @@ module Jekyll
137
177
  "page" => self.to_liquid
138
178
  }
139
179
  payload = payload.deep_merge(site_payload)
140
-
180
+
141
181
  do_layout(payload, layouts)
142
182
  end
143
-
183
+
144
184
  # Write the generated post file to the destination directory.
145
185
  # +dest+ is the String path to the destination dir
146
186
  #
147
187
  # Returns nothing
148
188
  def write(dest)
149
189
  FileUtils.mkdir_p(File.join(dest, dir))
150
-
151
- path = File.join(dest, self.url)
190
+
191
+ # The url needs to be unescaped in order to preserve the correct filename
192
+ path = File.join(dest, CGI.unescape(self.url))
193
+
194
+ if template[/\.html$/].nil?
195
+ FileUtils.mkdir_p(path)
196
+ path = File.join(path, "index.html")
197
+ end
198
+
152
199
  File.open(path, 'w') do |f|
153
200
  f.write(self.output)
154
201
  end
155
202
  end
156
-
203
+
157
204
  # Convert this post into a Hash for use in Liquid templates.
158
205
  #
159
206
  # Returns <Hash>
@@ -163,12 +210,34 @@ module Jekyll
163
210
  "date" => self.date,
164
211
  "id" => self.id,
165
212
  "topics" => self.topics,
213
+ "categories" => self.categories,
214
+ "next" => self.next,
215
+ "previous" => self.previous,
166
216
  "content" => self.content }.deep_merge(self.data)
167
217
  end
168
-
218
+
169
219
  def inspect
170
220
  "<Post: #{self.id}>"
171
221
  end
222
+
223
+ def next
224
+ pos = self.site.posts.index(self)
225
+
226
+ if pos && pos < self.site.posts.length-1
227
+ self.site.posts[pos+1]
228
+ else
229
+ nil
230
+ end
231
+ end
232
+
233
+ def previous
234
+ pos = self.site.posts.index(self)
235
+ if pos && pos > 0
236
+ self.site.posts[pos-1]
237
+ else
238
+ nil
239
+ end
240
+ end
172
241
  end
173
242
 
174
243
  end