SimpleなLINK集の作成と、WEBrick & ERB のお勉強

お勉強のための例題として、LINK集を作ってみました。
WEBrick を使って、ローカルサーバとして動作するものです。
複数台のPCや、複数種のブラウザでの共有ができるので便利かもしれません。
(2台のPCでfirefoxのブックマークを共有していてデータが消えたショックが開発動機の一つなのは秘密。)

まだ改良したいところはあるけれど、とりあえず動くんで公開してみます。
Ruby1.8.2の標準添付ライブラリだけで動くはずのつもり。

スラッシュドット ジャパン経由で知ったんですが、10分で作るRailsアプリ for Windowsというものがあるそうです。
ちょっとかぶっちゃってますが、気にせずに公開しちゃいます。
作ってるときには知らなかったので、インスパイアされたわけではありません。(笑)

トップページへ戻る

リスト

#!/usr/bin/env ruby
=begin

= SimpleなLINK集(WEBrick,ERB のお勉強)

動かしておいて、ブラウザで http://localhost:8001/urllist にアクセス
使い方は見たまんま(笑)
一応、データは保存されます

=end

require 'cgi'
require 'erb'
require 'webrick'
include WEBrick

require 'pstore'

#require 'urllistdata'
class CCData
  def initialize(_url="",_name="",_c_cnt=0,_date="",_prev=0,_next=0)
    @prev=_prev
    @next=_next
    @url=_url
    @date=_date
    @c_cnt=_c_cnt
    @name=_name
  end
  attr_accessor :prev,:next,:url,:date,:c_cnt,:name
end

class ClickList
  HEAD=0
  TAIL=-1
  def initialize
    @idxkey = 0
    @culist = Hash.new
    @culist[HEAD] = CCData.new
    @culist[HEAD].prev = HEAD
    @culist[HEAD].next = TAIL
    @culist[TAIL] = CCData.new
    @culist[TAIL].prev = HEAD
    @culist[TAIL].next = TAIL
  end
  def addtail(ccdata)
    @idxkey += 1
    _prev = @culist[TAIL].prev
    @culist[TAIL].prev = @idxkey
    @culist[_prev].next = @idxkey
    @culist[@idxkey] = ccdata
    @culist[@idxkey].prev = _prev
    @culist[@idxkey].next = TAIL
  end
  def each_seq
    return nil if @culist[HEAD].next == TAIL
    idx = @culist[HEAD].next
    until idx == TAIL
      _next = @culist[idx].next
      yield(idx, @culist[idx])
      idx = _next
    end
  end
  def head
    head = @culist[HEAD].next
    return nil if head == TAIL
    return @culist[head]
  end
  def tail
    tail = @culist[TAIL].prev
    return nil if tail == HEAD
    return @culist[tail]
  end
  def delete(idx)
    return nil if not @culist.has_key?(idx)
    #削除箇所を塞ぐ
    _prev = @culist[idx].prev
    _next = @culist[idx].next
    @culist[_prev].next = _next
    @culist[_next].prev = _prev
    #削除
    @culist.delete(idx)
  end
  def move(idx,to)#toの次に移動
    return nil if not @culist.has_key?(idx)
    return nil if not @culist.has_key?(to)
    #移動元を繋ぐ
    _prev = @culist[idx].prev
    _next = @culist[idx].next
    @culist[_prev].next = _next
    @culist[_next].prev = _prev
    #移動先を切る
    _next = @culist[to].next
    _prev = @culist[_next].prev
    @culist[to].next = idx
    @culist[_next].prev = idx
    #移動
    @culist[idx].prev = _prev
    @culist[idx].next = _next
  end
  def to_head(idx)
    move(idx,HEAD)
  end
  def to_tail(idx)
    move(idx,@culist[TAIL].prev)
  end
  def up(idx)
    return nil if not @culist.has_key?(idx)
    _prev = @culist[idx].prev
    return if _prev == HEAD
    move(_prev,idx)
  end
  def down(idx)
    return nil if not @culist.has_key?(idx)
    _next = @culist[idx].next
    return if _next == TAIL
    move(idx,_next)
  end
  def [](idx)
    return @culist[idx]
  end
  #attr_accessor :culist
end

DATAFILE = "clicklinklist.dat"
DATAROOT = "list"
def data_load
  store = PStore.new(DATAFILE)
  store.transaction do
    if store.root?(DATAROOT)
      $urllist = store[DATAROOT]
    else
      $urllist = ClickList.new
    end
  end
end
data_load

def data_save
  store = PStore.new(DATAFILE)
  store.transaction do
    store[DATAROOT] = $urllist
  end
end


URLLIST_RHTML = <<'URLLISTSRC'
<html lang="ja">
<head>
<meta http-equiv="Content-type" content="text/html; charset=Shift_JIS" />
<meta http-equiv="Refresh" content="300">
<meta http-equiv="Pragma" content="no-cache">
<title>URL Click List</title>
</head>

