Ruby websocket client for websocket-rails gem - ruby-on-rails

I am developing a rails webpage which need to use websocket functionality to communicate with an external ruby client. In order to do this, I am using the websocket-rails gem in the rails server, definning the client_connected client_disconnected event and a specific action to received the messages from the client (new_message).
On the client side I have tried to use different ruby gems like faye-websocket-ruby and websocket-client-simple but I always obtain errors when I try to send a message. On the server I can't find the way to process these messages. Both gems has a send method with only accepts a string (not being able to specify the name of the event)
The code that I have been using is the following:
Server side
app/controllers/chat_controller.rb
class ChatController < WebsocketRails::BaseController
def new_message
puts ')'*40
end
def client_connected
puts '-'*40
end
def client_disconnected
puts '&'*40
end
end
config/events.rb
WebsocketRails::EventMap.describe do
subscribe :client_connected, :to => ChatController, :with_method => :client_connected
subscribe :message, :to => ChatController, :with_method => :new_message
subscribe :client_disconnected, :to => ChatController, :with_method => :client_disconnected
end
config/initializers/websocket_rails.rb
WebsocketRails.setup do |config|
config.log_path = "#{Rails.root}/log/websocket_rails.log"
config.log_internal_events = true
config.synchronize = false
end
Client side
websocket-client-simple
require 'rubygems'
require 'websocket-client-simple'
ws = WebSocket::Client::Simple.connect 'ws://localhost:3000/websocket'
ws.on :message do |msg|
puts msg.data
end
ws.on :new_message do
hash = { channel: 'example' }
ws.send hash
end
ws.on :close do |e|
p e
exit 1
end
ws.on :error do |e|
p e
end
hash = { channel: 'Example', message: 'Example' }
ws.send 'new_message', hash
loop do
ws.send STDIN.gets.strip
end
faye-websocket
require 'faye/websocket'
require 'eventmachine'
EM.run {
ws = Faye::WebSocket::Client.new('ws://localhost:3000/websocket')
ws.on :open do |event|
p [:open]
end
ws.on :message do |event|
p [:message, event.data]
end
ws.on :close do |event|
p [:close, event.code, event.reason]
ws = nil
end
ws.send( 'Example Text' )
}
Thanks in advance. Regards.
PD: Let me know if you need more code.

Finally I have found the solution. The problem was that the message needs to be constructed with a certain format in order to be understood by websocket-rails.
Example: ws.send( '["new_message",{"data":"Example message"}]' )
where new_message is the event which websocket-rails is listening to.

Related

How can I test ActionCable using RSpec?

