Pregunta

I'm using Pubnub to publish live messages from a Server to a client (browser page). When using Pubnub, one must abide by their message size constraints, sometimes resulting in the need to chunk the message, send it in pieces, and reconstruct on the client side. Following Pubnub's advice, one can ensure delivery of each chunk of a message if the Pubnub.publish() function is not called too quickly (i.e. if the message pieces are simply being pumped through a for loop).

The Pubnub Ruby API specifies 3 required arguments in a Pubnub.publish(), a channel, a message, and a callback function. The callback function shown below is from Pubnub's Ruby examples:

    @my_callback = lambda { |message| puts(message) }
    pn.publish(:channel => :hello_world,
      :message => "hi",
      :callback => @my_callback)

The message in the callback (not the "hi" message in the publish), contains the status information of the publish() call with values like "sent" and "message to large", both of which would be accompanied by a unique identifier.

So somewhere under the Pubnub hood, this callback is getting a .call() - I'm wondering if there is a way for me to get inbetween this process. More detailed, say I have a message that needs to be broken up into three chunks, I would like for chunk 0 to be sent, and upon receipt of a "sent" status in the callback, I would like to then send chunk 1, etc...

I'm not very familiar with lambda functions and their scopes, and this was my first attempt:

    @my_callback = lambda { |message| 
                            puts(message) 
                            Rails.logger.debug("Setting pubnub_do_send to true from callback")
                            pubnub_do_send = true
                          }

    pubnub_do_send = true

    while !pubnub_message.nil?
      if pubnub_do_send 

        #Send pubnub message
        #Cut off first chunk of message (this is why I'm testing for nil)
        #Set pubnub_do_send to false

        Rails.logger.debug("Message #{message_id} chunk #{chunk_id} sent")
        pubnub_do_send = false
      end
    end

This resulted in an utter failure - getting the server completely locked into an infinite while loop because (if I had to guess) pubnub_do_send was never set to true again. Looking at the debug log, I see the first message print ("Message 1 chunk 0 sent") but never the output from the callback function. (Probably because of the infinite while loop it's found itself in)

There must be a clean way to do this, and I'm ok with refactoring code to some extent, chunking up the messages and storing into an array first and then simply looping through the array to send, but I feel like the solution can't be far off, I'm just not too handy with lambda functions and callbacks.

I feel like the solution should look like:

    @my_callback = lambda { |message| 
                            puts(message) 
                            send_pubnub_message(message_id, chunk_id, chunk)
                          }

    def send_pubnub_message(message_id, chunk_id, chunk)
      #Send pubnub message
      #Send message_id, next chunk_id, and next chunk to my_callback
    end

But the problem is my_callback is called by Pubnub when the Pubnub.publish() gets some status back about the message rather than me directly calling it! (Is there a way for me to insert message_id, chunk_id, and chunk into the callback while still letting pubnub attach its message to the mix? That sounds way wrong, but maybe with Ruby...)

Thanks in advance for all help.

¿Fue útil?

Solución

You shouldn't be trying to handle message chunk state in your callback. It has only a single responsibility, notifying about the status of the publish. However, you can insert things into the message. I might create a message wrapper that knows it's current state, and send that in the lambda, so you don't have to keep track of it. I haven't tested the following code, but here's an example of what I'm talking about:

class Message
  attr_accessor :id, :message, :chunked_message, :chunk_id
  def initialize(id, message)
    @id, @message = id, message
    chunk_message
  end
  def current_chunk
    @chunked_message[@chunk_id]
  end
  def next_chunk
    @chunk_id += 1
    self
  end
  def more?
    @chunked_message.length > chunk_id
  end
  private
  def chunk_message 
    implement splitting message here
  end
end 


def send_pubnub_message(message)
  pn.publish(:channel => :hello_world,
  :message => message.current_chunk
  :callback => lambda { |status| 
                        puts(status)
                        case status[0] // 1 = success, 0 = fail
                        when 1
                          send_pubnub_message(message.next_chunk) if message.more?
                        when 0
                          handle_pubnub_error(status[1], message)
                        end
                      }
end
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top