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()
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've seen a lot of tutorials do something like this with the subscribed method:
class MessagesChannel < ApplicationCable::Channel
def subscribed
conversation = Conversation.find(params[:id])
stream_from "conversation_#{conversation.id}"
end
end
The idea being to allow for multiple conversations between multiple users. But I'm not clear on how that id param is sent to the method. If my routes are nested so that the conversation id is in the url, it seems like it should work.
resources :conversations, only: [:index, :create] do
resources :messages, only: [:index, :create]
end
However, the channel code above gives this error:
[ActionCable] [user] Registered connection (Z2lkOi8vZnJhY3Rpb25jbHViL1VzZXIvMQ)
[ActionCable] [user] Could not execute command from ({"command"=>"subscribe", "identifier"=>"{\"channel\":\"MessagesChannel\"}"}) [ActiveRecord::RecordNotFound - Couldn't find Conversation without an ID]: /Users/user/.rvm/gems/ruby-2.5.0/gems/activerecord-5.2.0/lib/active_record/relation/finder_methods.rb:431:in `find_with_ids' | /Users/user/.rvm/gems/ruby-2.5.0/gems/activerecord-5.2.0/lib/active_record/relation/finder_methods.rb:69:in `find' | /Users/user/.rvm/gems/ruby-2.5.0/gems/activerecord-5.2.0/lib/active_record/querying.rb:5:in `find' | /Users/user/.rvm/gems/ruby-2.5.0/gems/activerecord-5.2.0/lib/active_record/core.rb:167:in `find' | /Users/user/code/project/app/channels/messages_channel.rb:3:in `subscribed'
How would I pass the conversation id to the subscribed method so that my users can have multiple private conversations?
Update 1: This is my messages.coffee file
App.messages = App.cable.subscriptions.create
channel: "MessagesChannel"
conversation_id: 1
connected: ->
console.log 'Connected'
disconnected: ->
console.log 'Disconnected'
received: (data) ->
console.log 'Received'
$('#messages').append(data.message)
speak: (message, conversation_id) ->
#perform 'speak', message: message, conversation_id: conversation_id
$(document).on 'turbolinks:load', ->
submit_message()
scroll_bottom()
submit_message = () ->
$('#response').on 'keydown', (event) ->
if event.keyCode is 13
App.messages.speak(event.target.value)
event.target.value = ""
event.preventDefault()
scroll_bottom = () ->
$('#messages').scrollTop($('#messages')[0].scrollHeight)
This is how I am tackling this,
In my view form:
<%= form_for Message.new,remote: true,html: {id: 'new-message',multipart: true} do |f| %>
<%= f.hidden_field :chat_id, value: chat.id,id: 'chat-id' %>
....
Notice the id I have given to form and then chat_id field.
Then in my chat.js:
return App.chat = App.cable.subscriptions.create({
channel: "ChatChannel",
chat_id: $('#new-message').find('#chat-id').val()
}
Now I can use this chat_id param in my ChatChannel like this:
def subscribed
stream_from "chat_#{params['chat_id']}_channel"
end
EDIT:
In your case:
your MessagesChannel subscribed action should be like this:
def subscribed
stream_from "conversation_#{params[:conversation_id]}"
end
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.
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.
Here is code of room.coffee :
App.room = App.cable.subscriptions.create "RoomChannel",
connected: ->
disconnected: ->
received: (data) ->
$('#messages').append "<p>#{data}</p>"
speak: (message) ->
#perform 'speak' , message: message
cable.coffee :
#App ||= {}
App.cable = ActionCable.createConsumer()
rooms.coffee:
$ ->
$messages = $('messages')
$messages.scrollTop $messages.prop('scrollHieght')
$('#message_input').focus()
$(document).on 'keypress','message_input','e'->
if e.keycode == 13 and e.target.value
App.room.speak(e.target.value)
e.target.value = ''
e.preventDefault()
roomchannel:
class RoomChannel < ApplicationCable::Channel
def subscribed
stream_from "room_channel"
end
def unsubscribed
# Any cleanup needed when channel is unsubscribed
end
def speak(data)
message.create content: data['message']
end
end
Broadcostmessage:
def perform(message)
Actioncable.server.broadcast 'room_channel',render_message(message)
end
private
def render_message(message)
ApplicationController.renderer.render_message
end
when create the new message it will not automatically load all the messages of my browser untill the page in not reload.
I had a similar issue. Try including cable.coffee/js in the room view.
<%= javascript_include_tag 'cable', 'data-turbolinks-track': 'reload' %>
You'll want to add cable.js to /config/initializers/assets.rb as well.
Rails.application.config.assets.precompile += %w( cable.js )
This old question that came up in a Google search, but did anyone mention the typo in:
$messages.scrollTop $messages.prop('scrollHieght')
That should be:
$messages.scrollTop $messages.prop('scrollHeight')