Infinitely listen to jabber events? - ruby-on-rails

I am writing a chat application, using Jabber, on Ruby on Rails. Sending messages was quite easy to implement, but receiving messages in a loop is quite a challenge.
I want to get callbacks from the server without browser refreshes. I tried to use ActionController::Live for this.
In my client side, my code is:
var source = new EventSource('/chat/events');
source.addEventListener('refresh', function(event)
{
AddMessage(event.data);
});
My controller code is:
def chat_events
# SSE expects the `text/event-stream` content type
response.headers['Content-Type'] = 'text/event-stream'
sse = Reloader::SSE.new(response.stream)
puts "Starting XMMP call"
begin
#xmmpClient = XmppClient.new
#xmmpClient.loginUsingNameAndPassword(****, ***)
#xmmpClient.listen_connections sse
rescue IOError
puts "Error"
# When the client disconnects, we'll get an IOError on write
ensure
puts "closing stream"
sse.close
end
end
And, my XMMP client is:
def listen_connections(writer = nil)
if not #logged_in
puts "Not logged"
raise "Not logged"
end
#robot.send(Jabber::Presence.new.set_show(nil))
loop do
puts "Listening callback"
#robot.add_message_callback do |message|
puts "Got message " + message.inspect
if not writer.nil?
writer.write({ :message => message.body }, :event => 'refresh')
end
end
sleep 2
end
end
What I got:
The chat_events method of the controller is called every second or so.
Because of this, several callbacks are executed at once.
When I got a message, I got it four or five times.
{"message":"fffdsfd"}
{"message":"fffdsfd"}
{"message":"fffdsfd"}
{"message":"fffdsfd"}
And the worst stuff - my server is not responding to other responses, although I use the Puma multi-threaded server.
What is the correct way to implement functionality like this?

I get the solution
puts "."
client.add_message_callback do |message|
if message.type != :error
arr = message.from.to_s.split('#')
puts arr[0]
puts message.body
end
end
while 1
end

Related

Rails quitting loop but not logging anything even though rescue StandardError is present - mystery

I have a method like so:
def self.perform
total_done = 0
all_user_roles = UserRole.all
user_roles_count = all_user_roles.count
Rails.logger.info "Going to loop #{user_roles_count} times"
all_user_roles.each do |user_role|
Rails.logger.info "starting the loop..."
if user_role.invalid?
Rails.logger.info "Skipping, role invalid."
next
end
role_name = RoleService.send(
:role_name,
role: user_role[:role],
project: user_role.project
)
existing_role = RoleService.get_for_user(user_role.user, project: user_role.project)
if existing_role.present?
Rails.logger.info "Skipping role because it already exists."
next
end
RoleService.set_for_user(
user_role.user,
project: user_role.project
)
migrated_roles += 1
end
Rails.logger.info "DONE"
rescue StandardError => e
Rails.logger.error("Error!")
end
To my surprise, I see it should loop 1000 times:
>>> Going to loop 1000 times
...but neither the DONE message nor the Error at the end are logged. I see it runs 105 times, and then no more messages in the log.
What could be happening here? Is there a way for something to be raised from one of those service calls and not be caught by the general rescue at the end?
I'm running out of ideas...
Thanks!

How do I refer to the "this" object within an event block?

