shinmun 0.1 → 0.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.
- data/.gitignore +2 -0
- data/README.md +127 -62
- data/Rakefile +23 -11
- data/bin/shinmun +6 -1
- data/lib/shinmun/admin_controller.rb +161 -0
- data/lib/shinmun/aggregations/delicious.rb +57 -0
- data/lib/shinmun/aggregations/flickr.rb +81 -0
- data/lib/shinmun/blog.rb +319 -0
- data/lib/shinmun/cache.rb +59 -0
- data/lib/shinmun/comment.rb +44 -0
- data/lib/shinmun/controller.rb +135 -0
- data/lib/shinmun/helpers.rb +116 -0
- data/lib/shinmun/post.rb +166 -0
- data/lib/shinmun/template.rb +39 -0
- data/lib/shinmun.rb +14 -411
- metadata +15 -81
- data/example/posts/2008/9/example.md +0 -19
- data/example/posts/blog.yml +0 -10
- data/example/posts/uuid.state +0 -3
- data/example/public/controllers/comments.php +0 -56
- data/example/public/images/loading.gif +0 -0
- data/example/public/javascripts/comments.js +0 -60
- data/example/public/javascripts/highlight.js +0 -505
- data/example/public/javascripts/images/bg-fill.png +0 -0
- data/example/public/javascripts/images/bg.png +0 -0
- data/example/public/javascripts/images/blockquote.png +0 -0
- data/example/public/javascripts/images/bold.png +0 -0
- data/example/public/javascripts/images/code.png +0 -0
- data/example/public/javascripts/images/h1.png +0 -0
- data/example/public/javascripts/images/hr.png +0 -0
- data/example/public/javascripts/images/img.png +0 -0
- data/example/public/javascripts/images/italic.png +0 -0
- data/example/public/javascripts/images/link.png +0 -0
- data/example/public/javascripts/images/ol.png +0 -0
- data/example/public/javascripts/images/redo.png +0 -0
- data/example/public/javascripts/images/separator.png +0 -0
- data/example/public/javascripts/images/ul.png +0 -0
- data/example/public/javascripts/images/undo.png +0 -0
- data/example/public/javascripts/images/wmd-on.png +0 -0
- data/example/public/javascripts/images/wmd.png +0 -0
- data/example/public/javascripts/jquery-form.js +0 -869
- data/example/public/javascripts/jquery.js +0 -3383
- data/example/public/javascripts/languages/1c.js +0 -82
- data/example/public/javascripts/languages/axapta.js +0 -52
- data/example/public/javascripts/languages/bash.js +0 -80
- data/example/public/javascripts/languages/diff.js +0 -64
- data/example/public/javascripts/languages/dos.js +0 -33
- data/example/public/javascripts/languages/dynamic.js +0 -460
- data/example/public/javascripts/languages/ini.js +0 -36
- data/example/public/javascripts/languages/javascript.js +0 -38
- data/example/public/javascripts/languages/lisp.js +0 -86
- data/example/public/javascripts/languages/mel.js +0 -50
- data/example/public/javascripts/languages/profile.js +0 -50
- data/example/public/javascripts/languages/renderman.js +0 -71
- data/example/public/javascripts/languages/smalltalk.js +0 -53
- data/example/public/javascripts/languages/sql.js +0 -50
- data/example/public/javascripts/languages/static.js +0 -175
- data/example/public/javascripts/languages/vbscript.js +0 -25
- data/example/public/javascripts/languages/www.js +0 -245
- data/example/public/javascripts/prettyDate.js +0 -36
- data/example/public/javascripts/showdown.js +0 -421
- data/example/public/javascripts/template.js +0 -165
- data/example/public/javascripts/wmd-base.js +0 -1799
- data/example/public/javascripts/wmd-plus.js +0 -311
- data/example/public/javascripts/wmd.js +0 -73
- data/example/public/stylesheets/grid.css +0 -243
- data/example/public/stylesheets/grid.png +0 -0
- data/example/public/stylesheets/highlight/ascetic.css +0 -38
- data/example/public/stylesheets/highlight/dark.css +0 -96
- data/example/public/stylesheets/highlight/default.css +0 -91
- data/example/public/stylesheets/highlight/far.css +0 -95
- data/example/public/stylesheets/highlight/idea.css +0 -75
- data/example/public/stylesheets/highlight/sunburst.css +0 -112
- data/example/public/stylesheets/highlight/zenburn.css +0 -108
- data/example/public/stylesheets/print.css +0 -76
- data/example/public/stylesheets/reset.css +0 -45
- data/example/public/stylesheets/style.css +0 -141
- data/example/public/stylesheets/typography.css +0 -59
- data/example/templates/feed.rxml +0 -21
- data/example/templates/layout.rhtml +0 -54
- data/example/templates/page.rhtml +0 -4
- data/example/templates/post.rhtml +0 -57
- data/example/templates/posts.rhtml +0 -10
data/lib/shinmun/post.rb
ADDED
@@ -0,0 +1,166 @@
|
|
1
|
+
module Shinmun
|
2
|
+
|
3
|
+
# This class represents a post or page.
|
4
|
+
# Each post has a header, encoded as YAML and a body.
|
5
|
+
#
|
6
|
+
# Example:
|
7
|
+
# ---
|
8
|
+
# category: Ruby
|
9
|
+
# date: 2008-09-05
|
10
|
+
#
|
11
|
+
# BlueCloth, a Markdown library
|
12
|
+
# =============================
|
13
|
+
#
|
14
|
+
# This is the summary, which is by definition the first paragraph of the
|
15
|
+
# article. The summary shows up in list views and rss feeds.
|
16
|
+
#
|
17
|
+
class Post
|
18
|
+
|
19
|
+
# Define accessor methods for head variable.
|
20
|
+
def self.head_accessor(*names)
|
21
|
+
names.each do |name|
|
22
|
+
name = name.to_s
|
23
|
+
define_method(name) { @head[name] }
|
24
|
+
define_method("#{name}=") {|v| @head[name] = v }
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
attr_accessor :prefix, :path, :type, :title, :head, :body, :summary, :body_html
|
29
|
+
head_accessor :author, :date, :category, :tags, :languages, :header
|
30
|
+
|
31
|
+
# Initialize empty post and set specified attributes.
|
32
|
+
def initialize(attributes={})
|
33
|
+
@head = {}
|
34
|
+
@body = ''
|
35
|
+
|
36
|
+
for k, v in attributes
|
37
|
+
send "#{k}=", v
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Shortcut for year of date
|
42
|
+
def year
|
43
|
+
date.year
|
44
|
+
end
|
45
|
+
|
46
|
+
# Shortcut for month of date
|
47
|
+
def month
|
48
|
+
date.month
|
49
|
+
end
|
50
|
+
|
51
|
+
def filename
|
52
|
+
"#{prefix}/#{path}.#{type}"
|
53
|
+
end
|
54
|
+
|
55
|
+
# Strips off extension and prefix.
|
56
|
+
def filename=(file)
|
57
|
+
if match = file.match(/^(.*?)\/(.*)\.(.*)/)
|
58
|
+
@prefix = match[1]
|
59
|
+
@path = match[2]
|
60
|
+
@type = match[3]
|
61
|
+
else
|
62
|
+
raise "incorrect filename: #{file}"
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# Split up the source into header and body. Load the header as
|
67
|
+
# yaml document. Render body and parse the summary from rendered html.
|
68
|
+
def parse(src)
|
69
|
+
# Parse YAML header if present
|
70
|
+
if src =~ /---.*?\n(.*?)\n\n(.*)/m
|
71
|
+
@head = YAML.load($1)
|
72
|
+
@body = $2
|
73
|
+
else
|
74
|
+
@body = src
|
75
|
+
end
|
76
|
+
|
77
|
+
@title = head['title'] or parse_title
|
78
|
+
@body_html = transform(body, type)
|
79
|
+
@summary = body_html.split("\n\n")[0]
|
80
|
+
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
# Parse title from different formats
|
85
|
+
def parse_title
|
86
|
+
lines = body.split("\n")
|
87
|
+
|
88
|
+
case type
|
89
|
+
when 'md'
|
90
|
+
@title = lines.shift.sub(/(^#+|#+$)/,'').strip
|
91
|
+
lines.shift if lines.first.match(/^(=|-)+$/)
|
92
|
+
|
93
|
+
when 'html'
|
94
|
+
@title = lines.shift.sub(/(<h1>|\<\/h1>)/,'').strip
|
95
|
+
|
96
|
+
when 'tt'
|
97
|
+
@title = lines.shift.sub(/(^h1.)/,'').strip
|
98
|
+
end
|
99
|
+
|
100
|
+
@body = lines.join("\n")
|
101
|
+
end
|
102
|
+
|
103
|
+
# Convert to yaml for caching.
|
104
|
+
def to_yaml
|
105
|
+
head.merge('author' => author,
|
106
|
+
'path' => path,
|
107
|
+
'type' => type,
|
108
|
+
'title' => title,
|
109
|
+
'summary' => summary,
|
110
|
+
'body_html' => body_html).to_yaml
|
111
|
+
end
|
112
|
+
|
113
|
+
# Convert to string representation, used to create new posts.
|
114
|
+
def dump
|
115
|
+
head = self.head.dup
|
116
|
+
body = self.body.dup
|
117
|
+
|
118
|
+
if type == 'md'
|
119
|
+
body = title + "\n" + ("=" * title.size) + "\n\n" + body
|
120
|
+
end
|
121
|
+
|
122
|
+
head.each do |k, v|
|
123
|
+
head.delete(k) if v.nil? || v.empty?
|
124
|
+
end
|
125
|
+
|
126
|
+
if head.empty?
|
127
|
+
body
|
128
|
+
else
|
129
|
+
head.to_yaml + "\n\n" + body
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
def load
|
134
|
+
parse(File.read(filename))
|
135
|
+
end
|
136
|
+
|
137
|
+
def save
|
138
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
139
|
+
File.open(filename, "w") { |io| io << dump }
|
140
|
+
self
|
141
|
+
end
|
142
|
+
|
143
|
+
# Variables used for templates.
|
144
|
+
def variables
|
145
|
+
head.merge(:author => author,
|
146
|
+
:path => path,
|
147
|
+
:title => title,
|
148
|
+
:body => body_html)
|
149
|
+
end
|
150
|
+
|
151
|
+
# Transform the body of this post according to given type.
|
152
|
+
# Defaults to Markdown.
|
153
|
+
def transform(src, type)
|
154
|
+
case type
|
155
|
+
when 'html'
|
156
|
+
RubyPants.new(src).to_html
|
157
|
+
when 'tt'
|
158
|
+
RubyPants.new(RedCloth.new(src).to_html).to_html
|
159
|
+
else
|
160
|
+
RubyPants.new(BlueCloth.new(src).to_html).to_html
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
end
|
165
|
+
|
166
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Shinmun
|
2
|
+
|
3
|
+
module Helpers
|
4
|
+
end
|
5
|
+
|
6
|
+
# This class renders an ERB template for a set of attributes, which
|
7
|
+
# are accessible as instance variables.
|
8
|
+
class Template
|
9
|
+
include Helpers
|
10
|
+
|
11
|
+
attr_reader :erb, :blog
|
12
|
+
|
13
|
+
# Initialize this template with an ERB instance.
|
14
|
+
def initialize(erb, file)
|
15
|
+
@erb = erb
|
16
|
+
@file = file
|
17
|
+
end
|
18
|
+
|
19
|
+
# Set instance variable for this template.
|
20
|
+
def set_variables(vars)
|
21
|
+
for name, value in vars
|
22
|
+
instance_variable_set("@#{name}", value)
|
23
|
+
end
|
24
|
+
self
|
25
|
+
end
|
26
|
+
|
27
|
+
# Render this template.
|
28
|
+
def render
|
29
|
+
@erb.result(binding)
|
30
|
+
rescue => e
|
31
|
+
e.backtrace.each do |s|
|
32
|
+
s.gsub!('(erb)', @file)
|
33
|
+
end
|
34
|
+
raise e
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
data/lib/shinmun.rb
CHANGED
@@ -2,419 +2,22 @@ require 'rubygems'
|
|
2
2
|
require 'fileutils'
|
3
3
|
require 'erb'
|
4
4
|
require 'yaml'
|
5
|
-
require '
|
5
|
+
require 'time'
|
6
|
+
|
6
7
|
require 'bluecloth'
|
7
8
|
require 'rubypants'
|
8
|
-
require 'rexml/document'
|
9
|
-
|
10
|
-
# A small and beautiful blog engine.
|
11
|
-
module Shinmun
|
12
|
-
|
13
|
-
# strip html tags from string
|
14
|
-
def self.strip_tags(html)
|
15
|
-
REXML::Document.new(html).each_element( './/text()' ).join
|
16
|
-
end
|
17
|
-
|
18
|
-
def self.urlify(string)
|
19
|
-
string.downcase.gsub(/[ -]+/, '-').gsub(/[^-a-z0-9_]+/, '')
|
20
|
-
end
|
21
|
-
|
22
|
-
|
23
|
-
# This class represents an article or page.
|
24
|
-
# A post has a header and body text.
|
25
|
-
# Example:
|
26
|
-
# ---
|
27
|
-
# category: Ruby
|
28
|
-
# date: 2008-09-05
|
29
|
-
# guid: 7ad04f10-5dd6-012b-b53c-001a92975b89
|
30
|
-
#
|
31
|
-
# BlueCloth, a Markdown library
|
32
|
-
# =============================
|
33
|
-
#
|
34
|
-
# This is the summary, which is by definition the first paragraph of the
|
35
|
-
# article. The summary shows up in list views and rss feeds.
|
36
|
-
class Post
|
37
|
-
|
38
|
-
attr_reader :blog, :title, :path, :head, :src
|
39
|
-
|
40
|
-
# Split up the source text into header and body.
|
41
|
-
# Load the header as yaml document.
|
42
|
-
def initialize(blog, path)
|
43
|
-
@src = File.read(path)
|
44
|
-
@blog = blog
|
45
|
-
@path = path.chomp('.md')
|
46
|
-
|
47
|
-
case @src
|
48
|
-
when /---.*?\n(.*?)\n\n(.*?)\n.*?\n(.*)/m
|
49
|
-
@head = YAML.load($1)
|
50
|
-
@title = $2
|
51
|
-
@body = $3
|
52
|
-
|
53
|
-
when /(.*?)\n.*?\n(.*)/m
|
54
|
-
@head = {}
|
55
|
-
@title = $1
|
56
|
-
@body = $2
|
57
|
-
else
|
58
|
-
raise "This file is empty!"
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
# Generates the body from source text.
|
63
|
-
def body
|
64
|
-
@__body__ ||= RubyPants.new(BlueCloth.new(@body).to_html).to_html
|
65
|
-
end
|
66
|
-
|
67
|
-
# Generate an unique id.
|
68
|
-
def generate_guid
|
69
|
-
@head['guid'] = UUID.new.generate
|
70
|
-
end
|
71
|
-
|
72
|
-
def date ; @head['date'] end
|
73
|
-
def tags ; @head['tags'] end
|
74
|
-
def languages; @head['languages'] end
|
75
|
-
def category ; @head['category'] end
|
76
|
-
def guid ; @head['guid'] end
|
77
|
-
def year ; date.year end
|
78
|
-
def month ; date.month end
|
79
|
-
|
80
|
-
# Return the first paragraph of rendered html.
|
81
|
-
def summary
|
82
|
-
body.split("\n\n")[0]
|
83
|
-
end
|
84
|
-
|
85
|
-
# Return doc root as relative path.
|
86
|
-
def root
|
87
|
-
slashes = path.count('/')
|
88
|
-
if slashes > 0
|
89
|
-
Array.new(slashes, '..').join('/') + '/'
|
90
|
-
else
|
91
|
-
''
|
92
|
-
end
|
93
|
-
end
|
94
|
-
|
95
|
-
# Return a hash of post attributes.
|
96
|
-
def variables
|
97
|
-
head.merge(:root => root,
|
98
|
-
:header => category || title,
|
99
|
-
:title => title,
|
100
|
-
:body => body,
|
101
|
-
:link => link)
|
102
|
-
end
|
103
|
-
|
104
|
-
# Return absolute link to this post.
|
105
|
-
def link
|
106
|
-
"#{blog.meta['blog_url']}/#{path}.html"
|
107
|
-
end
|
108
|
-
|
109
|
-
end
|
110
|
-
|
111
|
-
|
112
|
-
# This class renders an ERB template for a set of attributes, which
|
113
|
-
# are accessible as instance variables.
|
114
|
-
class Template
|
115
|
-
|
116
|
-
attr_reader :root
|
117
|
-
|
118
|
-
# Initialize this template with an ERB instance.
|
119
|
-
def initialize(erb)
|
120
|
-
@erb = erb
|
121
|
-
end
|
122
|
-
|
123
|
-
# Set instance variable for this template.
|
124
|
-
def set_variables(vars)
|
125
|
-
for name, value in vars
|
126
|
-
instance_variable_set("@#{name}", value)
|
127
|
-
end
|
128
|
-
end
|
129
|
-
|
130
|
-
# Render this template.
|
131
|
-
def render
|
132
|
-
@erb.result(binding)
|
133
|
-
end
|
134
|
-
|
135
|
-
# Render a hash as attributes for a HTML tag.
|
136
|
-
def attributes(attributes)
|
137
|
-
attributes.map { |k, v| %Q{#{k}="#{v}"} }.join(' ')
|
138
|
-
end
|
139
|
-
|
140
|
-
# Render a HTML tag with given name.
|
141
|
-
# The last argument specifies the attributes of the tag.
|
142
|
-
# The second argument may be the content of the tag.
|
143
|
-
def tag(name, *args)
|
144
|
-
text, attributes = args.first.is_a?(Hash) ? [nil, args.first] : args
|
145
|
-
"<#{name} #{attributes(attributes)}>#{text}</#{name}>"
|
146
|
-
end
|
147
|
-
|
148
|
-
# Render stylesheet link tags with fixed url.
|
149
|
-
def stylesheet_link_tag(*names)
|
150
|
-
names.map { |name|
|
151
|
-
mtime = File.mtime("public/stylesheets/#{name}.css").to_i
|
152
|
-
path = "#{root}stylesheets/#{name}.css?#{mtime}"
|
153
|
-
tag :link, :href => path, :rel => 'stylesheet', :media => 'screen'
|
154
|
-
}.join("\n")
|
155
|
-
end
|
156
|
-
|
157
|
-
# Render javascript tags with fixed url.
|
158
|
-
def javascript_tag(*names)
|
159
|
-
names.map { |name|
|
160
|
-
mtime = File.mtime("public/javascripts/#{name}.js").to_i
|
161
|
-
path = "#{root}javascripts/#{name}.js?#{mtime}"
|
162
|
-
tag :script, :src => path, :type => 'text/javascript'
|
163
|
-
}.join("\n")
|
164
|
-
end
|
165
|
-
|
166
|
-
# Render an image tag with fixed url.
|
167
|
-
def image_tag(src, options = {})
|
168
|
-
tag :img, options.merge(:src => root + 'images/' + src)
|
169
|
-
end
|
170
|
-
|
171
|
-
# Render a link with fixed url.
|
172
|
-
def link_to(text, path, options = {})
|
173
|
-
tag :a, text, options.merge(:href => root + path + '.html')
|
174
|
-
end
|
175
|
-
|
176
|
-
# Render a link for the navigation bar. If the text of the link
|
177
|
-
# matches the @header variable, the css class will be set to acitve.
|
178
|
-
def navi_link(text, path)
|
179
|
-
link_to text, path, :class => (text == @header) ? 'active' : nil
|
180
|
-
end
|
181
|
-
|
182
|
-
# Render a link to a post with fixed url.
|
183
|
-
def post_link(post)
|
184
|
-
link_to post.title, post.path
|
185
|
-
end
|
186
|
-
|
187
|
-
# Render a link to an archive page.
|
188
|
-
def month_link(year, month)
|
189
|
-
link_to "#{Date::MONTHNAMES[month]} #{year}", "#{year}/#{month}/index"
|
190
|
-
end
|
191
|
-
|
192
|
-
# Render a date or time in a nice human readable format.
|
193
|
-
def date(time)
|
194
|
-
"%s %d, %d" % [Date::MONTHNAMES[time.month], time.day, time.year]
|
195
|
-
end
|
196
|
-
|
197
|
-
# Render a date or time in rfc822 format. This will be used for rss rendering.
|
198
|
-
def rfc822(time)
|
199
|
-
time.strftime("%a, %d %b %Y %H:%M:%S %z")
|
200
|
-
end
|
201
|
-
|
202
|
-
def strip_tags(html)
|
203
|
-
Shinmun.strip_tags(html)
|
204
|
-
end
|
205
|
-
|
206
|
-
end
|
207
|
-
|
208
|
-
|
209
|
-
# This class represents a blog. You need to provide a source
|
210
|
-
# directory and the meta file `blog.yml` which defines some variables.
|
211
|
-
# Example for `blog.yml`:
|
212
|
-
# blog_title: Matthias Georgi
|
213
|
-
# blog_description: Webdev, Gamedev, Interaction Design
|
214
|
-
# blog_language: en
|
215
|
-
# blog_author: Matthias Georgi
|
216
|
-
# blog_url: http://www.matthias-georgi.de
|
217
|
-
# categories:
|
218
|
-
# - Ruby
|
219
|
-
# - Emacs
|
220
|
-
class Blog
|
221
|
-
|
222
|
-
attr_reader :meta, :posts, :pages
|
223
|
-
|
224
|
-
# Read all posts from disk.
|
225
|
-
def initialize
|
226
|
-
@uuid = UUID.new
|
227
|
-
|
228
|
-
Dir.chdir('posts') do
|
229
|
-
@meta = YAML.load(File.read('blog.yml'))
|
230
|
-
|
231
|
-
@posts, @pages = Dir['**/*.md'].
|
232
|
-
map { |path| Post.new(self, path) }.
|
233
|
-
partition {|p| p.date }
|
234
|
-
|
235
|
-
@posts = @posts.sort_by { |post| post.date }.reverse
|
236
|
-
end
|
237
|
-
|
238
|
-
@templates = {}
|
239
|
-
end
|
240
|
-
|
241
|
-
def categories
|
242
|
-
meta['categories']
|
243
|
-
end
|
244
|
-
|
245
|
-
def create_post(title)
|
246
|
-
date = Date.today
|
247
|
-
name = Shinmun.urlify(title)
|
248
|
-
file = "posts/#{date.year}/#{date.month}/#{name}.md"
|
249
|
-
|
250
|
-
if File.exist?(file)
|
251
|
-
raise "#{file} exists"
|
252
|
-
else
|
253
|
-
puts "creating #{file}"
|
254
|
-
head = {
|
255
|
-
'date' => date,
|
256
|
-
'guid' => @uuid.generate,
|
257
|
-
'category' => ''
|
258
|
-
}
|
259
|
-
FileUtils.mkdir_p(File.dirname(file))
|
260
|
-
File.open(file, "w") do |io|
|
261
|
-
io.puts head.to_yaml
|
262
|
-
io.puts
|
263
|
-
io.puts title
|
264
|
-
io.puts "=" * title.size
|
265
|
-
io.puts
|
266
|
-
end
|
267
|
-
end
|
268
|
-
end
|
269
|
-
|
270
|
-
# Return template variables as hash.
|
271
|
-
def variables
|
272
|
-
meta.merge(:posts => posts,
|
273
|
-
:months => months,
|
274
|
-
:categories => categories)
|
275
|
-
end
|
276
|
-
|
277
|
-
# Read and cache template file.
|
278
|
-
def template(name)
|
279
|
-
@templates[name] ||= ERB.new(File.read("templates/#{name}"))
|
280
|
-
end
|
281
|
-
|
282
|
-
# Render template with given variables.
|
283
|
-
def render_template(name, vars)
|
284
|
-
template = Template.new(template(name))
|
285
|
-
template.set_variables(vars)
|
286
|
-
template.render
|
287
|
-
end
|
288
|
-
|
289
|
-
# Render template and insert into layout with given variables.
|
290
|
-
def render(name, vars)
|
291
|
-
vars = variables.merge(vars)
|
292
|
-
content = render_template(name, vars)
|
293
|
-
if name =~ /\.rxml$/
|
294
|
-
content
|
295
|
-
else
|
296
|
-
render_template("layout.rhtml", vars.merge(:content => content))
|
297
|
-
end
|
298
|
-
end
|
299
|
-
|
300
|
-
# Write a file to output directory.
|
301
|
-
def write_file(path, data)
|
302
|
-
FileUtils.mkdir_p('public/' + File.dirname(path))
|
303
|
-
open('public/' + path, 'wb') do |file|
|
304
|
-
file << data
|
305
|
-
end
|
306
|
-
end
|
307
|
-
|
308
|
-
# Render a template and write to file.
|
309
|
-
def render_file(path, name, vars)
|
310
|
-
puts path
|
311
|
-
write_file(path, render(name, vars))
|
312
|
-
end
|
313
|
-
|
314
|
-
# Return all posts for a given month.
|
315
|
-
def posts_for_month(year, month)
|
316
|
-
posts.select { |p| p.year == year and p.month == month }
|
317
|
-
end
|
318
|
-
|
319
|
-
# Return all posts for given tag.
|
320
|
-
def posts_for_tag(tag)
|
321
|
-
tag = tag.downcase
|
322
|
-
posts.select { |p| p.tags.to_s.match(tag) }
|
323
|
-
end
|
324
|
-
|
325
|
-
# Return all posts in given category.
|
326
|
-
def posts_for_category(category)
|
327
|
-
posts.select { |p| p.category == category }
|
328
|
-
end
|
329
|
-
|
330
|
-
# Return all months as tuples of [year, month].
|
331
|
-
def months
|
332
|
-
posts.map { |p| [p.year, p.month] }.uniq.sort
|
333
|
-
end
|
334
|
-
|
335
|
-
# Write all posts.
|
336
|
-
def write_posts
|
337
|
-
for post in posts
|
338
|
-
render_file("#{post.path}.html",
|
339
|
-
"post.rhtml",
|
340
|
-
post.variables)
|
341
|
-
end
|
342
|
-
end
|
343
|
-
|
344
|
-
# Write all pages.
|
345
|
-
def write_pages
|
346
|
-
for page in pages
|
347
|
-
render_file("#{page.path}.html",
|
348
|
-
"page.rhtml",
|
349
|
-
page.variables)
|
350
|
-
end
|
351
|
-
end
|
352
|
-
|
353
|
-
# Write archive summaries.
|
354
|
-
def write_archives
|
355
|
-
for year, month in months
|
356
|
-
path = "#{year}/#{month}"
|
357
|
-
month_name = Date::MONTHNAMES[month]
|
358
|
-
posts = posts_for_month(year, month)
|
359
|
-
|
360
|
-
render_file("#{path}/index.html",
|
361
|
-
"posts.rhtml",
|
362
|
-
:header => "#{month_name} #{year}",
|
363
|
-
:year => year,
|
364
|
-
:month => month_name,
|
365
|
-
:posts => posts,
|
366
|
-
:root => '../../')
|
367
|
-
end
|
368
|
-
end
|
369
|
-
|
370
|
-
# Write category summaries.
|
371
|
-
def write_categories
|
372
|
-
for category in categories
|
373
|
-
posts = posts_for_category(category)
|
374
|
-
render_file("categories/#{category.downcase}.html",
|
375
|
-
"posts.rhtml",
|
376
|
-
:header => category,
|
377
|
-
:category => category,
|
378
|
-
:posts => posts,
|
379
|
-
:root => '../')
|
380
|
-
end
|
381
|
-
end
|
382
|
-
|
383
|
-
# Write index page.
|
384
|
-
def write_index
|
385
|
-
render_file("index.html",
|
386
|
-
"posts.rhtml",
|
387
|
-
:header => 'Home',
|
388
|
-
:posts => posts[0, 10],
|
389
|
-
:root => '')
|
390
|
-
end
|
391
|
-
|
392
|
-
# Write rss feeds for index page and categories.
|
393
|
-
def write_feeds
|
394
|
-
render_file("index.rss",
|
395
|
-
"feed.rxml",
|
396
|
-
:posts => posts[0, 10],
|
397
|
-
:root => '')
|
398
|
-
|
399
|
-
for category in categories
|
400
|
-
posts = posts_for_category(category)
|
401
|
-
render_file("categories/#{category.downcase}.rss",
|
402
|
-
"feed.rxml",
|
403
|
-
:category => category,
|
404
|
-
:posts => posts,
|
405
|
-
:root => '../')
|
406
|
-
end
|
407
|
-
end
|
408
9
|
|
409
|
-
|
410
|
-
|
411
|
-
write_pages
|
412
|
-
write_archives
|
413
|
-
write_categories
|
414
|
-
write_index
|
415
|
-
write_feeds
|
416
|
-
end
|
10
|
+
begin; require 'redcloth'; rescue LoadError; end
|
11
|
+
begin; require 'packr'; rescue LoadError; end
|
417
12
|
|
418
|
-
|
13
|
+
require 'shinmun/cache'
|
14
|
+
require 'shinmun/post'
|
15
|
+
require 'shinmun/comment'
|
16
|
+
require 'shinmun/template'
|
17
|
+
require 'shinmun/helpers'
|
18
|
+
require 'shinmun/blog'
|
419
19
|
|
420
|
-
|
20
|
+
require 'shinmun/aggregations/audioscrobbler'
|
21
|
+
require 'shinmun/aggregations/delicious'
|
22
|
+
require 'shinmun/aggregations/flickr'
|
23
|
+
require 'shinmun/aggregations/fortythree'
|