georgi-shinmun 0.4.1 → 0.5
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +23 -31
- data/assets/print.css +76 -0
- data/assets/styles.css +91 -0
- data/bin/shinmun +16 -8
- data/config.ru +16 -0
- data/lib/shinmun.rb +1 -4
- data/lib/shinmun/blog.rb +110 -100
- data/lib/shinmun/comment.rb +1 -1
- data/lib/shinmun/handlers.rb +19 -0
- data/lib/shinmun/helpers.rb +1 -1
- data/lib/shinmun/routes.rb +25 -36
- data/templates/404.rhtml +4 -0
- data/templates/_comment_form.rhtml +21 -0
- data/{test/templates → templates}/_comments.rhtml +0 -0
- data/{example/templates → templates}/archive.rhtml +0 -1
- data/{example/templates → templates}/category.rhtml +1 -2
- data/{example/templates → templates}/category.rxml +0 -0
- data/{example/templates → templates}/index.rhtml +0 -1
- data/{example/templates → templates}/index.rxml +0 -0
- data/templates/layout.rhtml +44 -0
- data/{example/templates → templates}/page.rhtml +0 -2
- data/{example/templates → templates}/post.rhtml +6 -21
- data/test/blog_spec.rb +48 -81
- metadata +17 -44
- data/example/Rakefile +0 -41
- data/example/assets/images/favicon.ico +0 -0
- data/example/assets/images/loading.gif +0 -0
- data/example/assets/javascripts/1-jquery.min.js +0 -32
- data/example/assets/javascripts/2-jquery-form.min.js +0 -5
- data/example/assets/javascripts/3-comments.js +0 -45
- data/example/assets/javascripts/4-coderay.js +0 -13
- data/example/assets/print.css +0 -76
- data/example/assets/stylesheets/1-reset.css +0 -23
- data/example/assets/stylesheets/2-typo.css +0 -40
- data/example/assets/stylesheets/3-table.css +0 -23
- data/example/assets/stylesheets/4-article.css +0 -15
- data/example/assets/stylesheets/5-comments.css +0 -20
- data/example/assets/stylesheets/6-diff.css +0 -25
- data/example/assets/stylesheets/7-blog.css +0 -33
- data/example/config.ru +0 -6
- data/example/config/blog.yml +0 -10
- data/example/pages/about.md +0 -7
- data/example/templates/_comment_form.rhtml +0 -90
- data/example/templates/_comments.rhtml +0 -11
- data/example/templates/_pagination.rhtml +0 -10
- data/example/templates/comments.rhtml +0 -1
- data/example/templates/layout.rhtml +0 -82
- data/lib/shinmun/aggregations/delicious.rb +0 -57
- data/lib/shinmun/aggregations/flickr.rb +0 -81
- data/lib/shinmun/post_handler.rb +0 -16
- data/test/templates/archive.rhtml +0 -6
- data/test/templates/category.rhtml +0 -6
- data/test/templates/category.rxml +0 -20
- data/test/templates/index.rhtml +0 -4
- data/test/templates/index.rxml +0 -21
- data/test/templates/layout.rhtml +0 -9
- data/test/templates/page.rhtml +0 -2
- data/test/templates/post.rhtml +0 -3
data/README.md
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
Shinmun - a git-based blog engine
|
2
|
-
|
2
|
+
=================================
|
3
3
|
|
4
4
|
Shinmun is a small git-based blog engine. Write posts in your favorite
|
5
5
|
editor, git-push it and serve your blog straight from a repository.
|
@@ -11,7 +11,6 @@ editor, git-push it and serve your blog straight from a repository.
|
|
11
11
|
* Deploy via [git-push][11]
|
12
12
|
* Index, category and archive listings
|
13
13
|
* RSS feeds
|
14
|
-
* Flickr and Delicious aggregations
|
15
14
|
* Syntax highlighting provided by [CodeRay][4]
|
16
15
|
* AJAX comment system with Markdown preview
|
17
16
|
|
@@ -21,9 +20,9 @@ editor, git-push it and serve your blog straight from a repository.
|
|
21
20
|
Install the gems:
|
22
21
|
|
23
22
|
$ gem sources -a http://gems.github.com
|
24
|
-
$ gem install rack BlueCloth rubypants coderay
|
23
|
+
$ gem install rack BlueCloth rubypants coderay georgi-git_store georgi-kontrol georgi-shinmun
|
25
24
|
|
26
|
-
Create a sample blog
|
25
|
+
Create a sample blog:
|
27
26
|
|
28
27
|
$ shinmun init myblog
|
29
28
|
|
@@ -48,9 +47,7 @@ folder:
|
|
48
47
|
shinmun post 'The title of the post'
|
49
48
|
|
50
49
|
Shinmun will then create a post file in the right place, for example
|
51
|
-
in `posts/2008/9/the-title-of-the-post.md
|
52
|
-
probably open the file, set the category and tags and start writing
|
53
|
-
your new article.
|
50
|
+
in `posts/2008/9/the-title-of-the-post.md` and open it with $EDITOR.
|
54
51
|
|
55
52
|
|
56
53
|
### Post Format
|
@@ -141,23 +138,15 @@ An example tree:
|
|
141
138
|
|
142
139
|
### Blog configuation
|
143
140
|
|
144
|
-
Inside `config
|
141
|
+
Inside `config.ru` you can set the properties of your blog:
|
145
142
|
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
### Assets
|
155
|
-
|
156
|
-
Shinmun serves asset files from your assets directory. Files in the
|
157
|
-
directories `assets/stylesheets` and `assets/javascripts` will be
|
158
|
-
served as one file each under the URLs `assets/stylesheets.css` and
|
159
|
-
`assets/javascripts.css`. You have to name them accordingly like
|
160
|
-
`1-reset.css` and `2-typo.css` to define the order.
|
143
|
+
blog.config = {
|
144
|
+
:language => 'en',
|
145
|
+
:title => "Blog Title",
|
146
|
+
:author => "The Author",
|
147
|
+
:categories => ["Ruby", "Javascript"],
|
148
|
+
:description => "Blog description"
|
149
|
+
}
|
161
150
|
|
162
151
|
|
163
152
|
### Templates
|
@@ -176,17 +165,20 @@ Layout and templates are rendered by *ERB*. The layout is defined in
|
|
176
165
|
</body>
|
177
166
|
</html>
|
178
167
|
|
179
|
-
The attributes of a post are accessible
|
180
|
-
template:
|
168
|
+
The attributes of a post are accessible via the @post variable:
|
181
169
|
|
182
|
-
<div class="article">
|
170
|
+
<div class="article">
|
171
|
+
|
172
|
+
<h1><%= @post.title %></h1>
|
173
|
+
|
183
174
|
<div class="date">
|
184
|
-
<%=
|
175
|
+
<%= human_date @post.date %>
|
185
176
|
</div>
|
186
|
-
|
187
|
-
<%= @
|
188
|
-
|
189
|
-
|
177
|
+
|
178
|
+
<%= @post.body_html %>
|
179
|
+
|
180
|
+
...
|
181
|
+
|
190
182
|
</div>
|
191
183
|
|
192
184
|
|
data/assets/print.css
ADDED
@@ -0,0 +1,76 @@
|
|
1
|
+
body {
|
2
|
+
line-height:1.5;
|
3
|
+
font-family:"Helvetica Neue", "Lucida Grande", Arial, Verdana, sans-serif;
|
4
|
+
color:#000;
|
5
|
+
background:none;
|
6
|
+
font-size:10pt;
|
7
|
+
}
|
8
|
+
|
9
|
+
.container {
|
10
|
+
background:none;
|
11
|
+
}
|
12
|
+
|
13
|
+
h1,h2,h3,h4,h5,h6 {
|
14
|
+
font-family:"Helvetica Neue", Arial, "Lucida Grande", sans-serif;
|
15
|
+
}
|
16
|
+
|
17
|
+
code {
|
18
|
+
font:.9em "Courier New", Monaco, Courier, monospace;
|
19
|
+
}
|
20
|
+
|
21
|
+
img {
|
22
|
+
float:left;
|
23
|
+
margin:1.5em 1.5em 1.5em 0;
|
24
|
+
}
|
25
|
+
|
26
|
+
a img {
|
27
|
+
border:none;
|
28
|
+
}
|
29
|
+
|
30
|
+
p img.top {
|
31
|
+
margin-top:0;
|
32
|
+
}
|
33
|
+
|
34
|
+
hr {
|
35
|
+
background:#ccc;
|
36
|
+
color:#ccc;
|
37
|
+
width:100%;
|
38
|
+
height:2px;
|
39
|
+
border:none;
|
40
|
+
margin:2em 0;
|
41
|
+
padding:0;
|
42
|
+
}
|
43
|
+
|
44
|
+
blockquote {
|
45
|
+
font-style:italic;
|
46
|
+
font-size:.9em;
|
47
|
+
margin:1.5em;
|
48
|
+
padding:1em;
|
49
|
+
}
|
50
|
+
|
51
|
+
.small {
|
52
|
+
font-size:.9em;
|
53
|
+
}
|
54
|
+
|
55
|
+
.large {
|
56
|
+
font-size:1.1em;
|
57
|
+
}
|
58
|
+
|
59
|
+
.quiet {
|
60
|
+
color:#999;
|
61
|
+
}
|
62
|
+
|
63
|
+
.hide {
|
64
|
+
display:none;
|
65
|
+
}
|
66
|
+
|
67
|
+
a:link,a:visited {
|
68
|
+
background:transparent;
|
69
|
+
font-weight:700;
|
70
|
+
text-decoration:underline;
|
71
|
+
}
|
72
|
+
|
73
|
+
a:link:after,a:visited:after {
|
74
|
+
content:" (" attr(href) ") ";
|
75
|
+
font-size:90%;
|
76
|
+
}
|
data/assets/styles.css
ADDED
@@ -0,0 +1,91 @@
|
|
1
|
+
html, body, div, span, object, iframe, h1, h2, h3, h4, h5, h6, p,
|
2
|
+
blockquote, pre, a, abbr, acronym, address, del, dfn, img,
|
3
|
+
q, fieldset, form, label, legend, table,
|
4
|
+
caption, tbody, tfoot, thead, tr, th, td {
|
5
|
+
margin: 0;
|
6
|
+
padding: 0;
|
7
|
+
border: 0;
|
8
|
+
font-weight: inherit;
|
9
|
+
font-style: inherit;
|
10
|
+
font-size: 100%;
|
11
|
+
font-family: inherit;
|
12
|
+
vertical-align: baseline;
|
13
|
+
}
|
14
|
+
|
15
|
+
body {
|
16
|
+
line-height: 1.5;
|
17
|
+
background: #fff;
|
18
|
+
margin:0.5em 0;
|
19
|
+
font-size: 80%;
|
20
|
+
color: #222;
|
21
|
+
font-family: Arial, sans-serif;
|
22
|
+
}
|
23
|
+
|
24
|
+
p {
|
25
|
+
margin-bottom: 1em;
|
26
|
+
}
|
27
|
+
|
28
|
+
a {
|
29
|
+
color: #444;
|
30
|
+
}
|
31
|
+
|
32
|
+
a:visited {
|
33
|
+
color: #444;
|
34
|
+
}
|
35
|
+
|
36
|
+
h1 a, h2 a, h3 a, h4 a, h5 a, h6 a {
|
37
|
+
text-decoration: none;
|
38
|
+
}
|
39
|
+
|
40
|
+
h1 { font-size: 2em; line-height: 1; margin-top: 1em; margin-bottom: 1em; }
|
41
|
+
h2 { font-size: 1.5em; margin-top: 0.75em; margin-bottom: 0.75em; }
|
42
|
+
h3 { font-size: 1.3em; line-height: 1; margin-top: 1.7em; margin-bottom: 1em; }
|
43
|
+
h4 { font-size: 1.2em; line-height: 1.25; margin-top: 1.25em; margin-bottom: 1.25em; }
|
44
|
+
h5 { font-size: 1em; font-weight: bold; margin-bottom: 1.5em; }
|
45
|
+
h6 { font-size: 1em; font-weight: bold; }
|
46
|
+
|
47
|
+
hr {
|
48
|
+
height: 1px;
|
49
|
+
color: #ccc;
|
50
|
+
}
|
51
|
+
|
52
|
+
.container {
|
53
|
+
width: 600px;
|
54
|
+
margin: 0 auto;
|
55
|
+
}
|
56
|
+
|
57
|
+
.article {
|
58
|
+
}
|
59
|
+
|
60
|
+
.article h2 {
|
61
|
+
margin-top:0px;
|
62
|
+
}
|
63
|
+
|
64
|
+
.article .date {
|
65
|
+
color: #666;
|
66
|
+
}
|
67
|
+
|
68
|
+
.article .tags a {
|
69
|
+
color: #69c;
|
70
|
+
text-decoration: none;
|
71
|
+
}
|
72
|
+
|
73
|
+
.comments {
|
74
|
+
margin-bottom: 2em;
|
75
|
+
}
|
76
|
+
|
77
|
+
.comments .comment, .preview .comment {
|
78
|
+
margin-top: 2em;
|
79
|
+
border: 1px solid #ccc;
|
80
|
+
}
|
81
|
+
|
82
|
+
.comments .comment .top, .preview .comment .top {
|
83
|
+
background: #F0F0F0;
|
84
|
+
color: #333;
|
85
|
+
padding: 3px 5px;
|
86
|
+
}
|
87
|
+
|
88
|
+
.comments .comment .body, .preview .comment .body {
|
89
|
+
background: #F8F8F8;
|
90
|
+
padding: 3px 5px;
|
91
|
+
}
|
data/bin/shinmun
CHANGED
@@ -2,21 +2,29 @@
|
|
2
2
|
|
3
3
|
require 'shinmun'
|
4
4
|
|
5
|
+
ENV['RACK_ENV'] = 'production'
|
6
|
+
|
5
7
|
case ARGV[0]
|
6
8
|
when 'init'
|
7
9
|
Shinmun::Blog.init ARGV[1]
|
8
10
|
|
9
11
|
when 'post'
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
12
|
+
blog = Shinmun::Blog.new('.')
|
13
|
+
post = blog.create_post(:title => ARGV[1], :date => Date.today)
|
14
|
+
path = blog.post_file(post)
|
15
|
+
|
16
|
+
`git checkout master posts`
|
17
|
+
|
18
|
+
exec "#{ENV['EDITOR']} #{path}"
|
14
19
|
|
15
20
|
when 'page'
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
21
|
+
blog = Shinmun::Blog.new('.')
|
22
|
+
post = blog.create_page(:title => ARGV[1])
|
23
|
+
path = blog.post_file(post)
|
24
|
+
|
25
|
+
`git checkout master pages`
|
26
|
+
|
27
|
+
exec "#{ENV['EDITOR']} #{path}"
|
20
28
|
|
21
29
|
else
|
22
30
|
puts "Usage:"
|
data/config.ru
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'shinmun'
|
2
|
+
|
3
|
+
use Rack::Session::Cookie
|
4
|
+
use Rack::Reloader
|
5
|
+
|
6
|
+
blog = Shinmun::Blog.new(File.dirname(__FILE__))
|
7
|
+
|
8
|
+
blog.config = {
|
9
|
+
:language => 'en',
|
10
|
+
:title => "Blog Title",
|
11
|
+
:author => "The Author",
|
12
|
+
:categories => ["Ruby", "Javascript"],
|
13
|
+
:description => "Blog description"
|
14
|
+
}
|
15
|
+
|
16
|
+
run blog
|
data/lib/shinmun.rb
CHANGED
@@ -10,11 +10,8 @@ begin; require 'redcloth'; rescue LoadError; end
|
|
10
10
|
|
11
11
|
require 'shinmun/bluecloth_coderay'
|
12
12
|
require 'shinmun/helpers'
|
13
|
+
require 'shinmun/handlers'
|
13
14
|
require 'shinmun/blog'
|
14
15
|
require 'shinmun/routes'
|
15
16
|
require 'shinmun/post'
|
16
17
|
require 'shinmun/comment'
|
17
|
-
require 'shinmun/post_handler'
|
18
|
-
|
19
|
-
require 'shinmun/aggregations/delicious'
|
20
|
-
require 'shinmun/aggregations/flickr'
|
data/lib/shinmun/blog.rb
CHANGED
@@ -1,104 +1,76 @@
|
|
1
1
|
module Shinmun
|
2
|
+
ROOT = File.expand_path(File.dirname(__FILE__) + '/../..')
|
2
3
|
|
3
4
|
class Blog < Kontrol::Application
|
4
|
-
|
5
|
-
EXAMPLE_DIR = File.expand_path(File.dirname(__FILE__) + '/../../example')
|
6
|
-
|
7
5
|
include Helpers
|
8
6
|
|
9
|
-
|
10
|
-
|
11
|
-
%w[ assets comments config posts pages templates ].each do |name|
|
12
|
-
define_method(name) { store.root.tree(name) }
|
13
|
-
end
|
7
|
+
attr_accessor :config, :store, :posts, :pages
|
14
8
|
|
15
|
-
%w[ title description language author
|
16
|
-
define_method(name) { config[
|
9
|
+
%w[ base_path title description language author categories ].each do |name|
|
10
|
+
define_method(name) { @config[name.to_sym] }
|
17
11
|
end
|
18
12
|
|
19
13
|
# Initialize the blog
|
20
14
|
def initialize(path)
|
21
15
|
super
|
22
16
|
|
23
|
-
@
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
@store = GitStore::FileStore.new(path)
|
29
|
-
end
|
30
|
-
|
31
|
-
@store.load
|
32
|
-
|
33
|
-
@repo = Grit::Repo.new(path) if defined?(Grit)
|
34
|
-
|
35
|
-
Thread.start do
|
36
|
-
loop do
|
37
|
-
load_aggregations
|
38
|
-
sleep 300
|
39
|
-
end
|
40
|
-
end
|
17
|
+
@config = {}
|
18
|
+
@store = GitStore.new(path)
|
19
|
+
@store.handler['md'] = PostHandler.new
|
20
|
+
@store.handler['rhtml'] = ERBHandler.new
|
21
|
+
@store.handler['rxml'] = ERBHandler.new
|
41
22
|
end
|
42
23
|
|
43
|
-
def self.init(
|
44
|
-
|
45
|
-
Dir.
|
46
|
-
FileUtils.cp_r EXAMPLE_DIR + '/.', '.'
|
47
|
-
`git init`
|
48
|
-
`git add .`
|
49
|
-
`git commit -m 'init'`
|
50
|
-
end
|
24
|
+
def self.init(path)
|
25
|
+
path = File.expand_path(path)
|
26
|
+
Dir.mkdir(path)
|
51
27
|
|
52
|
-
|
53
|
-
|
54
|
-
|
28
|
+
FileUtils.cp_r "#{ROOT}/assets", path
|
29
|
+
FileUtils.cp_r "#{ROOT}/templates", path
|
30
|
+
FileUtils.cp "#{ROOT}/config.ru", path
|
55
31
|
|
56
|
-
|
57
|
-
|
58
|
-
|
32
|
+
Dir.mkdir("#{path}/posts")
|
33
|
+
Dir.mkdir("#{path}/pages")
|
34
|
+
Dir.mkdir("#{path}/comments")
|
35
|
+
Dir.mkdir("#{path}/public")
|
59
36
|
|
60
|
-
|
61
|
-
store.refresh!
|
62
|
-
super
|
63
|
-
end
|
37
|
+
FileUtils.ln_s("../assets", "#{path}/public/assets")
|
64
38
|
|
65
|
-
|
66
|
-
|
67
|
-
|
39
|
+
Dir.chdir(path) do
|
40
|
+
`git init`
|
41
|
+
`git add .`
|
42
|
+
`git commit -m 'init'`
|
68
43
|
end
|
69
44
|
end
|
70
45
|
|
71
|
-
def
|
72
|
-
|
46
|
+
def load_template(file)
|
47
|
+
store['templates/' + file]
|
73
48
|
end
|
74
49
|
|
75
|
-
def
|
76
|
-
|
50
|
+
def render(name, vars = {})
|
51
|
+
super(name, vars.merge(:blog => self))
|
77
52
|
end
|
78
53
|
|
79
|
-
|
80
|
-
|
81
|
-
posts_by_date.select { |p| p.year == year and p.month == month }
|
54
|
+
def pages
|
55
|
+
store.tree('pages').values
|
82
56
|
end
|
83
57
|
|
84
|
-
|
85
|
-
|
86
|
-
return [] if tags.nil? or tags.empty?
|
87
|
-
tags = tags.split(',').map { |t| t.strip } if tags.is_a?(String)
|
88
|
-
posts.select do |post|
|
89
|
-
tags.any? do |tag|
|
90
|
-
post.tag_list.include?(tag)
|
91
|
-
end
|
92
|
-
end
|
58
|
+
def posts
|
59
|
+
store.tree('posts').values.sort_by { |post| post.date.to_s }.reverse
|
93
60
|
end
|
94
61
|
|
95
|
-
|
96
|
-
|
97
|
-
|
62
|
+
def call(env)
|
63
|
+
if ENV['RACK_ENV'] == 'production'
|
64
|
+
store.load if store.changed?
|
65
|
+
else
|
66
|
+
store.load(true)
|
67
|
+
end
|
68
|
+
|
69
|
+
super
|
98
70
|
end
|
99
71
|
|
100
|
-
def
|
101
|
-
|
72
|
+
def url
|
73
|
+
"http://#{request.host}"
|
102
74
|
end
|
103
75
|
|
104
76
|
def symbolize_keys(hash)
|
@@ -112,36 +84,54 @@ module Shinmun
|
|
112
84
|
store.transaction(message, &block)
|
113
85
|
end
|
114
86
|
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
87
|
+
def post_file(post)
|
88
|
+
'posts' + post_path(post) + '.' + post.type
|
89
|
+
end
|
90
|
+
|
91
|
+
def page_file(post)
|
92
|
+
'pages' + page_path(post) + '.' + post.type
|
93
|
+
end
|
94
|
+
|
95
|
+
def comment_file(post)
|
96
|
+
'comments/' + post_path(post) + '.yml'
|
121
97
|
end
|
122
98
|
|
123
|
-
def
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
99
|
+
def create_post(attr)
|
100
|
+
post = Post.new(attr)
|
101
|
+
path = post_file(post)
|
102
|
+
|
103
|
+
transaction "create post `#{post.title}'" do
|
104
|
+
store[path] = post
|
128
105
|
end
|
106
|
+
|
107
|
+
post
|
129
108
|
end
|
130
109
|
|
131
|
-
def
|
132
|
-
|
133
|
-
|
110
|
+
def create_page(attr)
|
111
|
+
post = Post.new(attr)
|
112
|
+
path = page_file(post)
|
113
|
+
|
114
|
+
transaction "create page `#{post.title}'" do
|
115
|
+
store[path] = post
|
134
116
|
end
|
117
|
+
|
118
|
+
post
|
135
119
|
end
|
136
120
|
|
137
|
-
def comments_for(
|
138
|
-
|
121
|
+
def comments_for(post)
|
122
|
+
store[comment_file post] || []
|
139
123
|
end
|
140
124
|
|
141
|
-
def
|
142
|
-
|
143
|
-
|
125
|
+
def create_comment(post, params)
|
126
|
+
path = comment_file(post)
|
127
|
+
comments = comments_for(post)
|
128
|
+
comment = Comment.new(params)
|
129
|
+
|
130
|
+
transaction "new comment for `#{post.title}'" do
|
131
|
+
store[path] = comments + [comment]
|
144
132
|
end
|
133
|
+
|
134
|
+
comment
|
145
135
|
end
|
146
136
|
|
147
137
|
def find_page(name)
|
@@ -149,23 +139,43 @@ module Shinmun
|
|
149
139
|
end
|
150
140
|
|
151
141
|
def find_post(year, month, name)
|
152
|
-
|
142
|
+
posts.find { |p| p.year == year and p.month == month and p.name == name }
|
153
143
|
end
|
154
144
|
|
155
145
|
def find_category(permalink)
|
156
|
-
name = categories.find { |name| urlify(name) == permalink }
|
157
|
-
|
158
|
-
{ :name => name,
|
146
|
+
name = categories.find { |name| urlify(name) == permalink }
|
147
|
+
|
148
|
+
{ :name => name,
|
149
|
+
:posts => posts.select { |p| p.category == name },
|
150
|
+
:permalink => permalink }
|
151
|
+
end
|
152
|
+
|
153
|
+
def recent_posts
|
154
|
+
posts[0, 20]
|
159
155
|
end
|
160
156
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
157
|
+
# Return all posts for a given month.
|
158
|
+
def posts_for_month(year, month)
|
159
|
+
posts.select { |p| p.year == year and p.month == month }
|
160
|
+
end
|
161
|
+
|
162
|
+
# Return all posts with any of given tags.
|
163
|
+
def posts_with_tags(tags)
|
164
|
+
return [] if tags.nil? or tags.empty?
|
165
|
+
tags = tags.split(',').map { |t| t.strip } if tags.is_a?(String)
|
166
|
+
|
167
|
+
posts.select do |post|
|
168
|
+
tags.any? do |tag|
|
169
|
+
post.tag_list.include?(tag)
|
170
|
+
end
|
166
171
|
end
|
167
172
|
end
|
168
|
-
|
169
|
-
|
173
|
+
|
174
|
+
# Return all archives as tuples of [year, month].
|
175
|
+
def archives
|
176
|
+
posts.map { |p| [p.year, p.month] }.uniq.sort
|
177
|
+
end
|
178
|
+
|
179
|
+
end
|
170
180
|
|
171
181
|
end
|