tutorial real rails cable active ruby-on-rails websocket ruby-on-rails-5 actioncable

ruby on rails - real - Conectando a ActionCable desde la aplicación iOS



ruby on rails real time updates (3)

// abrir la conexión de socket primero

var ws = new WebSocket("ws://localhost:3000/cable");

// suscribirse al canal
// ''i'' debería estar en json

var i = { ''command'': ''subscribe'', ''identifier'': {''channel'':''ProfileChannel'', ''Param_1'': ''Value_1'',...}}; ws.send(i);

// Después, recibirás datos dentro de la función ''onmessage''.

¡Aclamaciones!

He estado atrapado en esto todo el día. Tengo la aplicación de ejemplo ActionCable muy simple (la aplicación de chat) de David Heinemeier Hansson funcionando correctamente ( https://www.youtube.com/watch?v=n0WUjGkDFS0 ).

Estoy intentando conectar la conexión web con una aplicación de iPhone. Puedo recibir pings cuando me conecto a ws://localhost:3000/cable , pero no estoy seguro de cómo suscribirme a canales fuera de un contexto javascript.


En realidad, aquí está el fragmento de código que estoy usando para conectarme al cable de acción.

function WebSocketTest() { var ws = new WebSocket("ws://localhost:3000/cable"); ws.onopen = function(data) { var i = JSON.stringify({"command":"subscribe" , "identifier": JSON.stringify({"channel":"CHANNEL_NAME"})}); // send data request var j = JSON.stringify({"command":"message","identifier": JSON.stringify({"channel":"CHANNEL_NAME"}),"data": {"message":"Hello World","action": "METHOD_NAME_IN_CHANNEL","email": "[email protected]", "token" : "xxxxxxxxxxxxx", "id": {"id_message" : "something", "ddd" : "something"}}}) var response = ws.send(i); setTimeout(function() { var response1 = ws.send(j); }, 1000); }; ws.onmessage = function (evt) { var received_msg = evt.data; }; }


Oh hombre, también pasé por este problema después de leer esta pregunta.

Después de un tiempo, finalmente encontré esta página mágica de Github:

https://github.com/rails/rails/issues/22675

Entiendo que este parche rompería algunas pruebas. Eso no me sorprende. Pero creo que el problema original sigue siendo relevante y no debería cerrarse.

El siguiente JSON enviado al servidor debería tener éxito:

{"comando": "suscribirse", "identificador": {"canal": "ChangesChannel"}}

¡No es asi! En su lugar debes enviar esto:

{"comando": "suscribirse", "identificador": "{/" canal / ": /" ChangesChannel / "}"}

Finalmente obtuve la aplicación iOS para suscribirme al canal de la sala siguiendo la sugerencia del usuario de Github sobre el problema de Rails.

Mi configuración es la siguiente:

  • C objetivo
  • Usando el framework PocketSocket para hacer conexión de socket web
  • Rieles 5 RC1
  • Ruby 2.2.4p230

Supongo que sabes cómo usar Cocoapods para instalar PocketSocket.

Los códigos relevantes son los siguientes:

ViewController.h

#import <PocketSocket/PSWebSocket.h> @interface ViewController : UIViewController <PSWebSocketDelegate, UITableViewDelegate, UITableViewDataSource, UITextFieldDelegate> @property (nonatomic, strong) PSWebSocket *socket;

ViewController.m

- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. [self initViews]; [self initConstraints]; [self initSocket]; } -(void)initSocket { NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:@"ws://localhost:3000/cable"]]; self.socket = [PSWebSocket clientSocketWithRequest:request]; self.socket.delegate = self; [self.socket open]; } -(void)joinChannel:(NSString *)channelName { NSString *strChannel = @"{ /"channel/": /"RoomChannel/" }"; id data = @{ @"command": @"subscribe", @"identifier": strChannel }; NSData * jsonData = [NSJSONSerialization dataWithJSONObject:data options:0 error:nil]; NSString * myString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; NSLog(@"myString= %@", myString); [self.socket send:myString]; } #pragma mark - PSWebSocketDelegate Methods - -(void)webSocketDidOpen:(PSWebSocket *)webSocket { NSLog(@"The websocket handshake completed and is now open!"); [self joinChannel:@"RoomChannel"]; } -(void)webSocket:(PSWebSocket *)webSocket didReceiveMessage:(id)message { NSData *data = [message dataUsingEncoding:NSUTF8StringEncoding]; id json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil]; NSString *messageType = json[@"type"]; if(![messageType isEqualToString:@"ping"] && ![messageType isEqualToString:@"welcome"]) { NSLog(@"The websocket received a message: %@", json[@"message"]); [self.messages addObject:json[@"message"]]; [self.tableView reloadData]; } } -(void)webSocket:(PSWebSocket *)webSocket didFailWithError:(NSError *)error { NSLog(@"The websocket handshake/connection failed with an error: %@", error); } -(void)webSocket:(PSWebSocket *)webSocket didCloseWithCode:(NSInteger)code reason:(NSString *)reason wasClean:(BOOL)wasClean { NSLog(@"The websocket closed with code: %@, reason: %@, wasClean: %@", @(code), reason, (wasClean) ? @"YES": @"NO"); }