<body>

  <form method="POST" action="/urladd">
  <table border=1>
  <tr>
    <td rowspan=2>
      <input type="submit" value="新規登録">
    </td>
    <td>Name</td>
    <td><input type=text size=80 name="name"></td>
  </tr>
  <tr>
    <td>URL</td>
    <td><input type=text size=80 name="url"></td>
  </tr>
  </table>
  </form>


  <ul>
    <% $urllist.each_seq { |key, elm| %>
      <li>
        <a style=text-decoration:none href="urledit?idx=<%= key %>&act=5">△</a>
        <a style=text-decoration:none href="urledit?idx=<%= key %>&act=6">▽</a>
        <a style=text-decoration:none href="urleditform?idx=<%= key %>">◇</a>
        <a style=text-decoration:none href="jumptourl?idx=<%= key %>" target=_blank>&nbsp;<%= CGI::escapeHTML(elm.name) %></a>
      </li>
    <% } %>
  </ul>
</body>

</html>
URLLISTSRC

URLEDITFORM_RHTML = <<'URLEDITFORMSRC'
<html lang="ja">
<head>
<meta http-equiv="Content-type" content="text/html; charset=Shift_JIS" />
<title>URL edit form</title>
</head>

<body>
  <h1>登録URLの編集</h1>
  <form method="POST" action="urledit">
  <table border=1>
  <tr>
    <td>Name</td>
    <td><input type=text size=80 name="name" value="<%= name %>"></td>
  </tr>
  <tr>
    <td>URL</td>
    <td><input type=text size=80 name="url" value="<%= url %>"></td>
  </tr>
  <tr>
    <td></td>
    <td>
      <input type=submit value="処理実行">
      <input type=radio name="act" value=1 checked>更新
      <input type=radio name="act" value=3>上へ
      <input type=radio name="act" value=4>下へ
      <input type=radio name="act" value=2>削除
    </td>
  </tr>
  </table>
  <input type=hidden name="idx" value="<%= idx %>">
  </form>
</body>

</html>
URLEDITFORMSRC


s = HTTPServer.new(
  :Port            => 8001, # portの指定
  :DocumentRoot    => Dir::pwd + '/htdocs'
#,  :Logger => WEBrick::Log::new($stderr, WEBrick::Log::DEBUG)
)

class URLListServlet < HTTPServlet::AbstractServlet
  def initialize(server, *options)
    super
    #rhtml = File::readlines( "urllist.rhtml" ).join # rhtmlファイルを読む
    #@body = ERB.new( rhtml )
    @body = ERB.new( URLLIST_RHTML )
  end

  def do_GET(req, res)
    res.body = @body.result( binding )
    res['Content-Type'] = "text/html"
  end
end
s.mount("/urllist", URLListServlet)

#Jump to URL
s.mount_proc("/jumptourl"){|req,res|
  idx = req.query['idx'].to_i
  dat = $urllist[idx]
  dat.c_cnt += 1
  if dat.c_cnt > 1
    $urllist.to_head(idx)
    dat.c_cnt = 0
    data_save
  end
  res.set_redirect(HTTPStatus::MovedPermanently,dat.url)
}

#URL新規登録処理
s.mount_proc("/urladd"){|req,res|
  url = req.query['url']
  name = req.query['name']
  name = url if "" == name
  if "" != url
    $urllist.addtail(CCData.new(url,name))
    data_save
  end
  res.set_redirect(HTTPStatus::MovedPermanently,"urllist")
}

class URLEditFormServlet < HTTPServlet::AbstractServlet
  def initialize(server, *options)
    super
    @body = ERB.new( URLEDITFORM_RHTML )
  end

  def do_GET(req, res)
    idx = req.query['idx'].to_i
    dat = $urllist[idx]
    url = dat.url
    name = dat.name
    res.body = @body.result( binding )
    res['Content-Type'] = "text/html"
  end
end
s.mount("/urleditform", URLEditFormServlet)

#登録URL編集処理
s.mount_proc("/urledit"){|req,res|
  url = req.query['url']
  name = req.query['name']
  act = req.query['act'].to_i
  idx = req.query['idx'].to_i
  dat = $urllist[idx]
  case act
  when 1 #更新
    if "" != url
      dat.url = url
      name = url if "" == name
      dat.name = name
    end
  when 2 #削除
    $urllist.delete(idx)
  when 3 #上へ
    $urllist.to_head(idx)
  when 4 #下へ
    $urllist.to_tail(idx)
  when 5 #一つ上へ
    $urllist.up(idx)
  when 6 #一つ下へ
    $urllist.down(idx)
  end
  data_save if 1 <= act and act <= 4
  res.set_redirect(HTTPStatus::MovedPermanently,"urllist")
}

trap("INT"){ s.shutdown }
s.start

トップページへ戻る