How can I provide parameters when creating a child live view in Phoenix Live View? - phoenix-framework

In a live view I have this snippet:
<div id="chat"><%= live_render(#socket, MyAppWeb.ChatLive.Index, id: "watch-chats", guest: #guest) %></div>
In ChatLive.Index I have this mount function:
#impl true
def mount(%{"guest_id" => guest_id}, _session, socket) do
...
end
def mount(:not_mounted_at_router, session, socket) do
mount(%{"guest_id" => socket.assigns.guest.id}, session, socket)
end
but it gives this error:
key :guest not found in: %{flash: %{}, live_action: nil}
at the line with mount(%{"guest_id" => socket.assigns.guest.id}, session, socket).
How can I pass in a parameter such as guest to the live_render call and pick it up within the mount function?

If you need to pass data, you would need to put it in the assigns of the socket.

Related

Send additional information with Raven.send_event

I have integrated Sentry in my Ruby On Rails application and I want to send a custom event when a specific case happens, I am able to send the events with
Raven.send_event({:message => 'Custom event'})
How do i send additional information related to the same. I need to send for example, user_id, email_id, login_name and other custom parameters.
You can set user_context to raven using Raven.user_context method
You can write a method to set context in Application Controller and call the same in before action
For example
Raven.user_context(
# a unique ID which represents this user
id: current_user.id, # 1
# the actor's email address, if available
email: current_user.email, # "example#example.org"
# the actor's username, if available
username: current_user.username, # "foo"
# the actor's IP address, if available
ip_address: request.ip # '127.0.0.1'
)
You can write in application controller as
class ApplicationController < ActionController::Base
before_action :set_raven_context, if: proc { Rails.env.production? } //If you want to set only in prod
def set_raven_context
Raven.user_context(
# a unique ID which represents this user
id: current_user.id, # 1
# the actor's email address, if available
email: current_user.email, # "example#example.org"
# the actor's username, if available
username: current_user.username, # "foo"
# the actor's IP address, if available
ip_address: request.ip # '127.0.0.1'
)
#You can also set extra context using `Raven.extra_context`
Raven.extra_context app: url, environment: Rails.env, time: Time.now
end
end
Addition to #opensource-developer answer:
You can find more available extra parameters here => Sentry Usage Page
For anyone facing similar issue, i managed to do it via, under the key extra you can specify any extra keys which you want to send to sentry for further debugging
Raven.capture_message "custom message",
logger: 'logger',
extra: {
time_at: Time.now
},
tags: {
env: Rails.env
}

application and actioncable won't share cookie

I am using devise for authentication, but when I implemented your method I got "An unauthorized connection attempt was rejected"
After hours of searching I found out that:
cookies.signed['user.id']
returns nil. In the following code block.
def find_verified_user
if verified_user = User.find_by(id: cookies.signed['user.id'])
verified_user
else
reject_unauthorized_connection
end
end
I checked and there is definitely a cookie but it does not contain the cookie data set by Devise.
To check if the 'user.id' actually is set I raise it in the view. This, as excepted, return the user id
Signed in as ##{cookies.signed[:username]}.
- raise(cookies.signed['user.id'].inspect)
%br/
%br/
#messages
%br/
%br/
= form_for :message, url: messages_path, remote: true, id: 'messages-form' do |f|
= f.label :body, 'Enter a message:'
%br/
= f.text_field :body
%br/
= f.submit 'Send message'
My question/issue:
It seems like the cookie is not available at the actioncable server.
Is there a way to share the cookie set by Devise with the cable server?
https://github.com/stsc3000/actioncable-chat.git
Check the client-side JavaScript file that connects to your Action Cable server. Some tutorials have you put that in 'app/assets/javascripts/application_cable.coffee' and others in 'app/assets/javascripts/channels/index.coffee' but it looks like this:
#= require cable
#App = {}
App.cable = Cable.createConsumer("ws://cable.example.com:28080")
You need the WebSocket address to point to your Cable server and that address needs to share a cookie namespace with the rest of your app. Most likely yours is pointing at the wrong place, so for example if you're working on this locally you would need to change:
App.cable = Cable.createConsumer("ws://cable.example.com:28080")
to
App.cable = Cable.createConsumer("ws://localhost:28080")
assuming of course that your Cable server is running on port 28080 (specified in the bin/cable executable).
Also make sure to clear your browser cache so the updated file is the one being used by the browser.
Not sure if you got it running by now, but I had the same issue on Rails 5.0.0.beta3. I did not change to the following line:
App.cable = Cable.createConsumer("ws://localhost:3000")
I kept it as it was before
#App ||= {}
App.cable = ActionCable.createConsumer()
But what I did change had to do with the Cookies. No matter what. The cookie for my user_id would not display. So I made a work around. I got the cookie to save the username instead, then I was finally able to see it in the find_verified_user function call.
After the user logs in(Sessions#create), I call a helper function:
sessions_helper.rb
def set_cookie(user)
the_username = user.username.to_s
cookies.permanent.signed[:username] = the_username
end
The new find_verified_user
def find_verified_user
if current_user = User.find_by_username(cookies.signed[:username])
current_user
else
reject_unauthorized_connection
end
end
This may or may not be the best solution, but after hours of confusion and frustration this worked for my situation. I hope this can help someone
You need to configure in config/initializers/session_store.rb
# using cookie store
if Rails.env.production?
# to share across subdomains
Rails.application.config.session_store :cookie_store,
key: '_app_name_session', domain: ".example.com"
else
# to share with any domain
Rails.application.config.session_store :cookie_store,
key: '_app_name_session', domain: :all, tld_length: 2
end
#for redis store
elsif Rails.env.production?
# to share across subdomains
Rails.application.config.session_store :redis_store, {
servers: [
{ host: YourRedisHost, port: YourRedisPort},
],
key: '_app_name_session',
expire_after: 1.day,
domain: '.example.com'
}
else
# to share with any domain
Rails.application.config.session_store :redis_store, {
servers: [
{ host: YourRedisHost, port: YourRedisPort},
],
key: '_app_name_session',
expire_after: 1.day,
domain: :all,
tld_length: 2
}
end
the problem I found is: I have 2 different users logged in. One is logged in at 127.0.0.1, and the other is logged in at localhost.
so when I access my website using 127.0.0.1:3000, but my cable is configured to run on localhost like this:
config.action_cable.url = "ws://localhost:3000/cable"
in config/environments/development.rb
the user logged in at 127.0.0.1 makes cable request to "ws://localhost:3000/cable" (as per configuration), but this way the cookie saved for localhost is sent, even though I am making the request from 127.0.0.1, which is different user(or no user at all).
So the bottom root is actually what Pwnrar points above, cable address configuration and the way you access your website.
So to solve the problem, always access your website using the server address configured for your cable, otherwise cookies get mixed in.

How to access parameters of method being watched in ActiveSupport::Notification instrument?

I have been playing around with ActiveSupport::Notifications recently and I can't figure out how to get to the parameters of a method run in a block. Let me use an example.
You set up the following instrument.
ActiveSupport::Notifications.instrument('render', extra: :information) do
render text: 'Foo'
end
You can access the information by subscribing to the event.
ActiveSupport::Notifications.subscribe('render') do |name, start, finish, id, payload|
name # => String, name of the event (such as 'render' from above)
start # => Time, when the instrumented block started execution
finish # => Time, when the instrumented block ended execution
id # => String, unique ID for this notification
payload # => Hash, the payload
end
Looking at the documentation, I can't find anything that would give me the parameters of the render method - {text: 'foo'}
What am I missing? Is this possible? Thanks.
render text: 'Foo' is not a parameter; that's a Rails method that renders content back to the client.
ActiveSupport::Notificaitons.instrument("render", text: "foo")
is how you would "submit" the values to the notifier.
ActiveSupport::Notifications.subscribe("render") do |name, start, finish, id, payload|
text = payload[:text]
end
is how you would reference it in the subscriber.

Multi-Threading in Ruby

I have a TCPserver that I made in ruby, the server seems to work, I can see that two or more clients can connect and be served by the server, but, they sometime get stuck (as in need to wait for the other client to disconnect or just get unresponsive), usually after the "pass_ok" bit, When connecting only with one client I don't see this issue.
Here is my code:
def self.main_server
begin
server = TCPServer.open(#port)
rescue Exception => e
CoreLogging.syslog_error("Cant start server: #{e}")
end
#main_pid = Process.pid
# Main Loop
Thread.abort_on_exception = true
while true
Thread.fork(server.accept) do |client|
#client = client
sock_domain, remote_port, remote_hostname, remote_ip = #client.peeraddr # Get some info on the incoming connection
CoreLogging.syslog_error("Got new connection from #{#client.peeraddr[3]} Handeled by Thread: #{Thread.current}") # Log incoming connection
#client.puts "Please enter password: " # Password testing (later will be from a config file or DB)
action = #client.gets(4096).chomp # get client password response 'chomp' is super important
if action == #password
# what to do when password is right
pass_ok
Thread.exit
else
# what to do when password is wrong
pass_fail
Thread.exit
end
end
begin
CoreLogging.syslog_error("Thread Ended (SOFT)")
rescue Exception => e
CoreLogging.syslog_error("Thread was killed (HARD)")
end
end
end
I'll leave it here for future reference and hope someone in a close situation will find it useful.
The issue was the global #client variable, which got overwritten every new thread and then inherited to the subclasses inside the thread.
using a local client variable (without the '#') got it to work as supposed.

How can I only read one line of data from a TCPSocket in Ruby?

I'm using the following code to connect to a network service i'm writing (thats backed by EventMachine) and I'm having a bit of trouble getting into a situation allowing me to use one socket connection to execute multiple commands.
#!/usr/bin/env ruby
require 'socket'
opts = {
:address => "0.0.0.0",
:port => 2478
}
connection = TCPSocket.open opts[:address], opts[:port]
# Get ID
connection.print "ID something"
puts connection.read
# Status
connection.print "STATUS"
puts connection.read
# Close the connection
connection.close
Here's what my EventMachine server hander looks like...
module ConnectionHandler
def receive_data data
send_data "Some output #{data}"
end
end
However, my first ruby script hangs when it executes connection.read as I presume its waiting for the connection to close so it knows its got all of the data? This is not what I want to happen.
My socket server will just take one command (on one line) and return one line of output.
Any ideas how I can do this? Thanks.
It turns out the connection.gets method will return a line of data received if the server sends a response ending in a \n character. So I just added \n to the end of my send_data call and switch to using puts connection.gets and it worked great!

Resources