Nota IMPORTANTE:

También indagué un poco en el código fuente de la clase de suscripción:

def add(data) id_key = data[''identifier''] id_options = ActiveSupport::JSON.decode(id_key).with_indifferent_access subscription_klass = connection.server.channel_classes[id_options[:channel]] if subscription_klass subscriptions[id_key] ||= subscription_klass.new(connection, id_key, id_options) else logger.error "Subscription class not found (#{data.inspect})" end end

Observe la línea:

connection.server.channel_classes[id_options[:channel]]

Necesitamos usar el nombre de la clase para el canal.

El video de DHH en YouTube usa "room_channel" para el nombre de la sala, pero el archivo de clase para ese canal se llama "RoomChannel".

Necesitamos usar el nombre de la clase, no el nombre de la instancia del canal.

Enviando mensajes

En caso de que otros también quieran saber cómo enviar mensajes, aquí está mi código de iOS para enviar un mensaje al servidor:

-(void)sendMessage:(NSString *)message { NSString *strMessage = [[NSString alloc] initWithFormat:@"{ /"action/": /"speak/", /"message/": /"%@/" }", message]; NSString *strChannel = @"{ /"channel/": /"RoomChannel/" }"; id data = @{ @"command": @"message", @"identifier": strChannel, @"data": strMessage }; NSData * jsonData = [NSJSONSerialization dataWithJSONObject:data options:0 error:nil]; NSString * myString = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; NSLog(@"myString= %@", myString); [self.socket send:myString]; }

Esto supone que ha conectado su UITextField para manejar la pulsación de la tecla de retorno o algún botón "enviar" en algún lugar de su interfaz de usuario.

Toda esta aplicación de demostración fue un truco rápido, obviamente, si lo hiciera en una aplicación real, haría mi código más limpio, más reutilizable y lo abstraería en una clase.

Conexión al servidor Rails desde un dispositivo iPhone real:

Para que la aplicación del iPhone hable con el servidor Rails en un dispositivo real, no con el simulador de iPhone.

Haz lo siguiente:

  1. Verifique la dirección TCP / IP de su computadora. En mi iMac, por ejemplo, podría ser 10.1.1.10 en algunos días (puede cambiar automáticamente en el futuro si se usa DHCP).
  2. Edite el archivo config > environment > development.rb su Rail y colóquelo en la siguiente línea en algún lugar como antes de la palabra clave end :

    Rails.application.config.action_cable.allowed_request_origins = [''http://10.1.1.10:3000'']

  3. Inicie su servidor Rails usando el siguiente comando:

    rails server -b 0.0.0.0

  4. Construye y ejecuta tu aplicación de iPhone en el dispositivo iPhone. Debes poder conectarte y enviar mensajes ahora: D

Obtuve estas soluciones de los siguientes enlaces:

Origen de solicitud no permitido: http: // localhost: 3001 cuando se usa Rails5 y ActionCable

Servidor Rails 4.2; IP privada y pública no funciona

Espero que ayude a otros en el futuro.