矢島舞美ちゃん観察日記

http://www.largevocalmix.jp/diary/2008/02/10

2008年2月10日(日)

日常あるいは平穏な日々

『栞と紙魚子の怪奇事件簿』を消化。前田敦子の制服姿はかわいくないので先週みたいにニットのセーターにして頂きたい。

らぶぱらあんてな

りりあんのブログに未来日付の記事があるのでそれを拾わないようにした。他に '\0' が置換されることがあったんだけど面倒だから見なかったことにする。

#!/home/_/opt/ruby/bin/ruby -Ku


#------------------------------------------------------------
# 巡回用アンテナ
# ・仕様
#   ・失敗したら更新時刻を平成元年としてソート順の最後にする
#   ・RSSは実行時の日付を含む過去の記事を取得する
# ・メモ
#   ・コーディング規約
#     http://www.loveruby.net/w/RubyCodingStyle.html
#   ・Time.getlocal.w3cdtf
#     require 'rss' -> alias iso8601 -> alias xmlschema -> 文字列
#   ・繰り返しの制御
#     break : 繰り返しを中断し、繰り返しの中から抜ける
#------------------------------------------------------------

require 'rexml/document'
require 'net/http'
require 'time'
require 'rss'
require 'cgi'


#------------------------------------------------------------
# 定数の定義
# ・定数はアルファベットの大文字で始まる
#------------------------------------------------------------

APPLICATION_PATH = '/home/_/app/'
PUBLIC_PATH = '/home/_/www/meegle/'

SITE_LIST_XML_PATH = APPLICATION_PATH + '_'
ANTENNA_TEMPLATE_PATH = APPLICATION_PATH + '_'
ANTENNA_HTML_PATH = PUBLIC_PATH + '_'

CHECK_LAST_MODIFIED = 'LastModified'.downcase
CHECK_FEED = 'Feed'.downcase

RSS10 = 'RSS::RDF'.downcase
RSS20 = 'RSS::Rss'.downcase

HEISEI_GANNEN = '1989-01-08T00:00:00+09:00'
COMPARE_DATE_FORMAT = '%Y%m%d'
CURRENT_DATE = Time.now.getlocal.strftime(COMPARE_DATE_FORMAT)

USER_AGENT = '_'


#------------------------------------------------------------
# クラスの定義
# ・クラス名は大文字で始める
# ・インスタンス変数は '@' で始まる
# ・読み書き可能なインスタンス変数は attr_accessor で定義できる
# ・Class.new で initialize が呼び出される
# ・オブジェクトにとって必要な初期化処理は initialize に記述する
#------------------------------------------------------------

class Utils
    def self.kako?(date)
        date.strftime(COMPARE_DATE_FORMAT) <= CURRENT_DATE
    end
end

class Antenna
    attr_accessor :title, :uri, :feed, :update_date, :preview

    def initialize(in_title, in_uri, in_feed='')
        @title = in_title
        @uri = URI(in_uri)

        if not in_feed.empty?
            @feed = URI(in_feed)
        else
            @feed = nil
        end

        @update_date = HEISEI_GANNEN
        @preview = '初期値'
    end

    def to_s
        debug = ''
        debug += "[title]#{@title}" + "\n"
        debug += "[uri]#{@uri.to_s}" + "\n"
        debug += "[feed]#{@feed.to_s}" + "\n"
        debug += "[update_date]#{@update_date}" + "\n"
        debug += "[preview]#{@preview}"
    end

    def format_date
        @update_date.sub(/T/, ' ').sub(/\+09:00/, '')
    end

    def <=> (other)
        @update_date <=> other.update_date
    end
end