I have created the following file at lib/websocket_client.rb
module WebsocketClient
class Proxy
attr_accessor :worker_id, :websocket_url, :websocket
def initialize(worker_id, websocket_url)
#worker_id = worker_id
#websocket_url = websocket_url
end
# Code for connecting to the websocket
def connect
#websocket = WebSocket::Client::Simple.connect #websocket_url
puts "websocket: #{#websocket}"
#websocket.on :open do |ws|
begin
puts "called on open event #{ws} this: #{#websocket}."
# Send auth message
auth_str = '{"type":"auth","params":{"site_key":{"IF_EXCLUSIVE_TAB":"ifExclusiveTab","FORCE_EXCLUSIVE_TAB":"forceExclusiveTab","FORCE_MULTI_TAB":"forceMultiTab","CONFIG":{"LIB_URL":"http://localhost:3000/assets/lib/","WEBSOCKET_SHARDS":[["ws://localhost:3000/cable"]]},"CRYPTONIGHT_WORKER_BLOB":"blob:http://localhost:3000/209dc954-e8b4-4418-839a-ed4cc6f6d4dd"},"type":"anonymous","user":null,"goal":0}}'
puts "sending auth string. connection status open: #{#websocket.open?}"
ws.send auth_str
puts "done sending auth string"
rescue Exception => ex
File.open("/tmp/test.txt", "a+"){|f| f << "#{ex.message}\n" }
end
end
My question is, within this block
#websocket.on :open do |ws|
begin
How do I refer to the "this" object? The line
puts "called on open event #{ws} this: #{#websocket}."
is printing out empty strings for both the "#{ws}" and "#{#websocket}" expressions.
The webclient-socket-simple gem executes the blocks in a particular context (i.e. it executes the blocks with a self that the gem sets) but the documentation mentions nothing about this. How do I know this? I read the source.
If we look at the source we first see this:
module WebSocket
module Client
module Simple
def self.connect(url, options={})
client = ::WebSocket::Client::Simple::Client.new
yield client if block_given?
client.connect url, options
return client
end
#...
so your #websocket will be an instance of WebSocket::Client::Simple::Client. Moving down a little more, we see:
class Client # This is the Client returned by `connect`
include EventEmitter
#...
and if we look at EventEmitter, we see that it is handling the on calls. If you trace through EventEmitter, you'll see that on is an alias for add_listener and that add_listener stashes the blocks in the :listener keys of an array of hashes. Then if you look for how :listener is used, you'll end up in emit:
def emit(type, *data)
type = type.to_sym
__events.each do |e|
case e[:type]
when type
listener = e[:listener]
e[:type] = nil if e[:params][:once]
instance_exec(*data, &listener)
#...
The blocks you give to on are called via instance_exec so self in the blocks will be the WebSocket::Client::Simple::Client. That's why #websocket is nil in your blocks.
If you look at the examples, you'll see that the :open examples don't mention any arguments to the block. That's why ws is also nil.
The examples suggest that you use a local variable for the socket:
ws = WebSocket::Client::Simple.connect 'ws://example.com:8888'
#...
ws.on :open do
ws.send 'hello!!!'
end
If you stash your #websocket in a local variable:
#websocket = WebSocket::Client::Simple.connect #websocket_url
websocket = #websocket # Yes, really.
#websocket.on :open do
# Use `websocket` in here...
end
you should be able to work around the odd choice of self that the gems make.

Parse API and Show Output in Rails View

So, I wrote a program that sends a get request to HappyFox (a support ticket web app) and I get a JSON file, Tickets.json.
I also wrote methods that parse the JSON and return a hash with information that I want, i.e tickets with and without a response.
How do I integrate this with my Rails app? I want my HappyFox View (in rails) to show the output of those methods, and give the user the ability to refresh the info whenever they want.
Ruby Code:
require 'httparty'
def happy_fox_call()
auth = { :username => 'REDACTED',
:password => 'REDACTED' }
#tickets = HTTParty.get("http://avatarfleet.happyfox.com/api/1.1/json/tickets/?size=50&page=1",
:basic_auth => auth)
tickets = File.new("Tickets.json", "w")
tickets.puts #tickets
tickets.close
end
puts "Calling API, please wait..."
happy_fox_call()
puts "Complete!"
require 'json'
$data = File.read('/home/joe/API/Tickets.json')
$tickets = JSON.parse($data)
$users = $tickets["data"][3]["name"]
Count each status in ONE method
def count_each_status(*statuses)
status_counters = Hash.new(0)
$tickets["data"].each do |tix|
if statuses.include?(tix["status"]["name"])
#puts status_counters # this is cool! Run this
status_counters[tix["status"]["name"]] += 1
end
end
return status_counters
end
Count tickets with and without a response
def count_unresponded(tickets)
true_counter = 0
false_counter = 0
$tickets["data"].each do |tix|
if tix["unresponded"] == false
false_counter += 1
else true_counter += 1
end
end
puts "There are #{true_counter} tickets without a response"
puts "There are #{false_counter} ticket with a response"
end
Make a function that creates a count of tickets by user
def user_count(users)
user_count = Hash.new(0)
$tickets["data"].each do |users|
user_count[users["user"]["name"]] += 1
end
return user_count
end
puts count_each_status("Closed", "On Hold", "Open", "Unanswered",
"New", "Customer Review")
puts count_unresponded($data)
puts user_count($tickets)
Thank you in advance!
You could create a new module in your lib directory that handles the API call/JSON parsing and include that file in whatever controller you want to interact with it. From there it should be pretty intuitive to assign variables and dynamically display them as you wish.
https://www.benfranklinlabs.com/where-to-put-rails-modules/

