Skip to content

Instantly share code, notes, and snippets.

@seki

seki/ hitori-koto.rb Secret

Created Jan 2, 2010
Embed
What would you like to do?
require 'div'
require 'tofu/tofu'
require 'tofu/tofulet'
require 'monitor'
require 'rbtree'
require 'enumerator'
require 'rqr'
require 'time'
class Store
include MonitorMixin
def initialize
super()
@tree = RBTree.new
@enum = @tree.to_enum(:reverse_each)
end
attr_reader :tree
def qr_at(time)
_, qr = @tree[time]
qr || NotFoundQR
end
def each_slice(n, &blk)
synchronize do
@enum.each_slice(n, &blk)
nil
end
end
def import_string(str)
NKF.nkf('-sdXm0', str)
end
def self.qr_code(text)
tmp = Tempfile.new('hitori_koto')
RQR::QRCode.create do |qr|
qr.save(text, tmp.path, :png)
end
tmp.open
tmp.read
ensure
tmp.close(true) if tmp
end
def qr_code(text)
self.class.qr_code(text)
end
def to_qr(text)
qr_code(text)
rescue RQR::EncodeException
EncodeErrorQR
end
def add(str, context=nil)
str = import_string(str)
synchronize do
key = Time.now
latest, _ = @tree.last
while latest == key
key = Time.now
end
@tree[key] = [str, to_qr(str)]
key
end
end
NotFoundPNG = qr_code('not found')
EncodeErrorPNG = qr_code('QR encode error')
end
class HitoriKotoSession < Div::TofuSession
def initialize(bartender, hint=nil)
super
@content = Store.new
@base = BaseDiv.new(self)
@age = nil
end
attr_reader :content
def do_qr_code(context)
time = qr_path_to_time(context.req_path_info)
return false unless time
qr = @content.qr_at(time)
context.res_header('expires', expires.httpdate)
context.res_header('content-type', 'image/png; name="qrcode.png"')
context.res_body(qr)
return true
end
def qr_path(time)
"qr" + h(time_to_key(time)) + ".png"
end
def qr_path_to_time(path)
if /qr(.*)\.png$/ =~ path
key_to_time($1)
else
nil
end
end
def time_to_key(time)
(time.to_i * 1000000 + time.usec).to_s(36)
end
def key_to_time(str)
d = str.to_i(36)
i, u = d.divmod(1000000)
Time.at(i, u)
rescue
Time.now
end
def do_GET(context)
update_div(context)
return if do_qr_code(context)
context.res_header('content-type', 'text/html; charset=Shift_JIS')
context.res_body(@base.to_html(context))
end
end
class BaseDiv < Div::Div
set_erb('base.erb')
def initialize(session)
super(session)
@enter = EnterDiv.new(session)
@list = ListDiv.new(session)
end
end
class EnterDiv < Div::Div
set_erb('enter.erb')
def do_enter(context, params)
str ,= params['str']
str = '(nil)' if (str.nil? || str.empty?)
@session.content.add(str, context)
end
end
class ListDiv < Div::Div
set_erb('list.erb')
def initialize(session)
super(session)
@content = session.content
end
def group_header(time)
time.strftime("%H:%M")
end
end
unless $DEBUG
exit!(0) if fork
Process.setsid
exit!(0) if fork
end
uri = ARGV.shift || 'druby://localhost:54322'
tofu = Tofu::Bartender.new(HitoriKotoSession, 'hitori_' + uri.split(':').last)
DRb.start_service(uri, WEBrick::CGITofulet.new(tofu))
unless $DEBUG
STDIN.reopen('/dev/null')
STDOUT.reopen('/dev/null', 'w')
STDERR.reopen('/dev/null', 'w')
end
DRb.thread.join
<html>
<head>
<title>hitori-koto</title>
<style type="text/css" media="screen">
body {
font-family: Helvetica;
background: #FFFFFF;
color: #000000;
}
.EnterDiv * input {
box-sizing: border-box;
width: 100%;
padding: 16px 6px 6px 6px;
font-size: 16px;
font-weight: normal;
background: #6d84a2;
}
.ListDiv > ul {
margin: 0;
padding: 0;
}
.ListDiv > ul > li {
border-bottom: 1px solid #E0E0E0;
padding: 8px 0 8px 10px;
font-size: 14px;
font-weight: bold;
list-style: none:
}
.ListDiv > ul > li.group {
top: -1px;
margin-bottom: -2px;
border-top: 1px solid #7d7d7d;
border-bottom: 1px solid #999999;
padding: 1px 5px;
font-size: 13px;
font-weight: bold;
text-shadow: rgba(0, 0, 0, 0.4) 0 1px 0;
color: #222222;
}
</style>
</head>
<body>
<%= @enter.to_div(context)%>
<%= @list.to_div(context)%>
</body>
</html>
<%=form('enter', {}, context)%>
<input class='enter' type='text' size='40' name='str' value=''/>
</form>
#!/usr/local/bin/ruby
require 'drb/drb'
DRb.start_service
ro = DRbObject.new_with_uri('druby://localhost:54322')
ro.start(ENV.to_hash, $stdin, $stdout)
<ul><%
last_group = nil
@content.each_slice(30) do |ary|
ary.each do |time, v|
str, qr = v
group = group_header(time)
if group != last_group
%><li class="group"><%=h group %></li><%
last_group = group
end
png = "qr" + h(@session.time_to_key(time)) + ".png"
%><li><%=h str %><br /><img src="<%= png%>" /></li><%
end
break
end
%></ul>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment