Using RabbitMQ with Ruby and Node.js apps? - ruby-on-rails

I'm using the Bunny gem in Rails and AMQP in Node.js.
Rails is the publisher and the Node.js app is a subscriber.
Code from Rails:
conn = Bunny.new
conn.start
ch = conn.create_channel
x = Bunny::Exchange.new(ch, :direct, "messenger")
q1 = ch.queue("new_messages").bind(x, :routing_key => 'send')
x.publish('hi', :routing_key => 'send')
Code from Node.js:
var amqp = require('amqp');
var connection = amqp.createConnection();
connection.addListener('ready', function(){
var exchange = connection.exchange('messenger');
var queue = connection.queue('new_messages')
queue.bind('messenger', 'send')
queue.subscribe( {ack:true}, function(message){
console.log(message.data.toString())
queue.shift()
});
});
From the management plugin I can see that the Rails code works well, but the code from Node.js doesn't make sense.
Where is my mistake?

The main issue is in how you're attempting to bind your queue to the 'messenger' exchange. You've already used the exchange name to get a reference to the exchange object. Rather than pass in the string name again when binding the queue to the exchange, pass this reference into queue.bind(). Simply change the first queue.bind() argument:
var exchange = connection.exchange('messenger');
var queue = connection.queue('new_messages');
// Use the reference to the exchange object you just received;
// pass it in here instead of the exchange's name
queue.bind(exchange, 'send');
EDIT
Here is a full, working example; just drop in your own connection properties:
var amqp = require('amqp');
var connection = amqp.createConnection({
host: 'my_host',
login: 'my_login',
password: 'my_password',
authMechanism: 'AMQPLAIN',
vhost: 'my_vhost'
});
connection.addListener('ready', function(){
console.log('ready');
var exchange = connection.exchange('messenger');
var queue = connection.queue('new_messages');
queue.bind(exchange, 'send');
// if message successfully received, print message
queue.subscribe( {ack:true}, function(message){
console.log(message.data.toString());
queue.shift();
});
// test by sending a message
exchange.publish('send', 'this is a test message', {}, function() {})
});
Cheers.

Related

Server socket.io on('connection') callback not being called

I'm using Socket.io to connect a Digital Ocean Node.js server to an iOS Swift app. I've been using the same code with ngrok to build the app and I'm just now switching to Digital Ocean.
The connection event is happening because the Swift app is going through its checks that occur with a connect event (i.e., trying to pull data). However, the callback on the server is not being called so none of the listeners on the server are being called.
I'm doing a regular instantiation of SocketIOClient without any additional parameters:
var socket: SocketIOClient = SocketIOClient(socketURL: URL(string: "https://my_domain.com")!)
The connection function on the server looks like this:
socket.on("connect") {data, ack in }
I have the following server dependencies:
var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
var server = require('http').createServer(app);
var io = require('socket.io')(server);
var listeners = require('./listeners');
var bson = require('bson')
var MongoClient = require('mongodb').MongoClient;
var apn = require('apn');
var ObjectID = require('mongodb').ObjectID;
var querystring = require('querystring');
The connection function on the server looks like this:
io.on('connection', function(client) {
console.log("connected")
})
The issue is that nothing is getting printed or happening on the server. The listener is within MongoClient.connect but Mongo is connecting successfully and items are printing within that function, so things are just stopping with the on('connection') function.
Again this was working fine on ngrok.
Any help is appreciated.

Socket.io client not working properly

I have created chat application using following
Rails
Rabbitmq
nodejs ( with amqplib/callback_api and socket.io )
I have following code in server side
var amqp = require('amqplib/callback_api');
var amqpConn = null;
var app = require('http').createServer()
var io = require('socket.io')(app);
var fs = require('fs');
io.on('connection', function (socket) {
socket.emit('news/1', msg.content.toString());
// socket.disconnect()
});
client side
<%= javascript_include_tag "http://localhost:53597/socket.io/socket.io.js" %>
<script>
var socket = io('http://localhost:53597', {reconnect: true});
socket.on('connect', function () {
console.log('connected');
socket.on('news/1', function (msg) {
console.log('inside coming');
console.log(msg);
});
});
</script>
When I send message, message successfully pushed to queue and messages emitted to socket. The problem is I can get messages when only refresh page and messages are not deleted.
I can't understand what was wrong here ?
Eventually I fixed my issue, the problem is I have placed emit function inside connection event, so that I can get data when only connection established​, connection is established when page load that is the reason I get data when only page load.
I have the following code for emit data
io.sockets.emit('news/1', msg.content.toString());

Can I add request parameter to SockJs constructor so that it can be send to server

I initialize my SockJs URL as
var protocols = ['xhr-polling', 'xdr-polling', 'xdr-streaming', 'xhr-streaming'];
var options = {protocols_whitelist: protocols, debug: true,server:tets};
_ws = new SockJS(url, null, options);
I want to send out a request parameter , for example somesite/sockjs/info?someparam=tets"
Is it possible? Documentation of SockJs refers that options is map which can have key value but i am not sure what key to use here.
I verified URL at server which SockJs sends over and it is
http://http_backend/cecobrowsega-3.0.0.0.31/sockjs/testapp/620/4ydem52f/xhr?null
So in absence of request param its appending a null, seems there is a way to send over request param!
It's available in latest sock js client. Discussion here https://github.com/sockjs/sockjs-client/issues/72
we can pass query string along with the connection URL, same syntax as for any HTTP call
For those who needs code sample:
var socket = new SockJS('http://localhost/ws?token=AAA');
var stompClient = Stomp.over(socket);
stompClient.connect({}, function(frame) {
stompClient.subscribe('/topic/echo', function(data) {
// topic handler
});
}
}, function(err) {
// connection error
});
Now all the requests related to websocket will have parameter "?token=AAA"
http://localhost/ws/info?token=AAA&t=1446482506843
http://localhost/ws/515/z45wjz24/websocket?token=AAA
Tested with SockJS 1.0.3

Executing Local Query with loaded metadata fails

I'm new to breeze, this looks like a bug, but thought I'd ask here in case I just don't get it.
Setup loading metadata:
var metadataStore = new breeze.MetadataStore();
metadataStore.importMetadata(metadata);
queryOptions = new breeze.QueryOptions( {
fetchStrategy: breeze.FetchStrategy.FromLocalCache
});
mgr = new breeze.EntityManager({
serviceName: 'breeze',
metadataStore: metadataStore,
queryOptions: queryOptions
});
Executing local query explicitly works:
var q = breeze.EntityQuery.from("Boards")
.toType('Board')
.where('isImplicit', 'equals', withImplicits)
.orderBy('name');
return manager.executeQueryLocally(q) // returns result
But using query.using doesn't:
var q = breeze.EntityQuery.from("Boards")
.toType('Board')
.where('isImplicit', 'equals', withImplicits)
.orderBy('name');
q = q.using(breeze.FetchStrategy.FromLocalCache)
return manager.executeQuery(q)
UPDATE: To clarify, the above throws an error as it tries to fetchMetdata and there is no endpoint to fetch from. If I monkey patch the code below, it works fine. It seems like if the dataService .hasServerMetadata, you don't need to fetch it. I'm creating a test harness for a breeze adapter, so I want to be able to run without the backend
Looks like problem is this line in EntityManager:
if ( (!dataService.hasServerMetadata ) || this.metadataStore.hasMetadataFor(dataService.serviceName)) {
promise = executeQueryCore(this, query, queryOptions, dataService);
} else {
var that = this;
promise = this.fetchMetadata(dataService).then(function () {
return executeQueryCore(that, query, queryOptions, dataService);
});
}
I believe line should be if( dataService.hasServerMetadata || ..., but being new to Breeze thought I'd ask here before opening GH issue.
EntityManager.executeQueryLocally is a synchronous function and you can use its result immediately. i.e.
var myEntities = myEntityManager.executeQueryLocally(myQuery);
Whereas EntityManager.executeQuery is an asynchonous function ( even if the query has a 'using' call that specifies that this is a local query). So you need to call it like this:
var q2 = myQuery.using(breeze.FetchStrategy.FromLocalCache);
myEntityManager.executeQuery(q2).then(function(data) {
var myEntities = data.results;
});
The idea behind this is that with executeQuery you treat all queries in exactly the same fashion, i.e. asynchronously, regardless of whether they are actually asynchronous under the hood.
If you want to create an EntityManager that does not go to the server for metadata you can do the following:
var ds = new breeze.DataService({
serviceName: "none",
hasServerMetadata: false
});
var manager = new breeze.EntityManager({
dataService: ds
});

How to parse a XML string in a Firefox addon using Add-on SDK

I am trying to create a FF AddOn that brings some XML data from a website. But I can't find a way to parse my RESPONSE. First I used DOMParser but I get this error:
ReferenceError: DOMParser is not defined.
Someone suggested to use XMLHttpRequest, because the parsing is done automatically but then I get this other error:
Error: An exception occurred. Traceback (most recent call last):
File
"resource://jid0-a23vmnhgidl8wlymvolsst4ca98-at-jetpack/api-utils/lib/cuddlefish.js",
line 208, in require
let module, manifest = this.manifest[base], requirer = this.modules[base]; TypeError: this.manifest is undefined
I really don't know what else to do. I must note that I am using the AddOn Builder to achieve this.
Below the code that doesn't seem to work.
Option 1:
exports.main = function() {
require("widget").Widget({
id: "widgetID1",
label: "My Mozilla Widget",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function(event) {
var Request = require("request").Request;
var goblecontent = Request({
url: "http://www.myexperiment.org/search.xml?query=goble",
onComplete: function (response) {
var parser = new DOMParser();
var xml = parser.parseFromString(response.text, "application/xml");
var packs = xml.getElementsByTagName("packs");
console.log(packs);
}
});
goblecontent.get();
}
});
};
Option 2:
exports.main = function() {
require("widget").Widget({
id: "widgetID1",
label: "My Mozilla Widget",
contentURL: "http://www.mozilla.org/favicon.ico",
onClick: function(event) {
var request = new require("xhr").XMLHttpRequest();
request.open("GET", "http://www.myexperiment.org/search.xml?query=goble", false);
request.send(null);
if (request.status === 200) {
console.log(request.responseText);
}
}
});
};
DOMParser constructor isn't defined in the context of SDK modules. You can still get it using chrome authority however:
var {Cc, Ci} = require("chrome");
var parser = Cc["#mozilla.org/xmlextras/domparser;1"].createInstance(Ci.nsIDOMParser);
nsIDOMParser documentation.
That said, your approach with XMLHttpRequest should work as well. You used the new operator incorrectly however, the way you wrote it a new "require object" is being created. This way it should work however:
var {XMLHttpRequest} = require("xhr");
var request = new XMLHttpRequest();
Please consider using an asynchronous XMLHttpRequest object however, use request.onreadystatechange to attach your listener (the xhr module currently doesn't support other types of listeners or addEventListener).
If you use XMLHttpRequest (available via the xhr module) you can easily avoid the use of DOMParser. Bellow I provide an example supposing request is an XMLHttpRequest object which request is successfully completed:
Instead of:
var parser = new DOMParser();
var xmlDoc = parser.parseFromString(request.responseText, "application/xml");
Use:
var xmlDoc = request.responseXML;
An then you can:
var packs = xmlDoc.getElementsByTagName("packs");
console.log(packs);
Or whatever.

Resources