require 'celluloid/io' require 'json' module Hara class App include Celluloid::IO include Celluloid::Logger attr_reader :socket alias halt terminate Actions = {} def self.inherited klass ::Hara.const_set :Application, klass end def initialize socket @socket = socket async.setup end def after_connect end #return true or false def authentication true end def authentication_failed halt end def after_authentication end class << self def define_action action, &block action = action.to_s warn "Action #{action} duplication defined" if Actions.has_key? action Hara::Application.send :define_method, action, &block method = Hara::Application.send :instance_method, action Hara::Application.send :remove_method, action Actions[action] = method end end def process message @command = JSON.parse(message) action = @command["action"] args = @command["args"] info "#{Time.now} : #{@socket.remote_ip} request action: #{action}" before_process action, args call_action action, *args rescue JSON::ParserError warn "#{Time.now} : #{@socket.remote_ip} message can't parse" terminate rescue StandardError => e warn "#{Time.now} : #{@socket.remote_ip} want action: #{action} args: #{args} but process error:\n#{e.inspect}" terminate ensure after_process action, args end def before_process action, args end def after_process action, args end def action_missing action, args warn "#{Time.now} : #{socket.remote_ip} request action: #{action} args: #{args}, action not defined" end def command @command || {} end def on_close end def run while msg = @socket.read process msg if @closed terminate return end end rescue Reel::SocketError, EOFError #client disconnect info "#{Time.now} : #{@socket.remote_ip} disconnect, user: #{@user ? @user.id : "none"}" begin @closed = true unless @closed on_close ensure terminate end end def finalizer on_close unless @closed ensure @socket.close if @socket end private def setup info "#{Time.now} : #{socket.remote_ip} coming" after_connect unless authentication authentication_failed else after_authentication async.run end end def call_action action, *args if Actions.has_key? action Actions[action].bind(self).call *args else action_missing action, args end end end end