gabrielg-jekyll 0.2.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.
@@ -0,0 +1,24 @@
1
+ module Jekyll
2
+
3
+ module Filters
4
+ def date_to_string(date)
5
+ date.strftime("%d %b %Y")
6
+ end
7
+
8
+ def date_to_long_string(date)
9
+ date.strftime("%d %B %Y")
10
+ end
11
+
12
+ def date_to_xmlschema(date)
13
+ date.xmlschema
14
+ end
15
+
16
+ def xml_escape(input)
17
+ input.gsub("<", "&lt;").gsub(">", "&gt;")
18
+ end
19
+
20
+ def number_of_words(input)
21
+ input.split.length
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,48 @@
1
+ module Jekyll
2
+
3
+ class Layout
4
+ include Convertible
5
+
6
+ attr_accessor :ext
7
+ attr_accessor :data, :content
8
+
9
+ # Initialize a new Layout.
10
+ # +base+ is the String path to the <source>
11
+ # +name+ is the String filename of the post file
12
+ #
13
+ # Returns <Page>
14
+ def initialize(base, name)
15
+ @base = base
16
+ @name = name
17
+
18
+ self.data = {}
19
+
20
+ self.process(name)
21
+ self.read_yaml(base, name)
22
+ end
23
+
24
+ # Extract information from the layout filename
25
+ # +name+ is the String filename of the layout file
26
+ #
27
+ # Returns nothing
28
+ def process(name)
29
+ self.ext = File.extname(name)
30
+ end
31
+
32
+ # Add any necessary layouts to this post
33
+ # +layouts+ is a Hash of {"name" => "layout"}
34
+ # +site_payload+ is the site payload hash
35
+ #
36
+ # Returns nothing
37
+ def add_layout(layouts, site_payload)
38
+ payload = {"page" => self.data}.merge(site_payload)
39
+ self.content = Liquid::Template.parse(self.content).render(payload, [Jekyll::Filters])
40
+
41
+ layout = layouts[self.data["layout"]] || self.content
42
+ payload = {"content" => self.content, "page" => self.data}
43
+
44
+ self.content = Liquid::Template.parse(layout).render(payload, [Jekyll::Filters])
45
+ end
46
+ end
47
+
48
+ end
@@ -0,0 +1,64 @@
1
+ module Jekyll
2
+
3
+ class Page
4
+ include Convertible
5
+
6
+ attr_accessor :ext
7
+ attr_accessor :data, :content, :output
8
+
9
+ # Initialize a new Page.
10
+ # +base+ is the String path to the <source>
11
+ # +dir+ is the String path between <source> and the file
12
+ # +name+ is the String filename of the file
13
+ #
14
+ # Returns <Page>
15
+ def initialize(base, dir, name)
16
+ @base = base
17
+ @dir = dir
18
+ @name = name
19
+
20
+ self.data = {}
21
+
22
+ self.process(name)
23
+ self.read_yaml(File.join(base, dir), name)
24
+ #self.transform
25
+ end
26
+
27
+ # Extract information from the page filename
28
+ # +name+ is the String filename of the page file
29
+ #
30
+ # Returns nothing
31
+ def process(name)
32
+ self.ext = File.extname(name)
33
+ end
34
+
35
+ # Add any necessary layouts to this post
36
+ # +layouts+ is a Hash of {"name" => "layout"}
37
+ # +site_payload+ is the site payload hash
38
+ #
39
+ # Returns nothing
40
+ def add_layout(layouts, site_payload)
41
+ payload = {"page" => self.data}
42
+ do_layout(payload, layouts, site_payload)
43
+ end
44
+
45
+ # Write the generated page file to the destination directory.
46
+ # +dest+ is the String path to the destination dir
47
+ #
48
+ # Returns nothing
49
+ def write(dest)
50
+ FileUtils.mkdir_p(File.join(dest, @dir))
51
+
52
+ name = @name
53
+ if self.ext != ""
54
+ name = @name.split(".")[0..-2].join('.') + self.ext
55
+ end
56
+
57
+ path = File.join(dest, @dir, name)
58
+ File.open(path, 'w') do |f|
59
+ f.write(self.output)
60
+ end
61
+ end
62
+ end
63
+
64
+ end
@@ -0,0 +1,165 @@
1
+ module Jekyll
2
+
3
+ class Post
4
+ include Comparable
5
+ include Convertible
6
+
7
+ class << self
8
+ attr_accessor :lsi
9
+ end
10
+
11
+ MATCHER = /^(\d+-\d+-\d+)-(.*)(\.[^.]+)$/
12
+
13
+ # Post name validator. Post filenames must be like:
14
+ # 2008-11-05-my-awesome-post.textile
15
+ #
16
+ # Returns <Bool>
17
+ def self.valid?(name)
18
+ name =~ MATCHER
19
+ end
20
+
21
+ attr_accessor :date, :slug, :ext, :categories
22
+ attr_accessor :data, :content, :output
23
+
24
+ # Initialize this Post instance.
25
+ # +base+ is the String path to the dir containing the post file
26
+ # +name+ is the String filename of the post file
27
+ # +categories+ is an Array of Strings for the categories for this post
28
+ #
29
+ # Returns <Post>
30
+ def initialize(base, name)
31
+ @base = base
32
+ @name = name
33
+ @categories = base.split('/').reject { |p| ['.', '_posts'].include? p }
34
+
35
+ self.process(name)
36
+ self.read_yaml(base, name)
37
+ #Removed to avoid munging of liquid tags, replaced in convertible.rb#48
38
+ #self.transform
39
+ end
40
+
41
+ # Spaceship is based on Post#date
42
+ #
43
+ # Returns -1, 0, 1
44
+ def <=>(other)
45
+ self.date <=> other.date
46
+ end
47
+
48
+ # Extract information from the post filename
49
+ # +name+ is the String filename of the post file
50
+ #
51
+ # Returns nothing
52
+ def process(name)
53
+ m, date, slug, ext = *name.match(MATCHER)
54
+ self.date = Time.parse(date)
55
+ self.slug = slug
56
+ self.ext = ext
57
+ end
58
+
59
+ # The generated directory into which the post will be placed
60
+ # upon generation. This is derived from the permalink or, if
61
+ # permalink is absent, set to the default date
62
+ # e.g. "/2008/11/05/"
63
+ #
64
+ # Returns <String>
65
+ def dir
66
+ path = (@categories && !@categories.empty?) ? '/' + @categories.join('/') : ''
67
+ if permalink
68
+ permalink.to_s.split("/")[0..-2].join("/")
69
+ else
70
+ "#{path}" + date.strftime("/%Y/%m/%d/")
71
+ end
72
+ end
73
+
74
+ # The full path and filename of the post.
75
+ # Defined in the YAML of the post body
76
+ # (Optional)
77
+ #
78
+ # Returns <String>
79
+ def permalink
80
+ self.data && self.data['permalink']
81
+ end
82
+
83
+ # The generated relative url of this post
84
+ # e.g. /2008/11/05/my-awesome-post.html
85
+ #
86
+ # Returns <String>
87
+ def url
88
+ self.dir + self.slug + ".html"
89
+ end
90
+
91
+ # The UID for this post (useful in feeds)
92
+ # e.g. /2008/11/05/my-awesome-post
93
+ #
94
+ # Returns <String>
95
+ def id
96
+ self.dir + self.slug
97
+ end
98
+
99
+ # Calculate related posts.
100
+ #
101
+ # Returns [<Post>]
102
+ def related_posts(posts)
103
+ return [] unless posts.size > 1
104
+
105
+ if Jekyll.lsi
106
+ self.class.lsi ||= begin
107
+ puts "Running the classifier... this could take a while."
108
+ lsi = Classifier::LSI.new
109
+ posts.each { |x| $stdout.print(".");$stdout.flush;lsi.add_item(x) }
110
+ puts ""
111
+ lsi
112
+ end
113
+
114
+ related = self.class.lsi.find_related(self.content, 11)
115
+ related - [self]
116
+ else
117
+ (posts - [self])[0..9]
118
+ end
119
+ end
120
+
121
+ # Add any necessary layouts to this post
122
+ # +layouts+ is a Hash of {"name" => "layout"}
123
+ # +site_payload+ is the site payload hash
124
+ #
125
+ # Returns nothing
126
+ def add_layout(layouts, site_payload)
127
+ # construct post payload
128
+ related = related_posts(site_payload["site"]["posts"])
129
+ payload = {"page" => self.to_liquid.merge(self.data)}
130
+ do_layout(payload, layouts, site_payload.merge({"site" => {"related_posts" => related}}))
131
+ end
132
+
133
+ # Write the generated post file to the destination directory.
134
+ # +dest+ is the String path to the destination dir
135
+ #
136
+ # Returns nothing
137
+ def write(dest)
138
+ FileUtils.mkdir_p(File.join(dest, dir))
139
+
140
+ path = File.join(dest, self.url)
141
+ File.open(path, 'w') do |f|
142
+ f.write(self.output)
143
+ end
144
+ end
145
+
146
+ # Returns nil, or a Markdown formatted summary if a summary is set in the YAML
147
+ # front matter
148
+ def summary
149
+ self.data['summary'] ? RedCloth.new(self.data['summary']).to_html : nil
150
+ end
151
+
152
+ # Convert this post into a Hash for use in Liquid templates.
153
+ #
154
+ # Returns <Hash>
155
+ def to_liquid
156
+ self.data.merge({ "title" => self.data["title"] || "",
157
+ "url" => self.url,
158
+ "date" => self.date,
159
+ "id" => self.id,
160
+ "summary" => self.summary,
161
+ "content" => self.content })
162
+ end
163
+ end
164
+
165
+ end
@@ -0,0 +1,134 @@
1
+ module Jekyll
2
+
3
+ class Site
4
+ attr_accessor :source, :dest
5
+ attr_accessor :layouts, :posts
6
+
7
+ # Initialize the site
8
+ # +source+ is String path to the source directory containing
9
+ # the proto-site
10
+ # +dest+ is the String path to the directory where the generated
11
+ # site should be written
12
+ #
13
+ # Returns <Site>
14
+ def initialize(source, dest)
15
+ self.source = source
16
+ self.dest = dest
17
+ self.layouts = {}
18
+ self.posts = []
19
+ end
20
+
21
+ # Do the actual work of processing the site and generating the
22
+ # real deal.
23
+ #
24
+ # Returns nothing
25
+ def process
26
+ self.read_layouts
27
+ self.transform_pages
28
+ self.write_posts
29
+ end
30
+
31
+ # Read all the files in <source>/_layouts into memory for
32
+ # later use.
33
+ #
34
+ # Returns nothing
35
+ def read_layouts
36
+ base = File.join(self.source, "_layouts")
37
+ entries = Dir.entries(base)
38
+ entries = entries.reject { |e| File.directory?(File.join(base, e)) }
39
+
40
+ entries.each do |f|
41
+ name = f.split(".")[0..-2].join(".")
42
+ self.layouts[name] = Layout.new(base, f)
43
+ end
44
+ rescue Errno::ENOENT => e
45
+ # ignore missing layout dir
46
+ end
47
+
48
+ # Read all the files in <base>/_posts and create a new Post
49
+ # object with each one.
50
+ #
51
+ # Returns nothing
52
+ def read_posts(base)
53
+ entries = Dir.entries(base)
54
+ entries = entries.reject { |e| File.directory?(e) }
55
+
56
+ entries.each do |f|
57
+ p = Post.new(base, f)
58
+ if Post.valid?(f)
59
+ p.do_layout({}, {}, {})
60
+ self.posts << p
61
+ end
62
+ end
63
+
64
+ self.posts.sort!
65
+ rescue Errno::ENOENT => e
66
+ # ignore missing layout dir
67
+ end
68
+
69
+ # Write each post to <dest>/<year>/<month>/<day>/<slug>
70
+ #
71
+ # Returns nothing
72
+ def write_posts
73
+ self.posts.each do |post|
74
+ post.add_layout(self.layouts, site_payload)
75
+ post.write(self.dest)
76
+ end
77
+ end
78
+
79
+ # Copy all regular files from <source> to <dest>/ ignoring
80
+ # any files/directories that are hidden (start with ".") or contain
81
+ # site content (start with "_") unless they are "_posts" directories
82
+ # The +dir+ String is a relative path used to call this method
83
+ # recursively as it descends through directories
84
+ #
85
+ # Returns nothing
86
+ def transform_pages(dir = '')
87
+ base = File.join(self.source, dir)
88
+ entries = Dir.entries(base)
89
+ entries = entries.reject { |e|
90
+ (e != '_posts') and ['.', '_'].include?(e[0..0])
91
+ }
92
+
93
+ entries.each do |f|
94
+ if f == '_posts'
95
+ read_posts(File.join(base, f))
96
+ elsif File.directory?(File.join(base, f))
97
+ next if self.dest.sub(/\/$/, '') == File.join(base, f)
98
+ transform_pages(File.join(dir, f))
99
+ else
100
+ first3 = File.open(File.join(self.source, dir, f)) { |fd| fd.read(3) }
101
+
102
+ # if the file appears to have a YAML header then process it as a page
103
+ if first3 == "---"
104
+ page = Page.new(self.source, dir, f)
105
+ page.add_layout(self.layouts, site_payload)
106
+ page.write(self.dest)
107
+ # otherwise copy the file without transforming it
108
+ else
109
+ FileUtils.mkdir_p(File.join(self.dest, dir))
110
+ FileUtils.cp(File.join(self.source, dir, f), File.join(self.dest, dir, f))
111
+ end
112
+ end
113
+ end
114
+ end
115
+
116
+ # The Hash payload containing site-wide data
117
+ #
118
+ # Returns {"site" => {"time" => <Time>, "posts" => [<Post>]}}
119
+ def site_payload
120
+ # Build the category hash map of category ( names => arrays of posts )
121
+ # then sort each array in reverse order
122
+ categories = Hash.new { |hash,key| hash[key] = Array.new }
123
+ self.posts.each { |p| p.categories.each { |c| categories[c] << p } }
124
+ categories.values.map { |cats| cats.sort! { |a,b| b <=> a} }
125
+
126
+ {"site" => {
127
+ "time" => Time.now,
128
+ "posts" => self.posts.sort { |a,b| b <=> a },
129
+ "categories" => categories
130
+ }}
131
+ end
132
+ end
133
+
134
+ end
@@ -0,0 +1,37 @@
1
+ module Jekyll
2
+
3
+ class HighlightBlock < Liquid::Block
4
+ include Liquid::StandardFilters
5
+
6
+ def initialize(tag_name, lang, tokens)
7
+ super
8
+ @lang = lang.strip
9
+ end
10
+
11
+ def render(context)
12
+ if Jekyll.pygments
13
+ render_pygments(context, super.to_s)
14
+ else
15
+ render_codehighlighter(context, super.to_s)
16
+ end
17
+ end
18
+
19
+ def render_pygments(context, code)
20
+ "<notextile>" + Albino.new(code, @lang).to_s + "</notextile>"
21
+ end
22
+
23
+ def render_codehighlighter(context, code)
24
+ #The div is required because RDiscount blows ass
25
+ <<-HTML
26
+ <div>
27
+ <pre>
28
+ <code class='#{@lang}'>#{h(code).strip}</code>
29
+ </pre>
30
+ </div>
31
+ HTML
32
+ end
33
+ end
34
+
35
+ end
36
+
37
+ Liquid::Template.register_tag('highlight', Jekyll::HighlightBlock)