いずれ消え行く無駄な情報を、密やかに発信する装置。つまり日記。
このBlogを作成するのに使用しているHTMLパーサースクリプトを改修しました。元々、パースするスクリプトがあったのですが、Ruby1.8で実装しており、Ruby1.9にした際に使用できなくなったため、変換エンジンの部分を除いて全て一から書き直しました。残業まみれの日常だと、遅々としてプログラムが進みませんでした。
#!/usr/local/bin/ruby --encoding=UTF-8 # -*- encoding: utf-8 -*- # スクリプト名:myDiary.rb SCRIPT_NAME = "myDiary.rb" #Version:2.1 SCRIPT_VERSION = "2.1" #更新履歴 #2009/08/01:ver1.0 日記スクリプト作成 #2009/08/05:ver1.1 Amazoneアフィリエイト対応 #2011/11/20:ver2.0 Ruby1.9系対応、変換エンジン更新、編集フォームCGI化 #2011/12/01:ver2.1 RSS2.0対応 #== 本文パース規則 #=== 段落 #通常の本文は、特に書式をしていせずに書くことで表示する。 #<p>タグによる段落分けは空行で識別を行なっている。 #<br>タグによる段落内の改行は、改行コードをそのまま<br>タグに変換しているため、単純に入力に改行をするだけで表示することができる。 #==== サンプル(変換前) # 本文の入力は、書式していをせずに入力 # 改行タグの挿入は改行文字を段落内で見つけた場合に挿入する。 # # 本文の段落分けは、空行を挟むことで可能。 #==== サンプル(変換後) # <p> # 本文の入力は、書式していをせずに入力 # <br> # 改行タグの挿入は改行文字を段落内で見つけた場合に挿入する。 # </p> # <p> # 本文の段落分けは、空行を挟むことで可能。 # </p> #=== 見出し #見出しは「!」を行頭につけることで作成することができる。見出しはh2からh6まで指定が可能。 #==== サンプル(変換前) # !見出し1 # !!見出し2 #==== サンプル(変換後) # <h2>見出し1</h2> # <h3>見出し2</h3> #=== プレーンテキスト #プレーンテキストは「?」が行頭に登場してから、次に「?」が行頭に登場するまでを<pre>タグで囲む。 #==== サンプル(変換前) # ? # プレーンテキストは # 「?」を行頭につけることで表示出来る。 # ? #==== サンプル(変換後) # <pre>プレーンテキストは # 「?」を行頭につけることで表示出来る。</pre> #=== HTML文書 #明示的にHTML文章を書きたい場合は、行頭を「<」とすると、次の空行を見つけるまでパース処理をキャンセルする。 #=== 特殊文字のキャンセル #「!」、「?」、「<」で始まる通常の本文を書きたい場合は、行頭に空白文字を一文字入れることで特殊文字をエスケープすることができる。 class README end require "cgi" require "pathname" require "erb" require "date" require "time" require "fileutils" CGI::MAX_MULTIPART_COUNT = 512 #:nodoc: cgi.rbの受け取れるパラーメタを128個から512個にする。 CGI::MAX_MULTIPART_LENGTH = 512*1024*1024 #:nodoc: cgi.rbが受け取れる最大サイズを128MBから512MBにする。 #== 概要 #設定値を格納する。 class CONFIG #(Pathname)HTMLを出力した際の、トップのURL。 BASE_URL = Pathname.new("http://blog.psyche.gaso.jpn.org") #(Pathname)テンポラリファイルを作成するディレクトリ。 TMP_DIR = Pathname.new("./tmp").realpath #(Pathname)データファイルが保存されているディレクトリ。 DATA_DIR = Pathname.new("./data").realpath #(pathname)HTMLデータを保存するディレクトリ。 HTML_DIR = Pathname.new("./html").realpath #(pathname)スクリプトが置かれているディレクトリ。 SCRIPT_DIR = Pathname.new("./").realpath #(pathname)HTML書き出し時にHTML_DIRにコピーするディレクトリ。 COPY_DIR = Pathname.new("./data/files").realpath #(string)ステータスファイル名。 STATUS_FILE_NAME = "status.txt" #(string)画像の一時保存ディレクトリ名。 IMG_TMP_DIR_NAME = "img" #(string)HTML出力するトップページのファイル名。 TOP_FILE_NAME = "index.html" #(string)HTML出力するタグ一覧のファイル名。 ALL_TAG_LIST_FILE_NAME = "tag_list.html" #(sting)HTML出力するエントリー一覧のファイル名。 ENTRY_LIST_FILE_NAME = "entry_list.html" #(string)RSS出力するパス。 RSS_FILE_NAME = "index.rdf" #(string)置換文字列リストを保存するファイル名。 REPLACE_STR_FILE_NAME = "replace_str.dat" #(string)サイトのタイトル。 TITLE = "意伝子発信器" #(string)サイトの説明。 DESCRIPTION = "いずれ消え行く無駄な情報を、密やかに発信する装置。つまり日記。" #(int)RSS出力する記事の数。 RSS_ITEM_MAX = 10 #(pathname)HTML出力で使用するトップページのテンプレートファイル。 TEMPLATE_FILE_HTML_TOP = Pathname.new("./template/top.html").realpath #(pathname)HTML出力で使用する各日記ページのテンプレートファイル。 TEMPLATE_FILE_HTML_ENTRY = Pathname.new("./template/entry.html").realpath #(pathname)HTML出力で使用する日記リストページのテンプレートファイル。 TEMPLATE_FILE_HTML_ENTRY_LIST = Pathname.new("./template/entry_list.html").realpath #(pathname)HTML出力で使用するタグ一覧ページのテンプレートファイル。 TEMPLATE_FILE_HTML_TAG = Pathname.new("./template/tag.html").realpath #(Pathname)CGIモードで使用するリストページのテンプレートファイル。 TEMPLATE_FILE_CGI_LIST = Pathname.new("./template/cgi_list.html").realpath #(Pathname)CGIモードで使用する編集ページのテンプレートファイル。 TEMPLATE_FILE_CGI_EDIT = Pathname.new("./template/cgi_edit.html").realpath #(pathnaem)CGIモードで使用する待機ページのテンプレートファイル。 TEMPLATE_FILE_CGI_WAIT = Pathname.new("./template/cgi_wait.html").realpath #オリジナル画像の幅 IMG_WIDTH = 1440 #オリジナル画像の高さ。 IMG_HEIGHT = 1440 #オリジナル画像の品質 IMG_QUALITY = 95 #サムネイルの幅。 THUMB_WIDTH = 300 #サムネイルの高さ。 THUMB_HEIGHT = 300 #サムネイルの品質 THUMB_QUALITY = 80 #正方形サムネイルの一辺のサイズ。 SQUARE_THUMB_SIZE = 70 #正方形サムネイルの角を丸める大きさ。 SQUARE_THUMB_R_SIZE = 10 #画像ファイル(原寸)のファイル名につけるプレフィックス。 PREFIX_IMG_ORIGINAL = "image_" #画像ファイル(サムネイル)のファイル名につけるプレフィックス。 PREFIX_IMG_THUMB = "thumb_" #画像ファイル(正方形サムネイル)のファイル名につけるプレフィックス。 PREFIX_IMG_THUMB_SQUARE = "thumb_square_" #サムネイルの拡張子。 THUMB_EXTNAME = ".jpg" #デバッグモード DEBUG = false #デバッグモードの時に出力するログファイル。 LOG_FILE = "/tmp/log.txt" end #== 概要 #ステータス情報を処理する。 class Status #=== 概要 #ステータス情報を出力する。 #出力した時間を追加して出力する。 #=== 引数 #[(string)message] 出力するメッセージ。 def self.write(message) path = CONFIG::TMP_DIR + CONFIG::STATUS_FILE_NAME time = Time.now.strftime("%Y/%m/%d %H:%M:%S").to_s status = path.read if path.file? open(path.to_s, "w"){|file| file.write("[" + time + "]" + message.to_s + $/) file.write(status) } end #=== 概要 #ステータス情報を削除する。 def self.delete path = CONFIG::TMP_DIR + CONFIG::STATUS_FILE_NAME while path.file? path.delete sleep(1) end end end #== 概要 #デバッグ用のメッセージを出力する。 class Debug #=== 概要 #デバッグ用のメッセージを出力する。 #出力時に日時を追記する。 #=== 引数 #[(string)message] 出力メッセージ。 def Debug.print(message) return nil if !CONFIG::DEBUG time = Time.now.strftime("%Y/%m/%d %H:%M:%S").to_s File.open(CONFIG::LOG_FILE, "a+", 0770){|file| file.write("[" + time + "]" + message.to_s + $/)} end end #== 概要 #日記作成プログラム。 #cgi.param["mode"][0]で受け取ったパラメータを元に、各機能を呼び出す。 #== モード #[wait] upload, edit, convert, rebuildなどのサーバサイドでの処理が行われる際に、ユーザーに待機させるモード。CONFIG::STATUS_FILE_NAMEが存在すると、自動的にこのモードになる。 #[create] 日記を新しく作成する。作成後、新しく作成したページをeditモードで開く。 #[update] 日記編集ページで日記を更新するモード。更新後はeditモードで編集中のページを開く。 #[edit] 日記編集モード。編集ページを開く。 #[delete] 日記削除モード。日記を削除する。削除後はlistモードで開く。 #[upload] 画像アップロードモード。画像アップロード後は、editモードで編集中のページを開く。 #[convert] 日記変換モード。全てのHTMLデータを変換する。 #[rebuild] 日記再変換モード。全てのHTMLデータと画像データの変換を行う。 #[preview] 未実装。 #[list] 日記一覧を表示するモード。この一覧ページから、edit, create, convert, rebuildが呼び出される。 class MyDiary #=== 概要 #MyDiaryの初期化。 def initialize html = "" mode = "" if ARGV.length > 1 and ARGV[0]=="debug" mode = ARGV[1] else cgi = CGI.new mode = cgi.params["mode"][0] end mode = "wait" if (CONFIG::TMP_DIR + CONFIG::STATUS_FILE_NAME).file? Debug.print("mode=" + mode.to_s) case mode when "create" date = cgi.params["date"][0] html = create_entry(date) when "update" path = Pathname.new(cgi.params["path"][0]).realpath title = cgi.params["title"][0].to_s tags = cgi.params["tags"][0].to_s text = cgi.params["text"][0].to_s html = update_edit_entry(path, title, tags, text) when "edit" path = Pathname.new(cgi.params["path"][0]).realpath html = edit_entry(path) when "delete" path = Pathname.new(cgi.params["path"][0]).realpath html = delete_entry(path) when "upload" path = Pathname.new(cgi.params["path"][0].read).realpath files = cgi.params["files"] html = upload_img(path, files) when "convert" html = convert_html when "rebuild" html = rebuild when "preview" when "add_replace" search_str = cgi.params["search_str"][0].to_s replace_str = cgi.params["replace_str"][0].to_s add_replace_str(search_str, replace_str) html = make_listview when "del_replace" search_str = cgi.params["search_str"][0].to_s del_replace_str(search_str) html = make_listview when "wait" status_path = CONFIG::TMP_DIR + CONFIG::STATUS_FILE_NAME entry_path = nil begin entry_path = Pathname.new(cgi.params["path"][0]).realpath rescue entry_path = "" end html = show_wait_page(status_path, entry_path) else html = make_listview end output_page(html) end #=== 概要 #日記本文を置換する文字列セットを追加する。 #文字列セットはファイルに出力される。 #重複する文字列は上書きされる。 #=== 引数 #[(string)search_str] 検索する文字列。(置換前文字列) #[(string)replace_str] 置換する文字列。(置換後文字列) def add_replace_str(search_str, replace_str) replace = Replace.new(CONFIG::DATA_DIR + CONFIG::REPLACE_STR_FILE_NAME) replace.add_str(search_str, replace_str) end #=== 概要 #日記本文を置換する文字列セットから文字列を削除する。 #=== 引数 #[(string)search_str] 検索する文字列。(置換前文字列) def del_replace_str(search_str) replace = Replace.new(CONFIG::DATA_DIR + CONFIG::REPLACE_STR_FILE_NAME) replace.del_str(search_str) end #=== 概要 #生成済みのHTMLデータとサムネイルを全て削除した後、日記データと画像を全て変換する。 #変換処理はバックグラウンドで行われる。 #日記一覧ページのHTMLデータを戻り値として返す。 #=== 戻り値 #[(string)] HTMLデータ。 def rebuild Status.write("画像変換開始") fork{ pid = fork{ #HTMLディレクトリ内を削除 CONFIG::HTML_DIR.each_entry{|path| next if path.to_s=="." || path.to_s==".." path = CONFIG::HTML_DIR + path.to_s if path.directory? path.each_entry{|file| next if file.to_s=="." || file.to_s==".." (path + file.to_s).delete } end path.delete } #画像の変換 CONFIG::DATA_DIR.each_entry{|path| next if !(CONFIG::DATA_DIR+path.to_s).file? || path.to_s=="." || path.to_s==".." (CONFIG::HTML_DIR+path.basename(".*").to_s).mkdir(0770) convert_image(CONFIG::DATA_DIR+path.to_s){|index,file| Status.write("画像変換(#{path.basename(".*").to_s}:#{index.to_s}) #{file.to_s}") } } #日記データの変換 convert_html } watch_process(pid) } page_maker=WaitPageMaker.new(CONFIG::TMP_DIR + CONFIG::STATUS_FILE_NAME, "") html = page_maker.bind_template(CONFIG::TEMPLATE_FILE_CGI_WAIT) return html end #=== 概要 #* 日記をデータからHTMLに変換する。 #* RSSを出力する。 #* CONFIG::COPY_DIRをCONFIG::HTML_DIR以下にコピーする。 #* 変換処理はバックグラウンドで行われる。 #* 日記一覧ページのHTMLデータを戻り値として返す。 #=== 戻り値 #[(string)] HTMLデータ。 def convert_html Debug.print("HTML変換START") Status.write("HTML変換開始") fork{ pid = fork{ #各日記ページを処理 entries = Entries.new tags = Tags.new top_page = TopPageMaker.new entries.each{|entry| tags.add(entry) entry_page = EntryPageMaker.new(entry) top_page.add_page(entry_page) html = entry_page.bind_template(CONFIG::TEMPLATE_FILE_HTML_ENTRY) write_page(CONFIG::HTML_DIR + (entry.path.basename(".*").to_s + ".html"), html) Status.write("ENTRY変換 #{entry.to_s}") } #日記一覧ページを処理 entry_list_page = EntryListPageMaker.new(entries) html = entry_list_page.bind_template(CONFIG::TEMPLATE_FILE_HTML_ENTRY_LIST) write_page(CONFIG::HTML_DIR + CONFIG::ENTRY_LIST_FILE_NAME, html) #タグページを処理 tags.each{|tag| tag_page = TagPageMaker.new(tag) html = tag_page.bind_template(CONFIG::TEMPLATE_FILE_HTML_TAG) write_page(CONFIG::HTML_DIR + (tag.name.to_s + ".html"), html) Status.write("TAG変換 #{tag.name.to_s}") } #タグ一覧ページを処理 tag_page = TagPageMaker.new(tags) html = tag_page.bind_template(CONFIG::TEMPLATE_FILE_HTML_TAG) write_page(CONFIG::HTML_DIR + CONFIG::ALL_TAG_LIST_FILE_NAME, html) #Topページを処理 top_page.add_page(tag_page) html = top_page.bind_template(CONFIG::TEMPLATE_FILE_HTML_TOP) write_page(CONFIG::HTML_DIR + CONFIG::TOP_FILE_NAME, html) #RSSを処理 make_rss(entries) #ディレクトリコピー FileUtils.cp_r(CONFIG::COPY_DIR.to_s, CONFIG::HTML_DIR.to_s,{:remove_destination => true}) Debug.print("HTML変換END") } watch_process(pid) } page_maker=WaitPageMaker.new(CONFIG::TMP_DIR + CONFIG::STATUS_FILE_NAME, "") html = page_maker.bind_template(CONFIG::TEMPLATE_FILE_CGI_WAIT) return html end #=== 概要 #プロセスの監視を行う。 #監視プロセスが終了後、ステータス情報とTMPディレクトリの削除を行う。 def watch_process(pid) fork{ text = `ps #{pid.to_s} | grep #{pid.to_s}` while !(text=="") text = `ps #{pid.to_s} | grep #{pid.to_s}` Debug.print("プロセス監視中 " + pid.to_s) sleep(1) end Status.delete clear_tmp_dir Debug.print("プロセス監視終了 " + pid.to_s) } end #=== 概要 #バックグラウンドで処理が行われているときに、待機ページを表示させる。 #待機ページは一定時間でリロードを繰り返し、処理終了後に元のページに戻る。 #=== 引数 #[(pathname)status_path] ステータス情報ファイルのパス。 #[(pathname)entry_path] 日記ファイル。 #=== 戻り値 #[(string)] HTMLデータ。 def show_wait_page(status_path, entry_path) page_maker = WaitPageMaker.new(status_path, entry_path) html = page_maker.bind_template(CONFIG::TEMPLATE_FILE_CGI_WAIT) return html end #=== 概要 #新しい日記を作成する。 #日記の作成日時を元に、日記データの保存ファイル名を作成する。 #重複する場合を想定して日付の末尾に1から始まる連番を振る。 #日記を作成後、日記一覧ページのHTMLデータを戻り値として返す。 #=== 引数 #[(string)date] 日記の作成日時。 #=== 戻り値 #[(string)] HTMLデータ。 def create_entry(date) path = CONFIG::DATA_DIR + date.to_s i = 1 while (path.dirname + (path.basename(".txt").to_s + "-" + i.to_s + ".txt")).exist? i += 1 end path = path.dirname + (path.basename(".txt").to_s + "-" + i.to_s + ".txt") page_maker = EditPageMaker.new page_maker.write_entry(path, "", "", "") page_maker.read_entry(path) html = page_maker.bind_template(CONFIG::TEMPLATE_FILE_CGI_EDIT) return html end #=== 概要 #編集中の日記データを保存する。 #保存後は編集ページのHTMLデータを返す。 #=== 引数 #[(pathname)path] 日記データのパス。 #[(string)title] 日記タイトル。 #[(string)tags] タグ。 #[(string)text] 日記本文。 #=== 戻り値 #[(string)] HTMLデータ。 def update_edit_entry(path, title, tags, text) page_maker = EditPageMaker.new page_maker.write_entry(path, title, tags, text) page_maker.read_entry(path) html = page_maker.bind_template(CONFIG::TEMPLATE_FILE_CGI_EDIT) return html end #=== 概要 #日記の編集を行う。 #日記のタイトル、タグ、本文と、画像のアップロードを行うフォームを表示する。 #=== 引数 #[(pathname)path] 日記データのパス。 #=== 戻り値 #[(string)] HTMLデータ。 def edit_entry(path) page_maker = EditPageMaker.new page_maker.read_entry(path) html = page_maker.bind_template(CONFIG::TEMPLATE_FILE_CGI_EDIT) return html end #=== 概要 #日記を削除する。CONFIG::DATA_DIRとCONFIG::HTML_DIRに作成された日記ファイルと画像フォルダを削除する。 #削除後、日記一覧ページのHTMLデータを戻り値として返す。 #=== 引数 #[(pathname)path] 日記データのパス。 #=== 戻り値 #[(string)] HTMLデータ。 def delete_entry(path) dir_path = path.dirname + path.basename(".txt").to_s if dir_path.directory? dir_path.each_entry{|file| f = dir_path + file next if f.directory? f.delete } dir_path.delete end html_path = CONFIG::HTML_DIR + path.basename(".txt").to_s if html_path.directory? html_path.each_entry{|file| f = html_path + file next if f.directory? f.delete } html_path.delete end html_file = CONFIG::HTML_DIR + path.basename("") html_file.delete if html_file.file? path.delete page_maker = ListPageMaker.new html = page_maker.bind_template(CONFIG::TEMPLATE_FILE_CGI_LIST) return html end #=== 概要 #画像データをアップロードしサムネイルの作成を行う。 #CONFIG::TMP_DIRのクリアを行い、ブラウザからの画像データを一時保管を行う。 #サムネイル作成やファイルコピーを行うimage_converメソッドをデーモンとして起動する。 #編集ページのHTMLデータを戻り値として返す。 #=== 引数 #[(pathname)path] 日記データのパス。 #[(Array[File])files] cgiで取得したファイル。 #=== 戻り値 #[(string)] HTMLデータ。 def upload_img(path, files) clear_tmp_dir img_tmp_dir = CONFIG::TMP_DIR + CONFIG::IMG_TMP_DIR_NAME FileUtils.mkdir(img_tmp_dir.to_s,{:mode => 0770}) files.each{|file| tmp_file = img_tmp_dir + file.original_filename tmp_file.open("w+", 0770){|f| f.write file.read } } Status.write("画像変換開始 " + path.basename(".txt").to_s) #別プロセスで処理を行う。 fork{ pid = fork{ html_path = CONFIG::HTML_DIR + path.basename(".txt") if html_path.directory? html_path.each_entry{|file| next if file.to_s=="." || file.to_s==".." (html_path + file.to_s).delete } html_path.delete end FileUtils.mkdir(html_path.to_s, {:mode => 0770}) data_path = CONFIG::DATA_DIR + path.basename(".txt") FileUtils.mkdir(data_path.to_s, {:mode => 0770}) if !data_path.directory? img_tmp_path = CONFIG::TMP_DIR + CONFIG::IMG_TMP_DIR_NAME img_tmp_path.each_entry{|file| img_file = img_tmp_path + file.to_s next if img_file.directory? next if !(img_file.extname.downcase == ".jpg" || img_file.extname.downcase == ".png") FileUtils.copy(img_file.to_s, data_path.to_s, {:preserve => true}) } convert_image(path){|index, file| Status.write("画像変換(#{path.basename(".*").to_s}:#{index.to_s}) #{file.to_s}") } } watch_process(pid) } page_maker = WaitPageMaker.new(CONFIG::TMP_DIR + CONFIG::STATUS_FILE_NAME, path) html = page_maker.bind_template(CONFIG::TEMPLATE_FILE_CGI_WAIT) return html end #=== 概要 #日記一覧のHTMLを作成する。 #=== 戻り値 #[(string)]HTMLデータ。 def make_listview page_maker = ListPageMaker.new html = page_maker.bind_template(CONFIG::TEMPLATE_FILE_CGI_LIST) return html end #=== 概要 #TMP_DIR内のファイルを再帰的に全て削除する。 def clear_tmp_dir CONFIG::TMP_DIR.entries.each{|path| next if path.to_s==".." or path.to_s=="." tmp_path = CONFIG::TMP_DIR + path.to_s FileUtils.rm_rf(tmp_path.to_s) } end #=== 概要 #DATAディレクトリに保存されているjpgとpngからサムネイルを作成し、HTMLデータのディレクトリに保存する。 #=== 引数 #[(pathname)path] 日記データのパス。 #=== yield #[convert_image{|index, file|}] (int)indexと(pathname)fileの値を引数としてブロックを評価する。処理が完了した際に評価される。 def convert_image(path) html_path = CONFIG::HTML_DIR + path.basename(".txt") data_path = CONFIG::DATA_DIR + path.basename(".txt") if data_path.directory? index = 0 data_path.entries.sort.each{|file| next if file.to_s=="." || file.to_s==".." original_path = data_path + file.to_s img_path = html_path + (CONFIG::PREFIX_IMG_ORIGINAL + index.to_s + ".jpg") thumb_path = html_path + (CONFIG::PREFIX_IMG_THUMB + index.to_s + CONFIG::THUMB_EXTNAME) square_path = html_path + (CONFIG::PREFIX_IMG_THUMB_SQUARE + index.to_s + CONFIG::THUMB_EXTNAME) #ImageConverter.convert_image_format(original_path, img_path) ImageConverter.make_img(original_path, img_path, CONFIG::IMG_WIDTH, CONFIG::IMG_HEIGHT, CONFIG::IMG_QUALITY) ImageConverter.make_img(original_path, thumb_path, CONFIG::THUMB_WIDTH, CONFIG::THUMB_HEIGHT, CONFIG::THUMB_QUALITY) ImageConverter.make_square_img(original_path, square_path, CONFIG::SQUARE_THUMB_SIZE, CONFIG::SQUARE_THUMB_R_SIZE) index += 1 yield(index, file) } end end #=== 概要 #RSSを出力する。 #=== 引数 #[(Entries)entries] 日記の集合。 def make_rss(entries) i = 0 rss = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" rss << "<rss version=\"2.0\"" rss << " xmlns:dc=\"http://purl.org/dc/elements/1.1/\"" rss << " xmlns:sy=\"http://purl.org/rss/1.0/modules/syndication/\"" rss << " xmlns:admin=\"http://webns.net/mvcb/\"" rss << " xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\">" rss << "<channel>" rss << "<title>#{CONFIG::TITLE}</title>" rss << "<link>#{(CONFIG::BASE_URL + CONFIG::RSS_FILE_NAME).to_s}</link>" rss << "<description>#{CONFIG::DESCRIPTION}</description>" rss << "<dc:language>ja</dc:language>" entries.each{|entry| break if i > CONFIG::RSS_ITEM_MAX break if i >= entries.length date = Time.parse(DateTime.strptime("#{entry.date}+9", "%Y%m%d%Z").to_s).rfc822.to_s rss << "<item>" rss << "<title>#{entry.title}</title>" rss << "<link>#{(CONFIG::BASE_URL + (entry.path.basename(".*").to_s + ".html")).to_s}</link>" rss << "<pubDate>#{date}</pubDate>" rss << "</item>" i = i + 1 } rss << "</channel>" rss << "</rss>" write_page(CONFIG::HTML_DIR + CONFIG::RSS_FILE_NAME, rss) end #=== 概要 #HTMLページを出力する。 #=== 引数 #[(pathname)path] 書き出すパス。 #[(string)html] HTMLデータ。 def write_page(path, html) path.open("w+", 0777){|file| file.write(html) } end #=== 概要 #webページを出力する。 #出力する際に、ヘッダー情報を付加して出力を行う。 #=== 引数 #[(string)html] 出力するHTMLデータを格納した文字列。 def output_page(html) puts "Content-Type: text/html\n\n" puts html end end #== 概要 #画像を変換するクラス。 class ImageConverter #=== 概要 #画像形式を変換する。変換可能な画像形式の種類はRMagick(ImageMagick)による。 #=== 引数 #[(pathname)original_img_path] 元画像のパス。 #[(pathname)output_img_path] 変換後の画像パス。拡張子の画像形式に変換して出力する。 def self.convert_image_format(original_img_path, output_img_path) begin info = `/usr/local/bin/identify #{original_img_path.to_s}` type = info.split[1] width = info.split[2].split("x")[0] height = info.split[2].split("x")[1] `/usr/local/bin/convert -define #{type}:size=#{width}x#{height} -quality 95 #{original_img_path.to_s} #{output_img_path.to_s}` rescue => err Debug.print("Error ImageConverter path=" + original_img_path.to_s) Debug.print("ErrMes=" + err.to_s) end end #=== 概要 #縮小画像を作成する。 #=== 引数 #[(pathname)img_path] 元画像のパス。 #[(pathname)thumb_path] 出力するサムネイルのパス。拡張子の画像形式に変換して出力する。 #[(int)img_width] 出力する画像の幅の長さ。 #[(int)img_height] 出力する画像の高さの長さ。 #[(int)quality] 変換後の画像の品質。 def self.make_img(img_path, thumb_path, img_width, img_height, quality) begin info = `/usr/local/bin/identify #{img_path.to_s}` type = info.split[1] width = info.split[2].split("x")[0].to_f height = info.split[2].split("x")[1].to_f if width > img_width || height > img_height `/usr/local/bin/convert -define #{type}:size=#{width.to_s}x#{height.to_s} -resize #{img_width.to_s}x#{img_height.to_s} -quality #{quality.to_s} #{img_path.to_s} #{thumb_path.to_s}` else `/usr/local/bin/convert -define #{type}:size=#{width.to_s}x#{height.to_s} -quality #{quality.to_s} #{img_path.to_s} #{thumb_path.to_s}` end rescue => err Debug.print("Error ImageConverter path=" + original_img_path.to_s) Debug.print("ErrMes=" + err.to_s) end end #=== 概要 #正方形のサムネイルを作成する。画像の角を丸める処理を行う。 #=== 引数 #[(pathname)img_path] 元画像のパス。 #[(pathname)thumb_path] 出力するサムネイルのパス。拡張子の画像形式に変換して出力する。 #[(int)size] 出力する正方形サムネイルの一辺の長さ。 #[(int)r_size] 角を丸める大きさ。 def self.make_square_img(img_path, thumb_path, size, r_size) begin info = `/usr/local/bin/identify #{img_path.to_s}` type = info.split[1] width = info.split[2].split("x")[0].to_f height = info.split[2].split("x")[1].to_f resize = ((width / height)*size).ceil resize = ((height / width)*size).ceil if width < height `/usr/local/bin/convert -define #{type}:size=#{width.to_s}x#{height.to_s} -resize #{resize.to_s} -gravity center -crop #{size.to_s}x#{size.to_s}+0+0 -quality 80 #{img_path.to_s} #{thumb_path.to_s}` rescue => err Debug.print("Error ImageConverter path=" + img_path.to_s) Debug.print("ErrMes=" + err.to_s) end end end #== 概要 #CGI動作時にページを生成するクラスの基底クラス。 class PageMaker #=== 概要 #テンプレートを適用しHTMLデータを作成する。 #=== 引数 #[(pathname)template_file] 適用するテンプレートファイル。 #=== 戻り値 #[(string)] HTMLデータ。 def bind_template(template_file) template = template_file.open("r"){|f| f.read} erb = ERB.new(template) html = erb.result(binding) return html end end #== 概要 #待機ページを生成する。 #一定間隔でリロードを繰り返す動作をする。 class WaitPageMaker < PageMaker #=== 概要 #WaitPageMakerの初期化 #=== 引数 #[(pathname)status_path] ステータス情報ファイル。 #[(pathname)enrty_path] 日記のパス。nilや""を指定した場合はcgiモードをlistにし、リストページを再読み込みする。 #=== 戻り値 #[(string)] HTMLデータ。 def initialize(status_path, entry_path) status = "" status = status_path.readlines if status_path.file? @status_info = make_status_info(status) @reload_script = make_reload_script(entry_path) end private #=== 概要 #ステータス情報をステータスファイルから読み込んで出力する。 #=== 引数 #[(string)tatus] ステータス情報。 #=== 戻り値 #[(string)] HTMLデータ。 def make_status_info(status) html = "<pre>" status.each{|text| html << text.chomp + $/ } html << "</pre>" return html end #=== 概要 #自動でページをリロードするスクリプトを出力する。 #日記のパスで指定した編集ページを再読み込みしようとする。 #=== 引数 #[(pathname)enrty_path] 日記のパス。nilや""を指定した場合はcgiモードをlistにし、リストページを再読み込みする。 #=== 戻り値 #[(string)] HTMLデータ。 def make_reload_script(entry_path) html = "<script type='text/javascript'>" html << " setTimeout('document.send.submit()',2000);" html << "</script>" html << "<form name='send' method='post' action='myDiary.rb?#{Time.now.tv_sec.to_s}'>" html << " <intpu type='submit'>" if entry_path.to_s=="" || entry_path.to_s==nil html << " <input type='hidden' name='mode' value='list'>" else html << " <input type='hidden' name='mode' value='edit'>" html << " <input type='hidden' name='path' value='" + entry_path.to_s + "'>" end html << "</form>" return html end end #== 概要 #日記の編集ページを生成するクラス。 #== 継承 #PageMaker #== テンプレート #[<%=@edit_form%>] 日記編集用の入力欄。 #[<%=@edit_date%>] 編集中の日記の日付。 #[<%=@upload_form%>] 画像アップロードボタン。 #[<%=@update_time%>] 日記編集ページの更新時間。 #[<%=@thumb_icon%>] サムネイル。サムネイルが一枚もない場合は、"画像なし"が出力される。 class EditPageMaker < PageMaker #=== 概要 #EditPageMakerの初期化。 def initialize @edit_form = nil @edit_date = nil @upload_form = nil @thumb_icon = nil @update_time = Time.now.to_s end #=== 概要 #ファイルに日記データを書き込む。 #=== 引数 #[(pathname)path] 書きこむ日記ファイル。 #[(string)title] 日記タイトル。 #[(string)tags] タグ。 #[(string)text] 日記本文。 def write_entry(path, title, tags, text) path.open("w+", 0770){|file| file.write(title + $/) file.write(tags + $/) file.write(text) } @update_time = Time.now.to_s end #=== 概要 #ファイルから日記データを読み込む。 #=== 引数 #[(pathname)path] 読み込む日記ファイル。 def read_entry(path) entry = path.readlines @edit_form = make_edit_form(entry, path) @edit_date = path.basename(".txt").to_s @upload_form = make_upload_form(path) @thumb_icon = make_thumb_list(path) @update_time = Time.now.to_s end private #=== 概要 #サムネイルの画像一覧を作成する。 #=== 引数 #[(pathname)path] 読み込む日記ファイル。 #=== 戻り値 #[string] HTMLデータ。 def make_thumb_list(path) html_path = CONFIG::HTML_DIR + path.basename(".txt") html = "<div id='thumb'>" begin html_path.entries.each_with_index{|thumb, index| next if thumb.directory? || !thumb.to_s.include?(CONFIG::PREFIX_IMG_ORIGINAL) img_path = html_path + thumb.basename.to_s filename = thumb.basename(".*").to_s index = filename.delete(CONFIG::PREFIX_IMG_ORIGINAL) thumb_path = html_path + (CONFIG::PREFIX_IMG_THUMB_SQUARE+index+CONFIG::THUMB_EXTNAME) html << "#{index.to_s}<a target='./blank' href='#{img_path.relative_path_from(CONFIG::SCRIPT_DIR)}'><img name='#{index.to_s}' data-path='<%=@img[:thumb][#{index.to_s}]%>' src='#{thumb_path.relative_path_from(CONFIG::SCRIPT_DIR).to_s}'></a> " } rescue html << "<p>画像なし</p>" end html << "</div>" html << "<script type='text/javascript'>" html << "$(function(){" html << " $('div#thumb img').bind('contextmenu', function(){" html << " var path=$(this).data('path');" html << " insertText(path);" html << " return false;" html << " });" html << "});" html << "function insertText(text){" html << " var o = $('#area').get(0);" html << " o.focus();" html << " var s = o.value;" html << " var p = o.selectionStart;" html << " var np = p + text.length;" html << " o.value = s.substr(0, p) + text + s.substr(p);" html << " o.setSelectionRange(np, np);" html << "};" html << "</script>" return html end #=== 概要 #ファイルをアップトードするフォームを作成する。 #javascriptで、ファイルが未選択の時は、アップロードボタンを押せないようにする。 #=== 引数 #[(pathname)path] 読み込む日記ファイル。 #=== 戻り値 #[string] HTMLデータ。 def make_upload_form(path) html = "<script type='text/javascript'>" html << "function onFilesSelected(){" html << " var files = document.getElementById(\"fileSelectButton\").files;" html << " if(files.length >= 0){" html << " $(\"input#fileUploadButton\").removeAttr(\"disabled\");" html << " }else{" html << " $(\"input#fileUploadButton\").attr(\"disabled\",\"\");" html << " }" html << "};" html << "</script>" html << "<form method='post' action='myDiary.rb?#{Time.now.tv_sec.to_s}' enctype='multipart/form-data'>" html << "<input id='fileUploadButton' type='submit' name='mode' value='upload' disabled><input type='file' id='fileSelectButton' name='files' multiple onchange='onFilesSelected()'>" html << "<input type='hidden' name='path' value='#{path.to_s}'>" html << "</form>" return html end #=== 概要 #編集フォームを作成する。 #=== 引数 #[(Array[string])entry] ファイルから読み込んだ日記データ。 #[(pathname)path] 編集する日記データのパス。 #=== 戻り値 #[string] HTMLデータ。 def make_edit_form(entry, path) title = make_title_textbox(entry) tags = make_tags_textbox(entry) text = make_text_textarea(entry) html = "<form method='post' action='myDiary.rb?#{Time.now.tv_sec.to_s}'>" html << "<dl>" html << "<dt>タイトル</dt>" html << "<dd>#{title}</dd>" html << "<dt>タグ</dt>" html << "<dd>#{tags}</dd>" html << "<dt>本文</dt>" html << "<dd>#{text}</dd>" html << "<dt>処理</dt>" html << "<dd>" html << "<input type='submit' name='mode' value='update'>" html << "<input type='submit' name='mode' value='delete' onclick=\"return confirm('delete ok?')\">" html << "<input type='submit' name='mode' value='list'>" html << "</dd>" html << "</dl>" html << "<input type='hidden' name='path' value='#{path.to_s}'>" html << "</form>" return html end #=== 概要 #日記タイトルを編集するテキストボックスを作成する。 #=== 引数 #[(Array[string])entry] ファイルから読み込んだ日記データ。 #=== 戻り値 #[string] HTMLデータ。 def make_title_textbox(entry) title = "" if entry.length > 0 && entry[0]!=$/ title = entry[0].chomp end html = "<input class='text' type='text' name='title' value='" + title + "'>" return html end #=== 概要 #日記のタグ情報を編集するテキストボックスを作成する。 #=== 引数 #[(Array[string])entry] ファイルから読み込んだ日記データ。 #=== 戻り値 #[string] HTMLデータ。 def make_tags_textbox(entry) tags = "" if entry.length > 1 && entry[1]!=$/ tags = entry[1].chomp end html = "<input class='text' type='text' name='tags' value='" + tags + "'>" return html end #=== 概要 #日記本文を編集するテキストエリアを作成する。 #=== 引数 #[(Array[string])entry] ファイルから読み込んだ日記データ。 #=== 戻り値 #[string] HTMLデータ。 def make_text_textarea(entry) text = "" if entry.length > 2 && entry[2]!=$/ entry.each_with_index{|line, index| next if index == 0 || index == 1 text << line } end html = "<textarea id='area' name='text'>" html << text html << "</textarea>" return html end end #== 概要 #CGI動作時に日記一覧ページを生成するクラス。 #== 継承 #PageMaker #== テンプレート #[<%=@num_entries%>] 日記のエントリー数。 #[<%=@entry_list%>] 日記一覧(編集・削除ボタン付き)。 #[<%=@create_entry%>] 新しく日記を作成するフォーム。 #[<%=@convert_html%>] 日記をHTMLに変換するボタン。 #[<%=@create_replace%>] 新しく日記本文を置換する文字列を登録するフォーム。 #[<%=@replace_list%>] 置換文字列の一覧(削除ボタン付き)。 class ListPageMaker < PageMaker #=== 概要 #ListPageMakerの初期化。 def initialize entries = Array.new entries = get_entries @entry_list = make_entry_list(entries) @num_entries = entries.length @create_entry = make_create_entry_form @convert_html = make_html_convert_button @create_replace = make_create_replace_form @replace_list = make_html_replace_list end private #=== 概要 #置換文字列の一覧を作成する。 #各置換文字列に、削除ボタンを作成する。 #=== 戻り値 #[string] HTMLデータ。 def make_html_replace_list html = "<dl>" replace = Replace.new(CONFIG::DATA_DIR + CONFIG::REPLACE_STR_FILE_NAME) replace.replace_hash.keys.reverse.each{|key| html << "<dt><form method='post' action='myDiary.rb?#{Time.now.tv_sec.to_s}'>" html << "<input type='hidden' name='search_str' value='#{key.to_s}'>" html << "<input type='submit' name='mode' value='del_replace'>" html << ERB::Util.h(key.to_s) html << "</form></dt>" html << "<dd>#{ERB::Util.h(replace.replace_hash[key.intern].to_s)}</dd>" } html << "</dl>" return html end #=== 概要 #日記本文を置換する文字列を登録するフォームを作成する。 #=== 戻り値 #[string] HTMLデータ。 def make_create_replace_form html = "<form method='post' action='myDiary.rb?#{Time.now.tv_sec.to_s}'>" html << "search str:<input type='text' name='search_str' class='replace'>" html << "<br>" html << "replace str:<input type='text' name='replace_str' class='replace'>" html << "<br>" html << "<input type='submit' name='mode' value='add_replace'>" html << "</form>" end #=== 概要 #日記をHTMLに変換するボタンを作成する。 #=== 戻り値 #[string] HTMLデータ。 def make_html_convert_button html = "<form method='post' action='myDiary.rb?#{Time.now.tv_sec.to_s}'>" html << "<input type='submit' name='mode' value='convert'>" html << "<input type='submit' name='mode' value='rebuild'>" html << "</form>" return html end #=== 概要 #新しく日記を作成する。 #=== 戻り値 #[string] HTMLデータ。 def make_create_entry_form html = "<form method='post' action='myDiary.rb?#{Time.now.tv_sec.to_s}'>" html << "<input type='text' name='date' value='" + Time.now.strftime("%Y%m%d").to_s + "'>" html << "<input type='submit' name='mode' value='create'>" html << "</form>" return html end #=== 概要 #日記データのファイル一覧から各日記の編集ページへのリンクを作成する。 #=== 引数 #[(Array[pathname])entries] 日記データのファイル一覧。 #=== 戻り値 #[string] HTMLデータ。 def make_entry_list(entries) html = "<ul>" entries.each{|entry| html << "<li><form method='post' action='myDiary.rb?#{Time.now.tv_sec.to_s}' enctype='multipart/form-data'>" html << "<input type='hidden' name='path' value='" + entry.to_s + "'>" html << "<input type='submit' name='mode' value='edit'>" html << entry.basename(".txt").to_s html << " : " html << (CONFIG::DATA_DIR + entry.to_s).readlines[0] html << "</form></li>" } html << "</ul>" return html end #=== 概要 #データフォルダ内にある日記データのファイル一覧を取得する。 #ファイル一覧は最新日付順で並べられる。 #=== 戻り値 #[Array(pathname)] 日記データのファイル一覧。 def get_entries entries = Array.new CONFIG::DATA_DIR.each_entry{|path| data_path = CONFIG::DATA_DIR + path next if data_path.directory? || data_path.extname!=".txt" entries << data_path } return entries.sort{|a,b| b.to_s <=> a.to_s} end end #== 概要 #日記のトップページを作成するクラス。 #== 継承 #PageMaker #== テンプレート #[<%=@entries[index].path%>] EntryPageMakerのテンプレートが全て使用できる。 #[<%=@tag.tag_list%>] TagPageMakerのテンプレートが全て使用できる。 class TopPageMaker < PageMaker #=== 概要 #TopPageMakerの初期化。 def initialize @pages = Array.new @tag = nil end #=== 概要 #ページ配列への参照の定義。 #=== 引数 #[(int)index] 配列のインデックス。 #=== 戻り値 #[(EntryPageMaker)] ページ。 def [](index) return @pages[index] end #=== 概要 #日記ページを追加する。 #=== 引数 #[(EntryPageMaker || TagPageMaker)page] 日記ページ。 def add_page(page) @pages << page if page.class.to_s=="EntryPageMaker" @tag = page if page.class.to_s=="TagPageMaker" end end #== 概要 #エントリー一覧のページを作成するクラス。 #== 継承 #PageMaker #== テンプレート #[<%=@num%>] エントリーの総数。 #[<%=@entry_list%>] エントリー一覧のリスト。 class EntryListPageMaker < PageMaker #=== 概要 #EntryListPageMakerの初期化。 #=== 引数 #[(Entries)entries] 日記の集合。 def initialize(entries) @num = entries.length.to_s @entry_list = make_entry_list(entries) end private #=== 概要 #全てのエントリーのリストを作成する。 #=== 引数 #[(Entries)entries] エントリー。 #=== 戻り値 #[(string)] HTMLデータ。 def make_entry_list(entries) html = "<ul class='entry'>" entries.each{|entry| year = entry.date[0,4] month = entry.date[4,2] day = entry.date[6,2] html << "<li>" html << "#{year}年#{month}月#{day}日 " html << "<a href='#{ERB::Util.h(entry.path.basename(".*"))}.html'>#{entry.title}</a>" html << "</li>" } html << "</ul>" return html end end #== 概要 #タグ一覧のページを作成するクラス。 #== 継承 #PageMaker #== テンプレート #[<%=@tag_name%>] タグの名前。 #[<%=@tag_list%>] タグ一覧のリスト、又はタグを含むエントリーのリスト。 class TagPageMaker < PageMaker #タグの名前 attr_reader :tag_name #タグ一覧のリスト、又はタグを含むエントリーのリスト。 attr_reader :tag_list #=== 概要 #TagPageMakerの初期化。 #Tagオブジェクトで初期化を行うと、そのタグのエントリーリストを作成する。 #Tagsオブジェクトで初期化を行うと、タグのリストを作成する。 #=== 引数 #[(Tag又はTags)tag] タグ。 def initialize(tag) if tag.class.to_s == "Tag" @tag_name = tag.name @tag_list = make_list(tag) elsif tag.class.to_s == "Tags" @tag_name = "タグ一覧" @tag_list = make_tag_list(tag) end end private #=== 概要 #全てのタグの一覧のリストを作成する。 #=== 引数 #[(Tags)tags] 全てのタグ。 #=== 戻り値 #[(string)] HTMLデータ。 def make_tag_list(tags) html = "<ul class='tag'>" tags.each{|tag| html << "<li>" html << "<a href='#{ERB::Util.h(tag.name)}.html'>#{tag.name}</a> (#{tag.entries.size.to_s})" html << "</li>" } html << "</ul>" return html end #=== 概要 #タグを含むエントリーのリストを作成する。 #=== 引数 #[(Tag)tag] タグ。 #=== 戻り値 #[(string)] HTMLデータ。 def make_list(tag) html = "<ul class='tag'>" tag.entries.each{|entry| year = entry.date[0,4] month = entry.date[4,2] day = entry.date[6,2] html << "<li>" html << "#{year}年#{month}月#{day}日 " html << "<a href='#{ERB::Util.h(entry.path.basename(".*").to_s)}.html'>#{entry.title}</a>" html << "</li>" } html << "</ul>" return html end end #== 概要 #日記ページを作成するクラス。 #== 継承 #PageMaker #== テンプレート #[<%=@title%>] タイトル。 #[<%=@date%>] 日付。 #[<%=@tags%>] タグページへのリンク。 #[<%=@img%>] 画像へのリンクとサムネイル。<%=@img[:thumb][0]%>で1番目の画像のサムネイルとリンクのHTMLを返す。:thumbを:squareに変更すると、サムネイルを正方形にする。 #[<%=@text%>] 日記本文。 #[<%=@next_entry%>] 次のエントリーへのリンク。 #[<%=@prev_entry%>] 前のエントリーへのリンク。 #[<%=@permalink%>] エントリーへの絶対リンク。 class EntryPageMaker < PageMaker #[(string)] タイトル。 attr_reader :title #[(string)] 日付。 attr_reader :date #[(string)] タグページへのリンク。 attr_reader :tags #[(string)] 画像へのリンクとサムネイル。<%=@img[:thumb][0]%>で1番目の画像のサムネイルとリンクのHTMLを返す。:thumbを:squareに変更すると、サムネイルを正方形にする。 attr_reader :img #[(string)] 日記本文。 attr_reader :text #[(string)] 次のエントリーへのリンク。 attr_reader :next_entry #[(string)] 前のエントリーへのリンク。 attr_reader :prev_entry #[(string)] エントリーへの絶対リンク。 attr_reader :permalink #=== 概要 #EntryPageMakerの初期化。 #=== 引数 #[(Entry)entry] 日記。 def initialize(entry) @title = entry.title @date = entry.date @tags = make_link_tags(entry.tags) @img = make_link_img(entry.images) @text = ERB.new(parse_text(entry.text)).result(binding) @next_entry = make_link_next_entry(entry) @prev_entry = make_link_prev_entry(entry) @permalink = make_link_permalink(entry) end #=== 概要 #エントリーへの絶対リンクを作成する。 #=== 引数 #[(Entry)entry] 日記 #=== 戻り値 #[(string)] HTMLデータ。 def make_link_permalink(entry) path = entry.permalink.relative_path_from(CONFIG::HTML_DIR) html = "<a href='#{path.to_s}'>#{entry.title}</a>" return html end private #=== 概要 #次のエントリーへのリンクを作成する。 #=== 引数 #[(Entry)entry] 日記 #=== 戻り値 #[(string)] HTMLデータ。 def make_link_next_entry(entry) html = "Next" if entry.next_entry.class.to_s=="Pathname" path=entry.next_entry.relative_path_from(CONFIG::HTML_DIR) html = "<a href='#{path.to_s}'>Next</a>" end return html end private #=== 概要 #前のエントリーへのリンクを作成する。 #=== 引数 #[(Entry)entry] 日記 #=== 戻り値 #[(string)] HTMLデータ。 def make_link_prev_entry(entry) html = "Prev" if entry.prev_entry.class.to_s=="Pathname" path = entry.prev_entry.relative_path_from(CONFIG::HTML_DIR) html = "<a href='#{path.to_s}'>Prev</a>" end return html end #=== 概要 #画像をHTMLに変換する。 #=== 引数 #[(Array[pathname])images] HTMLフォルダに保存されたオリジナル画像のパスの配列。 #=== 戻り値 #[(Hash[:thumb=>Array[string], :square=>Array[string]])] HTMLデータ。 def make_link_img(images) thumb_arr = Array.new square_arr = Array.new images.each{|path| f = path.basename(".*").to_s.delete(CONFIG::PREFIX_IMG_ORIGINAL) + CONFIG::THUMB_EXTNAME d = path.dirname original_path = path.relative_path_from(CONFIG::HTML_DIR) thumb_path = (d+(CONFIG::PREFIX_IMG_THUMB+f)).relative_path_from(CONFIG::HTML_DIR) square_path = (d+(CONFIG::PREFIX_IMG_THUMB_SQUARE+f)).relative_path_from(CONFIG::HTML_DIR) thumb = "<a class='thumb' href='#{original_path.to_s}'>" thumb << "<img class='thumb' src='#{thumb_path}'>" thumb << "</a>" thumb_arr << thumb square = "<a class='square' href='#{original_path.to_s}'>" square << "<img class='square' src='#{square_path}'>" square << "</a>" square_arr << square } hash = Hash.new hash[:thumb] = thumb_arr hash[:square] = square_arr return hash end #=== 概要 #タグをHTMLに変換する。 #=== 引数 #[(string)tags] タグ。 #=== 戻り値 #[(string)] HTMLデータ。 def make_link_tags(tags) html = "<a class='tag' href='#{CONFIG::ALL_TAG_LIST_FILE_NAME}'>全てのタグ</a>" tags.split.sort.each{|tag| html << ", <a class='tag' href='#{tag}.html'>#{tag}</a>" } return html end #=== 概要 #日記本文をHTML文章に変換する。 #=== 引数 #[(Arrya[string])text] 日記本文。 #=== 戻り値 #[(string)] HTMLデータ。 def parse_text(text) #日記データの構文解析 html = Array.new text << "" flag_p = false flag_html = false flag_pre = false text.each{|line| line = line.chomp #空行時の処理 #通常モードとHTMLモード(非整形モード)を解除する if(line == "") if(flag_p) html << "</p>" elsif(flag_pre) html << ERB::Util.h(line) end flag_p = false flag_html = false ##空行でないときの処理 else #フラグが立っている場合の処理 if(flag_html) html << line elsif(flag_pre) #preモードの終わりを示す"?"があれば、モードを終了し</pre>タグを挿入する if(line[0,1] == "?") flag_pre = false html[html.length-1] = html.last + "</pre>" else html << ERB::Util.h(line) end #特殊文字"<", "?", "!"の処理 elsif(line[0,1] == "<" and line[1,1] != "%") flag_html = true html << line elsif(line[0,1] == "?") flag_pre = true line = line[1,line.length] if(flag_p) html << "</p>" flag_p = false end html << "<pre>" + ERB::Util.h(line) elsif(line[0,1] == '!') if(flag_p) html << "</p>" flag_p = false end if(line[1,1] == "!") if(line[2,1] == "!") if(line[3,1] == "!") if(line[4,1] == "!") html << "<h6>" + line[4,line.length] + "</h6>" end else html << "<h5>" + line[3,line.length] + "</h5>" end else html << "<h4>" + line[2,line.length] + "</h4>" end else html << "<h3>" + line[1,line.length] + "</h3>" end #文頭が特殊文字でない場合の通常処理 else #特殊文字を無効化する際に使う行頭スペースのの処理 if(line[0] == " ") line = line[1,line.length] end #すでにパラグラフ内にいれば"<br>"を付ける。 #そうでなければ"<p>"を付けパラグラフ内であるフラグを立てる。 if(flag_p) html << "<br>" html << line else html << "<p>" html << line flag_p = true end end end } str = "" html.each{|h| str << h str << $/ } return str end end #== 概要 #日記の集合 #日記データを読み込み、HTML化したデータに変換する。 class Entries #エントリーの数。 attr_reader :length #=== 概要 #Entriesの初期化を行う。 def initialize @entries = Array.new @entries = get_entries @length = @entries.length @tags = Tags.new end #=== 概要 #エントリー配列への参照の定義。 #=== 引数 #[(int)index] 配列のインデックス。 #=== 戻り値 #[(Entry)] エントリー。 def [](index) return @entries[index] end #=== 概要 #各エントリーに対してブロックを評価する。 def each @entries.each{|entry| yield(entry) } end private #=== 概要 #全ての日記データファイルを読み込み、Entryオブジェクトとしてパースする。 #=== 戻り値 #[(Array[Entry])] エントリーの配列。 def get_entries entries = Array.new path = CONFIG::DATA_DIR replace = Replace.new(CONFIG::DATA_DIR + CONFIG::REPLACE_STR_FILE_NAME) path.entries.sort{|a,b| b.to_s <=> a.to_s}.each{|file| full_path = path + file.to_s next if !full_path.file? next if full_path.extname!=".txt" entry = Entry.new(full_path) entry.date = file.to_s[0,8] data = full_path.readlines text = Array.new data.each_with_index{|str,index| str = str.to_s.chomp.to_s case index when 0 entry.title = str when 1 entry.tags = str else replace.replace_hash.each{|key, value| str.gsub!(/#{key.to_s}/, value.to_s) } text << str end } entry.text = text img_path = CONFIG::HTML_DIR + file.basename(".*").to_s if img_path.directory? img_path.each_entry{|file| next if file.directory? || !file.to_s.include?(CONFIG::PREFIX_IMG_ORIGINAL) entry.images << img_path + file.to_s } end entries << entry } entries.each_with_index{|entry, index| if index+1 != entries.length next_entry_path = entries[index+1].path.basename(".*").to_s + ".html" entry.next_entry = CONFIG::HTML_DIR + next_entry_path end if index!=0 prev_entry_path = entries[index-1].path.basename(".*").to_s + ".html" entry.prev_entry = CONFIG::HTML_DIR + prev_entry_path end permalink = (entry.path.basename(".*").to_s + ".html").to_s entry.permalink = CONFIG::HTML_DIR + permalink } return entries end end #== 概要 #日記を表現するクラス class Entry #[(pathname)] 日記データのパス。 attr_accessor :path #[(sting)] タイトル attr_accessor :title #[(sting)] 日付 attr_accessor :date #[(Array[sting])] 本文 attr_accessor :text #[(sting)] タグ一覧 attr_accessor :tags #[(Array[(pathname)]] 画像のパス。 attr_accessor :images #[(pathname)] 次のエントリーのパス。 attr_accessor :next_entry #[(pathname)] 前のエントリーのパス。 attr_accessor :prev_entry #[(pathname)] このエントリーのパス。 attr_accessor :permalink #=== 概要 #エントリーの初期化。 #=== 引数 #[(pathname)path] 日記データのパス。 def initialize(path) @path = path @title = "" @date = "" @tags = "" @text = Array.new @images = Array.new @next_entry = "" @prev_entry = "" @permalink = "" end #=== 概要 #日記の日付とタイトルを結合した文字列を返す。 #=== 戻り値 #[(sring)] 日付とタイトルを結合した文字列。 def to_s return "date=" + @date.to_s + " path=" + @path.basename.to_s + " title=" + @title.to_s end end #== 概要 #タグの集合を表現するクラス。 class Tags #=== 概要 #タグクラスの初期化。 def initialize @tags = Hash.new end #=== 概要 #エントリーに設定されたタグをタグリストに追加する。 #=== 引数 #[(Entry)entry] エントリー。 def add(entry) tag_arr = entry.tags.split(" ") tag_arr.each{|tag| if(@tags[tag.intern] == nil) @tags[tag.intern] = Tag.new(tag) end @tags[tag.intern].add_entry(entry) } end #=== 概要 #各タグに対してブロックを評価する。 #タグはソートして評価される。 def each @tags.values.sort{|a,b|a.name.to_s<=>b.name.to_s}.each{|tag| yield(tag) } end end #== 概要 #タグを表現するクラス。 class Tag #[(String)] タグの名前。 attr_reader :name #[Array(Entry)] このタグを含むエントリーの配列。 attr_reader :entries #=== 概要 #Tagの初期化。 #=== 引数 #[(String)name] タグの名前。 def initialize(name) @name = name.to_s @entries = Array.new end #=== 概要 #このタグを含むエントリーを追加する。 #=== 引数 #[(Entry)entry] 追加するエントリー。 def add_entry(entry) @entries << entry end end #== 概要 #置換文字列を管理するクラス。 class Replace #(Hash)検索文字列をkeyとした、置換文字列のハッシュ配列。 attr_reader :replace_hash #=== 概要 #Replaceの初期化。 #=== 引数 #[(pathname)path] 置換文字列を保存するファイル。 def initialize(path) @path = path @replace_hash = read_file end #=== 概要 #置換文字列を追加する。 #検索文字列が既に登録されている場合は上書きする。 #=== 引数 #[(string)replace_str] 検索する文字列。(置換前文字列) #[(string)replace_str] 置換する文字列。(置換後文字列) def add_str(search_str, replace_str) @replace_hash[search_str.chomp.intern] = replace_str.chomp write_file end #=== 概要 #置換文字列を削除する。 #=== 引数 #[(string)search_str] 削除する検索文字列。 def del_str(search_str) @replace_hash.delete(search_str.chomp.intern) write_file end private #=== 概要 #置換文字列を書き込む。 def write_file File.open(@path.to_s,"w",0770){|file| @replace_hash.each{|key, value| file.write(key.to_s + "\t" + value.to_s + $/) } } end #=== 概要 #置換文字列を読み込む。 #=== 戻り値 #[(Hash)] 検索文字列をkeyとした、置換文字列のハッシュ配列。 def read_file hash_arr = Hash.new if @path.exist? data = @path.readlines data.each{|str| arr = str.partition("\t") if arr[0]!="" hash_arr[arr[0].intern] = arr[2].chomp end } end return hash_arr end end #=====スクリプト実行開始===== begin myDiary = MyDiary.new rescue => err Debug.print("*****ERRROR*****") Debug.print(err.to_s) Debug.print("*****Trace******") err.backtrace.each{|str| Debug.print(str.to_s) } end #=====スクリプト実行終了=====
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script> <script type="text/javascript" src="./files/script.js"></script> <link rel="stylesheet" type="text/css" href="./files/default.css"> <title><%=CONFIG::TITLE %></title> </head> <!-- トップページに載せる日記の数。 --> <% ENTRY_MAX=5 %> <!-- 日記一覧の数。 --> <% LIST_MAX=15 %> <body> <div id="header"> <h1><%="<a href='#{CONFIG::TOP_FILE_NAME}'>#{CONFIG::TITLE}</a>" %></h1> <p><%=CONFIG::DESCRIPTION %></p> </div> <div id="menu"> <div class="nav"> <h2>最近のエントリー<a href="./index.rdf"><img src="./files/rss_icon.png"></a></h2> <p><%= "<a href='#{CONFIG::ENTRY_LIST_FILE_NAME}'>タイトル一覧</a>" %></p> <ul> <% LIST_MAX.times{|i| %> <li><%= @pages[i].permalink.to_s %></li> <% } %> </ul> </div> <!-- 広告 ここから --> <div class="nav"> <h2>欲しい技術書</h2> <iframe src="http://rcm-jp.amazon.co.jp/e/cm?t=gaso00-22&o=9&p=12&l=st1&mode=books-jp&search=FreeBSD%20Linux&fc1=999999<1=_blank&lc1=585CA8&bg1=000000&f=ifr" marginwidth="0" marginheight="0" width="300" height="250" border="0" frameborder="0" style="border:none;" scrolling="no"></iframe> </div> <div class="nav"> <h2>気になるフィギュア</h2> <iframe src="http://rcm-jp.amazon.co.jp/e/cm?t=gaso00-22&o=9&p=12&l=st1&mode=toys-jp&search=%E3%83%95%E3%82%A3%E3%82%AE%E3%83%A5%E3%82%A2%20%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%AB%20PVC&fc1=999999<1=_blank&lc1=585CA8&bg1=000000&f=ifr" marginwidth="0" marginheight="0" width="300" height="250" border="0" frameborder="0" style="border:none;" scrolling="no"></iframe> </div> <div class="nav"> <h2>買いたいカメラ機材</h2> <iframe src="http://rcm-jp.amazon.co.jp/e/cm?t=gaso00-22&o=9&p=12&l=bn1&mode=electronics-jp&browse=16462091&fc1=999999<1=_blank&lc1=585CA8&bg1=000000&f=ifr" marginwidth="0" marginheight="0" width="300" height="250" border="0" frameborder="0" style="border:none;" scrolling="no"></iframe> </div> <!-- 広告 ここまで --> <div class="nav"> <h2>カテゴリー</h2> <p><%= "<a href='#{CONFIG::ALL_TAG_LIST_FILE_NAME}'>タグ一覧</a>" %></p> <%= @tag.tag_list %> </div> <div class="nav"> <h2>リンク</h2> <ul> <li><a href="http://gaso.tumblr.com/">時系列記憶槽 - Tumblr</a></li> <li><a href="http://twitter.com/#!/G_A_S_O">G_A_S_O - twitter</a></li> </ul> </div> </div> <div id="section"> <!-- Topページの記事の繰り返し。 ここから --> <% ENTRY_MAX.times{|i| %> <div class="article"> <div class="date"><%= "#{@pages[i].date[0,4]}年#{@pages[i].date[4,2]}月#{@pages[i].date[6,2]}日" %></div> <h2><%= @pages[i].title %></h2> <%= @pages[i].text %> <div class="nav"> <!-- 正方形サムネイルで全ての画像を表示。 --> <% if @pages[i].img[:square].size != 0 %> <%= "<div class='info'>" %> <%= "<h2>全ての画像</h2>" %> <% @pages[i].img[:square].size.times{|j| %> <%= @pages[i].img[:square][j] %> <% } %> <%= "</div>" %> <% end %> <!-- パーマリンクとタグ --> <div class="info"> Permalink:<%= @pages[i].permalink.to_s %> <br> タグ:<%= @pages[i].tags %> </div> </div> </div> <% } %> <!-- Topページの記事の繰り返し。 ここまで --> <div class="nav"> <p class="center">Prev - <%= @pages[ENTRY_MAX-1].next_entry %></p> </div> </div> <div id="footer"> <p>このページ内で掲載している画像、文章等の転載・転用は自由に行ってください。</p> <address> author : G_A_S_O<br> address : gaso@psyche.gaso.jp<br> Copyright 2003-<%= Time.now.year.to_s %> G_A_S_O All rights reserved. </address> <p class="center"> Generated by <%=SCRIPT_NAME %> version <%=SCRIPT_VERSION %><br> Powerd by Ruby version <%=RUBY_VERSION %><br> Output Date <%=Time.now.to_s %> </p> </div> </body> </html>
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script> <script type="text/javascript" src="./files/script.js"></script> <link rel="stylesheet" type="text/css" href="./files/default.css"> <style type="text/css"> </style> <title><%=CONFIG::TITLE %></title> </head> <body> <div id="header"> <h1><%= "<a href='#{CONFIG::TOP_FILE_NAME}'>#{CONFIG::TITLE}</a>" %></h1> <p><%=CONFIG::DESCRIPTION %></p> </div> <div class="nav"> <p class="center"><%= @prev_entry %> - <%= @next_entry %></p> </div> <div id="menu"> <!-- 広告 ここから --> <div class="nav"> <h2>欲しい技術書</h2> <iframe src="http://rcm-jp.amazon.co.jp/e/cm?t=gaso00-22&o=9&p=12&l=st1&mode=books-jp&search=FreeBSD%20Linux&fc1=999999<1=_blank&lc1=585CA8&bg1=000000&f=ifr" marginwidth="0" marginheight="0" width="300" height="250" border="0" frameborder="0" style="border:none;" scrolling="no"></iframe> </div> <div class="nav"> <h2>気になるフィギュア</h2> <iframe src="http://rcm-jp.amazon.co.jp/e/cm?t=gaso00-22&o=9&p=12&l=st1&mode=toys-jp&search=%E3%83%95%E3%82%A3%E3%82%AE%E3%83%A5%E3%82%A2%20%E3%82%B9%E3%82%B1%E3%83%BC%E3%83%AB%20PVC&fc1=999999<1=_blank&lc1=585CA8&bg1=000000&f=ifr" marginwidth="0" marginheight="0" width="300" height="250" border="0" frameborder="0" style="border:none;" scrolling="no"></iframe> </div> <div class="nav"> <h2>買いたいカメラ機材</h2> <iframe src="http://rcm-jp.amazon.co.jp/e/cm?t=gaso00-22&o=9&p=12&l=bn1&mode=electronics-jp&browse=16462091&fc1=999999<1=_blank&lc1=585CA8&bg1=000000&f=ifr" marginwidth="0" marginheight="0" width="300" height="250" border="0" frameborder="0" style="border:none;" scrolling="no"></iframe> </div> <!-- 広告 ここまで --> </div> <div id="section"> <div class="article"> <div class="date"><%= "#{@date[0,4]}年#{@date[4,2]}月#{@date[6,2]}日" %></div> <h2><%= @title %></h2> <%= @text %> <div class="nav"> <!-- 正方形サムネイルで全ての画像を表示。 --> <% if @img[:square].size != 0 %> <%= "<div class='info'>" %> <%= "<h2>全ての画像</h2>" %> <% @img[:square].size.times{|j| %> <%= @img[:square][j] %> <% } %> <%= "</div>" %> <% end %> <!-- パーマリンクとタグ --> <div class="info"> Permalink:<%= @permalink.to_s %> <br> タグ:<%= @tags %> </div> </div> </div> </div> <div class="nav"> <p class="center"><%= @prev_entry %> - <%= @next_entry %></p> </div> <div id="footer"> <p>このページ内で掲載している画像、文章等の転載・転用は自由に行ってください。</p> <address> author : G_A_S_O<br> address : gaso@psyche.gaso.jp<br> Copyright 2003-<%= Time.now.year.to_s %> G_A_S_O All rights reserved. </address> <p class="center"> Generated by <%=SCRIPT_NAME %> version <%=SCRIPT_VERSION %><br> Powerd by Ruby version <%=RUBY_VERSION %><br> Output Date <%=Time.now.to_s %> </p> </div> </body> </html>
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script> <script type="text/javascript" src="./files/script.js"></script> <link rel="stylesheet" type="text/css" href="./files/default.css"> <style type="text/css"> div#section{margin: auto;} </style> <title><%=CONFIG::TITLE %></title> </head> <body> <div id="header"> <h1><%= "<a href='#{CONFIG::TOP_FILE_NAME}'>#{CONFIG::TITLE}</a>" %></h1> <p><%=CONFIG::DESCRIPTION %></p> </div> <div id="section"> <div class="nav"> <h2>日記一覧</h2> <p>総数:<%= @num %></p> <%= @entry_list %> </div> </div> <div id="footer"> <p>このページ内で掲載している画像、文章等の転載・転用は自由に行ってください。</p> <address> author : G_A_S_O<br> address : gaso@psyche.gaso.jp<br> Copyright 2003-<%= Time.now.year.to_s %> G_A_S_O All rights reserved. </address> <p class="center"> Generated by <%=SCRIPT_NAME %> version <%=SCRIPT_VERSION %><br> Powerd by Ruby version <%=RUBY_VERSION %><br> Output Date <%=Time.now.to_s %> </p> </div> </body> </html>
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script> <script type="text/javascript" src="./files/script.js"></script> <link rel="stylesheet" type="text/css" href="./files/default.css"> <style type="text/css"> div#section{margin: auto;} </style> <title><%=CONFIG::TITLE %></title> </head> <body> <div id="header"> <h1><%= "<a href='#{CONFIG::TOP_FILE_NAME}'>#{CONFIG::TITLE}</a>" %></h1> <p><%=CONFIG::DESCRIPTION %></p> </div> <div id="section"> <div class="nav"> <h2><%= @tag_name %></h2> <%= @tag_list %> </div> </div> <div id="footer"> <p>このページ内で掲載している画像、文章等の転載・転用は自由に行ってください。</p> <address> author : G_A_S_O<br> address : gaso@psyche.gaso.jp<br> Copyright 2003-<%= Time.now.year.to_s %> G_A_S_O All rights reserved. </address> <p class="center"> Generated by <%=SCRIPT_NAME %> version <%=SCRIPT_VERSION %><br> Powerd by Ruby version <%=RUBY_VERSION %><br> Output Date <%=Time.now.to_s %> </p> </div> </body> </html>
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script> <style type="text/css"> input.replace{width: 800px;} dt{color: red; text-decoration: underline;} </style> <title>日記編集</title> </head> <body> <h2>リンク</h2> <a href="#1">新規日記作成</a> <a href="#2">日記をHTMLに変換</a> <a href="#3">日記一覧</a> <a href="#4">置換文字列登録</a> <a href="#5">置換文字列一覧</a> <a href="./doc/">RDoc</a> <a href="./html/">表示確認</a> <h2 id="1">新規日記作成</h2> <%=@create_entry%> <h2 id="2">日記をHTMLに変換</h2> <%=@convert_html%> <h2 id="3">日記一覧</h2> <%=@entry_list%> <h2 id="4">置換文字列登録</h2> <%=@create_replace%> <h2 id="5">置換文字列一覧</h2> <%=@replace_list%> <h2>総日記数</h2> <%=@num_entries%> </body> </html>
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script> <style type="text/css"> input.text{width: 800px;} textarea{width: 800px; height: 400px;} </style> <title>test</title> </head> <body> <h2>日記編集 - <%=@edit_date%></h2> <%=@edit_form%> <h2>サムネイル</h2> <%=@thumb_icon%> <h2>画像アップロード</h2> <%=@upload_form%> <h2>処理日時</h2> <%=@update_time%> <br> <%=@debug%> </body> </html>
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.6.3/jquery.min.js"></script> <title>test</title> </head> <body> <h2>処理中</h2> <%=@status_info%> <%=@reload_script%> </body> </html>