td2planet 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/ChangeLog +3 -0
- data/MIT-LICENSE +20 -0
- data/README +63 -0
- data/README.ja +66 -0
- data/bin/td2planet.rb +10 -0
- data/config.yaml +57 -0
- data/data/td2planet/templates/day.rhtml +30 -0
- data/data/td2planet/templates/footer.rhtml +30 -0
- data/data/td2planet/templates/header.rhtml +1 -0
- data/data/td2planet/templates/layout.rhtml +33 -0
- data/data/td2planet/templates/opml.rxml +19 -0
- data/data/td2planet/templates/section.rhtml +23 -0
- data/lib/td2planet/default_formatter.rb +23 -0
- data/lib/td2planet/fetcher.rb +63 -0
- data/lib/td2planet/formatter.rb +224 -0
- data/lib/td2planet/runner.rb +60 -0
- data/lib/td2planet/sample_formatter.rb +26 -0
- data/lib/td2planet/version.rb +30 -0
- data/lib/td2planet/writer.rb +52 -0
- metadata +70 -0
data/ChangeLog
ADDED
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2006 Kazuhiro NISHIYAMA
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
= TD2Planet
|
2
|
+
This is a planet implementation of ruby, mainly for tdiary.
|
3
|
+
|
4
|
+
See http://www.planetplanet.org about planet.
|
5
|
+
|
6
|
+
== Install
|
7
|
+
=== Install using setup.rb
|
8
|
+
1. ruby setup.rb
|
9
|
+
|
10
|
+
=== Install using rubygems
|
11
|
+
1. gem build *.gemspec
|
12
|
+
2. gem install *.gem
|
13
|
+
|
14
|
+
== Usage
|
15
|
+
1. copy and edit config.yaml
|
16
|
+
2. run "td2planet.rb config.yaml"
|
17
|
+
3. copy output files to public directory if need be
|
18
|
+
4. set cron and so on if need be
|
19
|
+
|
20
|
+
=== Usage without install
|
21
|
+
1. copy and edit config.yaml
|
22
|
+
2. run "ruby -I lib bin/td2planet.rb config.yaml"
|
23
|
+
3. same as above
|
24
|
+
|
25
|
+
== SECURITY NOTICE
|
26
|
+
Do not add untrusted feeds.
|
27
|
+
All content:encoded in feeds output to the file of output_html as is,
|
28
|
+
even if content:encoded includes scripts and so on.
|
29
|
+
|
30
|
+
To avoid security problem, I recommend to divide the domain of
|
31
|
+
this planet from the domains of other contents.
|
32
|
+
(e.g. planet.example.jp)
|
33
|
+
|
34
|
+
== Customize Output
|
35
|
+
After customize, run "td2planet.rb -n config.yaml" to update output files.
|
36
|
+
|
37
|
+
=== Customize Template
|
38
|
+
1. make override templates directory
|
39
|
+
2. copy the file under templates to the override templates directory
|
40
|
+
3. edit the file
|
41
|
+
4. add the override templates directory to templates_path in config.yaml
|
42
|
+
|
43
|
+
=== Customize Formatter
|
44
|
+
1. copy default_formatter.rb or sample_formatter.rb to ./your_formatter.rb
|
45
|
+
2. edit the file (you must change class name from DefaultFormatter or SampleFormatter to YourFormatter if the filename is your_formatter.rb)
|
46
|
+
|
47
|
+
== Q&A
|
48
|
+
[Q. Why do this planet outputs content:encoded as is?]
|
49
|
+
A. I can not keep safe white lists and/or black lists.
|
50
|
+
(see one of white lists: http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A4%CA%A5%C0%A5%A4%A5%A2%A5%EA%A1%BCXSS%C2%D0%BA%F6 (Japanese))
|
51
|
+
[Q. Why do not output feeds include contents?]
|
52
|
+
A. If you use other feed reader, you can import original feeds
|
53
|
+
from opml to the feed reader.
|
54
|
+
|
55
|
+
== License
|
56
|
+
setup.rb:: GNU LGPL
|
57
|
+
other files:: MIT-LICENSE
|
58
|
+
|
59
|
+
Copyright (c) 2006, 2007 Kazuhiro NISHIYAMA
|
60
|
+
|
61
|
+
--
|
62
|
+
# -*- coding: utf-8 -*-
|
63
|
+
# vim: ts=2 sw=2 sts=2 fenc=utf-8:
|
data/README.ja
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
= TD2Planet
|
2
|
+
これはrubyによるplanet実装です。
|
3
|
+
主にtDiaryのmakerss.rbプラグインの出力を扱う用に調整してあります。
|
4
|
+
|
5
|
+
planetとは何かについては http://www.planetplanet.org などを
|
6
|
+
参照してください。
|
7
|
+
|
8
|
+
== インストール
|
9
|
+
=== setup.rbを使ったインストール
|
10
|
+
1. ruby setup.rb
|
11
|
+
|
12
|
+
=== rubygemsを使ったインストール
|
13
|
+
1. gem build *.gemspec
|
14
|
+
2. gem install *.gem
|
15
|
+
|
16
|
+
== 使い方
|
17
|
+
1. config.yamlを適当なところにコピーして編集
|
18
|
+
2. td2planet.rb config.yaml
|
19
|
+
3. 必要なら出力されたファイルを公開ディレクトリにコピー
|
20
|
+
4. 必要ならcronなどで定期的に実行
|
21
|
+
|
22
|
+
=== インストールせずに使う方法
|
23
|
+
1. config.yamlを編集
|
24
|
+
2. ruby -I lib bin/td2planet.rb config.yaml
|
25
|
+
3. 以降は上と同じ
|
26
|
+
|
27
|
+
== セキュリティ上の注意
|
28
|
+
信頼できないRSSは設定しないでください。
|
29
|
+
output_htmlで指定したファイルには元のRSSのcontent:encodedの内容が、
|
30
|
+
スクリプトなどを含んでいたとしても、そのまま出力されます。
|
31
|
+
|
32
|
+
セキュリティ上の問題を避けるため、このplanetのドメインを他の
|
33
|
+
コンテンツのドメインとわけることをお薦めします。
|
34
|
+
(例えば planet.example.jp)
|
35
|
+
|
36
|
+
|
37
|
+
== 出力のカスタマイズ
|
38
|
+
カスタマイズ後、"td2planet.rb -n config.yaml"で出力を更新できます。
|
39
|
+
|
40
|
+
=== テンプレートのカスタマイズ
|
41
|
+
1. カスタマイズするテンプレート用のディレクトリを作成
|
42
|
+
2. カスタマイズしたいテンプレートファイルをtemplates以下からコピー
|
43
|
+
3. コピーしたテンプレートファイルを編集
|
44
|
+
4. カスタマイズしたテンプレート用のディレクトリをconfig.yamlのtemplates_pathに追加
|
45
|
+
|
46
|
+
=== Formatterのカスタマイズ
|
47
|
+
1. default_formatter.rbかsample_formatter.rbを./your_formatter.rbなどにコピー
|
48
|
+
2. ファイルを編集(DefaultFormatterかSampleFormatterをファイル名がyour_formatter.rbならYourFormatterに変更する必要あり)
|
49
|
+
|
50
|
+
== Q&A
|
51
|
+
[Q. なぜcontent:encodedをそのまま出力?]
|
52
|
+
A. 安全なホワイトリストやブラックリストを保守できないため。
|
53
|
+
(ホワイトリストの例: http://d.hatena.ne.jp/keyword/%A4%CF%A4%C6%A4%CA%A5%C0%A5%A4%A5%A2%A5%EA%A1%BCXSS%C2%D0%BA%F6 )
|
54
|
+
[Q. RSSに内容をいれていないのはなぜ?]
|
55
|
+
A. 他のフィードリーダーを使っているのなら、opmlを使って
|
56
|
+
元のフィードをインポートして購読できるから。
|
57
|
+
|
58
|
+
== License
|
59
|
+
setup.rb:: GNU LGPL
|
60
|
+
other files:: MIT-LICENSE
|
61
|
+
|
62
|
+
Copyright (c) 2006, 2007 Kazuhiro NISHIYAMA
|
63
|
+
|
64
|
+
--
|
65
|
+
# -*- coding: utf-8 -*-
|
66
|
+
# vim: ts=2 sw=2 sts=2 fenc=utf-8:
|
data/bin/td2planet.rb
ADDED
data/config.yaml
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
## general config
|
2
|
+
title: TD2Planet Sample
|
3
|
+
base_uri: http://planet.example.jp/
|
4
|
+
|
5
|
+
## feed URIs
|
6
|
+
uri:
|
7
|
+
- http://kazuhiko.tdiary.net/index.rdf
|
8
|
+
- http://sho.tdiary.net/index.rdf
|
9
|
+
- http://www.tdiary.net/index.rdf
|
10
|
+
- http://www.tdiary.org/index.rdf
|
11
|
+
|
12
|
+
## feeds cache into cache_dir
|
13
|
+
cache_dir: cache
|
14
|
+
|
15
|
+
## generated files into output_dir
|
16
|
+
output_dir: output
|
17
|
+
output_html: planet.html
|
18
|
+
|
19
|
+
## themes
|
20
|
+
#tdiary_theme_path: http://localhost/tdiary/theme
|
21
|
+
tdiary_theme_path: /var/www/tdiary/theme
|
22
|
+
tdiary_theme: light-blue
|
23
|
+
|
24
|
+
## parts of templates
|
25
|
+
#author: Your name
|
26
|
+
#made: mailto:webmaster@example.jp
|
27
|
+
#favicon: /favicon.ico
|
28
|
+
#logo_html: <img src="http://planet.example.jp/images/logo.png" width="168" height="150" alt="">
|
29
|
+
date_strftime_format: '%Y-%m-%d'
|
30
|
+
sanchor_strftime_format: '%H:%M:%S'
|
31
|
+
|
32
|
+
## templates search path
|
33
|
+
#templates_path:
|
34
|
+
# - /path/to/override/templates
|
35
|
+
# - /path/to/other/override/templates
|
36
|
+
|
37
|
+
## formatter
|
38
|
+
formatter: default_formatter
|
39
|
+
#formatter: sample_formatter
|
40
|
+
#formatter: your_formatter
|
41
|
+
|
42
|
+
## spam filter (default_formatter feature)
|
43
|
+
## (filtered if last value is true, output if false)
|
44
|
+
filter: |
|
45
|
+
if (/ツッコミ/ =~ k(item.title) &&
|
46
|
+
(
|
47
|
+
(k(item.content_encoded).scan(/http:/).size >= 5) ||
|
48
|
+
(k(item.description).scan(/http:/).size >= 5) ||
|
49
|
+
(/\[\/url\]/ =~ k(item.content_encoded)) ||
|
50
|
+
(/\[\/url\]/ =~ k(item.description)) ||
|
51
|
+
(/★/ =~ k(item.dc_creator)) ||
|
52
|
+
/@google\.com/ =~ k(item.dc_creator)
|
53
|
+
))
|
54
|
+
true
|
55
|
+
else
|
56
|
+
false
|
57
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
<hr class="sep">
|
2
|
+
<div class="day">
|
3
|
+
<h2>
|
4
|
+
<span class="date"><%=h date_format(items[0]) %></span>
|
5
|
+
<span class="title">
|
6
|
+
<% if rss.channel.link -%>
|
7
|
+
<a href="<%=hk rss.channel.link %>"><%=hk rss.channel.title %></a>
|
8
|
+
<% else -%>
|
9
|
+
<%=hk rss.channel.title %>
|
10
|
+
<% end -%>
|
11
|
+
<% if rss.channel.dc_creator -%>
|
12
|
+
(<%=hk rss.channel.dc_creator %>)
|
13
|
+
<% end -%>
|
14
|
+
</span>
|
15
|
+
</h2>
|
16
|
+
<div class="body">
|
17
|
+
<% items.each do |item| -%>
|
18
|
+
<%= section(item, rss) -%>
|
19
|
+
<% end -%>
|
20
|
+
</div>
|
21
|
+
<% if /./ =~ rss.channel.dc_rights.to_s -%>
|
22
|
+
<div class="comment">
|
23
|
+
<!--<div class="caption"></div>-->
|
24
|
+
<div class="commentshort">
|
25
|
+
<!--<p><span class="canchor"></span><span class="commentator"></span> [...]</p>-->
|
26
|
+
<p><%=hk rss.channel.dc_rights %></p>
|
27
|
+
</div>
|
28
|
+
</div>
|
29
|
+
<% end -%>
|
30
|
+
</div>
|
@@ -0,0 +1,30 @@
|
|
1
|
+
</div>
|
2
|
+
<div class="sidebar">
|
3
|
+
<% if @config['logo_html'] -%>
|
4
|
+
<%= @config['logo_html'] %>
|
5
|
+
<% end -%>
|
6
|
+
<div class="block">
|
7
|
+
<h3>Subscriptions</h3>
|
8
|
+
<ul>
|
9
|
+
<% @rss_list.sort_by{|rss| -rss.item.date.to_i }.each do |rss| -%>
|
10
|
+
<li>
|
11
|
+
<a href="<%=hk rss.channel.link %>" title="<%=hk rss.channel.description %>"><%=hk rss.channel.title %></a>
|
12
|
+
<a href="<%=hk rss.channel.about %>">(feed)</a>
|
13
|
+
</li>
|
14
|
+
<% end -%>
|
15
|
+
</ul>
|
16
|
+
</div>
|
17
|
+
|
18
|
+
<div class="block">
|
19
|
+
<h3>Other formats</h3>
|
20
|
+
<ul>
|
21
|
+
<li><a href="rss10.xml">RSS 1.0</a></li>
|
22
|
+
<li><a href="rss20.xml">RSS 2.0</a></li>
|
23
|
+
<li><a href="opml.xml">opml</a></li>
|
24
|
+
</ul>
|
25
|
+
</div>
|
26
|
+
|
27
|
+
<div class="block">
|
28
|
+
<p>Last updated: <%= Time.now.localtime.strftime('%Y-%m-%d %H:%M:%S') %></p>
|
29
|
+
</div>
|
30
|
+
</div>
|
@@ -0,0 +1 @@
|
|
1
|
+
<div class="main">
|
@@ -0,0 +1,33 @@
|
|
1
|
+
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
2
|
+
<html>
|
3
|
+
<meta http-equiv="content-type" content="text/html; charset=utf-8">
|
4
|
+
<meta name="generator" content="<%=h TD2Planet.generator %>">
|
5
|
+
<meta http-equiv="content-script-type" content="text/plain"><!-- wish to disable scripts -->
|
6
|
+
<% if @config['author'] -%>
|
7
|
+
<meta name="author" content="<%=h @config['author'] %>">
|
8
|
+
<% end -%>
|
9
|
+
<% if @config['made'] -%>
|
10
|
+
<link rev="made" href="<%=h @config['made'] %>">
|
11
|
+
<% end -%>
|
12
|
+
<% if @config['favicon'] -%>
|
13
|
+
<link rel="shortcut icon" href="<%=h @config['favicon'] %>" type="image/x-icon">
|
14
|
+
<% end -%>
|
15
|
+
<meta http-equiv="content-style-type" content="text/css">
|
16
|
+
<link rel="stylesheet" href="<%=h @config['tdiary_theme_path'] %>/base.css" type="text/css" media="all">
|
17
|
+
<link rel="stylesheet" href="<%=h @config['tdiary_theme_path'] %>/<%=h @config['tdiary_theme'] %>/<%=h @config['tdiary_theme'] %>.css" title="<%=h @config['tdiary_theme'] %>" type="text/css" media="all">
|
18
|
+
<title><%=h @config['title'] %></title>
|
19
|
+
</head>
|
20
|
+
<body>
|
21
|
+
<h1><%=h @config['title'] %></h1>
|
22
|
+
<%= header %>
|
23
|
+
<% days.each do |day| -%>
|
24
|
+
<%= move_tukkomi_link(day(day[:items], day[:rss])) %>
|
25
|
+
<% end -%>
|
26
|
+
<hr class="sep">
|
27
|
+
<%= footer %>
|
28
|
+
<div class="footer">
|
29
|
+
Generated by <%=h TD2Planet.td2planet_version %><br>
|
30
|
+
Powered by <%=h TD2Planet.ruby_version %>
|
31
|
+
</div>
|
32
|
+
</body>
|
33
|
+
</html>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<?xml version="1.0"?>
|
2
|
+
<opml version="1.0">
|
3
|
+
<head>
|
4
|
+
<title><%=h @config['title'] %></title>
|
5
|
+
<dateCreated><%=h Time.now %></dateCreated>
|
6
|
+
<dateModified><%=h Time.now %></dateModified>
|
7
|
+
<% if @config.key?('author') -%>
|
8
|
+
<ownerName><%=h @config['author'] %></ownerName>
|
9
|
+
<% end -%>
|
10
|
+
<% if @config.key?('made') -%>
|
11
|
+
<ownerEmail><%=h @config['made'].sub(/^mailto:/, '') %></ownerEmail>
|
12
|
+
<% end -%>
|
13
|
+
</head>
|
14
|
+
<body>
|
15
|
+
<% rss_list.sort_by{|rss| -rss.item.date.to_i }.each do |rss| -%>
|
16
|
+
<outline text="<%=hk rss.channel.title %>" type="link" xmlUrl="<%=hk rss.channel.link %>"/>
|
17
|
+
<% end -%>
|
18
|
+
</body>
|
19
|
+
</opml>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
<div class="section">
|
2
|
+
<%
|
3
|
+
section_body = to_section_body(item)
|
4
|
+
# tdiary makerss.rb plugin
|
5
|
+
if /<h3/ =~ section_body
|
6
|
+
-%>
|
7
|
+
<%=
|
8
|
+
section_body.sub(/<h3.*?>/) do
|
9
|
+
$& + to_sanchor(item) # + to_categories(item) # categories in original h3
|
10
|
+
end.sub(/<\/h3>/) do
|
11
|
+
to_author(item) + $&
|
12
|
+
end
|
13
|
+
%>
|
14
|
+
<% else -%>
|
15
|
+
<h3>
|
16
|
+
<%= to_sanchor(item) %>
|
17
|
+
<%= to_categories(item) %>
|
18
|
+
<%=hk item.title %>
|
19
|
+
<%= to_author(item) %>
|
20
|
+
</h3>
|
21
|
+
<%= to_section_body(item) %>
|
22
|
+
<% end -%>
|
23
|
+
</div>
|
@@ -0,0 +1,23 @@
|
|
1
|
+
#--
|
2
|
+
# -*- mode: ruby; coding: utf-8 -*-
|
3
|
+
# vim: set filetype=ruby ts=2 sw=2 sts=2 fenc=utf-8:
|
4
|
+
#
|
5
|
+
# copyright (c) 2006, 2007 Kazuhiro NISHIYAMA
|
6
|
+
#++
|
7
|
+
|
8
|
+
require 'td2planet/formatter'
|
9
|
+
|
10
|
+
module TD2Planet
|
11
|
+
class DefaultFormatter < Formatter
|
12
|
+
def initialize(config)
|
13
|
+
super
|
14
|
+
unless @config.key?('filter')
|
15
|
+
@config['filter'] = 'true'
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def skip?(item)
|
20
|
+
eval(@config['filter']) or too_old?(item)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
#--
|
2
|
+
# -*- mode: ruby; coding: utf-8 -*-
|
3
|
+
# vim: set filetype=ruby ts=2 sw=2 sts=2 fenc=utf-8:
|
4
|
+
#
|
5
|
+
# copyright (c) 2006, 2007 Kazuhiro NISHIYAMA
|
6
|
+
#++
|
7
|
+
|
8
|
+
require 'erb'
|
9
|
+
require 'pathname'
|
10
|
+
require 'rss'
|
11
|
+
require 'uri'
|
12
|
+
|
13
|
+
module TD2Planet
|
14
|
+
class Fetcher
|
15
|
+
def initialize(cache_dir, dry_run=false)
|
16
|
+
@cache_dir = Pathname.new(cache_dir)
|
17
|
+
unless @cache_dir.exist?
|
18
|
+
@cache_dir.mkdir
|
19
|
+
end
|
20
|
+
@dry_run = dry_run
|
21
|
+
end
|
22
|
+
|
23
|
+
def fetch_all_rss(uris)
|
24
|
+
rss_list = []
|
25
|
+
uris.each do |uri|
|
26
|
+
cache_file = @cache_dir + ERB::Util.u(uri)
|
27
|
+
if @dry_run
|
28
|
+
puts "use cache: #{cache_file}"
|
29
|
+
text = cache_file.read
|
30
|
+
else
|
31
|
+
text = nil
|
32
|
+
begin
|
33
|
+
puts "fetch: #{uri}"
|
34
|
+
text = URI.parse(uri).read
|
35
|
+
rescue Timeout::Error
|
36
|
+
# fallback
|
37
|
+
puts "ERROR: timeout #{uri}"
|
38
|
+
text = cache_file.read
|
39
|
+
rescue Exception
|
40
|
+
puts "ERROR: #{$!} (#{$!.class}) on #{uri}"
|
41
|
+
raise
|
42
|
+
else
|
43
|
+
if text.status[0] == '200' && /rss/ =~ text
|
44
|
+
cache_file.open('wb'){|f| f.write(text) }
|
45
|
+
else
|
46
|
+
# fallback
|
47
|
+
puts "ERROR: fetch failed #{uri} #{text.status}"
|
48
|
+
text = cache_file.read
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
text = fixup_rss(text)
|
53
|
+
rss_list << RSS::Parser.parse(text, false)
|
54
|
+
end
|
55
|
+
rss_list
|
56
|
+
end
|
57
|
+
|
58
|
+
# euc-jp may fail to parse
|
59
|
+
def fixup_rss(text)
|
60
|
+
text.sub(/\bencoding="euc-jp"/ni, 'encoding="euc-jp-ms"')
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,224 @@
|
|
1
|
+
#--
|
2
|
+
# -*- mode: ruby; coding: utf-8 -*-
|
3
|
+
# vim: set filetype=ruby ts=2 sw=2 sts=2 fenc=utf-8:
|
4
|
+
#
|
5
|
+
# copyright (c) 2006, 2007 Kazuhiro NISHIYAMA
|
6
|
+
#++
|
7
|
+
|
8
|
+
require 'erb'
|
9
|
+
require 'nkf'
|
10
|
+
require 'uri'
|
11
|
+
require 'rss/maker'
|
12
|
+
|
13
|
+
module TD2Planet
|
14
|
+
class Formatter
|
15
|
+
include ERB::Util
|
16
|
+
|
17
|
+
ERB_METHODS = []
|
18
|
+
def self.def_erb_method(method_name, fname=nil)
|
19
|
+
if /\A\w+/ =~ method_name
|
20
|
+
fname ||= "#{$&}.rhtml"
|
21
|
+
end
|
22
|
+
ERB_METHODS << [method_name, fname]
|
23
|
+
end
|
24
|
+
def_erb_method('layout(days)')
|
25
|
+
def_erb_method('day(items, rss)')
|
26
|
+
def_erb_method('section(item, rss)')
|
27
|
+
def_erb_method('header()')
|
28
|
+
def_erb_method('footer()')
|
29
|
+
|
30
|
+
def k(s)
|
31
|
+
NKF.nkf('-wm0', s)
|
32
|
+
end
|
33
|
+
def hk(s)
|
34
|
+
h(k(s))
|
35
|
+
end
|
36
|
+
|
37
|
+
def default_templates_dir
|
38
|
+
basename = 'layout.rhtml'
|
39
|
+
dir = File.expand_path('../../data/td2planet/templates', File.dirname(__FILE__))
|
40
|
+
if File.exist?(File.join(dir, basename))
|
41
|
+
return dir
|
42
|
+
end
|
43
|
+
|
44
|
+
require 'rbconfig'
|
45
|
+
dir = File.expand_path('td2planet/templates', Config::CONFIG['datadir'])
|
46
|
+
if File.exist?(File.join(dir, basename))
|
47
|
+
return dir
|
48
|
+
end
|
49
|
+
raise "not found templates"
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize(config)
|
53
|
+
@config = config
|
54
|
+
@config['title'] ||= '(no title Planet)'
|
55
|
+
@config['tdiary_theme_path'] ||= '/tdiary/theme'
|
56
|
+
@config['tdiary_theme'] ||= 'default'
|
57
|
+
@config['date_strftime_format'] ||= '%Y-%m-%d'
|
58
|
+
@config['sanchor_strftime_format'] ||= '%H:%M:%S'
|
59
|
+
@base_uri = URI.parse(@config['base_uri'])
|
60
|
+
@config['templates_path'] ||= []
|
61
|
+
@config['templates_path'].push(default_templates_dir)
|
62
|
+
ERB_METHODS.each do |method_name, basename|
|
63
|
+
@config['templates_path'].find do |dir|
|
64
|
+
fname = File.expand_path(basename, dir)
|
65
|
+
if File.exist?(fname)
|
66
|
+
puts "use template #{basename}: #{fname}"
|
67
|
+
erb = ERB.new(File.read(fname), nil, '-')
|
68
|
+
eval("def self.#{method_name}\n#{erb.src}\nend\n", binding, fname, 0)
|
69
|
+
true
|
70
|
+
else
|
71
|
+
false
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
def date_format(item)
|
78
|
+
item.date.localtime.strftime(@config['date_strftime_format'])
|
79
|
+
end
|
80
|
+
def sanchor_format(item)
|
81
|
+
item.date.localtime.strftime(@config['sanchor_strftime_format'])
|
82
|
+
end
|
83
|
+
|
84
|
+
def too_old?(item, sec=7*24*60*60)
|
85
|
+
item.date < Time.now - sec
|
86
|
+
end
|
87
|
+
|
88
|
+
# override
|
89
|
+
def skip?(item)
|
90
|
+
false
|
91
|
+
end
|
92
|
+
|
93
|
+
def to_html(rss_list)
|
94
|
+
@rss_list = rss_list
|
95
|
+
day_rss = {}
|
96
|
+
rss_list.each do |rss|
|
97
|
+
next unless rss.items
|
98
|
+
rss.items.each do |item|
|
99
|
+
next if skip?(item)
|
100
|
+
day = (day_rss[[date_format(item), rss]] ||= Array.new)
|
101
|
+
day.push(item)
|
102
|
+
end
|
103
|
+
end
|
104
|
+
days = []
|
105
|
+
day_rss.keys.sort_by do |date, rss|
|
106
|
+
date
|
107
|
+
end.reverse_each do |key|
|
108
|
+
date, rss = key
|
109
|
+
items = day_rss[key]
|
110
|
+
items = items.sort_by do |item|
|
111
|
+
# tdiary makerss plugin generates same time entries
|
112
|
+
item.date.to_s + item.link
|
113
|
+
end
|
114
|
+
days << {:items => items, :rss => rss}
|
115
|
+
end
|
116
|
+
days = days.sort_by do |day|
|
117
|
+
-day[:items].collect{|item| item.date.to_i}.max
|
118
|
+
end
|
119
|
+
layout(days)
|
120
|
+
end
|
121
|
+
|
122
|
+
def relative_path_to_absolute_uri(attr_value, base_uri)
|
123
|
+
uri = URI.parse(attr_value)
|
124
|
+
if uri.scheme.nil?
|
125
|
+
URI.parse(base_uri) + uri
|
126
|
+
else
|
127
|
+
uri
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
def tag_attr_relative_path_to_absolute_uri(tag, attr_name, base_uri)
|
132
|
+
tag.gsub!(/#{attr_name}=([\"\'])([^\"\']+)\1/i) do
|
133
|
+
%Q!#{attr_name}=#{$1}#{relative_path_to_absolute_uri($2, base_uri)}#{$1}!
|
134
|
+
end or tag.gsub!(/#{attr_name}=(\S+)/) do
|
135
|
+
%Q!#{attr_name}=#{relative_path_to_absolute_uri($1, base_uri)}!
|
136
|
+
end
|
137
|
+
tag
|
138
|
+
end
|
139
|
+
|
140
|
+
def to_section_body(item)
|
141
|
+
if item.content_encoded
|
142
|
+
k(item.content_encoded).gsub(/<([aA]\b[\s\S]+?)>/) do
|
143
|
+
a = tag_attr_relative_path_to_absolute_uri($1, "href", item.link)
|
144
|
+
%Q!<#{a} rel="nofollow">!
|
145
|
+
#end.gsub(/<img\b[\s\S]+?>/i) do
|
146
|
+
# tag_attr_relative_path_to_absolute_uri($&, "src", item.link)
|
147
|
+
end.gsub(/<img\b[\s\S]+?>/i) do
|
148
|
+
img = $&
|
149
|
+
case img
|
150
|
+
when /alt=([\"\'])(.+?)\1/
|
151
|
+
$2
|
152
|
+
when /alt=(\S+?)/
|
153
|
+
$1
|
154
|
+
else
|
155
|
+
"[img]"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
else
|
159
|
+
'<p>' + h(k(item.description)).gsub(/\r?\n/, '<br>') + '</p>'
|
160
|
+
end
|
161
|
+
end
|
162
|
+
|
163
|
+
def to_sanchor(item)
|
164
|
+
%Q!<a href="#{hk item.link}"><span class="sanchor">#{h sanchor_format(item)}</span></a> !
|
165
|
+
end
|
166
|
+
|
167
|
+
def to_categories(item)
|
168
|
+
h(item.dc_subjects.collect{|s|"[#{k(s.content)}]" if /./ =~ s.content})
|
169
|
+
end
|
170
|
+
|
171
|
+
def to_author(item)
|
172
|
+
if item.dc_creator
|
173
|
+
" (#{hk(item.dc_creator)})"
|
174
|
+
else
|
175
|
+
""
|
176
|
+
end
|
177
|
+
end
|
178
|
+
|
179
|
+
TukkomiLinkRe = /^<p><a href="(.+)">ツッコミを入れる<\/a><\/p>$/
|
180
|
+
def move_tukkomi_link(html)
|
181
|
+
if TukkomiLinkRe =~ html
|
182
|
+
tukkomi_link = $&
|
183
|
+
tukkomi_uri = $1
|
184
|
+
re = Regexp.new(Regexp.quote(tukkomi_link))
|
185
|
+
tukkomi_moved_html = html.gsub(re, '')
|
186
|
+
re = Regexp.new(Regexp.quote('<!--<div class="caption"></div>-->'))
|
187
|
+
tukkomi_moved_html.sub!(re) { %Q|<div class="caption">[<a href="#{tukkomi_uri}">ツッコミを入れる</a>]</div>| }
|
188
|
+
# other day tukkomi_link found
|
189
|
+
if TukkomiLinkRe =~ tukkomi_moved_html
|
190
|
+
html.gsub!(TukkomiLinkRe) { %Q|<div class="caption">[<a href="#{$1}">ツッコミを入れる</a>]</div>| }
|
191
|
+
else
|
192
|
+
html = tukkomi_moved_html
|
193
|
+
end
|
194
|
+
end
|
195
|
+
html
|
196
|
+
end
|
197
|
+
|
198
|
+
|
199
|
+
def_erb_method('to_opml(rss_list)', 'opml.rxml')
|
200
|
+
|
201
|
+
def to_rss(rss_list, version='1.0', basename='rss.xml')
|
202
|
+
RSS::Maker.make(version) do |maker|
|
203
|
+
maker.channel.about = @base_uri + basename
|
204
|
+
maker.channel.title = @config['title']
|
205
|
+
maker.channel.link = @base_uri
|
206
|
+
maker.channel.description = "#{@base_uri} - #{@config['title']}"
|
207
|
+
|
208
|
+
maker.items.do_sort = true
|
209
|
+
|
210
|
+
rss_list.each do |rss|
|
211
|
+
rss.items.each do |item|
|
212
|
+
next if skip?(item)
|
213
|
+
new_item = maker.items.new_item
|
214
|
+
%w"link title date".each do |attr|
|
215
|
+
value = item.__send__(attr)
|
216
|
+
value = k(value) if value.is_a?(String)
|
217
|
+
new_item.__send__("#{attr}=", value)
|
218
|
+
end
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
end
|
224
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
#--
|
2
|
+
# -*- mode: ruby; coding: utf-8 -*-
|
3
|
+
# vim: set filetype=ruby ts=2 sw=2 sts=2 fenc=utf-8:
|
4
|
+
#
|
5
|
+
# copyright (c) 2006, 2007 Kazuhiro NISHIYAMA
|
6
|
+
#++
|
7
|
+
|
8
|
+
require 'optparse'
|
9
|
+
require 'td2planet/fetcher'
|
10
|
+
require 'td2planet/formatter'
|
11
|
+
require 'td2planet/writer'
|
12
|
+
require 'yaml'
|
13
|
+
|
14
|
+
module TD2Planet
|
15
|
+
module_function
|
16
|
+
|
17
|
+
def main(argv=ARGV)
|
18
|
+
opts = OptionParser.new
|
19
|
+
usage = proc { puts opts; exit(1) }
|
20
|
+
opts.banner << " config.yaml"
|
21
|
+
@dry_run = false
|
22
|
+
opts.on('-n', '--dry-run', 'do not fetch, generate files only') { @dry_run = true }
|
23
|
+
opts.on('-h', '--help', 'show this message') { usage.call }
|
24
|
+
opts.parse!(argv)
|
25
|
+
if argv.empty?
|
26
|
+
usage.call
|
27
|
+
end
|
28
|
+
argv.each do |filename|
|
29
|
+
config = YAML.load(File.read(filename))
|
30
|
+
run(config)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_formatter_class(formatter_name)
|
35
|
+
formatter_class_name = formatter_name.gsub(/(^|_)(.)/) { $2.upcase }
|
36
|
+
begin
|
37
|
+
require formatter_name
|
38
|
+
rescue LoadError
|
39
|
+
begin
|
40
|
+
require "td2planet/#{formatter_name}"
|
41
|
+
rescue LoadError
|
42
|
+
raise LoadError, "no such formatter: #{formatter_name}"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
formatter = const_get(formatter_class_name)
|
46
|
+
end
|
47
|
+
|
48
|
+
def run(config)
|
49
|
+
formatter_name = config['formatter'] || 'default_formatter'
|
50
|
+
formatter = get_formatter_class(formatter_name).new(config)
|
51
|
+
|
52
|
+
writer = Writer.new(config, formatter)
|
53
|
+
fetcher = Fetcher.new(config['cache_dir'], @dry_run)
|
54
|
+
rss_list = fetcher.fetch_all_rss(config['uri'])
|
55
|
+
|
56
|
+
writer.output_html(rss_list)
|
57
|
+
writer.output_opml(rss_list)
|
58
|
+
writer.output_rss(rss_list)
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
#--
|
2
|
+
# -*- mode: ruby; coding: utf-8 -*-
|
3
|
+
# vim: set filetype=ruby ts=2 sw=2 sts=2 fenc=utf-8:
|
4
|
+
#
|
5
|
+
# copyright (c) 2006, 2007 Kazuhiro NISHIYAMA
|
6
|
+
#++
|
7
|
+
|
8
|
+
require 'td2planet/formatter'
|
9
|
+
|
10
|
+
module TD2Planet
|
11
|
+
class SampleFormatter < Formatter
|
12
|
+
def spam?(item)
|
13
|
+
if /ツッコミ/ =~ k(item.title) &&
|
14
|
+
/casino/ =~ item.dc_creator &&
|
15
|
+
/casino/ =~ item.description
|
16
|
+
true
|
17
|
+
else
|
18
|
+
false
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def skip?(item)
|
23
|
+
spam?(item) or too_old?(item)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
#--
|
2
|
+
# -*- mode: ruby; coding: utf-8 -*-
|
3
|
+
# vim: set filetype=ruby ts=2 sw=2 sts=2 fenc=utf-8:
|
4
|
+
#
|
5
|
+
# copyright (c) 2006, 2007 Kazuhiro NISHIYAMA
|
6
|
+
#++
|
7
|
+
|
8
|
+
module TD2Planet
|
9
|
+
TD2PLANET_VERSION = "0.1.0"
|
10
|
+
TD2PLANET_RELEASE_DATE = "2007-02-22"
|
11
|
+
|
12
|
+
# return ruby version string (simulate output of ruby -v)
|
13
|
+
def self.ruby_version
|
14
|
+
if defined?(RUBY_PATCHLEVEL)
|
15
|
+
"ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE} patchlevel #{RUBY_PATCHLEVEL}) [#{RUBY_PLATFORM}]"
|
16
|
+
else
|
17
|
+
"ruby #{RUBY_VERSION} (#{RUBY_RELEASE_DATE}) [#{RUBY_PLATFORM}]"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# return TD2Planet version string
|
22
|
+
def self.td2planet_version
|
23
|
+
"TD2Planet #{TD2PLANET_VERSION} (#{TD2PLANET_RELEASE_DATE})"
|
24
|
+
end
|
25
|
+
|
26
|
+
# return string for meta generator (see templates/layout.rhtml)
|
27
|
+
def self.generator
|
28
|
+
"#{td2planet_version} / Powered by #{ruby_version}"
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
#--
|
2
|
+
# -*- mode: ruby; coding: utf-8 -*-
|
3
|
+
# vim: set filetype=ruby ts=2 sw=2 sts=2 fenc=utf-8:
|
4
|
+
#
|
5
|
+
# copyright (c) 2006, 2007 Kazuhiro NISHIYAMA
|
6
|
+
#++
|
7
|
+
|
8
|
+
require 'pathname'
|
9
|
+
|
10
|
+
module TD2Planet
|
11
|
+
class Writer
|
12
|
+
def initialize(config, formatter)
|
13
|
+
@config = config
|
14
|
+
@output_dir ||= Pathname.new(config['output_dir'])
|
15
|
+
unless @output_dir.exist?
|
16
|
+
@output_dir.mkdir
|
17
|
+
end
|
18
|
+
@formatter = formatter
|
19
|
+
end
|
20
|
+
|
21
|
+
def output_html(rss_list)
|
22
|
+
if @config.key?('output_html')
|
23
|
+
output_html = @output_dir + @config['output_html']
|
24
|
+
else
|
25
|
+
output_html = @output_dir + 'index.html'
|
26
|
+
end
|
27
|
+
|
28
|
+
output_html.open('wb') do |f|
|
29
|
+
f.write(@formatter.to_html(rss_list))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def output_opml(rss_list)
|
34
|
+
output_opml = @output_dir + 'opml.xml'
|
35
|
+
output_opml.open('wb') do |f|
|
36
|
+
f.write(@formatter.to_opml(rss_list))
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def output_rss(rss_list)
|
41
|
+
[
|
42
|
+
['1.0', 'rss10.xml'],
|
43
|
+
['2.0', 'rss20.xml'],
|
44
|
+
].each do |rss_version, basename|
|
45
|
+
output_rss = @output_dir + basename
|
46
|
+
output_rss.open('wb') do |f|
|
47
|
+
f.write(@formatter.to_rss(rss_list, rss_version, basename))
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
metadata
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.2
|
3
|
+
specification_version: 1
|
4
|
+
name: td2planet
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: 0.1.0
|
7
|
+
date: 2007-02-22 00:00:00 +09:00
|
8
|
+
summary: planet of ruby, mainly for tdiary
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: zn@mbf.nifty.com
|
12
|
+
homepage: http://rubyforge.org/projects/td2planet/
|
13
|
+
rubyforge_project: td2planet
|
14
|
+
description:
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Kazuhiro NISHIYAMA
|
31
|
+
files:
|
32
|
+
- ChangeLog
|
33
|
+
- MIT-LICENSE
|
34
|
+
- README
|
35
|
+
- README.ja
|
36
|
+
- bin/td2planet.rb
|
37
|
+
- config.yaml
|
38
|
+
- data/td2planet/templates/day.rhtml
|
39
|
+
- data/td2planet/templates/footer.rhtml
|
40
|
+
- data/td2planet/templates/header.rhtml
|
41
|
+
- data/td2planet/templates/layout.rhtml
|
42
|
+
- data/td2planet/templates/opml.rxml
|
43
|
+
- data/td2planet/templates/section.rhtml
|
44
|
+
- lib/td2planet/default_formatter.rb
|
45
|
+
- lib/td2planet/fetcher.rb
|
46
|
+
- lib/td2planet/formatter.rb
|
47
|
+
- lib/td2planet/runner.rb
|
48
|
+
- lib/td2planet/sample_formatter.rb
|
49
|
+
- lib/td2planet/version.rb
|
50
|
+
- lib/td2planet/writer.rb
|
51
|
+
test_files: []
|
52
|
+
|
53
|
+
rdoc_options:
|
54
|
+
- --charset
|
55
|
+
- utf-8
|
56
|
+
- --inline-source
|
57
|
+
- --line-numbers
|
58
|
+
- --main
|
59
|
+
- README
|
60
|
+
extra_rdoc_files:
|
61
|
+
- README
|
62
|
+
- README.ja
|
63
|
+
executables:
|
64
|
+
- td2planet.rb
|
65
|
+
extensions: []
|
66
|
+
|
67
|
+
requirements: []
|
68
|
+
|
69
|
+
dependencies: []
|
70
|
+
|