#!/usr/bin/env ruby begin require "rast_xmlrpc_client" rescue LoadError require "rast" end require "cgi" require "erb" require "yaml" class RastSearch MAX_PAGES = 20 SORT_OPTIONS = [ ["score", "スコア順"], ["last_modified", "日付順"], ] SORT_PROPERTIES = ["last_modified"] ORDER_OPTIONS = [ ["asc", "昇順"], ["desc", "降順"], ] NUM_OPTIONS = [10, 20, 30, 50, 100] DISPLAY_METHOD_OPTIONS = [ ["verbose", "詳細"], ["simple", "簡易"], ] def initialize(cgi) @cgi = cgi @script_name = @cgi.script_name || "" parse_args format_form read_conf if @mode == "error" || @mode == "help" return end search end def read_conf begin conf_file = File.join(File.dirname(__FILE__), "rast-search.conf") conf = YAML::load_file(conf_file) @template_path = conf[:template_path] if @template_path.nil? if FileTest::symlink?(__FILE__) @template_path = File::dirname(File::readlink(__FILE__)) else @template_path = File::dirname(__FILE__) end end @db_list = conf[:db_list] @db_list.each do |db_list_item| rule = db_list_item[:replace_rule] if rule rule.each do |rule_item| rule_item[:from] = Regexp.new(rule_item[:from]) end end end rescue @msg = "Error in loading #{conf_file}." @mode = "error" end end def eval_rhtml rhtml = ["header", @mode, "footer"].collect { |file| File.read(File.join(@template_path, "#{file}.rhtml")) }.join ERB.new(rhtml).result(binding) end private def db_name_list return @db_list.collect do |db_list_item| db_list_item[:name] end end def parse_args @query = @cgi["query"].strip if !@cgi["help"].empty? @mode = "help" else @mode = "result" if @query.empty? @mode = "error" @msg = "検索条件を入力して、「Rast 検索」ボタンを押してください" end end if !@cgi["ie"].empty? begin require "iconv" @query = Iconv.conv("utf-8", @cgi["ie"], @query) rescue @query = NKF.nkf("-w", @query) end end @start = @cgi["start"].to_i @num = @cgi["num"].to_i if @num < 1 @num = 10 elsif @num > 100 @num = 100 end @sort = @cgi["sort"].empty? ? "score" : @cgi["sort"] @order = @cgi["order"].empty? ? "desc" : @cgi["order"] if @cgi["display_method"].empty? @display_method = "verbose" else @display_method = @cgi["display_method"] end end def search dbs = [] begin dbs = @db_list.collect do |db_list_item| Rast::DB.open(db_list_item[:path], Rast::DB::RDONLY) end @db = Rast::Merger.open(dbs) options = create_search_options t = Time.now @result = @db.search(@query, options) if @result.hit_count == 0 @mode = "error" @msg = "#{_(@query)} に該当するページが見つかりませんでした。" end @secs = Time.now - t @hit_count = @result.hit_count @items = @result.items.collect do |i| format_result_item(i) end rescue => e @mode = "error" @msg = "エラー: #{_(e.to_s)}" ensure @db.close if @db dbs.each do |db| db.close end end end def format_result_item(item) db_list_item = @db_list[item.db_index] uri, title, last_modified = *item.properties rules = db_list_item[:replace_rule] if rules rules.each do |rule| uri = uri.gsub(rule[:from], rule[:to]) end end title = uri if title.empty? summary = _(item.summary) || "" for term in @result.terms summary.gsub!(Regexp.new(Regexp.quote(term.term), true, "u"), "\\&") end return { :uri => uri, :title => title, :last_modified => last_modified, :summary => summary, :score => item.score } end def format_links if @result.hit_count <= 0 return "" end page_count = (@result.hit_count - 1) / @num + 1 current_page = @start / @num + 1 first_page = current_page - (MAX_PAGES / 2 - 1) if first_page < 1 first_page = 1 end last_page = first_page + MAX_PAGES - 1 if last_page > page_count last_page = page_count end buf = %Q|
\n") return buf end def format_link(label, start, num) return format('%s ', _(@cgi.script_name ? @cgi.script_name : ""), CGI::escape(@query), start, num, _(@sort), _(@order), _(label)) end def create_search_options options = { "properties" => [ "uri", "title", "last_modified" ], "need_summary" => true, "summary_nchars" => 150, "start_no" => @start, "num_items" => @num } if SORT_PROPERTIES.include?(@sort) options["sort_method"] = Rast::SORT_METHOD_PROPERTY options["sort_property"] = @sort end if @order == "asc" options["sort_order"] = Rast::SORT_ORDER_ASCENDING else options["sort_order"] = Rast::SORT_ORDER_DESCENDING end return options end def format_options(options, value) return options.collect { |val, label| if val == value "" else "" end }.join("\n") end def format_form @num_options = NUM_OPTIONS.collect { |n| if n == @num "" else "" end }.join("\n") @sort_options = format_options(SORT_OPTIONS, @sort) @order_options = format_options(ORDER_OPTIONS, @order) @display_method_options = format_options(DISPLAY_METHOD_OPTIONS, @display_method) end def _(str) str ? CGI::escapeHTML(str) : "??" end end begin cgi = CGI::new rast_search = RastSearch::new(cgi) head = { "type" => "text/html", "Vary" => "User-Agent" } body = rast_search.eval_rhtml head["charset"] = "utf-8" head["Content-Length"] = body.size.to_s head["Pragma"] = "no-cache" head["Cache-Control"] = "no-cache" print cgi.header(head) print body rescue Exception => err if cgi print cgi.header({"type" => "text/html"}) else print "Content-Type: text/html\n\n" end puts ""
puts CGI.escapeHTML("#{err} (#{err.class})\n")
puts CGI.escapeHTML(err.backtrace.join("\n"))
puts ""
end