Domanda

Per la href="https://meta.stackexchange.com/questions/57316/offer-an-xmpp-method-for-chat/63420#63420"> interfaccia sto parsing feed JSON dalla chat e generando rubino oggetti per ogni eventi di chat, come ad esempio i messaggi inviati, le modifiche inviati, gli utenti che accedono in o out, ecc ho anche generare eventi per "slash-comandi" inviati al server XMPP , come "/ help" o "/ auth" al fine di consentire all'utente XMPP di autenticarsi con il proprio account di chat Stack Overflow.

Ho creato queste classi in una sensazione di gerarchia che fa buon senso logico:

class SOChatEvent # base class
 |
 |--- class SOXMPPEvent # base for all events that are initiated via XMPP
 | |
 | |--- class SOXMPPMessage # messages sent to the XMPP bridge via XMPP
 | | |
 | | |--- class SOXMPPMessageToRoom # messages sent from an XMPP user to an XMPP MUC
 | | |
 | | |--- class SOXMPPUserCommand # class for "slash commands", that is, messages starting
 | | | |                          # with /, used for sending commands to the bridge
 | | | |
 | | | |--- class SOXMPPUserHelpCommand
 | | | |--- class SOXMPPUserLoginCommand
 | | | |--- class SOXMPPUserBroadcastCommand
 |
 |--- class SOChatRoomEvent # base class for all events that originate from an SO chat room
 | |
 | |--- class SOChatMessage # messages sent to an SO chat room via the SO chat system
 | | |
 | | |--- class SOChatMessageEdit # edits made to a prior SOChatMessage
 | |
 | |--- class SOChatUserEvent # events related to SO chat users
 | | |
 | | |--- class SOChatUserJoinRoom #Event for when a So user joins a room
 | | |--- class SOChatUserLeaveRoom #Event for when a So user leaves a room

 (etc)

È possibile visualizzare l'intera gerarchia e fonte in Trac via SVN .

La mia domanda è duplice: in primo luogo, qual è il modo migliore per creare un'istanza di questi eventi? Quello che sto facendo attualmente è l'analisi degli eventi JSON utilizzando un'istruzione switch gigante --well, è rubino quindi è una dichiarazione case - e, non è gigante ancora , ma sarà se continuo in questo modo:

rooms.each do |room|
  rid = "r"+"#{room.room_id}"
  if !data[rid].nil?
    @last_update = data[rid]['t'] if data[rid]['t']

    if data[rid]["e"]
      data[rid]["e"].each do |e|
        puts "DEBUG: found an event: #{e.inspect}"
        case e["event_type"]
          when 1
            event = SOChatMessage.new(room,e['user_name'])
            event.encoded_body = e['content']
            event.server = @server
            events.push event
          when 2
            event = SOChatMessageEdit.new(room,e['user_name'])
            event.encoded_body = e['content']
            event.server = @server
            events.push event
          when 3
            user = SOChatUser.new(e['user_id'], e['user_name'])
            event = SOChatUserJoinRoom.new(room,user)
            event.server = @server
            events.push event
          when 4
            user = SOChatUser.new(e['user_id'], e['user_name'])
            event = SOChatUserLeaveRoom.new(room,user)
            event.server = @server
            events.push event
        end
      end
    end
  end
end

Ma immagino che ci deve essere un modo migliore per gestire questo! Qualcosa di simile SOChatEvent.createFromJSON( json_data ) ... Ma, qual è il modo migliore per strutturare il mio codice in modo che gli oggetti della sottoclasse appropriate vengono creati in risposta ad un dato event_type?

In secondo luogo, non sto usando in realtà ancora sottoclassi formica di SOXMPPUserCommand. Adesso tutti i comandi sono solo esempi di SOXMPPUserCommand stesso, e che classe ha un metodo execute singola che interruttori basa regex del comando. Più o meno lo stesso problema - So che c'è un modo migliore, ho solo non sono sicuro di quello che il modo migliore è:

def handle_message(msg)
    puts "Room \"#{@name}\" handling message: #{msg}"
    puts "message: from #{msg.from} type #{msg.type} to #{msg.to}: #{msg.body.inspect}"

    event = nil

    if msg.body =~ /\/.*/
      #puts "DEBUG: Creating a new SOXMPPUserCommand"
      event = SOXMPPUserCommand.new(msg)
    else
      #puts "DEBUG: Creating a new SOXMPPMessageToRoom"
      event = SOXMPPMessageToRoom.new(msg)
    end

    if !event.nil?
      event.user = get_soxmpp_user_by_jid event.from
      handle_event event
    end
  end

e

