td2planet 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/ChangeLog ADDED
@@ -0,0 +1,3 @@
1
+ 2007-02-22 Kazuhiro NISHIYAMA <zn@mbf.nifty.com>
2
+
3
+ * Initial public release.
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
@@ -0,0 +1,10 @@
1
+ #! /usr/bin/ruby1.8 -Ku
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
+ require 'td2planet/runner'
8
+ require 'td2planet/version'
9
+
10
+ TD2Planet.main
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>&nbsp;[...]</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
+