I'm creating a live chat on Ruby on Rails application and i'm getting trouble to create a spec test. I try to search some examples tests, but I didn't find anything.
How do I create test to this class?
class MessageBroadcastJob < ApplicationJob
queue_as :default
def perform(message)
sender = message.person
recipient = message.conversation.opposed_person(sender)
broadcast_to_sender(sender, message)
broadcast_to_recipient(recipient, message)
end
private
def broadcast_to_sender(person, message)
ActionCable.server.broadcast(
"conversations-#{person.id}",
message: render_message(message, person),
conversation_id: message.conversation_id
)
end
def broadcast_to_recipient(person, message)
ActionCable.server.broadcast(
"conversations-#{person.id}",
window: render_window(message.conversation, person),
message: render_message(message, person),
conversation_id: message.conversation_id
)
end
def render_message(message, person)
ApplicationController.render(
partial: 'messages/message',
locals: { message: message, person: person }
)
end
def render_window(conversation, person)
ApplicationController.render(
partial: 'conversations/conversation',
locals: { conversation: conversation, person: person }
)
end
end
You would test this job just like any other job with rspec-rails. Perhaps with an expectation that ActionCable.server receives the proper broadcast messages.
Related
I have an rails app that sends a webhook (localhost:3000) with data. Before, I used a online webhook monitor (Pipedream) but I want now to build a second rails app to receive the data (localhost:3000 -> localhost:3300). I've created a route /webhook/receive and the controller for it but get nothing from localhost:3000.
What I'm doing wrong?
Thats on my first app:
def send_create_webhook(webhook_url)
company = User.find_by(email: 'test1#test.de').company_account
webhook_url = 'tcp://0.0.0.0:3300/data/receive'
SendWebhookJob.perform_later(webhook_url, {
subscription: {
type: "#{#tool.title} successfully subscribed",
date: "#{Time.zone.now}",
subscribed: Time.now.strftime("%Y-%d-%m %H:%M:%S %Z"),
user: {
company: "#{company.title}",
name: current_user.name,
email: current_user.email,
}
}
})
end
class SendWebhookJob < ApplicationJob
queue_as :default
WEBHOOK_RETRIES = 3
def perform(webhook_url, data, retry_count = 0)
p "COUNT #{retry_count} : #{Time.zone.now} : Sending data to #{webhook_url}"
# make a request
begin
response = HTTP.post(webhook_url, json:data)
successful = response.code == 200
rescue StandardError => e
successful = false
end
if successful
p "SUCCESSFUL WEBHOOK"
elsif retry_count < WEBHOOK_RETRIES
wait = 2 ** retry_count
SendWebhookJob.set(wait: wait.seconds).perform_later(webhook_url, data,retry_count + 1)
end
end
end
The second app I just created:
resources :data do
get :receive
post :receive
end
def DataController < ApplicationController
require "http"
def receive
response = HTTP.get('tcp://0.0.0.0:3000')
end
end
I am following the Learn Enough Action Cable tutorial. I have gotten to the end of section 6, adding #mentions to create notifications to the mentioned user. One of the exercises is to append the sending user's name to the alert text. So far I am only getting "#undefined". I'm guessing data.mention.username is not the correct call to append. In the console to pull the username from a message I did User.find_by(id: Message.last.user_id).username, but I don't know how to translate that to working coffeescript.
room.coffee
App.room = App.cable.subscriptions.create "RoomChannel",
connected: ->
# Called when the subscription is ready for use on the server
disconnected: ->
# Called when the subscription has been terminated by the server
received: (data) ->
alert("You have a new mention from #" + data.mention.username) if data.mention
if (data.message && !data.message.blank?)
$('#messages-table').append data.message
scroll_bottom()
$(document).on 'turbolinks:load', ->
submit_message()
scroll_bottom()
submit_message = () ->
$('#message_content').on 'keydown', (event) ->
if event.keyCode is 13 && !event.shiftKey
$('input').click()
event.target.value = ""
event.preventDefault()
scroll_bottom = () ->
$('#messages').scrollTop($('#messages')[0].scrollHeight)
messages_controller.rb
class MessagesController < ApplicationController
before_action :logged_in_user
before_action :get_messages
def index
end
def create
message = current_user.messages.build(message_params)
if message.save
ActionCable.server.broadcast 'room_channel',
message: render_message(message)
message.mentions.each do |mention|
ActionCable.server.broadcast "room_channel_user_#{mention.id}",
mention: true
end
end
end
private
def get_messages
#messages = Message.for_display
#message = current_user.messages.build
end
def message_params
params.require(:message).permit(:content)
end
def render_message(message)
render(partial: 'message', locals: { message: message })
end
end
message.rb
class Message < ApplicationRecord
belongs_to :user
validates :content, presence: true
scope :for_display, -> { order(:created_at).last(50) }
# Returns a list of users #mentioned in message content.
def mentions
content.scan(/#(#{User::NAME_REGEX})/).flatten.map do |username|
User.find_by(username: username)
end.compact
end
end
room_channel.rb
class RoomChannel < ApplicationCable::Channel
def subscribed
stream_from "room_channel"
stream_from "room_channel_user_#{message_user.id}"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
end
Have to define the username within the create method of messages controller. So the create method in messages_controller.rb looks something like:
def create
message = current_user.messages.build(message_params)
if message.save
ActionCable.server.broadcast 'room_channel',
message: render_message(message)
message.mentions.each do |mention|
ActionCable.server.broadcast "room_channel_user_#{mention.id}",
mention: true,
origin: "##{message.user.username}"
end
end
end
And the room.coffee alert calls data.origin like so:
received: (data) ->
alert("You have a new mention from " + data.origin) if data.mention
if (data.message && !data.message.blank?)
$('#messages-table').append data.message
scroll_bottom()
Thanks to LIUSINING of LearnEnough Society for pointing me in the right direction.
I'm having some problems when i'm trying to run some tests with rspec and paperclip.
i'm getting this message:
Failure/Error: #event = Event.new(event_params)
Paperclip::AdapterRegistry::NoHandlerError:
No handler found for "/events/rails.png?1453566649"
Here is my test class:
context "user creates a new event" do
describe "with valid fields" do
before(:each) do
#image = Rack::Test::UploadedFile.new(Rails.root.join('spec/images/rails.png'), 'image/png')
end
it "must persist the event" do
event = build(:event, photo: nil, photo_attachment: nil)
event.photo_attachment = #image
post :create, event: {title: event.title, description: event.description, place: event.place, address: event.address, opening_date: event.opening_date, category: event.category, price: event.price, photo_attachment: event.photo_attachment, hour: event. hour}
expect(assigns(:event).id.nil?).to be false
end
Here is my controller:
class EventsController < ApplicationController
def new
#event = Event.new
end
def create
#event = Event.new(event_params)
if #event.save
redirect_to "success"
end
end
private
def event_params
params.require(:event).permit(:title, :description, :photo, :place, :address, :opening_date, :ending_date, :category, :price, :photo_attachment, :hour)
end
end
And here is my factory ( I'm using factory girl ):
FactoryGirl.define do
factory :event do
title { Faker::Name.name }
description 'A simples description'
photo { Faker::Bitcoin.address }
place 'Anywhere'
address { Faker::Address.street_address }
opening_date Date.today
ending_date Faker::Date.forward(2)
category 'any category'
price 1.99
photo_attachment {File.new("#{Rails.root}/spec/images/rails.png")}
hour '00:00'
end
trait :soccer_game do
title 'Soccer Game'
place 'Maracana Stadium'
opening_date Date.today
hour '14:00'
end
end
Could you help me with this ?
I tried action cable example from https://github.com/rails/actioncable-examples. It worked well with ActiveRecord. I am getting ActiveJob::SerializationError (Unsupported argument type: Comment) when using with NoBrainer for rethinkDB.
So I just passed id instead of self like below for activejob. But action cable server is not able to listen to the any channels and CommentsChannel methods are not invoked.
comment.rb
after_save { CommentRelayJob.perform_later(self.id) }
*****comment_relay_job.rb*****
def perform(comment_id)
comment = Comment.find(comment_id)
ActionCable.server.broadcast "messages:#{comment.message_id}:comments",
comment: CommentsController.render(partial: 'comments/comment', locals: { comment: comment }
end
comments_channel.rb
module ApplicationCable
class Channel < ActionCable::Channel::Base; end
end
class CommentsChannel < ApplicationCable::Channel
def follow(data)
stop_all_streams
stream_from "messages:#{data['message_id'].to_i}:comments"
end
def unfollow
stop_all_streams
end
end
comments.coffee
App.comments = App.cable.subscriptions.create "CommentsChannel", collection: -> $("[data-channel='comments']")
connected: ->
setTimeout =>
#followCurrentMessage()
#installPageChangeCallback()
, 1000
received: (data) ->
#collection().append(data.comment) unless #userIsCurrentUser(data.comment)
userIsCurrentUser: (comment) ->
$(comment).attr('data-user-id') is $('meta[name=current-user]').attr('id')
followCurrentMessage: ->
if messageId = #collection().data('message-id')
#perform 'follow', message_id: messageId
else
#perform 'unfollow'
installPageChangeCallback: ->
unless #installedPageChangeCallback
#installedPageChangeCallback = true
$(document).on 'page:change', -> App.comments.followCurrentMessage()
I have the following model;
(app/models/student_inactivation_log.rb)
class StudentInactivationLog < ActiveRecord::Base
belongs_to :student
belongs_to :institution_user
belongs_to :period
validates_presence_of :student_id, :inactivated_on, :inactivation_reason
INACTIVATION_REASONS = [{ id: 1, short_name: "HTY", name: "You didn't study enough!"},
{ id: 2, short_name: "KS", name: "Graduated!"},
{ id: 3, short_name: "SBK",name: "Other Reason"}]
Class methods
class << self
def inactivation_reason_ids
INACTIVATION_REASONS.collect{|v| v[:id]}
end
def inactivation_reason_names
INACTIVATION_REASONS.collect{|v| v[:name]}
end
def inactivation_reason_name(id)
INACTIVATION_REASONS.select{|t| t[:id] == id}.first[:name]
end
def inactivation_reason_short_name(id)
INACTIVATION_REASONS.select{|t| t[:id] == id}.first[:short_name]
end
def inactivation_reason_id(name)
INACTIVATION_REASONS.select{|t| t[:name] == name}.first[:id]
end
end
# Instance methods
def inactivation_reason_name
self.class.inactivation_reason_name(self.inactivation_reason)
end
def inactivation_reason_short_name
self.class.inactivation_reason_short_name(self.inactivation_reason)
end
def inactivation_reason_id
self.class.inactivation_reason_id(self.inactivation_reason)
end
end
I would like to call these inactivation reasons from my controller, which is app/controllers/student/session_controllers.rb file:
class Student::SessionsController < ApplicationController
layout 'session'
def create
student = Student.authenticate(params[:student_number], params[:password])
if student.active
session[:student_id] = student.id
redirect_to student_main_path, :notice => 'Welcome!'
elsif (student and student.student_status == 3) or (student and !student.active)
flash.now.alert = "You can't login because #REASON_I_AM_TRYING_TO_CALL"
render 'new'
else
....
end
end
I would like to show students their inactivation reason on the systems if they can't login.
How can I call my INACTIVATION_REASONS from this controller file? Is it possible?
Thanks in advance!
That's just a constant, so you can call it as constant anywhere.
StudentInactivationLog::INACTIVATION_REASONS
Update
I realized actually what you want is to use a reason code or short name saved in db to represent the string.
If so, I recommend you to use the short name directly as Hash. "id" looks redundant for this light case.
INACTIVATION_REASONS = {"HTY"=>"You didn't study enough!",
"KS"=>"Graduated!",
"SBK"=>"Other Reason"}
validates :inactivation_reason, inclusion: { in: INACTIVATION_REASONS.keys,
message: "%{value} is not a valid short name" }
def full_reason_message
INACTIVATION_REASONS[self.inactivation_reason]
end
Then, to show full message of a reason in controller
reason = #student.full_reason_message
This is the idea. I havn't checked your other model codes. You'll need to save reason as the short name instead of id, and need to revise/remove some code if you decide to use it in this way.