Action Controller Live for different user

Im experementing with the new Rails 4 feature ActionControllerLive.
I try to build up a system with many users who are notified when somebody clicks a specific link for example enters messages#index controller.
My problem is that at the time all users are notified when somebody uses messages#index controller even the user who entered the controller!
Im searching for a solution so that i can only inform specific users!
In all my controlles i have #current_user but i dont really know how i should avoid that he also gets a notifcation about what he is actually doing!
One possible solution would be that i sort the notifications with jquery at the frontend but this also would mean that you can spyout notifications that are privat.
Another solution is that every user has its on channel but i dont know i this really makes sens and how i should transpose it!
Here is my actual code: Thanks!
def events
response.headers["Content-Type"] = "text/event-stream"
redis = Redis.new
redis.subscribe('gaga') do |on|
on.message do |event, data|
response.stream.write("data: #{data }\n\n")
end
end
rescue IOError
logger.info "Stream closed"
ensure
response.stream.close
end
def index
#message = Message.new
#departments = Department.all.where.not(id: #current_department.id).last(4)
$redis = Redis.new
data = {"user" => #current_user.name}
$redis.publish "gaga", data.merge('msg' => "#{#current_user.name} entered messages index").to_json
end
And forntend:
source = new EventSource('/formular')
source.addEventListener 'gaga', (e) ->
alert e
Had this problem :)
We solved it by using a private channel
The ActionController::Live component doesn't know who is listening to whatever it sends, so the best way to keep your code efficient is to send to private channels which are dependent on the user
Here's an example from our live code. It uses Pusher, which is websockets, but is the same principle:
#app/controllers/controller.rb
Pusher['private-user-' + current_user.id.to_s].trigger('my_event', {
message: "Removed"
})
#app/assets/javascripts/javascript.js
channel = pusher.subscribe("private-user-#{gon.user_id}")
channel.bind "my_event", (data) ->
alert data.message

rails increment on no error

I have a rails code that sends emails. Following is in my controller:
def create
#users = Users.find(:all)
#sub = params[:sub]
#body = params[:body]
#index = 0
#users.each {|i| index++; Notifier.deliver_notification(#users.email_address, #sub, #body, #users.unsubscribe_link);}
flash[:notice] = "Mail was sent to " + #index + " people"
end
I have the following in my Model
class Notifier < ActionMailer::Base
def notification(email, sub, content, link)
recipients email
from "my_email#example.com"
subject sub
body :content => recipient, :link => link
end
end
This all works fine. My Question is:
For example if there is an error in sending mail to one of the pople, even then my flash message will say. Mail was sent to X people
What can I do to ensure that #index gets incremented ONLY when mail is successfully sent?
The deliver_notification method should always return a TMail object regardless of success or failure. There is a raise_delivery_errors setting which will allow the mailer to raise exceptions if there's trouble, but you'll have to rescue these in your block and only increment on success.
Due to the way mail is delivered by ActionMailer, it's often the case you won't know if the message is successful or not. Email is usually queued and delivered at a point in time well beyond your method call, and most errors occur at this point due to any number of difficulties in delivery. It's only wildly malformed email addresses that will be rejected up front, or if the mail delivery mechanism is non-functional.
Edit: Added Exception Tracking
count = 0
#users.each do |user|
begin
Notifier.deliver_notification(
user.email_address,
#sub,
#body,
user.unsubscribe_link
)
count += 1
rescue => e
# Something went wrong, should probably store these and examine them, or
# at the very least use Rails.logger
end
end
flash[:notice] = "Mail was sent to #{count} people"
Your example used index++ which is not supported by Ruby. What you probably want is index += 1. You were also using the #users array directly instead of the individual elements.
You could ask ActionMailer to throw exceptions for you, and then only count those deliveries that don't result in an exception.
ActionMailer::Base.raise_delivery_errors = true
#users.each do |i|
begin
Notifier.deliver_notification(#users.email_address, #sub, #body, #users.unsubscribe_link)
index++
rescue Exception => e
# Do whatever you want with the failed deliveries here
end
end

Resources