serif 0.1.2 → 0.1.3
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile.lock +3 -9
- data/README.md +61 -2
- data/lib/serif/config.rb +14 -0
- data/lib/serif/server.rb +10 -1
- data/lib/serif/site.rb +140 -6
- data/serif.gemspec +2 -2
- data/statics/skeleton/_config.yml +4 -0
- data/statics/skeleton/_drafts/sample-draft +3 -0
- data/statics/skeleton/_posts/2012-01-05-sample-post +4 -0
- data/statics/skeleton/_templates/archive_page.html +9 -0
- data/statics/skeleton/archive.html +7 -0
- metadata +11 -7
data/Gemfile.lock
CHANGED
@@ -1,26 +1,21 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
serif (0.1.
|
4
|
+
serif (0.1.2)
|
5
5
|
liquid (~> 2.4)
|
6
6
|
pygments.rb (~> 0.3)
|
7
7
|
rack (~> 1.0)
|
8
|
+
rack-rewrite (~> 1.3.0)
|
8
9
|
rake (~> 0.9)
|
9
10
|
redcarpet (~> 2.2)
|
10
11
|
redhead (~> 0.0.6)
|
11
12
|
sinatra (~> 1.3)
|
12
13
|
slop (~> 3.3)
|
13
|
-
yui-compressor
|
14
14
|
|
15
15
|
GEM
|
16
16
|
remote: http://rubygems.org/
|
17
17
|
specs:
|
18
|
-
POpen4 (0.1.4)
|
19
|
-
Platform (>= 0.4.0)
|
20
|
-
open4
|
21
|
-
Platform (0.4.0)
|
22
18
|
liquid (2.4.1)
|
23
|
-
open4 (1.3.0)
|
24
19
|
posix-spawn (0.3.6)
|
25
20
|
pygments.rb (0.3.2)
|
26
21
|
posix-spawn (~> 0.3.6)
|
@@ -28,6 +23,7 @@ GEM
|
|
28
23
|
rack (1.4.1)
|
29
24
|
rack-protection (1.2.0)
|
30
25
|
rack
|
26
|
+
rack-rewrite (1.3.1)
|
31
27
|
rake (0.9.2.2)
|
32
28
|
redcarpet (2.2.2)
|
33
29
|
redhead (0.0.6)
|
@@ -38,8 +34,6 @@ GEM
|
|
38
34
|
slop (3.3.3)
|
39
35
|
tilt (1.3.3)
|
40
36
|
yajl-ruby (1.1.0)
|
41
|
-
yui-compressor (0.9.6)
|
42
|
-
POpen4 (>= 0.1.4)
|
43
37
|
|
44
38
|
PLATFORMS
|
45
39
|
ruby
|
data/README.md
CHANGED
@@ -95,7 +95,8 @@ The structure of a Serif site is something like this:
|
|
95
95
|
│ ├── 2012-02-28-another-post
|
96
96
|
│ └── 2012-03-30-and-a-third
|
97
97
|
├── _templates
|
98
|
-
│
|
98
|
+
│ ├─── post.html
|
99
|
+
│ └─── archive_page.html
|
99
100
|
├── _trash
|
100
101
|
├── _config.yml
|
101
102
|
├── css
|
@@ -147,7 +148,12 @@ The headers are similar to Jekyll's YAML front matter, but here there are no for
|
|
147
148
|
|
148
149
|
## `_templates`
|
149
150
|
|
150
|
-
|
151
|
+
Two templates are available:
|
152
|
+
|
153
|
+
* `post.html`, which will be used to render individual post pages.
|
154
|
+
* `archive_page.html`, which will be used to render individual archive pages.
|
155
|
+
|
156
|
+
Both must be valid Liquid templates.
|
151
157
|
|
152
158
|
## `_trash`
|
153
159
|
|
@@ -207,6 +213,59 @@ In both cases, the output is, of course:
|
|
207
213
|
|
208
214
|
If you have a file like `feed.xml` that you wish to _not_ be contained within a layout, specify `layout: none` in the header for the file.
|
209
215
|
|
216
|
+
# Archive pages
|
217
|
+
|
218
|
+
By default, archive pages are made available at `/archive/:year/month`, e.g., `/archive/2012/11`. Individual archive pages can be customised by editing the `_templates/archive_page.html` file, which is used for each month.
|
219
|
+
|
220
|
+
Within the `archive_page.html` template, you have access to the variables `month`, which is a Ruby Date instance, and `posts` for the posts within that month.
|
221
|
+
|
222
|
+
To disable archive pages, or configure the URL format, see the section on configuration.
|
223
|
+
|
224
|
+
## Linking to archive pages
|
225
|
+
|
226
|
+
To link to archive pages, there is a `site.archives` template variable available in all pages. The structure of `site.archives` is a nested map starting at years:
|
227
|
+
|
228
|
+
```
|
229
|
+
{
|
230
|
+
"posts" => [...],
|
231
|
+
"years" => [
|
232
|
+
{
|
233
|
+
"date" => Date.new(2012),
|
234
|
+
"posts" => [...],
|
235
|
+
"months" => [
|
236
|
+
{ "date" => Date.new(2012, 12), "archive_url" => "/archive/2012/12", "posts" => [...] },
|
237
|
+
{ "date" => Date.new(2012, 11), "archive_url" => "/archive/2012/11", "posts" => [...] },
|
238
|
+
...
|
239
|
+
]
|
240
|
+
}
|
241
|
+
]
|
242
|
+
}
|
243
|
+
```
|
244
|
+
|
245
|
+
Using `site.archives`, you can iterate over `years`, then iterate over `months` and use `archive_url` to link to the archive page for that given month within the year.
|
246
|
+
|
247
|
+
# Configuration
|
248
|
+
|
249
|
+
Configuration goes in `_config.yml` and must be valid YAML. Here's a sample configuration with available options:
|
250
|
+
|
251
|
+
```
|
252
|
+
admin:
|
253
|
+
username: myusername
|
254
|
+
password: mypassword
|
255
|
+
permalink: /posts/:title
|
256
|
+
archive:
|
257
|
+
enabled: yes
|
258
|
+
url_format: /archive/:year/:month
|
259
|
+
```
|
260
|
+
|
261
|
+
`admin` contains the credentials used when accessing the admin interface. This information is private, of course.
|
262
|
+
|
263
|
+
`permalink` is the URL format for individual post pages. The default permalink value is `/:title`. For an explanation of the format of permalinks, see above.
|
264
|
+
|
265
|
+
`archive` contains configuration options concerning archive pages. `enabled` can be used to toggle whether archive pages are generated. If set to `no` or `false`, no archive pages will be generated. By default, this value is `yes`.
|
266
|
+
|
267
|
+
The `archive` `url_format` configuration option is the format used for archive pages. The default value is `/archive/:year/:month`. **This must include both year and month.** Visiting, e.g., `/archive/2012/11` would render posts made in November 2012. See the section on archive pages above for more details.
|
268
|
+
|
210
269
|
# Deploying
|
211
270
|
|
212
271
|
To serve the site, set any web server to use `/path/to/site/directory/_site` as its root. *NOTE:* URLs generated in the site do not contain `.html` "extensions" by default, so you will need a rewrite rule. Here's an example rewrite for nginx:
|
data/lib/serif/config.rb
CHANGED
@@ -21,5 +21,19 @@ class Config
|
|
21
21
|
def permalink
|
22
22
|
yaml["permalink"] || "/:title"
|
23
23
|
end
|
24
|
+
|
25
|
+
def archive_enabled?
|
26
|
+
a = yaml["archive"]
|
27
|
+
|
28
|
+
if a
|
29
|
+
a["enabled"]
|
30
|
+
else
|
31
|
+
false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def archive_url_format
|
36
|
+
(yaml["archive"] || {})["url_format"] || "/archive/:year/:month"
|
37
|
+
end
|
24
38
|
end
|
25
39
|
end
|
data/lib/serif/server.rb
CHANGED
@@ -13,7 +13,16 @@ class DevelopmentServer
|
|
13
13
|
|
14
14
|
# it seems Rack::Rewrite doesn't like public_folder files, so here we are
|
15
15
|
get "*" do
|
16
|
-
|
16
|
+
# attempt the exact name + an extension
|
17
|
+
file = Dir[File.expand_path("_site#{params[:splat].join("/")}.*")].first
|
18
|
+
|
19
|
+
# try index.html under the directory if it failed. useful for archive directory requests.
|
20
|
+
file ||= Dir[File.expand_path("_site#{params[:splat].join("/")}/index.html")].first
|
21
|
+
|
22
|
+
# make a naive assumption that there's a 404 file at 404.html
|
23
|
+
file ||= Dir[File.expand_path("_site/404.html")].first
|
24
|
+
|
25
|
+
File.read(file)
|
17
26
|
end
|
18
27
|
end
|
19
28
|
|
data/lib/serif/site.rb
CHANGED
@@ -19,6 +19,7 @@ else
|
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
+
module Serif
|
22
23
|
module Filters
|
23
24
|
def strip(input)
|
24
25
|
input.strip
|
@@ -36,8 +37,9 @@ module Filters
|
|
36
37
|
input.xmlschema
|
37
38
|
end
|
38
39
|
end
|
40
|
+
end
|
39
41
|
|
40
|
-
Liquid::Template.register_filter(Filters)
|
42
|
+
Liquid::Template.register_filter(Serif::Filters)
|
41
43
|
|
42
44
|
module Serif
|
43
45
|
class Site
|
@@ -49,8 +51,10 @@ class Site
|
|
49
51
|
@source_directory
|
50
52
|
end
|
51
53
|
|
54
|
+
# Returns all of the site's posts, in reverse chronological order
|
55
|
+
# by creation time.
|
52
56
|
def posts
|
53
|
-
Post.all(self)
|
57
|
+
Post.all(self).sort_by { |entry| entry.created }.reverse
|
54
58
|
end
|
55
59
|
|
56
60
|
def drafts
|
@@ -78,7 +82,97 @@ class Site
|
|
78
82
|
!%w[.html .xml].include?(File.extname(filename))
|
79
83
|
end
|
80
84
|
|
81
|
-
#
|
85
|
+
# Returns the relative archive URL for the given date,
|
86
|
+
# using the value of config.archive_url_format
|
87
|
+
def archive_url_for_date(date)
|
88
|
+
format = config.archive_url_format
|
89
|
+
|
90
|
+
parts = {
|
91
|
+
"year" => date.year.to_s,
|
92
|
+
"month" => date.month.to_s.rjust(2, "0")
|
93
|
+
}
|
94
|
+
|
95
|
+
output = format
|
96
|
+
|
97
|
+
parts.each do |placeholder, value|
|
98
|
+
output = output.gsub(Regexp.quote(":" + placeholder), value)
|
99
|
+
end
|
100
|
+
|
101
|
+
output
|
102
|
+
end
|
103
|
+
|
104
|
+
# Returns a nested hash with the following structure:
|
105
|
+
#
|
106
|
+
# {
|
107
|
+
# :posts => [],
|
108
|
+
# :years => [
|
109
|
+
# {
|
110
|
+
# :date => Date.new(2012),
|
111
|
+
# :posts => [],
|
112
|
+
# :months => [
|
113
|
+
# { :date => Date.new(2012, 12), :archive_url => "/archive/2012/12", :posts => [] },
|
114
|
+
# { :date => Date.new(2012, 11), :archive_url => "/archive/2012/11", :posts => [] },
|
115
|
+
# # ...
|
116
|
+
# ]
|
117
|
+
# },
|
118
|
+
#
|
119
|
+
# # ...
|
120
|
+
# ]
|
121
|
+
# }
|
122
|
+
def archives
|
123
|
+
h = {}
|
124
|
+
h[:posts] = posts
|
125
|
+
|
126
|
+
# group posts by Date instances for the first day of the year
|
127
|
+
year_groups = posts.group_by { |post| Date.new(post.created.year) }.to_a
|
128
|
+
|
129
|
+
# collect all elements as maps for the year start date and the posts in that year
|
130
|
+
year_groups.map! do |year_start_date, posts_by_year|
|
131
|
+
{
|
132
|
+
:date => year_start_date,
|
133
|
+
:posts => posts_by_year.sort_by { |post| post.created }
|
134
|
+
}
|
135
|
+
end
|
136
|
+
|
137
|
+
year_groups.sort_by! { |year_hash| year_hash[:date] }
|
138
|
+
year_groups.reverse!
|
139
|
+
|
140
|
+
year_groups.each do |year_hash|
|
141
|
+
year_posts = year_hash[:posts]
|
142
|
+
|
143
|
+
# group the posts in the year by month
|
144
|
+
month_groups = year_posts.group_by { |post| Date.new(post.created.year, post.created.month) }.to_a
|
145
|
+
|
146
|
+
# collect the elements as maps for the month start date and the posts in that month
|
147
|
+
month_groups.map! do |month_start_date, posts_by_month|
|
148
|
+
{
|
149
|
+
:date => month_start_date,
|
150
|
+
:posts => posts_by_month.sort_by { |post| post.created },
|
151
|
+
:archive_url => archive_url_for_date(month_start_date)
|
152
|
+
}
|
153
|
+
end
|
154
|
+
|
155
|
+
month_groups.sort_by! { |month_hash| month_hash[:date] }
|
156
|
+
month_groups.reverse!
|
157
|
+
|
158
|
+
# set the months for the current year
|
159
|
+
year_hash[:months] = month_groups
|
160
|
+
end
|
161
|
+
|
162
|
+
h[:years] = year_groups
|
163
|
+
|
164
|
+
# return the final hash
|
165
|
+
h
|
166
|
+
end
|
167
|
+
|
168
|
+
def to_liquid
|
169
|
+
{
|
170
|
+
"posts" => posts,
|
171
|
+
"latest_update_time" => latest_update_time,
|
172
|
+
"archive" => self.class.stringify_keys(archives)
|
173
|
+
}
|
174
|
+
end
|
175
|
+
|
82
176
|
def generate
|
83
177
|
FileUtils.cd(@source_directory)
|
84
178
|
|
@@ -88,7 +182,7 @@ class Site
|
|
88
182
|
files = Dir["**/*"].select { |f| f !~ /\A_/ && File.file?(f) }
|
89
183
|
|
90
184
|
layout = Liquid::Template.parse(File.read("_layouts/default.html"))
|
91
|
-
posts = self.posts
|
185
|
+
posts = self.posts
|
92
186
|
|
93
187
|
files.each do |path|
|
94
188
|
puts "Processing file: #{path}"
|
@@ -119,9 +213,9 @@ class Site
|
|
119
213
|
end
|
120
214
|
|
121
215
|
if layout_option == "none"
|
122
|
-
f.puts Liquid::Template.parse(file.to_s).render!("site" =>
|
216
|
+
f.puts Liquid::Template.parse(file.to_s).render!("site" => self)
|
123
217
|
else
|
124
|
-
f.puts layout.render!("page" => { "title" => [title].compact }, "content" => Liquid::Template.parse(file.to_s).render!("site" =>
|
218
|
+
f.puts layout.render!("page" => { "title" => [title].compact }, "content" => Liquid::Template.parse(file.to_s).render!("site" => self))
|
125
219
|
end
|
126
220
|
end
|
127
221
|
end
|
@@ -137,6 +231,8 @@ class Site
|
|
137
231
|
end
|
138
232
|
end
|
139
233
|
|
234
|
+
generate_archives(layout)
|
235
|
+
|
140
236
|
if Dir.exist?("_site")
|
141
237
|
FileUtils.mv("_site", "/tmp/_site.#{Time.now.strftime("%Y-%m-%d-%H-%M-%S")}")
|
142
238
|
end
|
@@ -144,5 +240,43 @@ class Site
|
|
144
240
|
FileUtils.mv("tmp/_site", ".") && FileUtils.rm_rf("tmp/_site")
|
145
241
|
FileUtils.rmdir("tmp")
|
146
242
|
end
|
243
|
+
|
244
|
+
private
|
245
|
+
|
246
|
+
# Uses config.archive_url_format to generate pages
|
247
|
+
# using the archive_page.html template.
|
248
|
+
def generate_archives(layout)
|
249
|
+
return unless config.archive_enabled?
|
250
|
+
|
251
|
+
template = Liquid::Template.parse(File.read("_templates/archive_page.html"))
|
252
|
+
|
253
|
+
months = posts.group_by { |post| Date.new(post.created.year, post.created.month) }
|
254
|
+
|
255
|
+
months.each do |month, posts|
|
256
|
+
archive_path = tmp_path(archive_url_for_date(month))
|
257
|
+
|
258
|
+
FileUtils.mkdir_p(archive_path)
|
259
|
+
|
260
|
+
File.open(File.join(archive_path, "index.html"), "w") do |f|
|
261
|
+
f.puts layout.render!("content" => template.render!("site" => self, "month" => month, "posts" => posts))
|
262
|
+
end
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
def self.stringify_keys(obj)
|
267
|
+
return obj unless obj.is_a?(Hash) || obj.is_a?(Array)
|
268
|
+
|
269
|
+
if obj.is_a?(Array)
|
270
|
+
return obj.map do |el|
|
271
|
+
stringify_keys(el)
|
272
|
+
end
|
273
|
+
end
|
274
|
+
|
275
|
+
result = {}
|
276
|
+
obj.each do |key, value|
|
277
|
+
result[key.to_s] = stringify_keys(value)
|
278
|
+
end
|
279
|
+
result
|
280
|
+
end
|
147
281
|
end
|
148
282
|
end
|
data/serif.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Gem::Specification.new do |s|
|
2
2
|
s.name = "serif"
|
3
|
-
s.version = "0.1.
|
3
|
+
s.version = "0.1.3"
|
4
4
|
s.authors = ["Adam Prescott"]
|
5
5
|
s.email = ["adam@aprescott.com"]
|
6
6
|
s.homepage = "https://github.com/aprescott/serif"
|
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
|
|
15
15
|
[
|
16
16
|
"rake", "~> 0.9",
|
17
17
|
"rack", "~> 1.0",
|
18
|
-
"
|
18
|
+
"rack-rewrite", "~> 1.3.0",
|
19
19
|
"redcarpet", "~> 2.2",
|
20
20
|
"pygments.rb", "~> 0.3",
|
21
21
|
"sinatra", "~> 1.3",
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: serif
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.3
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-11-
|
12
|
+
date: 2012-11-21 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -44,21 +44,21 @@ dependencies:
|
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '1.0'
|
46
46
|
- !ruby/object:Gem::Dependency
|
47
|
-
name:
|
47
|
+
name: rack-rewrite
|
48
48
|
requirement: !ruby/object:Gem::Requirement
|
49
49
|
none: false
|
50
50
|
requirements:
|
51
|
-
- -
|
51
|
+
- - ~>
|
52
52
|
- !ruby/object:Gem::Version
|
53
|
-
version:
|
53
|
+
version: 1.3.0
|
54
54
|
type: :runtime
|
55
55
|
prerelease: false
|
56
56
|
version_requirements: !ruby/object:Gem::Requirement
|
57
57
|
none: false
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - ~>
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version:
|
61
|
+
version: 1.3.0
|
62
62
|
- !ruby/object:Gem::Dependency
|
63
63
|
name: redcarpet
|
64
64
|
requirement: !ruby/object:Gem::Requirement
|
@@ -179,9 +179,13 @@ files:
|
|
179
179
|
- statics/templates/admin/index.liquid
|
180
180
|
- statics/templates/admin/edit_post.liquid
|
181
181
|
- statics/skeleton/index.html
|
182
|
+
- statics/skeleton/_templates/archive_page.html
|
182
183
|
- statics/skeleton/_templates/post.html
|
184
|
+
- statics/skeleton/_drafts/sample-draft
|
183
185
|
- statics/skeleton/_config.yml
|
186
|
+
- statics/skeleton/_posts/2012-01-05-sample-post
|
184
187
|
- statics/skeleton/_layouts/default.html
|
188
|
+
- statics/skeleton/archive.html
|
185
189
|
- statics/assets/js/mousetrap.min.js
|
186
190
|
- statics/assets/js/jquery.autosize.js
|
187
191
|
- bin/serif
|