お勉強のための例題として、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> <%= 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