This is my NotificationChannel
class NotificationChannel < ApplicationCable::Channel
def subscribed
stream_from "notification_user_#{user.id}"
end
def unsubscribed
stop_all_streams
end
end
How can I write test for this ActionCable channels
This is my Rspec
require 'rails_helper'
require_relative 'stubs/test_connection'
RSpec.describe NotificationChannel, type: :channel do
before do
#user = create(:user)
#connection = TestConnection.new(#user)
#channel = NotificationChannel.new #connection, {}
#action_cable = ActionCable.server
end
let(:data) do
{
"category" => "regular",
"region" => "us"
}
end
it 'notify user' do
#error is in below line
expect(#action_cable).to receive(:broadcast).with("notification_user_#{#user.id}")
#channel.perform_action(data)
end
end
when I run this spec it gives error
Wrong number of arguments. Expected 2, got 1
I used this link to write code for stub and this file.
Rails version - 5.0.0.1
Ruby version - 2.3.1
expect(#action_cable).to receive(:broadcast).with("notification_user_#{#user.id}")
Looking closely broadcast needs two parameters so
expect(#action_cable).to receive(:broadcast).with("notification_user_#{#user.id}", data)
I cant guess what is going on however one issue is
let(:data) do
{
"action" => 'action_name',
"category" => "regular",
"region" => "us"
}
end
You need an action for perform_action.
However you dont have any action defined in NotificationsChannel.
Otherwise you can try
NotificationChannel.broadcast_to("notification_user_#{#user.id}", data )

Rails scheduled SMS sender

I want send an SMS each 5 minutes to my users. At the moment, my application sends an SMS during the creation of an account.
# users_controller.rb
def create
#user = User.new(user_params)
if #user.save
#user.send_activation_email
#user.send_daily_sms
flash[:info] = "Veuillez contrôler votre boîte mail pour activer votre compte."
redirect_to root_url
else
render 'new'
end
end
# user.rb
def send_daily_sms
# put your own credentials here
account_sid = '**********************'
auth_token = '**********************'
# set up a client to talk to the Twilio REST API
#client = Twilio::REST::Client.new account_sid, auth_token
#client.account.messages.create({
:from => '**********',
:to => '***********',
:body => 'Salut',
})
end
I already have scheduled mails working in my project by doing this :
# schedule.rb
every :day, :at => '12pm' do
rake "email_sender_daily"
end
# My task
task :email_sender_daily => :environment do |_, args|
User.find_each do |user|
UserMailer.daily_mail(user).deliver_now if user.daily == true
end
end
# My UserMailer
def daily_mail(user)
#user = user
mail to: user.email, subject: "Mail journalier"
end
I'm showing you this because, with the UserMailer, I know how to access it from an other file. Here, I'd like to do the exactly the same for SMS, but how can I access the method that is in my Model ? If not, where can I put this method to be able to access it from my rake task ?
Twilio developer evangelist here.
It looks to me like you have all the parts you need. If send_daily_sms is a method in your User class then all you require is a rake task like so:
task :sms_sender_daily => :environment do |_, args|
User.find_each do |user|
user.send_daily_sms if user.daily == true
end
end
And then your schedule.rb would look like:
every :day, :at => '12pm' do
rake "email_sender_daily"
rake "sms_sender_daily"
end
I would warn that sending sms messages to all your users via one method that calls the API over and over again is somewhat fragile. If one message fails to send because of a timeout or some other error then the task will throw an error and not be able to complete sending all the messages.
I'd suggest sending both emails and sms messages by workers using a background queue, like Rails's ActiveJob. If you are on the latest Rails 4.2 then you can use a gem called Textris that works much like ActionMailer and then you could define a UserTexter class like this:
class UserTexter < Textris::Base
default :from => YOUR_NUMBER
def daily_sms(user)
#user = user
text :to => #user.phone_number
end
end
Then your tasks could look like this:
task :email_sender_daily => :environment do |_, args|
User.find_each do |user|
UserMailer.daily_mail(user).deliver_later if user.daily == true
end
end
task :sms_sender_daily => :environment do |_, args|
User.find_each do |user|
UserTexter.daily_sms(user).deliver_later if user.daily == true
end
end
Check out the Textris documentation for more on how to use the gem.
Let me know if this helps at all!

Test remote image url with carrierwave using rspec

I want to speed up my spec tests for my remote image url upload using the carrierwave gem. In my model I will only allow an image upload by remote url not by upload. For testing the model I use FactoryGirl with the remote_image_url field and a placeholder image.
My tests run successful but it takes a lot of time to for each test (approx. 3-4 sec) because of downloading the image. I know I can stub my tests to avoid these external http requests but I do not have an idea how to do it with factory girl. Can anyone help?
Model:
class Store < ActiveRecord::Base
mount_uploader :image, StoreUploader
attr_accessible :name, :remote_image_url
validates :remote_image_url, presence: true
end
Factory:
FactoryGirl.define do
factory :store do
name "Corner store"
remote_image_url "http://placehold.it/800x600"
end
end
RspecController:
describe Api::StoreController, type: :api do
let!(:store) { create :store }
before(:each) do
get :show, id: store.id
end
it "returns a successful response" do
expect(response).to be_success
end
it "each store has the correct fields" do
expect(json).to match("name", "image")
end
end
I think you can use some public image in your dropbox, but if you want to have testing isolated and able to run offline then you can look at gem "webmock".
Other option start rack web-app in parallel thread and serve image from there.
require 'sinatra/base'
require 'webrick'
module TestingPurposeServer
def self.run!(port = nil)
if port.nil?
# ask system to pick free port for us
server = TCPServer.new('127.0.0.1', 0)
port = server.addr[1]
server.close
end
thread = Thread.new do
begin
options = { :Port => port, :BindAddress => '127.0.0.1', :AccessLog => [], :Logger => WEBrick::Log.new("/dev/null"), :OutputBufferSize => 5 } #WEBrick::Log::new("/dev/null") }
server = ::WEBrick::HTTPServer.new(options)
server.mount "/", Rack::Handler::WEBrick, VtDirectServer::Server
server.start
rescue Exception => e
puts e.message
puts e.backtrace
end
end
# wait for opening port
while port_open?('127.0.0.1', port, 1)
sleep 0.01
end
sleep 0.1
port
end
def self.port_open?(ip, port, seconds = 1)
Timeout::timeout(seconds) do
begin
TCPSocket.new(ip, port).close
true
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
false
end
end
rescue Timeout::Error
false
end
class Server < ::Sinatra::Base
# your sinatra app there
end
end
port = TestingPurposeServer.run!
local_server_url = "http://127.0.0.1:#{port}/"

Ruby script not running when Process.daemon is added

I have the following simple script, which checks an email account and if there is new mail it forwards the email and sends an SMS. This happens as expected when the script is run without Process.daemon. When it is added, and email is received at the email account, nothing happens (nothing is forwarded and no SMS is sent) and there are no error messages in the console. Any suggestions?
#!/usr/bin/env ruby
require "bundler/setup"
require "mailman"
require "twilio-ruby"
Mailman.config.pop3 = {
:username => 'address#gmail.com',
:password => 'password',
:server => 'pop.gmail.com',
:port => 995,
:ssl => true
}
Mailman.config.poll_interval = 60
Mailman::Application.run do
default do
begin
Ticket.receive_mail(message)
MailForwarder.forwarded_email(message).deliver
#account_sid = 'xxxxxxxxxxx'
#auth_token = 'xxxxxxxxxx'
#client = Twilio::REST::Client.new(#account_sid, #auth_token)
#account = #client.account
#sms = #account.sms.messages.create(
:from => '+1111111111',
:to => '+122222222',
:body => message.subject
)
puts #sms
puts "#{message.subject}"
rescue Exception => e
Mailman.logger.error "Exception occurred whle receiving message:\n#{message}"
Mailman.logger.error [e, *e.backtrace].join("\n")
end
end
Process.daemon
end
I believe you need to set up your script as a daemon before you start up the mailman application. I did a bit of testing, and it worked fine if I called Process.daemon before calling the Mailman::Application.run but it didn't work if I put it where you had it.
So I had it as:
....
Mailman.config.poll_interval = 15
Process.daemon
Mailman::Application.run do
default do
end
end

