#!/usr/bin/ruby require 'cgi' class CGI def get_param(name) val = self.params[name] return nil unless val return nil if val.empty? val.to_s end end def main cgi = CGI.new res = respond(cgi) print "Content-Type: #{res.content_type}\r\n" print "Content-Length: #{res.content_length}\r\n" print "Cache-Control: no-cache\r\n" print "Pragma: no-cache\r\n" print "\r\n" print res.body end def respond(req) q = req.query_string.to_s.strip if /\A\d+,\d+\z/ =~ q x, y = *q.split(',', 2).map {|n| [[0, n.to_i].max, 640].min } return edit_response(x, y) else cmd = req.get_param('cmd').to_s.downcase case cmd when 'add' x = req.get_param('x').to_i y = req.get_param('y').to_i text = req.get_param('text').to_s add_node load_graph(), Node.new(x, y, text) return view_response() when 'img' return image_response() else return view_response() end end end CGI_URL = File.basename($0) def view_response Response.new('text/html', < view

HTML end def image_response Response.new('image/png', File.read(IMAGE_FILE)) end def edit_response(x, y) Response.new('text/html; charset="euc-jp"', < edit

日本語通りません

HTML end class Response def initialize(type, body) @type = type @body = body end def content_type @type end def content_length @body.length end attr_reader :body end require 'GD' LOGFILE = 'log' IMAGE_FILE = 'graph.png' def load_graph File.open(LOGFILE) {|f| return Marshal.load(f) } rescue return Graph.new end def add_node(graph, node) lock { graph.add node File.open("tmp.#{$$}", 'w') {|f| f.write generate_image(graph).png(f) } File.rename "tmp.#{$$}", IMAGE_FILE File.open(LOGFILE, 'w') {|f| Marshal.dump(graph, f) } } end INIT_W = 640 INIT_H = 640 FGCOLOR = '#000000' BGCOLOR = '#ffffff' def generate_image(graph) image = GD::Image.new(640, 640) image.transparent(image.colorAllocate('#fffffe')) image.interlace = true fgcolor = image.colorAllocate(FGCOLOR) bgcolor = image.colorAllocate(BGCOLOR) graph.each_node do |n| image.filledRectangle n.x, n.y, n.x2, n.y2, bgcolor image.rectangle n.x, n.y, n.x2, n.y2, fgcolor n.each_text_line do |line, x, y| image.string GD::Font::MediumFont, x, y, line, fgcolor end end image end def lock File.open('lockfile') {|f| begin f.flock(File::LOCK_EX) yield ensure f.flock(File::LOCK_UN) end } end class Graph def initialize @nodes = [] end def add(node) @nodes.push node end def width @nodes.map {|n| n.x }.max end def height @nodes.map {|n| n.y }.max end def each_node(&block) @nodes.each(&block) end end class Node def initialize(x, y, text) @x = x @y = y @edges = [] @lines = text.map {|line| line.rstrip } @created_at = Time.now end attr_reader :x attr_reader :y attr_reader :created_at def each_text_line(&block) y = @y + GAP_H @lines.each do |line| yield line, @x + GAP_W, y y += CHAR_HEIGHT + 1 end end CHAR_WIDTH = 7 CHAR_HEIGHT = 13 GAP_W = 6 GAP_H = 3 def width @lines.map {|line| CHAR_WIDTH * line.size }.max + GAP_W * 2 end def height CHAR_HEIGHT * @lines.size + GAP_H * 2 end def x2 @x + width() end def y2 @y + height() end def each_edge(&block) @edges.each(&block) end end main