require 'yaml' require 'tmpdir' module Oria class Server < EventMachine::Connection class << self @@timer = nil def debug? @@debug end def log(value, app_key = nil) logger.debug("#{"#{app_key}: " if app_key}#{value}") if debug? end def logger require 'logger' @@logger ||= Logger.new(log_file, 0, 100 * 1024 * 1024) end def start(server, port, app_key = nil, debug = false) at_exit do Oria::Server.stop end pid = Process.pid File.open(pid_file, 'w') do |file| file.puts pid end app_key ||= 'default' @@servers ||= if File.exists?(yaml_store) YAML.load_file(yaml_store) else {} end @@debug = !!debug @@servers[app_key] ||= {} EventMachine.run do EventMachine.start_server server, port, Oria::Server end end def stop if File.exists?(pid_file) if pid Process.kill(9, pid) end File.unlink(pid_file) end end def store_hash(app_key) @@timer.cancel if @@timer @@timer = EventMachine::Timer.new(10) do log "Writing changes to disk", app_key File.open(yaml_store, 'w') do |store| store.puts YAML.dump(@@servers) end @@timer = nil end end protected def log_file @@log_file ||= File.join(Dir.tmpdir, 'oria.log') end def pid @@pid ||= File.read(pid_file).to_i if File.exists?(pid_file) end def pid_file @@pid_file ||= File.join(Dir.tmpdir, 'oria.pid') end def yaml_store @@yaml_store ||= File.join(Dir.tmpdir, 'oria.yml') end end def post_init Oria::Server.log "Client connected" end def receive_data(data) Oria::Server.log "Responding to #{data}" data = data.split(' ') method = data.shift data = JSON.parse(data.join(' ')) @app_key = data.delete('app_key') case method when 'GET' response = hash[data['key']] when 'DELETE' if data.empty? hash.clear else response = hash.delete(data['key']) end when 'PUT' if data.key?('key') hash[data['key']] = data['value'] response = data['value'] elsif data.key?('value') response = random_key hash[response] = data['value'] end end if defined?(response) response = JSON.generate({:response => response}) Oria::Server.log "Sending response: #{response}" Oria::Server.store_hash(@app_key) send_data response end rescue JSON::ParserError send_data "Invalid request" end def unbind end private def hash @hash ||= @@servers[@app_key || 'default'] ||= {} end def random_key chars = ['a'..'z', 'A'..'Z', 0..9].map(&:to_a).flatten max = [hash.length, 2].max while hash.key?(key = (1..max).map{|i| chars[rand(chars.length)]}.join) key = (1..max).map{|i| chars[rand(chars.length)]}.join end key end end end