pipin 0.0.3 → 0.1.0
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/README.rdoc +22 -7
- data/lib/misc/hikidoc.rb +906 -0
- data/lib/misc/mixistyle.rb +8 -0
- data/lib/pipin/build_tasks.rb +57 -0
- data/lib/pipin/templates/Rakefile +3 -0
- data/lib/pipin/templates/pipinrc +56 -0
- data/lib/pipin/version.rb +1 -1
- data/lib/pipin/views/archives.haml +5 -0
- data/{views → lib/pipin/views}/layout.haml +15 -3
- data/lib/pipin/views/list.haml +8 -0
- data/lib/pipin/views/post.haml +7 -0
- data/lib/pipin/views/sitemap.haml +5 -0
- data/lib/pipin.rb +92 -28
- metadata +17 -10
- data/views/list.haml +0 -4
- data/views/post.haml +0 -4
- /data/{templatefiles/datasample → lib/pipin/templates/data}/20110620.txt +0 -0
- /data/{templatefiles/datasample → lib/pipin/templates/data}/20110622.txt +0 -0
- /data/{templatefiles/datasample → lib/pipin/templates/data}/about.txt +0 -0
- /data/{templatefiles/datasample → lib/pipin/templates/data}/hello.src +0 -0
- /data/{templatefiles/pipinrc → lib/pipin/templates/stylesheets/pipin.css} +0 -0
data/README.rdoc
CHANGED
@@ -10,18 +10,33 @@
|
|
10
10
|
== build
|
11
11
|
|
12
12
|
cd myblog
|
13
|
-
|
14
|
-
ruby -r pipin -e 'Pipin.command :build'
|
13
|
+
rake
|
15
14
|
|
16
15
|
== todo
|
17
16
|
|
18
17
|
* 原稿の入力
|
19
|
-
|
20
|
-
|
18
|
+
* html, txt(hd)
|
19
|
+
* to_html
|
21
20
|
|
22
21
|
* 原稿の検索
|
23
|
-
|
22
|
+
* カテゴリ
|
24
23
|
|
25
24
|
* html出力
|
26
|
-
|
27
|
-
|
25
|
+
* ページ一覧
|
26
|
+
* 月別ページ
|
27
|
+
|
28
|
+
== trap
|
29
|
+
|
30
|
+
* 原稿ファイルを削除した場合、削除したファイルはrakeビルド時の更新条件から除外されます
|
31
|
+
** htmlが再出力されない場合は、そのhtmlファイルを手動で削除してからrakeを実行してください
|
32
|
+
|
33
|
+
== from tDiary
|
34
|
+
|
35
|
+
* 静的なhtmlとして保存できる
|
36
|
+
** postのhtmlを生成するプラグインや
|
37
|
+
** 写真とかもそのままいける
|
38
|
+
* urlの互換
|
39
|
+
* exportはレンダリングされたhtmlから
|
40
|
+
* todo: コメント...
|
41
|
+
* todo: カテゴリなどの付属情報はxml的に...
|
42
|
+
|
data/lib/misc/hikidoc.rb
ADDED
@@ -0,0 +1,906 @@
|
|
1
|
+
# Copyright (c) 2005, Kazuhiko <kazuhiko@fdiary.net>
|
2
|
+
# Copyright (c) 2007 Minero Aoki
|
3
|
+
# All rights reserved.
|
4
|
+
#
|
5
|
+
# Redistribution and use in source and binary forms, with or without
|
6
|
+
# modification, are permitted provided that the following conditions are
|
7
|
+
# met:
|
8
|
+
#
|
9
|
+
# * Redistributions of source code must retain the above copyright
|
10
|
+
# notice, this list of conditions and the following disclaimer.
|
11
|
+
# * Redistributions in binary form must reproduce the above
|
12
|
+
# copyright notice, this list of conditions and the following
|
13
|
+
# disclaimer in the documentation and/or other materials provided
|
14
|
+
# with the distribution.
|
15
|
+
# * Neither the name of the HikiDoc nor the names of its
|
16
|
+
# contributors may be used to endorse or promote products derived
|
17
|
+
# from this software without specific prior written permission.
|
18
|
+
#
|
19
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
20
|
+
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
21
|
+
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
22
|
+
# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
23
|
+
# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
24
|
+
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
25
|
+
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
26
|
+
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
27
|
+
# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
28
|
+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
29
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
|
31
|
+
require "stringio"
|
32
|
+
require "strscan"
|
33
|
+
require "uri"
|
34
|
+
begin
|
35
|
+
require "syntax/convertors/html"
|
36
|
+
rescue LoadError
|
37
|
+
end
|
38
|
+
|
39
|
+
class HikiDoc
|
40
|
+
VERSION = "0.0.2" # FIXME
|
41
|
+
|
42
|
+
class Error < StandardError
|
43
|
+
end
|
44
|
+
|
45
|
+
class UnexpectedError < Error
|
46
|
+
end
|
47
|
+
|
48
|
+
def HikiDoc.to_html(src, options = {})
|
49
|
+
new(HTMLOutput.new(">"), options).compile(src)
|
50
|
+
end
|
51
|
+
|
52
|
+
def HikiDoc.to_xhtml(src, options = {})
|
53
|
+
new(HTMLOutput.new(" />"), options).compile(src)
|
54
|
+
end
|
55
|
+
|
56
|
+
def initialize(output, options = {})
|
57
|
+
@output = output
|
58
|
+
@options = default_options.merge(options)
|
59
|
+
@header_re = nil
|
60
|
+
@level = options[:level] || 1
|
61
|
+
@plugin_syntax = options[:plugin_syntax] || method(:valid_plugin_syntax?)
|
62
|
+
end
|
63
|
+
|
64
|
+
def compile(src)
|
65
|
+
@output.reset
|
66
|
+
escape_plugin_blocks(src) {|escaped|
|
67
|
+
compile_blocks escaped
|
68
|
+
@output.finish
|
69
|
+
}
|
70
|
+
end
|
71
|
+
|
72
|
+
# for backward compatibility
|
73
|
+
def to_html
|
74
|
+
$stderr.puts("warning: HikiDoc#to_html is deprecated. Please use HikiDoc.to_html or HikiDoc.to_xhtml instead.")
|
75
|
+
self.class.to_html(@output, @options)
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def default_options
|
81
|
+
{
|
82
|
+
:allow_bracket_inline_image => true,
|
83
|
+
:use_wiki_name => true,
|
84
|
+
:use_not_wiki_name => true,
|
85
|
+
}
|
86
|
+
end
|
87
|
+
|
88
|
+
#
|
89
|
+
# Plugin
|
90
|
+
#
|
91
|
+
|
92
|
+
def valid_plugin_syntax?(code)
|
93
|
+
/['"]/ !~ code.gsub(/'(?:[^\\']+|\\.)*'|"(?:[^\\"]+|\\.)*"/m, "")
|
94
|
+
end
|
95
|
+
|
96
|
+
def escape_plugin_blocks(text)
|
97
|
+
s = StringScanner.new(text)
|
98
|
+
buf = ""
|
99
|
+
@plugin_blocks = []
|
100
|
+
while chunk = s.scan_until(/\{\{/)
|
101
|
+
tail = chunk[-2, 2]
|
102
|
+
chunk[-2, 2] = ""
|
103
|
+
buf << chunk
|
104
|
+
# plugin
|
105
|
+
if block = extract_plugin_block(s)
|
106
|
+
@plugin_blocks.push block
|
107
|
+
buf << "\0#{@plugin_blocks.size - 1}\0"
|
108
|
+
else
|
109
|
+
buf << "{{"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
buf << s.rest
|
113
|
+
yield(buf)
|
114
|
+
end
|
115
|
+
|
116
|
+
def restore_plugin_block(str)
|
117
|
+
str.gsub(/\0(\d+)\0/) {
|
118
|
+
"{{" + plugin_block($1.to_i) + "}}"
|
119
|
+
}
|
120
|
+
end
|
121
|
+
|
122
|
+
def evaluate_plugin_block(str, buf = nil)
|
123
|
+
buf ||= @output.container
|
124
|
+
str.split(/(\0\d+\0)/).each do |s|
|
125
|
+
if s[0, 1] == "\0" and s[-1, 1] == "\0"
|
126
|
+
buf << @output.inline_plugin(plugin_block(s[1..-2].to_i))
|
127
|
+
else
|
128
|
+
buf << @output.text(s)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
buf
|
132
|
+
end
|
133
|
+
|
134
|
+
def plugin_block(id)
|
135
|
+
@plugin_blocks[id] or raise UnexpectedError, "must not happen: #{id.inspect}"
|
136
|
+
end
|
137
|
+
|
138
|
+
def extract_plugin_block(s)
|
139
|
+
pos = s.pos
|
140
|
+
buf = ""
|
141
|
+
while chunk = s.scan_until(/\}\}/)
|
142
|
+
buf << chunk
|
143
|
+
buf.chomp!("}}")
|
144
|
+
if @plugin_syntax.call(buf)
|
145
|
+
return buf
|
146
|
+
end
|
147
|
+
buf << "}}"
|
148
|
+
end
|
149
|
+
s.pos = pos
|
150
|
+
nil
|
151
|
+
end
|
152
|
+
|
153
|
+
#
|
154
|
+
# Block Level
|
155
|
+
#
|
156
|
+
|
157
|
+
def compile_blocks(src)
|
158
|
+
f = LineInput.new(StringIO.new(src))
|
159
|
+
while line = f.peek
|
160
|
+
case line
|
161
|
+
when COMMENT_RE
|
162
|
+
f.gets
|
163
|
+
when HEADER_RE
|
164
|
+
compile_header f.gets
|
165
|
+
when HRULE_RE
|
166
|
+
f.gets
|
167
|
+
compile_hrule
|
168
|
+
when LIST_RE
|
169
|
+
compile_list f
|
170
|
+
when DLIST_RE
|
171
|
+
compile_dlist f
|
172
|
+
when TABLE_RE
|
173
|
+
compile_table f
|
174
|
+
when BLOCKQUOTE_RE
|
175
|
+
compile_blockquote f
|
176
|
+
when INDENTED_PRE_RE
|
177
|
+
compile_indented_pre f
|
178
|
+
when BLOCK_PRE_OPEN_RE
|
179
|
+
compile_block_pre f
|
180
|
+
else
|
181
|
+
if /^$/ =~ line
|
182
|
+
f.gets
|
183
|
+
next
|
184
|
+
end
|
185
|
+
compile_paragraph f
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
COMMENT_RE = %r<\A//>
|
191
|
+
|
192
|
+
def skip_comments(f)
|
193
|
+
f.while_match(COMMENT_RE) do |line|
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
HEADER_RE = /\A!+/
|
198
|
+
|
199
|
+
def compile_header(line)
|
200
|
+
@header_re ||= /\A!{1,#{7 - @level}}/
|
201
|
+
level = @level + (line.slice!(@header_re).size - 1)
|
202
|
+
title = strip(line)
|
203
|
+
@output.headline level, compile_inline(title)
|
204
|
+
end
|
205
|
+
|
206
|
+
HRULE_RE = /\A----$/
|
207
|
+
|
208
|
+
def compile_hrule
|
209
|
+
@output.hrule
|
210
|
+
end
|
211
|
+
|
212
|
+
ULIST = "*"
|
213
|
+
OLIST = "#"
|
214
|
+
LIST_RE = /\A#{Regexp.union(ULIST, OLIST)}+/
|
215
|
+
|
216
|
+
def compile_list(f)
|
217
|
+
typestack = []
|
218
|
+
level = 0
|
219
|
+
@output.list_begin
|
220
|
+
f.while_match(LIST_RE) do |line|
|
221
|
+
list_type = (line[0,1] == ULIST ? "ul" : "ol")
|
222
|
+
new_level = line.slice(LIST_RE).size
|
223
|
+
item = strip(line.sub(LIST_RE, ""))
|
224
|
+
if new_level > level
|
225
|
+
(new_level - level).times do
|
226
|
+
typestack.push list_type
|
227
|
+
@output.list_open list_type
|
228
|
+
@output.listitem_open
|
229
|
+
end
|
230
|
+
@output.listitem compile_inline(item)
|
231
|
+
elsif new_level < level
|
232
|
+
(level - new_level).times do
|
233
|
+
@output.listitem_close
|
234
|
+
@output.list_close typestack.pop
|
235
|
+
end
|
236
|
+
@output.listitem_close
|
237
|
+
@output.listitem_open
|
238
|
+
@output.listitem compile_inline(item)
|
239
|
+
elsif list_type == typestack.last
|
240
|
+
@output.listitem_close
|
241
|
+
@output.listitem_open
|
242
|
+
@output.listitem compile_inline(item)
|
243
|
+
else
|
244
|
+
@output.listitem_close
|
245
|
+
@output.list_close typestack.pop
|
246
|
+
@output.list_open list_type
|
247
|
+
@output.listitem_open
|
248
|
+
@output.listitem compile_inline(item)
|
249
|
+
typestack.push list_type
|
250
|
+
end
|
251
|
+
level = new_level
|
252
|
+
skip_comments f
|
253
|
+
end
|
254
|
+
level.times do
|
255
|
+
@output.listitem_close
|
256
|
+
@output.list_close typestack.pop
|
257
|
+
end
|
258
|
+
@output.list_end
|
259
|
+
end
|
260
|
+
|
261
|
+
DLIST_RE = /\A:/
|
262
|
+
|
263
|
+
def compile_dlist(f)
|
264
|
+
@output.dlist_open
|
265
|
+
f.while_match(DLIST_RE) do |line|
|
266
|
+
dt, dd = split_dlitem(line.sub(DLIST_RE, ""))
|
267
|
+
@output.dlist_item compile_inline(dt), compile_inline(dd)
|
268
|
+
skip_comments f
|
269
|
+
end
|
270
|
+
@output.dlist_close
|
271
|
+
end
|
272
|
+
|
273
|
+
def split_dlitem(line)
|
274
|
+
re = /\A((?:#{BRACKET_LINK_RE}|.)*?):/o
|
275
|
+
if m = re.match(line)
|
276
|
+
return m[1], m.post_match
|
277
|
+
else
|
278
|
+
return line, ""
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
TABLE_RE = /\A\|\|/
|
283
|
+
|
284
|
+
def compile_table(f)
|
285
|
+
lines = []
|
286
|
+
f.while_match(TABLE_RE) do |line|
|
287
|
+
lines.push line
|
288
|
+
skip_comments f
|
289
|
+
end
|
290
|
+
@output.table_open
|
291
|
+
lines.each do |line|
|
292
|
+
@output.table_record_open
|
293
|
+
split_columns(line.sub(TABLE_RE, "")).each do |col|
|
294
|
+
mid = col.sub!(/\A!/, "") ? "table_head" : "table_data"
|
295
|
+
span = col.slice!(/\A[\^>]*/)
|
296
|
+
rs = span_count(span, "^")
|
297
|
+
cs = span_count(span, ">")
|
298
|
+
@output.__send__(mid, compile_inline(col), rs, cs)
|
299
|
+
end
|
300
|
+
@output.table_record_close
|
301
|
+
end
|
302
|
+
@output.table_close
|
303
|
+
end
|
304
|
+
|
305
|
+
def split_columns(str)
|
306
|
+
cols = str.split(/\|\|/)
|
307
|
+
cols.pop if cols.last.chomp.empty?
|
308
|
+
cols
|
309
|
+
end
|
310
|
+
|
311
|
+
def span_count(str, ch)
|
312
|
+
c = str.count(ch)
|
313
|
+
c == 0 ? nil : c + 1
|
314
|
+
end
|
315
|
+
|
316
|
+
BLOCKQUOTE_RE = /\A""[ \t]?/
|
317
|
+
|
318
|
+
def compile_blockquote(f)
|
319
|
+
@output.blockquote_open
|
320
|
+
lines = []
|
321
|
+
f.while_match(BLOCKQUOTE_RE) do |line|
|
322
|
+
lines.push line.sub(BLOCKQUOTE_RE, "")
|
323
|
+
skip_comments f
|
324
|
+
end
|
325
|
+
compile_blocks lines.join("")
|
326
|
+
@output.blockquote_close
|
327
|
+
end
|
328
|
+
|
329
|
+
INDENTED_PRE_RE = /\A[ \t]/
|
330
|
+
|
331
|
+
def compile_indented_pre(f)
|
332
|
+
lines = f.span(INDENTED_PRE_RE)\
|
333
|
+
.map {|line| rstrip(line.sub(INDENTED_PRE_RE, "")) }\
|
334
|
+
.map {|line| @output.text(line) }
|
335
|
+
@output.preformatted restore_plugin_block(lines.join("\n"))
|
336
|
+
end
|
337
|
+
|
338
|
+
BLOCK_PRE_OPEN_RE = /\A<<<\s*(\w+)?/
|
339
|
+
BLOCK_PRE_CLOSE_RE = /\A>>>/
|
340
|
+
|
341
|
+
def compile_block_pre(f)
|
342
|
+
m = BLOCK_PRE_OPEN_RE.match(f.gets) or raise UnexpectedError, "must not happen"
|
343
|
+
str = restore_plugin_block(f.break(BLOCK_PRE_CLOSE_RE).join.chomp)
|
344
|
+
f.gets
|
345
|
+
@output.block_preformatted(str, m[1])
|
346
|
+
end
|
347
|
+
|
348
|
+
BLANK = /\A$/
|
349
|
+
PARAGRAPH_END_RE = Regexp.union(BLANK,
|
350
|
+
HEADER_RE, HRULE_RE, LIST_RE, DLIST_RE,
|
351
|
+
BLOCKQUOTE_RE, TABLE_RE,
|
352
|
+
INDENTED_PRE_RE, BLOCK_PRE_OPEN_RE)
|
353
|
+
|
354
|
+
def compile_paragraph(f)
|
355
|
+
lines = f.break(PARAGRAPH_END_RE)\
|
356
|
+
.reject {|line| COMMENT_RE =~ line }
|
357
|
+
if lines.size == 1 and /\A\0(\d+)\0\z/ =~ strip(lines[0])
|
358
|
+
@output.block_plugin plugin_block($1.to_i)
|
359
|
+
else
|
360
|
+
line_buffer = @output.container(:paragraph)
|
361
|
+
lines.each_with_index do |line, i|
|
362
|
+
buffer = @output.container
|
363
|
+
line_buffer << buffer
|
364
|
+
compile_inline(lstrip(line).chomp, buffer)
|
365
|
+
end
|
366
|
+
@output.paragraph(line_buffer)
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
#
|
371
|
+
# Inline Level
|
372
|
+
#
|
373
|
+
|
374
|
+
BRACKET_LINK_RE = /\[\[.+?\]\]/
|
375
|
+
URI_RE = /(?:https?|ftp|file|mailto):[A-Za-z0-9;\/?:@&=+$,\-_.!~*\'()#%]+/
|
376
|
+
WIKI_NAME_RE = /\b(?:[A-Z]+[a-z\d]+){2,}\b/
|
377
|
+
|
378
|
+
def inline_syntax_re
|
379
|
+
if @options[:use_wiki_name]
|
380
|
+
if @options[:use_not_wiki_name]
|
381
|
+
/ (#{BRACKET_LINK_RE})
|
382
|
+
| (#{URI_RE})
|
383
|
+
| (#{MODIFIER_RE})
|
384
|
+
| (\^?#{WIKI_NAME_RE})
|
385
|
+
/xo
|
386
|
+
else
|
387
|
+
/ (#{BRACKET_LINK_RE})
|
388
|
+
| (#{URI_RE})
|
389
|
+
| (#{MODIFIER_RE})
|
390
|
+
| (#{WIKI_NAME_RE})
|
391
|
+
/xo
|
392
|
+
end
|
393
|
+
else
|
394
|
+
/ (#{BRACKET_LINK_RE})
|
395
|
+
| (#{URI_RE})
|
396
|
+
| (#{MODIFIER_RE})
|
397
|
+
/xo
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
def compile_inline(str, buf = nil)
|
402
|
+
buf ||= @output.container
|
403
|
+
re = inline_syntax_re
|
404
|
+
pending_str = nil
|
405
|
+
while m = re.match(str)
|
406
|
+
str = m.post_match
|
407
|
+
|
408
|
+
link, uri, mod, wiki_name = m[1, 4]
|
409
|
+
if wiki_name and wiki_name[0, 1] == "^"
|
410
|
+
pending_str = m.pre_match + wiki_name[1..-1]
|
411
|
+
next
|
412
|
+
end
|
413
|
+
|
414
|
+
pre_str = "#{pending_str}#{m.pre_match}"
|
415
|
+
pending_str = nil
|
416
|
+
evaluate_plugin_block(pre_str, buf)
|
417
|
+
compile_inline_markup(buf, link, uri, mod, wiki_name)
|
418
|
+
end
|
419
|
+
evaluate_plugin_block(pending_str || str, buf)
|
420
|
+
buf
|
421
|
+
end
|
422
|
+
|
423
|
+
def compile_inline_markup(buf, link, uri, mod, wiki_name)
|
424
|
+
case
|
425
|
+
when link
|
426
|
+
buf << compile_bracket_link(link[2...-2])
|
427
|
+
when uri
|
428
|
+
buf << compile_uri_autolink(uri)
|
429
|
+
when mod
|
430
|
+
buf << compile_modifier(mod)
|
431
|
+
when wiki_name
|
432
|
+
buf << @output.wiki_name(wiki_name)
|
433
|
+
else
|
434
|
+
raise UnexpectedError, "must not happen"
|
435
|
+
end
|
436
|
+
end
|
437
|
+
|
438
|
+
def compile_bracket_link(link)
|
439
|
+
if m = /\A(?>[^|\\]+|\\.)*\|/.match(link)
|
440
|
+
title = m[0].chop
|
441
|
+
uri = m.post_match
|
442
|
+
fixed_uri = fix_uri(uri)
|
443
|
+
if can_image_link?(uri)
|
444
|
+
@output.image_hyperlink(fixed_uri, title)
|
445
|
+
else
|
446
|
+
@output.hyperlink(fixed_uri, compile_modifier(title))
|
447
|
+
end
|
448
|
+
else
|
449
|
+
fixed_link = fix_uri(link)
|
450
|
+
if can_image_link?(link)
|
451
|
+
@output.image_hyperlink(fixed_link)
|
452
|
+
else
|
453
|
+
@output.hyperlink_namedpage(fixed_link, @output.text(link))
|
454
|
+
end
|
455
|
+
end
|
456
|
+
end
|
457
|
+
|
458
|
+
def can_image_link?(uri)
|
459
|
+
image?(uri) and @options[:allow_bracket_inline_image]
|
460
|
+
end
|
461
|
+
|
462
|
+
def compile_uri_autolink(uri)
|
463
|
+
if image?(uri)
|
464
|
+
@output.image_hyperlink(fix_uri(uri))
|
465
|
+
else
|
466
|
+
@output.hyperlink(fix_uri(uri), @output.text(uri))
|
467
|
+
end
|
468
|
+
end
|
469
|
+
|
470
|
+
def fix_uri(uri)
|
471
|
+
if /\A(?:https?|ftp|file):(?!\/\/)/ =~ uri
|
472
|
+
uri.sub(/\A\w+:/, "")
|
473
|
+
else
|
474
|
+
uri
|
475
|
+
end
|
476
|
+
end
|
477
|
+
|
478
|
+
IMAGE_EXTS = %w(.jpg .jpeg .gif .png)
|
479
|
+
|
480
|
+
def image?(uri)
|
481
|
+
IMAGE_EXTS.include?(File.extname(uri).downcase)
|
482
|
+
end
|
483
|
+
|
484
|
+
STRONG = "'''"
|
485
|
+
EM = "''"
|
486
|
+
DEL = "=="
|
487
|
+
|
488
|
+
STRONG_RE = /'''.+?'''/
|
489
|
+
EM_RE = /''.+?''/
|
490
|
+
DEL_RE = /==.+?==/
|
491
|
+
|
492
|
+
MODIFIER_RE = Regexp.union(STRONG_RE, EM_RE, DEL_RE)
|
493
|
+
|
494
|
+
MODTAG = {
|
495
|
+
STRONG => "strong",
|
496
|
+
EM => "em",
|
497
|
+
DEL => "del"
|
498
|
+
}
|
499
|
+
|
500
|
+
def compile_modifier(str)
|
501
|
+
buf = @output.container
|
502
|
+
while m = / (#{MODIFIER_RE})
|
503
|
+
/xo.match(str)
|
504
|
+
evaluate_plugin_block(m.pre_match, buf)
|
505
|
+
case
|
506
|
+
when chunk = m[1]
|
507
|
+
mod, s = split_mod(chunk)
|
508
|
+
mid = MODTAG[mod]
|
509
|
+
buf << @output.__send__(mid, compile_inline(s))
|
510
|
+
else
|
511
|
+
raise UnexpectedError, "must not happen"
|
512
|
+
end
|
513
|
+
str = m.post_match
|
514
|
+
end
|
515
|
+
evaluate_plugin_block(str, buf)
|
516
|
+
buf
|
517
|
+
end
|
518
|
+
|
519
|
+
def split_mod(str)
|
520
|
+
case str
|
521
|
+
when /\A'''/
|
522
|
+
return str[0, 3], str[3...-3]
|
523
|
+
when /\A''/
|
524
|
+
return str[0, 2], str[2...-2]
|
525
|
+
when /\A==/
|
526
|
+
return str[0, 2], str[2...-2]
|
527
|
+
else
|
528
|
+
raise UnexpectedError, "must not happen: #{str.inspect}"
|
529
|
+
end
|
530
|
+
end
|
531
|
+
|
532
|
+
def strip(str)
|
533
|
+
rstrip(lstrip(str))
|
534
|
+
end
|
535
|
+
|
536
|
+
def rstrip(str)
|
537
|
+
str.sub(/[ \t\r\n\v\f]+\z/, "")
|
538
|
+
end
|
539
|
+
|
540
|
+
def lstrip(str)
|
541
|
+
str.sub(/\A[ \t\r\n\v\f]+/, "")
|
542
|
+
end
|
543
|
+
|
544
|
+
|
545
|
+
class HTMLOutput
|
546
|
+
def initialize(suffix = " />")
|
547
|
+
@suffix = suffix
|
548
|
+
@f = nil
|
549
|
+
end
|
550
|
+
|
551
|
+
def reset
|
552
|
+
@f = StringIO.new
|
553
|
+
end
|
554
|
+
|
555
|
+
def finish
|
556
|
+
@f.string
|
557
|
+
end
|
558
|
+
|
559
|
+
def container(_for=nil)
|
560
|
+
case _for
|
561
|
+
when :paragraph
|
562
|
+
[]
|
563
|
+
else
|
564
|
+
""
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
#
|
569
|
+
# Procedures
|
570
|
+
#
|
571
|
+
|
572
|
+
def headline(level, title)
|
573
|
+
@f.puts "<h#{level}>#{title}</h#{level}>"
|
574
|
+
end
|
575
|
+
|
576
|
+
def hrule
|
577
|
+
@f.puts "<hr#{@suffix}"
|
578
|
+
end
|
579
|
+
|
580
|
+
def list_begin
|
581
|
+
end
|
582
|
+
|
583
|
+
def list_end
|
584
|
+
@f.puts
|
585
|
+
end
|
586
|
+
|
587
|
+
def list_open(type)
|
588
|
+
@f.puts "<#{type}>"
|
589
|
+
end
|
590
|
+
|
591
|
+
def list_close(type)
|
592
|
+
@f.print "</#{type}>"
|
593
|
+
end
|
594
|
+
|
595
|
+
def listitem_open
|
596
|
+
@f.print "<li>"
|
597
|
+
end
|
598
|
+
|
599
|
+
def listitem_close
|
600
|
+
@f.puts "</li>"
|
601
|
+
end
|
602
|
+
|
603
|
+
def listitem(item)
|
604
|
+
@f.print item
|
605
|
+
end
|
606
|
+
|
607
|
+
def dlist_open
|
608
|
+
@f.puts "<dl>"
|
609
|
+
end
|
610
|
+
|
611
|
+
def dlist_close
|
612
|
+
@f.puts "</dl>"
|
613
|
+
end
|
614
|
+
|
615
|
+
def dlist_item(dt, dd)
|
616
|
+
case
|
617
|
+
when dd.empty?
|
618
|
+
@f.puts "<dt>#{dt}</dt>"
|
619
|
+
when dt.empty?
|
620
|
+
@f.puts "<dd>#{dd}</dd>"
|
621
|
+
else
|
622
|
+
@f.puts "<dt>#{dt}</dt>"
|
623
|
+
@f.puts "<dd>#{dd}</dd>"
|
624
|
+
end
|
625
|
+
end
|
626
|
+
|
627
|
+
def table_open
|
628
|
+
@f.puts %Q(<table border="1">)
|
629
|
+
end
|
630
|
+
|
631
|
+
def table_close
|
632
|
+
@f.puts "</table>"
|
633
|
+
end
|
634
|
+
|
635
|
+
def table_record_open
|
636
|
+
@f.print "<tr>"
|
637
|
+
end
|
638
|
+
|
639
|
+
def table_record_close
|
640
|
+
@f.puts "</tr>"
|
641
|
+
end
|
642
|
+
|
643
|
+
def table_head(item, rs, cs)
|
644
|
+
@f.print "<th#{tdattr(rs, cs)}>#{item}</th>"
|
645
|
+
end
|
646
|
+
|
647
|
+
def table_data(item, rs, cs)
|
648
|
+
@f.print "<td#{tdattr(rs, cs)}>#{item}</td>"
|
649
|
+
end
|
650
|
+
|
651
|
+
def tdattr(rs, cs)
|
652
|
+
buf = ""
|
653
|
+
buf << %Q( rowspan="#{rs}") if rs
|
654
|
+
buf << %Q( colspan="#{cs}") if cs
|
655
|
+
buf
|
656
|
+
end
|
657
|
+
private :tdattr
|
658
|
+
|
659
|
+
def blockquote_open
|
660
|
+
@f.print "<blockquote>"
|
661
|
+
end
|
662
|
+
|
663
|
+
def blockquote_close
|
664
|
+
@f.puts "</blockquote>"
|
665
|
+
end
|
666
|
+
|
667
|
+
def block_preformatted(str, info)
|
668
|
+
syntax = info ? info.downcase : nil
|
669
|
+
if syntax
|
670
|
+
begin
|
671
|
+
convertor = Syntax::Convertors::HTML.for_syntax(syntax)
|
672
|
+
@f.puts convertor.convert(str)
|
673
|
+
return
|
674
|
+
rescue NameError, RuntimeError
|
675
|
+
end
|
676
|
+
end
|
677
|
+
preformatted(text(str))
|
678
|
+
end
|
679
|
+
|
680
|
+
def preformatted(str)
|
681
|
+
@f.print "<pre>"
|
682
|
+
@f.print str
|
683
|
+
@f.puts "</pre>"
|
684
|
+
end
|
685
|
+
|
686
|
+
def paragraph(lines)
|
687
|
+
@f.puts "<p>#{lines.join("\n")}</p>"
|
688
|
+
end
|
689
|
+
|
690
|
+
def block_plugin(str)
|
691
|
+
@f.puts %Q(<div class="plugin">{{#{escape_html(str)}}}</div>)
|
692
|
+
end
|
693
|
+
|
694
|
+
#
|
695
|
+
# Functions
|
696
|
+
#
|
697
|
+
|
698
|
+
def hyperlink(uri, title)
|
699
|
+
%Q(<a href="#{escape_html_param(uri)}">#{title}</a>)
|
700
|
+
end
|
701
|
+
|
702
|
+
def hyperlink_namedpage(uri, title)
|
703
|
+
%Q(<a href="#{escape_html_param(uri)}">#{title}</a>)
|
704
|
+
end
|
705
|
+
|
706
|
+
def wiki_name(name)
|
707
|
+
hyperlink_namedpage(name, text(name))
|
708
|
+
end
|
709
|
+
|
710
|
+
def image_hyperlink(uri, alt = nil)
|
711
|
+
alt ||= uri.split(/\//).last
|
712
|
+
alt = escape_html(alt)
|
713
|
+
%Q(<img src="#{escape_html_param(uri)}" alt="#{alt}"#{@suffix})
|
714
|
+
end
|
715
|
+
|
716
|
+
def strong(item)
|
717
|
+
"<strong>#{item}</strong>"
|
718
|
+
end
|
719
|
+
|
720
|
+
def em(item)
|
721
|
+
"<em>#{item}</em>"
|
722
|
+
end
|
723
|
+
|
724
|
+
def del(item)
|
725
|
+
"<del>#{item}</del>"
|
726
|
+
end
|
727
|
+
|
728
|
+
def text(str)
|
729
|
+
escape_html(str)
|
730
|
+
end
|
731
|
+
|
732
|
+
def inline_plugin(src)
|
733
|
+
%Q(<span class="plugin">{{#{src}}}</span>)
|
734
|
+
end
|
735
|
+
|
736
|
+
#
|
737
|
+
# Utilities
|
738
|
+
#
|
739
|
+
|
740
|
+
def escape_html_param(str)
|
741
|
+
escape_quote(escape_html(str))
|
742
|
+
end
|
743
|
+
|
744
|
+
def escape_html(text)
|
745
|
+
text.gsub(/&/, "&").gsub(/</, "<").gsub(/>/, ">")
|
746
|
+
end
|
747
|
+
|
748
|
+
def unescape_html(text)
|
749
|
+
text.gsub(/>/, ">").gsub(/</, "<").gsub(/&/, "&")
|
750
|
+
end
|
751
|
+
|
752
|
+
def escape_quote(text)
|
753
|
+
text.gsub(/"/, """)
|
754
|
+
end
|
755
|
+
end
|
756
|
+
|
757
|
+
|
758
|
+
class LineInput
|
759
|
+
def initialize(f)
|
760
|
+
@input = f
|
761
|
+
@buf = []
|
762
|
+
@lineno = 0
|
763
|
+
@eof_p = false
|
764
|
+
end
|
765
|
+
|
766
|
+
def inspect
|
767
|
+
"\#<#{self.class} file=#{@input.inspect} line=#{lineno()}>"
|
768
|
+
end
|
769
|
+
|
770
|
+
def eof?
|
771
|
+
@eof_p
|
772
|
+
end
|
773
|
+
|
774
|
+
def lineno
|
775
|
+
@lineno
|
776
|
+
end
|
777
|
+
|
778
|
+
def gets
|
779
|
+
unless @buf.empty?
|
780
|
+
@lineno += 1
|
781
|
+
return @buf.pop
|
782
|
+
end
|
783
|
+
return nil if @eof_p # to avoid ARGF blocking.
|
784
|
+
line = @input.gets
|
785
|
+
line = line.sub(/\r\n/, "\n") if line
|
786
|
+
@eof_p = line.nil?
|
787
|
+
@lineno += 1
|
788
|
+
line
|
789
|
+
end
|
790
|
+
|
791
|
+
def ungets(line)
|
792
|
+
return unless line
|
793
|
+
@lineno -= 1
|
794
|
+
@buf.push line
|
795
|
+
line
|
796
|
+
end
|
797
|
+
|
798
|
+
def peek
|
799
|
+
line = gets()
|
800
|
+
ungets line if line
|
801
|
+
line
|
802
|
+
end
|
803
|
+
|
804
|
+
def next?
|
805
|
+
peek() ? true : false
|
806
|
+
end
|
807
|
+
|
808
|
+
def skip_blank_lines
|
809
|
+
n = 0
|
810
|
+
while line = gets()
|
811
|
+
unless line.strip.empty?
|
812
|
+
ungets line
|
813
|
+
return n
|
814
|
+
end
|
815
|
+
n += 1
|
816
|
+
end
|
817
|
+
n
|
818
|
+
end
|
819
|
+
|
820
|
+
def gets_if(re)
|
821
|
+
line = gets()
|
822
|
+
if not line or not (re =~ line)
|
823
|
+
ungets line
|
824
|
+
return nil
|
825
|
+
end
|
826
|
+
line
|
827
|
+
end
|
828
|
+
|
829
|
+
def gets_unless(re)
|
830
|
+
line = gets()
|
831
|
+
if not line or re =~ line
|
832
|
+
ungets line
|
833
|
+
return nil
|
834
|
+
end
|
835
|
+
line
|
836
|
+
end
|
837
|
+
|
838
|
+
def each
|
839
|
+
while line = gets()
|
840
|
+
yield line
|
841
|
+
end
|
842
|
+
end
|
843
|
+
|
844
|
+
def while_match(re)
|
845
|
+
while line = gets()
|
846
|
+
unless re =~ line
|
847
|
+
ungets line
|
848
|
+
return
|
849
|
+
end
|
850
|
+
yield line
|
851
|
+
end
|
852
|
+
nil
|
853
|
+
end
|
854
|
+
|
855
|
+
def getlines_while(re)
|
856
|
+
buf = []
|
857
|
+
while_match(re) do |line|
|
858
|
+
buf.push line
|
859
|
+
end
|
860
|
+
buf
|
861
|
+
end
|
862
|
+
|
863
|
+
alias span getlines_while # from Haskell
|
864
|
+
|
865
|
+
def until_match(re)
|
866
|
+
while line = gets()
|
867
|
+
if re =~ line
|
868
|
+
ungets line
|
869
|
+
return
|
870
|
+
end
|
871
|
+
yield line
|
872
|
+
end
|
873
|
+
nil
|
874
|
+
end
|
875
|
+
|
876
|
+
def getlines_until(re)
|
877
|
+
buf = []
|
878
|
+
until_match(re) do |line|
|
879
|
+
buf.push line
|
880
|
+
end
|
881
|
+
buf
|
882
|
+
end
|
883
|
+
|
884
|
+
alias break getlines_until # from Haskell
|
885
|
+
|
886
|
+
def until_terminator(re)
|
887
|
+
while line = gets()
|
888
|
+
return if re =~ line # discard terminal line
|
889
|
+
yield line
|
890
|
+
end
|
891
|
+
nil
|
892
|
+
end
|
893
|
+
|
894
|
+
def getblock(term_re)
|
895
|
+
buf = []
|
896
|
+
until_terminator(term_re) do |line|
|
897
|
+
buf.push line
|
898
|
+
end
|
899
|
+
buf
|
900
|
+
end
|
901
|
+
end
|
902
|
+
end
|
903
|
+
|
904
|
+
if __FILE__ == $0
|
905
|
+
puts HikiDoc.to_html(ARGF.read(nil))
|
906
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'pipin'
|
2
|
+
require 'rake/clean'
|
3
|
+
|
4
|
+
def dst_html(label)
|
5
|
+
File.join DSTDIR, "#{label}.html"
|
6
|
+
end
|
7
|
+
|
8
|
+
SRCDIR = 'data'
|
9
|
+
DSTDIR = 'public'
|
10
|
+
SRC_EXTNAMES = %w(html txt)
|
11
|
+
EXTS = SRC_EXTNAMES.join(',')
|
12
|
+
|
13
|
+
DIARY_SRCS = FileList["#{SRCDIR}/#{Pipin::Diary_pattern}.{#{EXTS}}"]
|
14
|
+
SRCS = FileList["#{SRCDIR}/#{Pipin::Post_pattern}.{#{EXTS}}"]
|
15
|
+
DSTS = SRCS.pathmap("#{DSTDIR}/%n.html")
|
16
|
+
MONTH_DSTS = Pipin::Post.year_months.map {|year, months|
|
17
|
+
months.map {|month| dst_html(year + month) }
|
18
|
+
}.flatten
|
19
|
+
|
20
|
+
task :default => [:index, :archives, :months, :sitemap] + DSTS.sort.reverse
|
21
|
+
|
22
|
+
# posts
|
23
|
+
SRC_EXTNAMES.each do |extname|
|
24
|
+
rule /public\/\w+\.html/ => "#{SRCDIR}/%n.#{extname}" do |t|
|
25
|
+
Pipin.build 'post', File.basename(t.name, '.html')
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# mouths
|
30
|
+
task :months => MONTH_DSTS
|
31
|
+
Pipin::Post.year_months.each do |year, months|
|
32
|
+
months.each do |month|
|
33
|
+
dst = dst_html(year + month)
|
34
|
+
srcs = FileList["#{SRCDIR}/#{year + month}[0-9][0-9]*.{#{EXTS}}"]
|
35
|
+
file dst => srcs do
|
36
|
+
Pipin.build 'month', year, month
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# other
|
42
|
+
OTHER_TASKS ={
|
43
|
+
'index' => DIARY_SRCS.sort.reverse[0, 3],
|
44
|
+
'archives' => DIARY_SRCS,
|
45
|
+
'sitemap' => SRCS,
|
46
|
+
}
|
47
|
+
OTHER_TASKS.each do |pagename, srcs|
|
48
|
+
dst = dst_html(pagename)
|
49
|
+
task pagename => dst
|
50
|
+
file dst => srcs do
|
51
|
+
Pipin.build pagename
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# clean
|
56
|
+
OTHER_DSTS = OTHER_TASKS.keys.map {|page| dst_html(page) }
|
57
|
+
CLEAN.include(DSTS, MONTH_DSTS, OTHER_DSTS)
|
@@ -0,0 +1,56 @@
|
|
1
|
+
# -*- encoding: UTF-8 -*-
|
2
|
+
def root_path
|
3
|
+
File.dirname(File.expand_path(__FILE__))
|
4
|
+
end
|
5
|
+
|
6
|
+
# 各種パラメータ設定
|
7
|
+
def config
|
8
|
+
{
|
9
|
+
# ブログのURLに書き換えてください
|
10
|
+
#:base_url => 'http://dgames.jp/pipin', # for pipinrss
|
11
|
+
:title => 'Thie Pipin Diary',
|
12
|
+
#:description => 'Please set config[:description] in pipinrc',
|
13
|
+
:dir => {
|
14
|
+
#:posts => '/Users/dan/w/piping/datasample',
|
15
|
+
#:views => '/Users/dan/w/piping/views',
|
16
|
+
#:plugins => "#{root_path}/config/plugins",
|
17
|
+
},
|
18
|
+
:limit => 5,
|
19
|
+
# 複数のcssを使用する場合は配列で指定してください
|
20
|
+
:css => '/stylesheets/pipin.css',
|
21
|
+
#:css => '/stylesheets/nobunaga/nobunaga.css',
|
22
|
+
}
|
23
|
+
end
|
24
|
+
|
25
|
+
# 記事のコンパイラを設定
|
26
|
+
def add_compilers
|
27
|
+
# HikiDocを登録(拡張「.txt」に関連付ける)
|
28
|
+
require 'misc/hikidoc.rb'
|
29
|
+
Pipin::Post.add_compiler('.txt') {|post|
|
30
|
+
HikiDoc.to_html(post.body, :level => 2)
|
31
|
+
# 記事にpluginを埋め込むなら以下を使う
|
32
|
+
# 記事に書く例: {{amazon '4777512924'}}
|
33
|
+
#eval_hiki_plugin(HikiDoc.to_html(post.body, :level => 2))
|
34
|
+
}
|
35
|
+
|
36
|
+
# mixiスタイルを登録
|
37
|
+
require 'misc/mixistyle.rb'
|
38
|
+
Pipin::Post.add_compiler('.mixi') {|post|
|
39
|
+
MixiStyle.to_html(post.body)
|
40
|
+
}
|
41
|
+
|
42
|
+
# 拡張子「.src」のソースをそのまま表示する例
|
43
|
+
Pipin::Post.add_compiler('.src') {|post|
|
44
|
+
"<h2>#{h post.filename}</h2><pre>#{h post.body}</pre>"
|
45
|
+
}
|
46
|
+
end
|
47
|
+
|
48
|
+
def setup_environment
|
49
|
+
if defined? Encoding
|
50
|
+
Encoding.default_external = 'UTF-8' # 1.9
|
51
|
+
else
|
52
|
+
$KCODE = 'UTF-8' # 1.8
|
53
|
+
end
|
54
|
+
add_compilers
|
55
|
+
#Pipin::Post.posts_dir = config[:dir][:posts]
|
56
|
+
end
|
data/lib/pipin/version.rb
CHANGED
@@ -6,10 +6,14 @@
|
|
6
6
|
%meta{:content => "text/html; charset=UTF-8", "http-equiv" => "Content-Type"}/
|
7
7
|
%meta{:content => "text/javascript", "http-equiv" => "Content-Script-Type"}/
|
8
8
|
%meta{:category => "ruby; setup"}/
|
9
|
-
|
10
|
-
%
|
11
|
-
%title= @title# || config[:title]
|
9
|
+
%link{:rel => "stylesheet", :type => 'text/css', :href => 'pipin.css' || config[:css]}/
|
10
|
+
%title&= @title || config[:title]
|
12
11
|
%body
|
12
|
+
%h1&= config[:title]
|
13
|
+
.menus
|
14
|
+
%a{:href => './index' + htmlsufix} Latest
|
15
|
+
%a{:href => './archives' + htmlsufix} Archives
|
16
|
+
%a{:href => './about' + htmlsufix} About
|
13
17
|
.main
|
14
18
|
=# render_plugin_hook(:flavour_header, self)
|
15
19
|
.content
|
@@ -17,3 +21,11 @@
|
|
17
21
|
!= yield
|
18
22
|
.before_content=# render_plugin_hook(:after_content, self)
|
19
23
|
=# render_plugin_hook(:flavour_footer, self)
|
24
|
+
.footer
|
25
|
+
.sitemap
|
26
|
+
%a{:href => './sitemap.html'} Sitemap
|
27
|
+
%a{:href => 'https://github.com/dan5/pipin'} Pipin
|
28
|
+
&= Pipin::VERSION
|
29
|
+
on
|
30
|
+
%a{:href => 'http://www.ruby-lang.org/'} Ruby
|
31
|
+
&= RUBY_VERSION
|
data/lib/pipin.rb
CHANGED
@@ -2,40 +2,76 @@ require 'pipin/version'
|
|
2
2
|
require 'haml'
|
3
3
|
require 'fileutils'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
def rootdir
|
9
|
-
File.join(File.dirname(File.expand_path(__FILE__)).untaint, '..')
|
10
|
-
end
|
5
|
+
def rootdir() File.join(File.dirname(File.expand_path(__FILE__)), 'pipin') end
|
6
|
+
def htmlsufix() '.html' end
|
11
7
|
|
12
8
|
module Pipin
|
13
|
-
|
14
|
-
|
9
|
+
Diary_pattern = '[0-9]' * 8 + '*'
|
10
|
+
Post_pattern = '[0-9a-zA-Z]*'
|
11
|
+
|
12
|
+
def self.build(page_name, *args)
|
13
|
+
command :build, page_name, *args
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.command(cmd, *args)
|
17
|
+
opts = args.empty? ? ARGV : args
|
18
|
+
__send__ "command_#{cmd}", *opts
|
15
19
|
end
|
16
20
|
|
17
21
|
def self.command_new(dir)
|
18
22
|
File.exist?(dir) && raise("File exists - #{dir}")
|
19
|
-
FileUtils.cp_r File.join(rootdir ,'
|
23
|
+
FileUtils.cp_r File.join(rootdir ,'templates'), dir
|
20
24
|
puts Dir.glob(File.join(dir, '**/*')).map {|e| ' create ' + e }
|
21
25
|
end
|
22
26
|
|
23
|
-
def self.command_build(
|
24
|
-
|
25
|
-
build = Builder.new('public')
|
26
|
-
build.render_posts
|
27
|
-
build.render_list('index', '20*', :limit => 3)
|
27
|
+
def self.command_build(page_name, *args)
|
28
|
+
Builder.new('public').__send__('render_' + page_name, *args)
|
28
29
|
end
|
29
30
|
|
30
31
|
class Builder
|
31
|
-
def initialize(
|
32
|
-
@
|
32
|
+
def initialize(dist_dir)
|
33
|
+
@dist_dir = dist_dir
|
34
|
+
load './pipinrc'
|
35
|
+
setup_environment
|
33
36
|
end
|
34
37
|
|
35
|
-
def
|
36
|
-
Post.find(
|
37
|
-
|
38
|
-
|
38
|
+
def render_sitemap
|
39
|
+
posts = Post.find(Post_pattern)
|
40
|
+
write_html 'sitemap', render_with_layout(:sitemap, binding)
|
41
|
+
end
|
42
|
+
|
43
|
+
def render_archives
|
44
|
+
years = Post.year_months
|
45
|
+
write_html 'archives', render_with_layout(:archives, binding)
|
46
|
+
end
|
47
|
+
|
48
|
+
#def render_months
|
49
|
+
# years = Post.year_months
|
50
|
+
# years.each do |year, months|
|
51
|
+
# months.each do |month|
|
52
|
+
# render_month(year, month)
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
#end
|
56
|
+
|
57
|
+
def render_month(year, month)
|
58
|
+
name = year + month
|
59
|
+
render_list(name, name + '*')
|
60
|
+
end
|
61
|
+
|
62
|
+
#def render_posts
|
63
|
+
# Post.find(Post_pattern).each do |post|
|
64
|
+
# write_html post.label, render_with_layout(:post, binding)
|
65
|
+
# end
|
66
|
+
#end
|
67
|
+
|
68
|
+
def render_post(label)
|
69
|
+
post = Post.find(label).first
|
70
|
+
write_html label, render_with_layout(:post, binding)
|
71
|
+
end
|
72
|
+
|
73
|
+
def render_index
|
74
|
+
render_list('index', Diary_pattern, :limit => 3)
|
39
75
|
end
|
40
76
|
|
41
77
|
def render_list(name, pattern, options = {})
|
@@ -44,8 +80,10 @@ module Pipin
|
|
44
80
|
end
|
45
81
|
|
46
82
|
def write_html(label, html)
|
47
|
-
Dir.mkdir @
|
48
|
-
File.
|
83
|
+
Dir.mkdir @dist_dir unless File.exist?(@dist_dir)
|
84
|
+
filename = File.join(@dist_dir, label + '.html')
|
85
|
+
File.open(filename, 'w') {|f| f.write html }
|
86
|
+
puts ' create ' + filename
|
49
87
|
end
|
50
88
|
|
51
89
|
def render_with_layout(template, b = binding)
|
@@ -59,18 +97,42 @@ module Pipin
|
|
59
97
|
end
|
60
98
|
|
61
99
|
class Post
|
62
|
-
@@entries_dir = 'datasample'
|
63
100
|
def self.find(pattern, options = {})
|
64
|
-
|
65
|
-
|
66
|
-
|
101
|
+
self.find_srcs(pattern, options).map {|e| Post.new(e) }
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.find_srcs(pattern, options = {})
|
105
|
+
files = Dir.chdir(@@posts_dir) { Dir.glob(pattern + '.{txt,html}') }.sort.reverse
|
106
|
+
options[:limit] ? files[0, options[:limit]] : files
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.year_months
|
110
|
+
result = {}
|
111
|
+
files = find_srcs(Diary_pattern)
|
112
|
+
first_year = files.last[/^\d{4}/]
|
113
|
+
last_year = files.first[/^\d{4}/]
|
114
|
+
(first_year..last_year).to_a.reverse.map do |year|
|
115
|
+
result[year] = ('01'..'12').select {|month| find_srcs("#{year}#{month}*")[0] }
|
116
|
+
end
|
117
|
+
result
|
118
|
+
end
|
119
|
+
|
120
|
+
@@compilers = [[nil, lambda {|post| post.body }]]
|
121
|
+
def self.add_compiler(extname = nil, &block)
|
122
|
+
@@compilers.unshift [extname, block]
|
123
|
+
end
|
124
|
+
|
125
|
+
@@posts_dir = 'data'
|
126
|
+
def self.posts_dir=(dir)
|
127
|
+
@@posts_dir = dir
|
67
128
|
end
|
68
129
|
|
69
130
|
attr_reader :filename, :header, :body
|
70
131
|
def initialize(filename)
|
71
132
|
@filename = filename
|
72
|
-
@header, @body = Dir.chdir(@@
|
133
|
+
@header, @body = Dir.chdir(@@posts_dir) { File.read(@filename) }.split(/^__$/, 2)
|
73
134
|
@header, @body = nil, @header unless @body
|
135
|
+
@header and p(@header)
|
74
136
|
end
|
75
137
|
|
76
138
|
def label
|
@@ -78,7 +140,9 @@ module Pipin
|
|
78
140
|
end
|
79
141
|
|
80
142
|
def to_html
|
81
|
-
|
143
|
+
(@@compilers.assoc(File.extname filename) || @@compilers.last)[1].call(self)
|
144
|
+
rescue
|
145
|
+
Haml::Helpers::html_escape $!
|
82
146
|
end
|
83
147
|
end
|
84
148
|
end
|
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: pipin
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 0.0
|
5
|
+
version: 0.1.0
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- dan5ya
|
@@ -10,7 +10,7 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-06-
|
13
|
+
date: 2011-06-23 00:00:00 +09:00
|
14
14
|
default_executable:
|
15
15
|
dependencies: []
|
16
16
|
|
@@ -28,17 +28,24 @@ files:
|
|
28
28
|
- Gemfile
|
29
29
|
- README.rdoc
|
30
30
|
- Rakefile
|
31
|
+
- lib/misc/hikidoc.rb
|
32
|
+
- lib/misc/mixistyle.rb
|
31
33
|
- lib/pipin.rb
|
34
|
+
- lib/pipin/build_tasks.rb
|
35
|
+
- lib/pipin/templates/Rakefile
|
36
|
+
- lib/pipin/templates/data/20110620.txt
|
37
|
+
- lib/pipin/templates/data/20110622.txt
|
38
|
+
- lib/pipin/templates/data/about.txt
|
39
|
+
- lib/pipin/templates/data/hello.src
|
40
|
+
- lib/pipin/templates/pipinrc
|
41
|
+
- lib/pipin/templates/stylesheets/pipin.css
|
32
42
|
- lib/pipin/version.rb
|
43
|
+
- lib/pipin/views/archives.haml
|
44
|
+
- lib/pipin/views/layout.haml
|
45
|
+
- lib/pipin/views/list.haml
|
46
|
+
- lib/pipin/views/post.haml
|
47
|
+
- lib/pipin/views/sitemap.haml
|
33
48
|
- pipin.gemspec
|
34
|
-
- templatefiles/datasample/20110620.txt
|
35
|
-
- templatefiles/datasample/20110622.txt
|
36
|
-
- templatefiles/datasample/about.txt
|
37
|
-
- templatefiles/datasample/hello.src
|
38
|
-
- templatefiles/pipinrc
|
39
|
-
- views/layout.haml
|
40
|
-
- views/list.haml
|
41
|
-
- views/post.haml
|
42
49
|
has_rdoc: true
|
43
50
|
homepage: https://github.com/dan5/pipin
|
44
51
|
licenses: []
|
data/views/list.haml
DELETED
data/views/post.haml
DELETED
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|