class SOXMPPUserCommand < SOXMPPMessage
  def execute
    case @body
      when "/help"
        "Available topics are: help auth /fkey /cookie\n\nFor information on a topic, send: /help <topic>"
      when "/help auth"
        "To use this system, you must send your StackOverflow chat cookie and fkey to the system. To do this, use the /fkey and /cookie commands"
      when "/help /fkey"
        "Usage: /fkey <fkey>. Displays or sets your fkey, used for authentication. Send '/fkey' alone to display your current fkey, send '/fkey <something>' to set your fkey to <something>. You can obtain your fkey via the URL: javascript:alert(fkey().fkey)"
      when "/help /cookie"
        "Usage: /cookie <cookie>. Displays or sets your cookie, used for authentication. Send '/cookie' alone to display your current fkey, send '/cookie <something>' to set your cookie to <something>"
      when /\/fkey( .*)?/
        if $1.nil?
          "Your fkey is \"#{@user.fkey}\""
        else
          @user.fkey = $1.strip
          if @user.authenticated?
            "fkey set to \"#{@user.fkey}\". You are now logged in and can send messages to the chat"
          else
            "fkey set to \"#{@user.fkey}\". You must also send your cookie with /cookie before you can chat"
          end
        end
      when /\/cookie( .*)?/
        if $1.nil?
          "Your cookie is: \"#{@user.cookie}\""
        else
          if $1 == " chocolate chip"
            "You get a chocolate chip cookie!"
          else
            @user.cookie = $1.strip
            if @user.authenticated?
              "cookie set to \"#{@user.cookie}\". You are now logged in and can send messages to the chat"
            else
              "cookie set to \"#{@user.cookie}\". You must also send your fkey with /fkey before you can chat"
            end
          end
        end
      else
        "Unknown Command \"#{@body}\""
    end
  end
end

So che c'è un modo migliore per fare questo, basta non sicuro di quello specifico si tratta. Qualora la responsabilità di creare sottoclassi di SOXMPPUserCommand cadere su SOXMPPUserCommand stessa? Se tutte le sottoclassi registrarsi con il genitore? Ho bisogno di una nuova classe?

Qual è il modo migliore per gli oggetti istanziare di sottoclassi in una tale struttura gerarchica?

È stato utile?

Soluzione

Affrontare la vostra prima domanda. Ecco alcune idee come si potrebbe prendere in considerazione

In primo luogo, si struttura sottoclassi così tutti usano gli stessi parametri di iniziazione. Inoltre, si potrebbe mettere un po 'di altro codice di avvio del procedimento, cosi' (come ad esempio le encoded_body e server di accesso Ecco uno scheletro di quello che voglio dire:.

# SOChat Class skeleton structure
class SOChatSubClass  #< inherit from whatever parent class is appropriate
  attr_accessor :encoded_body, :server, :from, :to, :body

  def initialize(event, room, server)
    @encoded_body = event['content']
    @server = server
    SOChatEvent.events.push event

    #class specific code 
    xmpp_message = event['message']
    @from = xmpp_message.from
    @to = xmpp_message.to
    @body = xmpp_message.body
    #use super to call parent class initialization methods and to DRY up your code
  end
end 

Si noti che nel mio esempio avrai ancora codice duplicato nelle sottoclassi. Idealmente ci si tira fuori la duplicazione mettendolo nella classe padre appropriata.

In caso di problemi durante la creazione di un elenco comune di iniziazione parametri, quindi piuttosto che passare in un elenco di argomenti (evento, camera, server), modificare le classi di accettare una lista di argomenti come hash {: evento => evento, : camera => stanza,:. Server => Server, etc}

In ogni caso, una volta che hai una struttura parametro comune per l'inizializzazione delle classi, è possibile li inizializzare un po 'più dinamico, eliminando la necessità per l'istruzione case.

class SOChatEvent
     class << self; attr_accessor :events; end
     @events = []

      @@event_parser = {
                                0 => SOChatSubClass, #hypothetical example for testing
                                1 => SOChatMessage,
                                2 => SOChatMessageEdit,
                                #etc
                              }
    def self.create_from_evt( json_event_data, room=nil, server=nil)
      event_type = json_event_data["event_type"]
      event_class =  @@event_parser[event_type]
      #this creates the class defined by class returned in the @@event_parser hash
      event_obj = event_class.new(json_event_data, room, server)
    end

    #rest of class
end

@@event_parser contiene la mappatura tra il tipo di evento e la classe di attuare tale tipo di evento. Basta assegna la classe appropriata a una variabile e trattarlo proprio come la classe reale.

codice come il seguente creerebbe un oggetto della classe appropriata:

event_obj = SOChatEvent.create_from_evt( json_event_data,
                                        "some room", 
                                        "some server")

Nota:. Ci sono ulteriori ottimizzazioni che si potrebbero fare a quello che ho fornito per essere ancora più pulito e più conciso, ma spero che questo ti aiuta a superare la gobba della dichiarazione caso

Edit: Ho dimenticato di dire la SOChatEvent.events variabile di istanza di classe creato con questo: class << self; attr_accessor :events; end @events = []

Hai spingendo eventi per uno stack evento, ma non era chiaro dove si voleva quella pila di esistere e se era un elenco di eventi globale, o specifici per una particolare classe. Quello che ho fatto è globale, quindi sentitevi liberi di cambiarlo se si voleva lo stack evento vincolato a determinate classi o istanze.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top