oaktree 0.4.4
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.
- checksums.yaml +7 -0
- data/COPYING +6 -0
- data/README.md +46 -0
- data/Rakefile +21 -0
- data/bin/oak +285 -0
- data/lib/oaktree/kramdown/oak_html.rb +48 -0
- data/lib/oaktree/post_data.rb +236 -0
- data/lib/oaktree/specification.rb +145 -0
- data/lib/oaktree/template/base.rb +26 -0
- data/lib/oaktree/template/blog.rb +340 -0
- data/lib/oaktree/template/post.rb +91 -0
- data/lib/oaktree/template/post_archive.rb +53 -0
- data/lib/oaktree/template.rb +19 -0
- data/lib/oaktree.rb +144 -0
- metadata +90 -0
@@ -0,0 +1,145 @@
|
|
1
|
+
require 'date'
|
2
|
+
|
3
|
+
class OakTree
|
4
|
+
|
5
|
+
# Specifications for the blog, operates similar to Gem::Specification.
|
6
|
+
# URLs and paths are strings and should not end in a slash.
|
7
|
+
class Specification
|
8
|
+
@@KEY_VALUE_PATTERN = /^\s*(?<key>[\w_]+)\s*:\s*(?<value>.*?)\s*(#|$)/
|
9
|
+
@@DEFAULT_DATE_PATH_FORMAT = '%Y/%m/'
|
10
|
+
@@DEFAULT_SLUG_SEPARATOR = '_'
|
11
|
+
|
12
|
+
def self.default_date_path_format
|
13
|
+
@@DEFAULT_DATE_PATH_FORMAT
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.default_slug_separator
|
17
|
+
@@DEFAULT_SLUG_SEPARATOR
|
18
|
+
end
|
19
|
+
|
20
|
+
# The blog's title
|
21
|
+
attr_accessor :title
|
22
|
+
# A description of the blog
|
23
|
+
attr_accessor :description
|
24
|
+
# The blog root, where files are stored locally.
|
25
|
+
# Beneath this directory, there should be /source and /public directories,
|
26
|
+
# where post sources and the blog output, respectively, are stored. If these
|
27
|
+
# don't exist, they'll be created when generating the blog.
|
28
|
+
# This cannot be changed.
|
29
|
+
attr_reader :blog_root
|
30
|
+
# The name of the blog's author (currently assumes a single author)
|
31
|
+
attr_accessor :author
|
32
|
+
# The number of posts displayed per page
|
33
|
+
attr_accessor :posts_per_page
|
34
|
+
# Whether the timeline is reversed
|
35
|
+
attr_accessor :reversed
|
36
|
+
# The date format for post paths
|
37
|
+
attr_accessor :date_path_format
|
38
|
+
# The separator for words in slugs. May not be whitespace if loaded from a
|
39
|
+
# blog_spec file.
|
40
|
+
attr_accessor :slug_separator
|
41
|
+
# The length of the RSS feed in number of posts. Defaults to 20-most-recent.
|
42
|
+
attr_accessor :rss_length
|
43
|
+
|
44
|
+
# Sets the post path (i.e., the subdirectory where posts are stored).
|
45
|
+
# Should not begin with a slash, but can have a trailing slash if you want.
|
46
|
+
# If there is no trailing slash, it will be part of the filename up to a
|
47
|
+
# a point.
|
48
|
+
def post_path= path
|
49
|
+
raise "post_path provided is nil" if path.nil?
|
50
|
+
raise "post_path provided is not a string" unless path.kind_of?(String)
|
51
|
+
|
52
|
+
@post_path = path.clone().freeze()
|
53
|
+
end
|
54
|
+
|
55
|
+
# Gets the post path (i.e., the subdirectory where posts are stored).
|
56
|
+
def post_path
|
57
|
+
@post_path
|
58
|
+
end
|
59
|
+
|
60
|
+
def date_path_format= format
|
61
|
+
if format.empty?
|
62
|
+
@date_path_format = self.class.default_date_path_format
|
63
|
+
else
|
64
|
+
@date_path_format = format
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Sets the base URL of the blog (i.e., http://localhost/blog) - should have
|
69
|
+
# a trailing slash.
|
70
|
+
def base_url= url
|
71
|
+
url = String.new(url)
|
72
|
+
url << '/' unless url.end_with? '/'
|
73
|
+
@base_url = url.freeze()
|
74
|
+
end
|
75
|
+
|
76
|
+
# Gets the base URL of the blog (i.e., http://localhost/blog).
|
77
|
+
def base_url
|
78
|
+
@base_url
|
79
|
+
end
|
80
|
+
|
81
|
+
def sources_root
|
82
|
+
@sources_root ||= "#{@blog_root}source/"
|
83
|
+
end
|
84
|
+
|
85
|
+
# Loads a specification from a file.
|
86
|
+
def self.from_file(path)
|
87
|
+
raise "Spec file does not exist" unless File.exists? path
|
88
|
+
|
89
|
+
self.new { |spec|
|
90
|
+
|
91
|
+
spec_contents = File.open(path, 'r') { |io| io.read }
|
92
|
+
spec_hash = Psych.load(spec_contents)
|
93
|
+
spec_hash.each {
|
94
|
+
|key, value|
|
95
|
+
setter_sym = :"#{key}="
|
96
|
+
if spec.respond_to? setter_sym
|
97
|
+
spec.send setter_sym, value
|
98
|
+
else
|
99
|
+
raise "Invalid key/value in spec: #{key} => #{value}"
|
100
|
+
end
|
101
|
+
}
|
102
|
+
|
103
|
+
Dir.chdir(File.dirname(path))
|
104
|
+
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
# Initializes the Specification with its default values.
|
109
|
+
def initialize
|
110
|
+
# initialize default values for most properties
|
111
|
+
self.title = ''
|
112
|
+
self.description = ''
|
113
|
+
self.base_url = ''
|
114
|
+
self.post_path = 'post/'
|
115
|
+
self.author = ''
|
116
|
+
self.posts_per_page = 10
|
117
|
+
self.reversed = false
|
118
|
+
self.date_path_format = self.class.default_date_path_format
|
119
|
+
self.slug_separator = self.class.default_slug_separator
|
120
|
+
self.rss_length = 20
|
121
|
+
|
122
|
+
yield self if block_given?
|
123
|
+
|
124
|
+
@blog_root = File.expand_path(Dir.getwd) + '/'
|
125
|
+
end
|
126
|
+
|
127
|
+
def export_string
|
128
|
+
<<-EOT
|
129
|
+
# metadata
|
130
|
+
title: #{@title}
|
131
|
+
description: #{@description}
|
132
|
+
author: #{@author}
|
133
|
+
posts_per_page: 10
|
134
|
+
|
135
|
+
# public URL
|
136
|
+
base_url: #{@base_url}
|
137
|
+
|
138
|
+
# public content paths
|
139
|
+
post_path: #{@post_path}
|
140
|
+
EOT
|
141
|
+
end
|
142
|
+
|
143
|
+
end # Specification
|
144
|
+
|
145
|
+
end # OakTree
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'date'
|
2
|
+
require 'mustache'
|
3
|
+
require 'oaktree/template'
|
4
|
+
|
5
|
+
class OakTree
|
6
|
+
|
7
|
+
module Template
|
8
|
+
|
9
|
+
class Base < Mustache
|
10
|
+
DEFAULT_DATETIME_FORMAT = '%-d %B %Y @ %l:%M %p'
|
11
|
+
|
12
|
+
def proc_for_datetime time
|
13
|
+
proc { |format|
|
14
|
+
format ||= DEFAULT_DATETIME_FORMAT
|
15
|
+
|
16
|
+
render(time.strftime(format))
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
self.template_path = 'template'
|
21
|
+
|
22
|
+
end # Base
|
23
|
+
|
24
|
+
end # Template
|
25
|
+
|
26
|
+
end # OakTree
|
@@ -0,0 +1,340 @@
|
|
1
|
+
require 'mustache'
|
2
|
+
require 'date'
|
3
|
+
require 'oaktree/template/base'
|
4
|
+
require 'oaktree/template/post'
|
5
|
+
require 'oaktree/template/post_archive'
|
6
|
+
require 'uri'
|
7
|
+
|
8
|
+
class OakTree
|
9
|
+
|
10
|
+
module Template
|
11
|
+
|
12
|
+
class Blog < Base
|
13
|
+
|
14
|
+
@@MODES = [:home, :archive, :single, :statics, :rss_feed].freeze()
|
15
|
+
@@MODE_TEMPLATES = {
|
16
|
+
:home => 'home'.freeze(),
|
17
|
+
:archive => 'archive'.freeze(),
|
18
|
+
:single => 'single'.freeze(),
|
19
|
+
:statics => 'statics'.freeze(),
|
20
|
+
:rss_feed => 'rss_feed'.freeze()
|
21
|
+
}
|
22
|
+
|
23
|
+
def self.modes
|
24
|
+
@@MODES
|
25
|
+
end
|
26
|
+
|
27
|
+
# tag to provide simple URL encoding
|
28
|
+
def url_encode
|
29
|
+
proc {
|
30
|
+
|input|
|
31
|
+
render(URI.encode_www_form_component(render(input)))
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
# returns an enumerator for all modes supported by the template
|
36
|
+
def modes
|
37
|
+
self.class.modes
|
38
|
+
end
|
39
|
+
|
40
|
+
# the default template file
|
41
|
+
self.template_name = 'blog'
|
42
|
+
|
43
|
+
def self.template_for_mode(mode)
|
44
|
+
tmp = @@MODE_TEMPLATES[mode]
|
45
|
+
tpath = File.expand_path("#{self.template_path}/#{tmp}.mustache")
|
46
|
+
(tmp && File.exists?(tpath)) ? tmp : self.template_name
|
47
|
+
end
|
48
|
+
|
49
|
+
def initialize tree, options = {}
|
50
|
+
@tree = tree
|
51
|
+
@spec = tree.blogspec
|
52
|
+
@page_index = 0
|
53
|
+
self.mode = options[:mode] || :home
|
54
|
+
raise "Invalid mode" unless @@MODES.include? @mode
|
55
|
+
|
56
|
+
@postdata = tree.posts.map { |post|
|
57
|
+
Post.new(@spec, post)
|
58
|
+
}.select(&:published?)
|
59
|
+
|
60
|
+
@posts = @postdata.select(&:post?)
|
61
|
+
@sorted_posts = @posts.sort_by { |p| p.post_data.time }.reverse!
|
62
|
+
@statics = @postdata.select(&:static?)
|
63
|
+
|
64
|
+
@posts.freeze
|
65
|
+
@statics.freeze
|
66
|
+
|
67
|
+
# build the archive
|
68
|
+
@archive = []
|
69
|
+
@posts.map { |post|
|
70
|
+
data = post.post_data
|
71
|
+
time = data.time
|
72
|
+
|
73
|
+
arch = @archive.last
|
74
|
+
if arch.nil? || arch.year != time.year || arch.month != time.month
|
75
|
+
arch = PostArchive.new(time.year, time.month, [], @spec, self) { |a|
|
76
|
+
a.next_archive = arch
|
77
|
+
arch.previous_archive = a unless arch.nil?
|
78
|
+
}
|
79
|
+
@archive << arch
|
80
|
+
end
|
81
|
+
|
82
|
+
arch.posts << post
|
83
|
+
}
|
84
|
+
@archive.freeze
|
85
|
+
|
86
|
+
page = options[:page]
|
87
|
+
page = page.nil? ? 0 : page - 1
|
88
|
+
@page_index = page
|
89
|
+
end
|
90
|
+
|
91
|
+
def mode= mode
|
92
|
+
raise "Invalid mode" unless @@MODES.include? mode
|
93
|
+
return mode if @mode == mode
|
94
|
+
@mode = mode
|
95
|
+
self.template_name = self.class.template_for_mode(mode)
|
96
|
+
end
|
97
|
+
|
98
|
+
def mode
|
99
|
+
@mode
|
100
|
+
end
|
101
|
+
|
102
|
+
### blog tags
|
103
|
+
|
104
|
+
def local_path
|
105
|
+
path = @spec.blog_root + "public/"
|
106
|
+
date_format = @spec.date_path_format
|
107
|
+
|
108
|
+
if home? && @page_index == 0
|
109
|
+
path << "index.html"
|
110
|
+
elsif single? || statics?
|
111
|
+
return post.post_data.public_path
|
112
|
+
elsif rss_feed?
|
113
|
+
path << 'feeds/rss.xml'
|
114
|
+
else
|
115
|
+
path << @spec.post_path
|
116
|
+
|
117
|
+
case mode
|
118
|
+
when :home
|
119
|
+
path << "#{@page_index}.html"
|
120
|
+
when :archive
|
121
|
+
arch = @archive[@page_index]
|
122
|
+
archdate = DateTime.new(arch.year, arch.month, 1)
|
123
|
+
path << "#{archdate.strftime(date_format)}"
|
124
|
+
end
|
125
|
+
|
126
|
+
path << 'index.html' if path.end_with? '/'
|
127
|
+
end
|
128
|
+
|
129
|
+
path
|
130
|
+
end
|
131
|
+
|
132
|
+
def blog_title
|
133
|
+
@spec.title
|
134
|
+
end
|
135
|
+
|
136
|
+
def blog_author
|
137
|
+
@spec.author
|
138
|
+
end
|
139
|
+
|
140
|
+
def blog_description
|
141
|
+
@spec.description
|
142
|
+
end
|
143
|
+
|
144
|
+
def blog_url
|
145
|
+
@spec.base_url
|
146
|
+
end
|
147
|
+
|
148
|
+
### page tags
|
149
|
+
|
150
|
+
def archive?
|
151
|
+
@mode == :archive
|
152
|
+
end
|
153
|
+
|
154
|
+
def single?
|
155
|
+
@mode == :single
|
156
|
+
end
|
157
|
+
|
158
|
+
def home?
|
159
|
+
@mode == :home
|
160
|
+
end
|
161
|
+
|
162
|
+
def statics?
|
163
|
+
@mode == :statics
|
164
|
+
end
|
165
|
+
|
166
|
+
def rss_feed?
|
167
|
+
@mode == :rss_feed
|
168
|
+
end
|
169
|
+
|
170
|
+
def rss_feed_url
|
171
|
+
"#{@spec.base_url}feeds/rss.xml"
|
172
|
+
end
|
173
|
+
|
174
|
+
# uses the input as a format string for the first day of the month and year
|
175
|
+
# of the archive page.
|
176
|
+
# this returns nil if not in archive mode.
|
177
|
+
def archive_date
|
178
|
+
return nil unless archive?
|
179
|
+
proc_for_datetime(@archive[@page_index])
|
180
|
+
end
|
181
|
+
|
182
|
+
def statics
|
183
|
+
@statics
|
184
|
+
end
|
185
|
+
|
186
|
+
# returns the number of pages
|
187
|
+
def pages
|
188
|
+
case mode
|
189
|
+
# you can render multiple home pages, but it's not something I recommend
|
190
|
+
# since re-syncing all homepage posts is a nightmare at some point
|
191
|
+
when :home
|
192
|
+
if @spec.posts_per_page > 0
|
193
|
+
(@posts.length / @spec.posts_per_page) + 1
|
194
|
+
else
|
195
|
+
1
|
196
|
+
end
|
197
|
+
|
198
|
+
when :archive ; @archive.length
|
199
|
+
when :single ; @posts.length
|
200
|
+
when :statics ; @statics.length
|
201
|
+
when :rss_feed ; 1
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
# returns the current page number
|
206
|
+
def page
|
207
|
+
@page_index + 1
|
208
|
+
end
|
209
|
+
|
210
|
+
def page= page_num
|
211
|
+
@next_url_cache = nil
|
212
|
+
@prev_url_cache = nil
|
213
|
+
@posts_cache = nil
|
214
|
+
@page_index = (page_num - 1)
|
215
|
+
end
|
216
|
+
|
217
|
+
def paged?
|
218
|
+
has_previous? || has_next?
|
219
|
+
end
|
220
|
+
|
221
|
+
# determines whether there's a previous page. previous also means older.
|
222
|
+
def has_previous?
|
223
|
+
self.page < self.pages
|
224
|
+
end
|
225
|
+
|
226
|
+
# determines whether there is a next page. next also means newer.
|
227
|
+
def has_next?
|
228
|
+
1 < self.page
|
229
|
+
end
|
230
|
+
|
231
|
+
def next_url
|
232
|
+
return "" unless has_next?
|
233
|
+
|
234
|
+
@next_url_cache ||= case mode
|
235
|
+
when :home
|
236
|
+
if @page_index == 1
|
237
|
+
blog_url
|
238
|
+
else
|
239
|
+
blog_url + @spec.post_path + "#{@page_index - 1}.html"
|
240
|
+
end
|
241
|
+
when :archive ; @archive[@page_index - 1].permalink
|
242
|
+
when :single ; @posts[@page_index - 1].permalink
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def previous_url
|
247
|
+
return "" unless has_previous?
|
248
|
+
|
249
|
+
@prev_url_cache ||= case mode
|
250
|
+
when :home ; blog_url + @spec.post_path + "#{@page_index + 1}.html"
|
251
|
+
when :archive ; @archive[@page_index + 1].permalink
|
252
|
+
when :single ; @posts[@page_index + 1].permalink
|
253
|
+
end
|
254
|
+
end
|
255
|
+
|
256
|
+
### archive tags
|
257
|
+
|
258
|
+
def archives
|
259
|
+
@archive
|
260
|
+
end
|
261
|
+
|
262
|
+
# the current archive
|
263
|
+
def archive
|
264
|
+
@archive[@page_index] if archive?
|
265
|
+
end
|
266
|
+
|
267
|
+
### post tags
|
268
|
+
|
269
|
+
# returns all visible posts
|
270
|
+
# note: visible means whatever is in the current page
|
271
|
+
def posts
|
272
|
+
@posts_cache ||= case mode
|
273
|
+
when :home
|
274
|
+
if @spec.posts_per_page > 0
|
275
|
+
page_start = @page_index * @spec.posts_per_page
|
276
|
+
page_end = page_start + @spec.posts_per_page - 1
|
277
|
+
if @posts.length < page_end
|
278
|
+
page_end = @posts.length
|
279
|
+
end
|
280
|
+
|
281
|
+
return [] unless page_start < @posts.length
|
282
|
+
|
283
|
+
@posts[page_start .. page_end]
|
284
|
+
else
|
285
|
+
@posts[0..-1]
|
286
|
+
end
|
287
|
+
|
288
|
+
when :archive ; @archive[@page_index].posts
|
289
|
+
when :single ; [@posts[@page_index]]
|
290
|
+
when :statics ; [@statics[@page_index]]
|
291
|
+
|
292
|
+
when :rss_feed
|
293
|
+
rss_length = @spec.rss_length - 1
|
294
|
+
rss_length = -1 if rss_length < -1
|
295
|
+
@sorted_posts[0..(rss_length - 1)]
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
# if there's only one post being displayed, this returns its template.
|
300
|
+
# only works in single mode.
|
301
|
+
def post
|
302
|
+
case mode
|
303
|
+
when :single ; @posts[@page_index]
|
304
|
+
when :statics ; @statics[@page_index]
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
# returns the next post (you generally shouldn't use this except to get
|
309
|
+
# some small bit of info about the next post).
|
310
|
+
# only works in single mode.
|
311
|
+
def next_post
|
312
|
+
single? && has_next? ? @posts[page_index - 1] : nil
|
313
|
+
end
|
314
|
+
|
315
|
+
# returns the previous post.
|
316
|
+
# only works in single mode.
|
317
|
+
def previous_post
|
318
|
+
single? && has_previous? ? @posts[page_index + 1] : nil
|
319
|
+
end
|
320
|
+
|
321
|
+
def next_archive
|
322
|
+
archive? && has_next? ? @archive[page_index - 1] : nil
|
323
|
+
end
|
324
|
+
|
325
|
+
def previous_archive
|
326
|
+
archive? && has_previous? ? @archive[page_index + 1] : nil
|
327
|
+
end
|
328
|
+
|
329
|
+
### general tags
|
330
|
+
|
331
|
+
# treats the input text as a format string for today's date and time
|
332
|
+
def today
|
333
|
+
proc_for_datetime(DateTime.now)
|
334
|
+
end
|
335
|
+
|
336
|
+
end # Blog
|
337
|
+
|
338
|
+
end # Template
|
339
|
+
|
340
|
+
end # OakTree
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'kramdown'
|
2
|
+
require 'oaktree/template'
|
3
|
+
require 'oaktree/kramdown/oak_html'
|
4
|
+
|
5
|
+
class OakTree
|
6
|
+
|
7
|
+
module Template
|
8
|
+
|
9
|
+
class Post < Base
|
10
|
+
|
11
|
+
self.template_name = 'post'
|
12
|
+
|
13
|
+
def initialize spec, post
|
14
|
+
@post = post
|
15
|
+
@content = nil
|
16
|
+
@spec = spec
|
17
|
+
document = ::Kramdown::Document.new(@post.content)
|
18
|
+
@content, warnings = ::OakTree::Kramdown::OakHtml.convert(document.root, :auto_id_prefix => @post.time.strftime('%Y_%m_%d_'))
|
19
|
+
puts warnings unless warnings.empty?
|
20
|
+
end
|
21
|
+
|
22
|
+
def post_data
|
23
|
+
@post
|
24
|
+
end
|
25
|
+
|
26
|
+
def title
|
27
|
+
@post.title
|
28
|
+
end
|
29
|
+
|
30
|
+
def url
|
31
|
+
if source_link?
|
32
|
+
source_link
|
33
|
+
else
|
34
|
+
permalink
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def permalink
|
39
|
+
@post.permalink
|
40
|
+
end
|
41
|
+
|
42
|
+
def source_link
|
43
|
+
@post.link
|
44
|
+
end
|
45
|
+
|
46
|
+
def source_link?
|
47
|
+
plink = @post.link
|
48
|
+
! (plink.nil? || plink.empty?)
|
49
|
+
end
|
50
|
+
|
51
|
+
def static?
|
52
|
+
@post.kind == :static
|
53
|
+
end
|
54
|
+
|
55
|
+
def post?
|
56
|
+
@post.kind == :post
|
57
|
+
end
|
58
|
+
|
59
|
+
def content
|
60
|
+
@content
|
61
|
+
end
|
62
|
+
|
63
|
+
def time
|
64
|
+
proc_for_datetime(@post.time)
|
65
|
+
end
|
66
|
+
|
67
|
+
def public_path
|
68
|
+
@post.public_path
|
69
|
+
end
|
70
|
+
|
71
|
+
def slug
|
72
|
+
@post.slug
|
73
|
+
end
|
74
|
+
|
75
|
+
def published?
|
76
|
+
@post.status == :published
|
77
|
+
end
|
78
|
+
|
79
|
+
def unpublished?
|
80
|
+
! published?
|
81
|
+
end
|
82
|
+
|
83
|
+
def status
|
84
|
+
@post.status
|
85
|
+
end
|
86
|
+
|
87
|
+
end # Post
|
88
|
+
|
89
|
+
end # Template
|
90
|
+
|
91
|
+
end # OakTree
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'oaktree/template/base'
|
2
|
+
require 'mustache'
|
3
|
+
require 'date'
|
4
|
+
|
5
|
+
class OakTree
|
6
|
+
|
7
|
+
module Template
|
8
|
+
|
9
|
+
class PostArchive < Base
|
10
|
+
self.template_name = "postarchive"
|
11
|
+
|
12
|
+
attr_accessor :year
|
13
|
+
attr_accessor :month
|
14
|
+
attr_accessor :posts
|
15
|
+
# the next most recent archive
|
16
|
+
attr_accessor :next_archive
|
17
|
+
# the next oldest archive
|
18
|
+
attr_accessor :previous_archive
|
19
|
+
|
20
|
+
def initialize year=1, month=1, posts=[], spec, blog
|
21
|
+
@year = year
|
22
|
+
@month = month
|
23
|
+
@posts = posts
|
24
|
+
@spec = spec
|
25
|
+
@blog = blog
|
26
|
+
@datetime = DateTime.new(year, month, 1)
|
27
|
+
formatted_date = @datetime.strftime(@spec.date_path_format)
|
28
|
+
@permalink = "#{@spec.base_url}#{@spec.post_path}#{formatted_date}"
|
29
|
+
|
30
|
+
yield self if block_given?
|
31
|
+
end
|
32
|
+
|
33
|
+
def date
|
34
|
+
proc_for_datetime(@datetime)
|
35
|
+
end
|
36
|
+
|
37
|
+
def datetime
|
38
|
+
@datetime
|
39
|
+
end
|
40
|
+
|
41
|
+
def permalink
|
42
|
+
@permalink
|
43
|
+
end
|
44
|
+
|
45
|
+
def open?
|
46
|
+
self == @blog.archive
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'mustache'
|
2
|
+
require 'date'
|
3
|
+
|
4
|
+
class OakTree
|
5
|
+
|
6
|
+
module Template
|
7
|
+
|
8
|
+
# Probably shouldn't do this, but it simplifies life.
|
9
|
+
Mustache.template_path = 'template'
|
10
|
+
|
11
|
+
autoload :Base, 'oaktree/template/base'
|
12
|
+
autoload :PostArchive, 'oaktree/template/post_archive'
|
13
|
+
autoload :Post, 'oaktree/template/post'
|
14
|
+
autoload :Blog, 'oaktree/template/blog'
|
15
|
+
|
16
|
+
end # Template
|
17
|
+
|
18
|
+
end # OakTree
|
19
|
+
|