class LastModified < Antenna
    def to_html
        %(<li>[#{format_date}] <a href="#{@uri.to_s}">#{CGI.escapeHTML(@title)}</a><div class="preview">not feed</div></li>)
    end

    def check
        req = Net::HTTP::Head.new(@uri.path)
        req['User-Agent'] = USER_AGENT

        res = Net::HTTP.start(@uri.host, @uri.port) do |http|
            http.request(req)
        end

        case res
        when Net::HTTPSuccess
            @update_date = Time.parse(res['Last-Modified']).getlocal.w3cdtf
        end
    end
end

class Feed < Antenna
    def to_html
        if @preview.empty?
            %(<li>[#{format_date}] <a href="#{@uri.to_s}">#{CGI.escapeHTML(@title)}</a><div class="preview">empty feed</div></li>)
        else
            %(<li>[#{format_date}] <a href="#{@uri.to_s}">#{CGI.escapeHTML(@title)}</a><div class="preview">#{CGI.escapeHTML(@preview)}</div></li>)
        end
    end

    # jugem.jp/?mode=rss用
    def path
        if @feed.query == nil
            @feed.path
        else
            "#{@feed.path}?#{@feed.query}"
        end
    end

    def check
        req = Net::HTTP::Get.new(path)
        req['User-Agent'] = USER_AGENT

        res = Net::HTTP.start(@feed.host, @feed.port) do |http|
            http.request(req)
        end

        case res
        when Net::HTTPSuccess
            rss = RSS::Parser.parse(res.body)

            case rss.class.name.downcase
            when RSS10
                rss.items.sort_by{|a| a.dc_date}.reverse.each do |item|
                    if Utils.kako?(item.dc_date.getlocal)
                        @update_date = item.dc_date.getlocal.w3cdtf
                        @preview = item.title.chomp
                        break
                    end
                end
            when RSS20
                #RSS 2.0でもDublin Coreモジュールを使う場合がある
                if rss.channel.items.first.dc_date
                    rss.channel.items.sort_by{|a| a.dc_date}.reverse.each do |item|
                        if Utils.kako?(item.dc_date.getlocal)
                            @update_date = item.dc_date.getlocal.w3cdtf
                            @preview = item.title.chomp
                            break
                        end
                    end
                else
                    rss.channel.items.sort_by{|a| a.pubDate}.reverse.each do |item|
                        if Utils.kako?(item.pubDate.getlocal)
                            @update_date = item.pubDate.getlocal.w3cdtf
                            @preview = item.title.chomp
                            break
                        end
                    end
                end
            end

        else
            #HTTPステータス異常
            @preview = res.class.name
        end
    end
end


#------------------------------------------------------------
# メソッドの定義
# ・戻り値は最後に評価した式になる
# ・return で直接戻り値を返せる
#------------------------------------------------------------

def read_site_list
    site_list = Array.new

    xml = REXML::Document.new(File.new(SITE_LIST_XML_PATH))
    xml.elements.each('sitelist/site') do |site|
        info = site.elements

        case info['type'].text.downcase
        when CHECK_LAST_MODIFIED
            site_list.push(LastModified.new(info['title'].text, info['uri'].text))
        when CHECK_FEED
            site_list.push(Feed.new(info['title'].text, info['uri'].text, info['feed'].text))
        end
    end

    return site_list
end

def write_antenna(site_list)
    template = File.read(ANTENNA_TEMPLATE_PATH)
    antenna = ''
    indent = ' ' * 8

    site_list.each do |site|
        antenna += indent + site.to_html + "\n"
    end

    template.sub!(/antenna_body/, antenna)
    template.sub!(/ruby_version/, RUBY_VERSION)
    template.sub!(/rss_parser_version/, RSS::VERSION)

    File.open(ANTENNA_HTML_PATH, 'w') do |writer|
        writer.puts(template)
    end

    File.chmod(0604, ANTENNA_HTML_PATH)
end


#------------------------------------------------------------
# 処理の開始
#------------------------------------------------------------

site_list = read_site_list

site_list.each do |site|
    begin
        site.check
    rescue => ex
        site.preview = ex.class.name
    rescue Timeout::Error
        site.preview = 'Timeout::Error'
    end
end

write_antenna(site_list.sort.reverse)
                
2008, AfternoonTea     Site Index     RSS1.0