NoMethodError - undefined method `safe_constantize' for nil:NilClass - ruby-on-rails

I am trying to create an ActionCable chatroom following the actioncable guide on GoRails.com.
As I am trying to connect to ActionCable I get following error:
NoMethodError - undefined method `safe_constantize' for nil:NilClass
[ActionCable] [User 1] Could not execute command from
{"command"=>"subscribe", "identifier"=>"
{\"ChatroomsChannel\":\"ChatroomsChannel\",\"room_id\":1}"})
[NoMethodError - undefined method `safe_constantize' for
nil:NilClass]: /Users/Mathias/.rvm/gems/ruby-
2.2.3#chatroomv1/gems/actioncable-
5.0.0.rc2/lib/action_cable/connection/subscriptions.rb:29:in `add' |
/Users/Mathias/.rvm/gems/ruby-2.2.3#chatroomv1/gems/actioncable-
5.0.0.rc2/lib/action_cable/connection/subscriptions.rb:15:in
`execute_command' | /Users/Mathias/.rvm/gems/ruby-
2.2.3#chatroomv1/gems/actioncable-
5.0.0.rc2/lib/action_cable/connection/base.rb:88:in
`dispatch_websocket_message' | /Users/Mathias/.rvm/gems/ruby-
2.2.3#chatroomv1/gems/actioncable-
5.0.0.rc2/lib/action_cable/server/worker.rb:54:in `block in invoke' |
/Users/Mathias/.rvm/gems/ruby-2.2.3#chatroomv1/gems/actioncable-
5.0.0.rc2/lib/action_cable/server/worker.rb:39:in `block in work'
My app/channels/chatrooms_channel.rb looks like this:
def subscribed
#chatroom = Chatroom.find(params[:room_id])
#puts params[:room_id]
#current_user.join_room(#chatroom)
stream_from "chatrooms:#{#chatroom.id}"
# stream_from "some_channel"
end
Maybe it is worth mentioning that I upgraded to rails 5.0.0.rc2 from rails 4.
Thank you,

in assets/javascripts/channels/chatroom.coffee
If you need to assign params, channel name must be in params hash too:
App['chatroom' + roomId] = App.cable.subscriptions.create {
channel: 'ChatroomChannel',
room_id: roomId
}, {
received: ->
...
}
And only if you don't need params, you can write channel name without hash:
App.chatroom = App.cable.subscriptions.create 'ChatroomChannel',
received: ->
...

Related

ActiveJob perform_later from inside Redis subscription block

I want to use the ruby redis client to subscribe to a channel. When subscription was successful I want to start an ActiveJob (Sidekiq is used) job using perform_later. This Job will then do some work and produce a result which will be published to the channel Redis channel.
This produces the following error:
Processing by Api::LocationController#yyy as */*
Parameters: {"zip_code"=>"503400"}
[ActiveJob] Failed enqueuing XXXJob to Sidekiq(default): NoMethodError (undefined method `call_pipeline' for #<Redis::SubscribedClient:0x00007fd9222cdf78 #client=#<Redis::Client:0x00007fd921e1b890 #options={:url=>"redis://localhost?db=xxx", :scheme=>"redis", :host=>"localhost", :port=>6379, :path=>nil, :read_timeout=>0, :write_timeout=>5.0, :connect_timeout=>5.0, :timeout=>5.0, :username=>nil, :password=>nil, :db=>0, :driver=>Redis::Connection::Ruby, :id=>nil, :tcp_keepalive=>0, :reconnect_attempts=>1, :reconnect_delay=>0.0, :reconnect_delay_max=>0.5, :inherit_socket=>false, :logger=>nil, :sentinels=>nil, :role=>:master, :_parsed=>true}, #reconnect=true, #logger=nil, #connection=#<Redis::Connection::Ruby:0x00007fd921df08c0 #sock=#<Redis::Connection::TCPSocket:fd 26>>, #command_map={}, #pending_reads=-3, #connector=#<Redis::Client::Connector:0x00007fd921e1a2d8 #options={:url=>"redis://localhost?db=xxx", :scheme=>"redis", :host=>"localhost", :port=>6379, :path=>nil, :read_timeout=>5.0, :write_timeout=>5.0, :connect_timeout=>5.0, :timeout=>5.0, :username=>nil, :password=>nil, :db=>0, :driver=>Redis::Connection::Ruby, :id=>nil, :tcp_keepalive=>0, :reconnect_attempts=>1, :reconnect_delay=>0.0, :reconnect_delay_max=>0.5, :inherit_socket=>false, :logger=>nil, :sentinels=>nil, :role=>:master, :_parsed=>true}>, #pid=54943>>)
Completed 500 Internal Server Error in 26ms (Allocations: 2374)
NoMethodError (undefined method `call_pipeline' for #<Redis::SubscribedClient:0x00007fd9222cdf78 #client=#<Redis::Client:0x00007fd921e1b890 #options={:url=>"redis://localhost?db=xxx", :scheme=>"redis", :host=>"localhost", :port=>6379, :path=>nil, :read_timeout=>5.0, :write_timeout=>5.0, :connect_timeout=>5.0, :timeout=>5.0, :username=>nil, :password=>nil, :db=>0, :driver=>Redis::Connection::Ruby, :id=>nil, :tcp_keepalive=>0, :reconnect_attempts=>1, :reconnect_delay=>0.0, :reconnect_delay_max=>0.5, :inherit_socket=>false, :logger=>nil, :sentinels=>nil, :role=>:master, :_parsed=>true}, #reconnect=true, #logger=nil, #connection=#<Redis::Connection::Ruby:0x00007fd921df08c0 #sock=nil>, #command_map={}, #pending_reads=-3, #connector=#<Redis::Client::Connector:0x00007fd921e1a2d8 #options={:url=>"redis://localhost?db=xxx", :scheme=>"redis", :host=>"localhost", :port=>6379, :path=>nil, :read_timeout=>5.0, :write_timeout=>5.0, :connect_timeout=>5.0, :timeout=>5.0, :username=>nil, :password=>nil, :db=>0, :driver=>Redis::Connection::Ruby, :id=>nil, :tcp_keepalive=>0, :reconnect_attempts=>1, :reconnect_delay=>0.0, :reconnect_delay_max=>0.5, :inherit_socket=>false, :logger=>nil, :sentinels=>nil, :role=>:master, :_parsed=>true}>, #pid=54943>>):
app/controllers/api/location_controller.rb:10:in `block (3 levels) in yyy'
app/controllers/api/location_controller.rb:8:in `block in yyy'
app/controllers/api/location_controller.rb:5:in `yyy'
The yyy function is part of a controller that gets called on a GET request:
def yyy
REDIS_POOL.with { |r|
data = r.get("key")
if data.nil?
r.subscribe("channel") { |on|
on.subscribe {
XXXJob.perform_later(params)
}
on.message { |_, message|
puts message
render json: message
r.unsubscribe("channel")
}
}
else
render json: data
end
}
end
It seems to me like there's some context being lost from the Redis subscribe block which is important for the perform_later function. The same code works if I move the XXXJob.perform_later(params) call above the r.subscribe("channel") call and thus outside of the block. I however want to make sure that the job is only started after the subscription to the Redis channel was successful. Is there any way to achieve this?
It's difficult. It will not work because Sidekiq's default connection pool always reuses the thread's current connection, which is handling the subscribe call and that call is not reentrant. You need to manually create a separate connection pool and use it directly.
ANOTHER_POOL = ConnectionPool.new { Redis.new }
on.subscribe do
Sidekiq::Client.via(POOL) do
SomeWorker.perform_async(1,2,3)
SomeOtherWorker.perform_async(1,2,3)
end
end

RuntimeError - Unable to find subscription with identifier: when ios app try to send a message

I am using WebSocket for comments on a post. ios user subscribe to the comment_channel and I got confirmation that the user successfully subscribed to comment_channel.
[ActionCable] [23] CommentRoomsChannel is transmitting the subscription confirmation
[ActionCable] [23] CommentRoomsChannel is streaming from comments_291
But the issue is when the user tries to comment I get this error.
Could not execute command from ({"identifier"=>"{\"channel\":\"CommentRoomsChannel\",\"post_id\":\"291\"}", "command"=>"message", "data"=>"{\"description\":\"Aaaa\",\"post_id\":\"291\"}"}) [RuntimeError - Unable to find subscription with identifier: {"channel":"CommentRoomsChannel","post_id":"291"}]: /home/ubuntu/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/actioncable-6.0.3.2/lib/action_cable/connection/subscriptions.rb:74:in `find' | /home/ubuntu/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/actioncable-6.0.3.2/lib/action_cable/connection/subscriptions.rb:55:in `perform_action' | /home/ubuntu/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/actioncable-6.0.3.2/lib/action_cable/connection/subscriptions.rb:19:in `execute_command' | /home/ubuntu/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/actioncable-6.0.3.2/lib/action_cable/connection/base.rb:87:in `dispatch_websocket_message' | /home/ubuntu/.rbenv/versions/2.7.1/lib/ruby/gems/2.7.0/gems/actioncable-6.0.3.2/lib/action_cable/server/worker.rb:59:in `block in invoke'
I have added delay but this error even occurs when users try to comment for the first time.
here's my code:
class CommentRoomsChannel < ApplicationCable::Channel
def subscribed
stream_from "comments_#{params[:post_id]}"
end
def unsubscribed
stop_all_streams
end
def receive(data)
post = Post.find_by(id: data["post_id"])
post_data = {
id: comment['id'],
post_id: comment['post_id'],
user_id: comment['user_id'],
description: comment['description'].to_s,
user: {
id: current_user['id'],
first_name: current_user['first_name'],
last_name: current_user['last_name'],
username: current_user['username'],
is_hide_name: current_user['is_hide_name'],
profile_image: current_user['profile_image']
}
}
comment = current_user.comments.create!(description: data["description"], post_id: data["post_id"])
ActionCable.server.broadcast "comments_#{data['post_id']}", post_data.as_json
NotificationService.new(post.user_id, current_user.id, 2, data['post_id'].to_i).call unless post.user_id == current_user.id
end
end
this error only occurs when the ios user tries to comment, the same code is working with android.
I resoved the issue by removing the parameter from the identifier. so before the issue my command was
{"identifier"=>"{\"channel\":\"CommentRoomsChannel\",\"post_id\":\"291\"}"
after removing parameter
{"identifier"=>"{\"channel\":\"CommentRoomsChannel\"}"

Ruby error: undefined method `casecmp' for nil:NilClass

I have the following code (with a few debug lines added):
Ruby:
re_dict = {}
re_dict['state'] = 'pending' #set initial status to pending
puts re_dict, re_dict.class.to_s
puts re_dict['state'], re_dict['state'].class.to_s
puts re_dict['state'].casecmp('pending')
while re_dict['state'].casecmp('pending') == 0 do
stuff
end
Output
state: pending
state class: String
class compared to 'pending': 0
Completed 500 Internal Server Error in 66ms
NoMethodError (undefined method `casecmp' for nil:NilClass):
What is causing this? How am I losing the value of my hash?
This will happen only when you remove 'state' key from re_dict hash inside your while loop:
while re_dict['state'].casecmp('pending') == 0 do
puts re_dict
re_dict = {}
end
#=> {"state"=>"pending"}
#=> NoMethodError: undefined method `casecmp' for nil:NilClass
Since, key 'state' is not available anymore, calling re_dict['state'] will give nil, that's why you're getting undefined method casecmp' for nil:NilClass

undefined method `each' for "1":String

Im getting a weird string error when trying to assign attributes on a Rails model:
# POST /apartments
# POST /apartments.xml
def create
#apartment = Apartment.new(params[:apartment])
Params
{"utf8"=>"✓",
"authenticity_token"=>"Kxxxxx=",
"apartment"=>{"street"=>"123 Main St",
"unit"=>"",
"hide_address"=>"0",
"city"=>"White Plains",
"state"=>"New York",
"zipcode"=>"10840",
"country"=>"United States",
"price"=>"4000",
"security_deposit"=>"",
"application_fee"=>"",
"bedrooms"=>"4",
"bathrooms"=>"3",
"size"=>"",
"available_date"=>"",
"unit_amenities"=>["air conditioning"],
"description"=>"",
"rooms"=>"1"},
"commit"=>"Publish Your Listing"}
Error
NoMethodError in ApartmentsController#create
undefined method `each' for "1":String
Thoughts?

Google Calendar in Rails 3 App

I am using the 0.5.5 version of the gcal4ruby gem on my rails 3.0 app and I am
seemingly having trouble creating an event. I can start service, and
create a calendar just fine, but I get the following error when I try
to create an event:
NoMethodError: undefined method `debug' for #<GCal4Ruby::Calendar:
0x1036d8a68>
from /opt/local/lib/ruby/gems/1.8/gems/gcal4ruby-0.5.5/lib/gcal4ruby/
event.rb:242:in `to_xml'
from /opt/local/lib/ruby/gems/1.8/gems/gcal4ruby-0.5.5/lib/gcal4ruby/
event.rb:236:in `map'
from /opt/local/lib/ruby/gems/1.8/gems/gcal4ruby-0.5.5/lib/gcal4ruby/
event.rb:236:in `to_xml'
from /opt/local/lib/ruby/gems/1.8/gems/gcal4ruby-0.5.5/lib/gcal4ruby/
event.rb:230:in `create'
from /opt/local/lib/ruby/gems/1.8/gems/gdata4ruby-0.1.5/lib/
gdata4ruby/gdata_object.rb:155:in `save'
from /opt/local/lib/ruby/gems/1.8/gems/gcal4ruby-0.5.5/lib/gcal4ruby/
event.rb:225:in `save'
from (irb):92
I looked at the code in this line, and it seems like it is checking
whether or not the service.debug = true, not the calendar, so i'm not
sure why it is throwing this error. Here's how I got here in IRB,
line by line:
service = GCal4Ruby::Service.new
service.authenticate(MY_GOOGLE_LOGIN, MY_GOOGLE_PASS)
calendar = GCal4Ruby::Calendar.find(service, 'Test 2', :first)
event = GCal4Ruby::Event.new(calendar)
event.title = "test title"
event.content = "test content"
event.where = "my house"
event.start = Time.now
event.end = 2.hours.from_now
event.all_day = false
event.calendar = calendar[0]
event.save
NoMethodError: undefined method `debug' for #<GCal4Ruby::Calendar:
0x1036d9990>
from /opt/local/lib/ruby/gems/1.8/gems/gcal4ruby-0.5.5/lib/gcal4ruby/
event.rb:242:in `to_xml'
from /opt/local/lib/ruby/gems/1.8/gems/gcal4ruby-0.5.5/lib/gcal4ruby/
event.rb:236:in `map'
from /opt/local/lib/ruby/gems/1.8/gems/gcal4ruby-0.5.5/lib/gcal4ruby/
event.rb:236:in `to_xml'
from /opt/local/lib/ruby/gems/1.8/gems/gcal4ruby-0.5.5/lib/gcal4ruby/
event.rb:230:in `create'
from /opt/local/lib/ruby/gems/1.8/gems/gdata4ruby-0.1.5/lib/
gdata4ruby/gdata_object.rb:155:in `save'
from /opt/local/lib/ruby/gems/1.8/gems/gcal4ruby-0.5.5/lib/gcal4ruby/
event.rb:225:in `save'
I must be missing something here, but I'm not sure what. Obviously I
am getting a valid calendar object...is there some attribute that I am
missing?
Alternatively, if anyone has any other suggestions as to what to use for a calendar solution I would love to hear it. Basically I need to allow users to add predefined events to a calendar, which can be recurring, and ideally I'd like for them to be able to export these calendars for use in outlook, ical, etc... Is there a way to do this via web?
Thanks!
well this is annoying, i seem to have it working. it was a matter of changing:
event = GCal4Ruby::Event.new(calendar)
to
event = GCal4Ruby::Event.new(service)
even though that's directly contrary to what i had read in the documentation.

Resources