#! /usr/local/bin/ruby # LIRS class # # by TAKAHASHI 'Maki' Masayoshi (maki@rubycolor.org) # 2002/07/26 v0.3.0 for LIRS 2.1 # check invalid CR/LF # add expiresCount, localTimeZone # add load_gzip, save_gzip # 2000/06/03 v0.2.0 for LIRS 2.0 # add comment for access methods # fix (un)escape # rechange gzip # 2000/05/24 v0.1.2 fix each_host (by not) # modify SiteInfo.unescape # 2000/05/23 v0.1.1 change gzip for Win9x # 2000/05/21 v0.1.0 # 2000/01/24 v0.0.0 # Original (in Perl):: # Last modified Information Relaying Specification (LIRS) Package # ver.1.02(19991122) # Copyright (C) 1999 hiya All Rights Reserved. =begin = LIRS module LIRSに関するモジュール。 LIRSについては、 * Last modified Information Relaying Specification (()) を参照してください。 == Module Variable/Method/Sub Class === モジュール変数 ==== @@expiresCount expires されるまでの時間(秒数)。 ==== @@gzip gzipの場所。標準は、環境変数PATHの値を元に、一番最初に 見つかったgzipというコマンドのパスが入る。見つからなかった 場合は空のまま。 ==== @@localTimeZone サーバーの時刻帯(GMTよりの誤差を秒で:日本は-32400) === LIRS::SiteInfo クラス 個々のサイトの更新情報を扱うクラス。 === クラスメソッド ==== SiteInfo::new(lm, ld, lag, len, url, title, author, remote, *other) 新しいSiteInfoオブジェクトを作る。引数は順に、 * 更新時刻(Last-Modified:GMT) * 更新時刻を取得した時刻(Last-Detected:GMT) * 対象サイトのGMTとの誤差(秒) * 対象サイトの容量(Content-Length:バイト) * 対象サイトのURL * 対象サイトのタイトル * 対象サイトの管理者名 * 情報取得元サイトのURL * (拡張部) となっている。 拡張部はArrayとなっている。 ==== SiteInfo::parse(str) str をLIRSのデータの一行だとして解釈し、結果を新しいSiteInfo として作る。 解釈に失敗すると例外 LIRSParseError をあげる。 ==== SiteInfo::unescape(str) LIRSのデータのエスケープを解除する。 === メソッド ==== SiteInfo#to_a SiteInfoオブジェクトの情報を配列に変換する。 ==== SiteInfo#to_s SiteInfoオブジェクトの情報をLIRS形式で出力する。 ==== SiteInfo#lm ==== SiteInfo#ld ==== SiteInfo#lag ==== SiteInfo#len ==== SiteInfo#url ==== SiteInfo#title ==== SiteInfo#author ==== SiteInfo#remote ==== SiteInfo#other 各属性の値を得る。 ==== SiteInfo#lm= ==== SiteInfo#ld= ==== SiteInfo#lag= ==== SiteInfo#len= ==== SiteInfo#url= ==== SiteInfo#title= ==== SiteInfo#author= ==== SiteInfo#remote= ==== SiteInfo#other= 各属性の値を代入する。 === LIRS::SiteList クラス サイトの更新情報のリストを扱うクラス。 === メソッド ==== SiteList#save(file, compress=nil) SiteListオブジェクトの情報をファイルに保存する。 compressを指定した場合、そのファイルの形式で保存する (現在はgzipのみ対応)。 === SiteList#load(file, compress=nil) SiteListオブジェクトの情報をファイルから読み込む。 compressを指定した場合、そのファイルの形式で読み込む (現在はgzipのみ対応)。 === SiteList#each_host{|host, siteInfoArray| ... } host別にsiteInfoを取り出す。hostをキー、siteInfoのArrayを 値とするHashに対するeachのような感じで使う。 例: siteList.each_host{ |host, siteInfo| http.connect(host,port){|s| res = s.get(siteInfo.url.path) ... } } == Copyright and License (C) 2000-2002 TAKAHASHI Masayoshi all right reserve. Rubyと同様のライセンスで使用・改変・再配布可能です。 =end require 'uri' class LIRSError < StandardError; end class LIRSParseError < LIRSError ; end module LIRS @@expire_count = 28800 ## expires count @@gzip = nil @@localTimeZone = +32400 ## search gzip command ENV['PATH'].split(":").each{|path| if FileTest.exist? path+'/gzip' @@gzip = path+'/gzip' end } def LIRS::gzip(); @@gzip; end def LIRS::gzip=(gzip); @@gzip = gzip; end def LIRS::expiresCount @@expiresCount end def LIRS::expiresCount=(expiresCount) @@expiresCount = expiresCount end # def LIRS::localTimeZone # @@localTimeZone # end # def LIRS::localTimeZone=(localTimeZone) # @@localTimeZone = localTimeZone # end class SiteInfo attr_accessor :lm, :ld, :lag, :len, :url, :title, :author, :remote, :other ## unescapeする。 ## strには改行文字等が含まれていないことを期待している。 def SiteInfo.unescape(str) if str == '0' return '' else return str.gsub("%2c",',') end end def initialize(lm, ld, lag, len, url, title, author, remote, *other) @lm = lm.to_i @ld = ld.to_i @lag = lag.to_i @len = len.to_i @url = URI.parse(url) @title = title @author = author @remote = URI.parse(remote) @other = other end def SiteInfo.parse(str) unless /^#/ =~ str ## 「\」があった時には必ずその後ろの文字にそのまま出力する。 ## 「\,」→「,」 「\\」→「\」 「\a」→「a」 #### ちょっと拡大解釈? head, lm, ld, lag, length, url, title, author, remote, *other = str.gsub(/\\(.)/){ if $1 == ',' '%2c' else $1 end }.split(",") if (other[-1] == "\n" or other[-1] == "\r\n") && head == 'LIRS' url = unescape(url) title = unescape(title) author = unescape(author) other = other.collect{|elem| unescape(elem)} other.pop return SiteInfo.new(lm, ld, lag, length, url, title, author, remote, *other) else raise LIRSParseError, "Illigul data in LIRS [#{str}]." end end end def escape(item) save_item = item.gsub(/\\/){'\\\\'}.gsub(','){'\\,'} if save_item == "" 0 else save_item end end private :escape def to_a return @lm, @ld, @lag, @len, @url, @title, @author, @remote, @other end def to_s url = escape(@url.to_s) title = escape(@title.to_s) author = escape(@author.to_s) remote = escape(@remote.to_s) other = @other.collect{|elem| escape(elem) }.join(",") if @lm != 0 and @lm != nil ld = lm = 0 else ld, lm = @ld, @lm end lag, length = @lag, @length ret = "LIRS,#{lm},#{ld},#{lag},#{length},#{url},#{title},#{author},#{remote}," ret += "#{other}," if other != "" ret += "\n" return ret end end ## class SiteInfo class SiteList < Array def save(file) File.open(file, 'w'){|f| self.each{|elem| f.write elem.to_s } } end def save_gzip(file) gzip = ::LIRS::gzip() if gzip.nil? or !FileTest.exist? gzip raise LIRSError, "cannot find gzip command." end open("|#{gzip} -c >#{file}",'w'){|f| self.each{|elem| f.write elem.to_s } } end def load(file) File.open(file,'r'){|f| while line = f.gets self << SiteInfo.parse(line) end } end def load_gzip(file) gzip = ::LIRS::gzip() if gzip.nil? or !FileTest.exist? gzip raise LIRSError, "cannot find gzip command." end open("|#{gzip} -cd #{file}",'r'){|f| while line = f.gets self << SiteInfo.parse(line) end } end def each_host tmp_hash = {} self.each{|site| tmp_hash[site.url.host] = [] unless tmp_hash.key?(site.url.host) tmp_hash[site.url.host] << site } tmp_hash.each{|key,value| yield key,value } end end ## class SiteList end ## module LIRS ##### test if __FILE__ == $0 print "## test...\n" str1 = "LIRS,938779260,938781002,32400,49383,http://aniki.hauN.org/d/,隠語日記,ひや,http://amano.hauN.org/,etc,etc..\\,..\\\\..\\\\\\,..etc\\\\,\r\n" str2 = "LIRS,938779260,938781002,32400,49383,http://aniki.hauN.org/d/,隠語日記,ひや,http://amano.hauN.org/,\r\n" print "\n## create siteinfo\n" print "LIRS: [",str1,"]\n" lirs1 = LIRS::SiteInfo.parse(str1) lirs2 = LIRS::SiteInfo.parse(str2) print "LIRS::SiteInfo: [",lirs1.inspect,"]\n" print "LIRS: [",lirs1.to_s,"]\n" # LIRS::gzip='/local/GNU/bin/gzip' print "\n## create sitelist\n" s = LIRS::SiteList.new() s << lirs1 s << lirs2 print "\n## save sitelist\n" s.save("./hoge.lirs") print "\n## load sitelist\n" s2 = LIRS::SiteList.new() s2.load("./hoge.lirs") # s2.each{|e| print e.to_s} print s2.to_s print "\n## save sitelist with gzip\n" s.save_gzip("./hoge.lirs.gz") print "\n## load sitelist with gzip\n" s3 = LIRS::SiteList.new() s3.load_gzip("./hoge.lirs.gz") print s3.to_s end