ddemaree-jekyll 0.2.1

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,158 @@
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
+ # Convert this post into a Hash for use in Liquid templates.
147
+ #
148
+ # Returns <Hash>
149
+ def to_liquid
150
+ self.data.merge({ "title" => self.data["title"] || "",
151
+ "url" => self.url,
152
+ "date" => self.date,
153
+ "id" => self.id,
154
+ "content" => self.content })
155
+ end
156
+ end
157
+
158
+ end
@@ -0,0 +1,130 @@
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
+ self.posts << Post.new(base, f) if Post.valid?(f)
58
+ end
59
+
60
+ self.posts.sort!
61
+ rescue Errno::ENOENT => e
62
+ # ignore missing layout dir
63
+ end
64
+
65
+ # Write each post to <dest>/<year>/<month>/<day>/<slug>
66
+ #
67
+ # Returns nothing
68
+ def write_posts
69
+ self.posts.each do |post|
70
+ post.add_layout(self.layouts, site_payload)
71
+ post.write(self.dest)
72
+ end
73
+ end
74
+
75
+ # Copy all regular files from <source> to <dest>/ ignoring
76
+ # any files/directories that are hidden (start with ".") or contain
77
+ # site content (start with "_") unless they are "_posts" directories
78
+ # The +dir+ String is a relative path used to call this method
79
+ # recursively as it descends through directories
80
+ #
81
+ # Returns nothing
82
+ def transform_pages(dir = '')
83
+ base = File.join(self.source, dir)
84
+ entries = Dir.entries(base)
85
+ entries = entries.reject { |e|
86
+ (e != '_posts') and ['.', '_'].include?(e[0..0])
87
+ }
88
+
89
+ entries.each do |f|
90
+ if f == '_posts'
91
+ read_posts(File.join(base, f))
92
+ elsif File.directory?(File.join(base, f))
93
+ next if self.dest.sub(/\/$/, '') == File.join(base, f)
94
+ transform_pages(File.join(dir, f))
95
+ else
96
+ first3 = File.open(File.join(self.source, dir, f)) { |fd| fd.read(3) }
97
+
98
+ # if the file appears to have a YAML header then process it as a page
99
+ if first3 == "---"
100
+ page = Page.new(self.source, dir, f)
101
+ page.add_layout(self.layouts, site_payload)
102
+ page.write(self.dest)
103
+ # otherwise copy the file without transforming it
104
+ else
105
+ FileUtils.mkdir_p(File.join(self.dest, dir))
106
+ FileUtils.cp(File.join(self.source, dir, f), File.join(self.dest, dir, f))
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ # The Hash payload containing site-wide data
113
+ #
114
+ # Returns {"site" => {"time" => <Time>, "posts" => [<Post>]}}
115
+ def site_payload
116
+ # Build the category hash map of category ( names => arrays of posts )
117
+ # then sort each array in reverse order
118
+ categories = Hash.new { |hash,key| hash[key] = Array.new }
119
+ self.posts.each { |p| p.categories.each { |c| categories[c] << p } }
120
+ categories.values.map { |cats| cats.sort! { |a,b| b <=> a} }
121
+
122
+ {"site" => {
123
+ "time" => Time.now,
124
+ "posts" => self.posts.sort { |a,b| b <=> a },
125
+ "categories" => categories
126
+ }}
127
+ end
128
+ end
129
+
130
+ 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)