require 'celluloid/io' require 'json' module Hara class App include Celluloid::IO include Celluloid::Logger attr_reader :socket alias halt terminate finalizer :app_finalizer Actions = {} class << self def inherited klass ::Hara.const_set :Application, klass end 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 init end #return true or false def authentication true end def authentication_failed halt end #after_authentication only called when authentication succeeded def after_authentication end def before_action action, args end def after_action action, args end def after_process end def action_missing action, args info " #{socket.remote_ip} request action: #{action} args: #{args.inspect}, action not defined" end def command @command || {} end def on_close end private def initialize socket @socket = socket async.setup end def setup init info "#{socket.remote_ip} coming" unless authentication authentication_failed else after_authentication async.run end end def run while msg = @socket.read process msg if @closed terminate return end end rescue Reel::SocketError, EOFError #client disconnect info "#{socket.remote_ip} disconnect" begin @closed = true unless @closed on_close ensure terminate end end def process message @command = JSON.parse(message) action = @command["action"] args = @command["args"] info "#{socket.remote_ip} request action: #{action} args: #{args.inspect}" before_action action, *args call_action action, *args after_action action, *args rescue JSON::ParserError info "#{socket.remote_ip} message can't parse" terminate rescue StandardError => e info "#{socket.remote_ip} processing error:\n#{e.inspect}" terminate ensure after_process end def call_action action, *args if Actions.has_key? action Actions[action].bind(self).call *args else action_missing action, *args end end def app_finalizer on_close unless @closed ensure @socket.close if @socket end end end