exception_notification for delayed_job

Is there a exception_notification-like gem for delayed_job?
Preferably that works with REE-1.8.7 and Rails 2.3.10.
I've done something like this in the past for delayed job rake tasks:
require 'action_mailer'
class ExceptionMailer < ActionMailer::Base
def setup_mail
#from = ExceptionNotifier.sender_address
#sent_on = Time.now
#content_type = "text/plain"
end
def exception_message(subject, message)
setup_mail
#subject = subject
#recipients = ExceptionNotifier.exception_recipients
#body = message
end
end
namespace :jobs do
desc "sync the local database with the remote CMS"
task(:sync_cms => :environment) do
Resort.sync_all!
result = Delayed::Job.work_off
unless result[1].zero?
ExceptionMailer.deliver_exception_message("[SYNC CMS] Error syncing CMS id: #{Delayed::Job.last.id}", Delayed::Job.last.last_error)
end
end
end
Include this module in classes which are to be delayed:
require 'exception_notifier'
module Delayed
module ExceptionNotifier
# Send error via exception notifier
def error(job, e)
env = {}
env['exception_notifier.options'] = {
:sections => %w(backtrace delayed_job),
:email_prefix => '[Delayed Job ERROR] ',
:exception_recipients => %w(some#email.com),
:sender_address => %(other#email.com)
}
env['exception_notifier.exception_data'] = {:job => job}
::ExceptionNotifier::Notifier.exception_notification(env, e).deliver
end
end
end
and create a template for the notification in app/views/exception_notifier/_delayed_job.text.erb:
Job name: <%= #job.name %>
Job: <%= raw #job.inspect %>
* Process: <%= raw $$ %>
* Server : <%= raw `hostname -s`.chomp %>

Resources