Rails 5 ActionCable on Heroku uninitialized constant NameError - ruby-on-rails

I'm aware of the fact that Heroku only allows 2 ports, i.e. 80 and 443 for connection, which is why you need to run a separate server for WebSockets. I came across this guide which claims to allow you to run both processes on the same server. I also changed the line:
if Faye::WebSocket.websocket?(env) to:
::WebSocket::Driver.websocket?(env) as per a comment by a user, since I'm not using Faye.
However, I get the error uninitialized constant ChatActionCable (NameError). I tried the guide on this page, yet no gain.
My application.rb file looks like this:
require_relative 'boot'
require 'rails/all'
module Third
class Application < Rails::Application
config.autoload_paths += %W(#{config.root}/lib)
end
end
TIA.
Update: The problem with the guide was the name of the middleware file. I changed it to chat_action_cable.rb and at least the middleware starts to run. Now the problem lies with ::WebSocket::Driver.websocket?(env) line as Uninitialized constant, NameError.

I believe the examples online are incomplete.
Your middleware should inherit from ActionCable::Connection::WebSocket
class ChatActionCable < ActionCable::Connection::WebSocket
def initialize(app, options={})
#app = app
end
def call(env)
if ::WebSocket::Driver.websocket?(env)
ActionCable.server.call(env)
else
#app.call(env)
end
end
end
For people looking how to setup ActionCable with Heroku, here is a step by step guide :
Create the middleware file :
/app/middleware/chat_action_cable.rb
class ChatActionCable < ActionCable::Connection::WebSocket
def initialize(app, options={})
#app = app
end
def call(env)
if ::WebSocket::Driver.websocket?(env)
ActionCable.server.call(env)
else
#app.call(env)
end
end
end
Set it up in config :
/config/environments/production.rb
config.middleware.use ChatActionCable
config.web_socket_server_url = "wss://your-heroku-app.herokuapp.com/"
Set redis plugin on heroku (it should already be included) :
heroku plugins:install heroku-redis (in case)
Create a new redis heroku instance :
heroku addons:create heroku-redis:hobby-dev -a young-home-19338
(replace young-home-19338 with your instance name)
It will set a REDIS_URL env, you can grab it with :
heroku config | grep REDIS
Now, set the config for actionCable :
/config/cable.yml
development: &development
:url: redis://localhost:6379
:host: localhost
:port: 6379
:timeout: 1
:inline: true
test: *development
production: &production
:url: redis://redistogo:192csd9c30ca49585ce9d85daf0fer90#tarpon.redistogo.com:49382
:host: tarpon.redistogo.com
:port: 49382
:password: 349f93483fv9erfv849dfvdfbds9
:inline: true
:timeout: 1
The host is between # and :
The port is after :
The password :
heroku run rails c
uri = URI.parse(<your redis URL>)
uri.password
That should be all.
I used these two articles as reference :
http://www.thegreatcodeadventure.com/deploying-action-cable-to-heroku/
https://blog.heroku.com/real_time_rails_implementing_websockets_in_rails_5_with_action_cable

Related

Override Radiant CMS's routes from the extension to set protocol to https

I am using a CMS called Radiant (version 0.9.1), Rails 2.3.18 and Ruby 1.8.7. I have to make the routes in this gem use the 'https'. I need to do it in such a way that I won't edit the gem source files itself, but rather override the gem's routes in the extension. How do I do this?
The configuration of the server really depends on what your server stack looks like
To configure your rails application to use SSL you need to force ssl
In your config/environments/production.rb:
config.force_ssl = true
To test ssl locally I would suggest trying thin as a webserver (also put config.force_ssl in development.rb to test this )
Add:
gem 'thin'
To your gemfile and start a thin ssl server:
$ thin start --ssl -p 3000
EDIT Rails 2 :
For Rails 2 this should work:
lib/force_ssl.rb
class ForceSSL
def initialize(app)
#app = app
end
def call(env)
if env['HTTPS'] == 'on' || env['HTTP_X_FORWARDED_PROTO'] == 'https'
#app.call(env)
else
req = Rack::Request.new(env)
[301, { "Location" => req.url.gsub(/^http:/, "https:") }, []]
end
end
end
config/production.rb
config.middleware.use "ForceSSL"
config/application.rb
require File.expand_path('../../lib/force_ssl.rb', __FILE__)
source: Force SSL using ssl_requirement in Rails 2 app

Resque workers fail immediately: undefined method `write' for nil:NilClass

I am using Resque (and resque-scheduler) in my Rails app to run a recurring job. This was working fine for me, until today. I made some code changes, which I thought were unrelated, but now every worker fails before the perform method is even entered (checked with a debug statement). The same worker method works fine when I run it in the rails console. It only fails via resque on the development localhost (Postgres DB).
The error shown in the resque console for the failed worker is:
Exception
NoMethodError
Error
undefined method `write' for nil:NilClass
There is no additional stack trace for the error. Any idea why this is failing?
Additional info:
lib/tasks/resque.rake
# Resque tasks
require 'resque/tasks'
require 'resque_scheduler/tasks'
namespace :resque do
task :setup do
require 'resque'
require 'resque_scheduler'
require 'resque/scheduler'
# you probably already have this somewhere
Resque.redis = 'localhost:6379'
# If you want to be able to dynamically change the schedule,
# uncomment this line. A dynamic schedule can be updated via the
# Resque::Scheduler.set_schedule (and remove_schedule) methods.
# When dynamic is set to true, the scheduler process looks for
# schedule changes and applies them on the fly.
# Note: This feature is only available in >=2.0.0.
#Resque::Scheduler.dynamic = true
# The schedule doesn't need to be stored in a YAML, it just needs to
# be a hash. YAML is usually the easiest.
Resque.schedule = YAML.load_file("#{Rails.root}/config/resque_schedule.yml")
# If your schedule already has +queue+ set for each job, you don't
# need to require your jobs. This can be an advantage since it's
# less code that resque-scheduler needs to know about. But in a small
# project, it's usually easier to just include you job classes here.
# So, something like this:
# require 'jobs'
end
end
task "resque:setup" => :environment do
#ENV['QUEUE'] = '*'
Resque.before_fork = Proc.new { ActiveRecord::Base.establish_connection }
end
config/resque.yml
development: localhost:6379
test: localhost:6379:1
staging: redis1.se.github.com:6379
fi: localhost:6379
production: redis1.ae.github.com:6379
initializers/resque.rb
rails_root = Rails.root || File.dirname(__FILE__) + '/../..'
rails_env = Rails.env || 'development'
resque_config = YAML.load_file(rails_root.to_s + '/config/resque.yml')
Resque.redis = resque_config[rails_env]
# This will make the tabs show up.
require 'resque_scheduler'
require 'resque_scheduler/server'
config/resque_schedule.yml
populate_game_data:
# you can use rufus-scheduler "every" syntax in place of cron if you prefer
every: 1m
# By default the job name (hash key) will be taken as worker class name.
# If you want to have a different job name and class name, provide the 'class' option
class: PopulateDataWorker
queue: high
args:
description: "This job populates the game and data"
Should note that the above files were not changed between working and non-working state.
We had the same issue this morning, and we pinned it down to a gem update by New Relic.
Version 3.5.6.46 of newrelic_rpm was yanked on rubygems, but it was somehow installed by bundle update.
They are still on the beta track for 3.5.6 and had some issues with Resque. See https://github.com/newrelic/rpm/commit/e81889c2bce97574ec682dafee12015e13ccb2e1
The fix was to add '~> 3.5.5.38' in our Gemfile for newrelic_rpm

How to set default port for Webrick?

I want to set the default port when I do
rails s
to 3010, instead of having to say:
rails s -p 3010
...every time. Any ideas?
You can override the Port by adding the following code to config/boot.rb
require 'rails/commands/server'
module Rails
class Server
alias :default_options_alias :default_options
def default_options
default_options_alias.merge!(:Port => 3010)
end
end
end

analytics if on production site, not a local or heroku subdomain

This question is about getting an analytics script to run in one of these three environments.
mysite.heroku.com
mysite-staging.heroku.com
mysite.com - this is the only one I want it to run on.
This is how I plan to lay it out, but any suggestions are welcome.
In my helper
def render_analytics
if local_request? || #on a Heroku subdomain
false
else
true
end
end
In my layout
<%= render 'shared/analytics' if render_analytics %>
render_analytics returns a boolean: true if on mysite.com, false if a local_request? or on a Heroku subdomain (ex: mysite.heroku.com || mysite-staging.heroku.com)
So how can I find out if it is coming from Heroku.
Use hostname:
if local_request? || `hostname` =~ /heroku/i
A cleaner solution is to set a constant in your environment during deployment that allows you to know whether you are on Heroku. As the Heroku deploy process is pretty opaque in terms of letting you dork around with config files, you might have your method memoize the result so you aren't doing a system call each time you render a view.
I just did something similar with a method that checks the database adapter to account for differences between my development environment and Heroku. Here's my lib/adapter.rb:
class Adapter
cattr_reader :adapter
def self.postgres?
##adapter ||= Rails.configuration.database_configuration[Rails.env]['adapter']
adapter == 'postgresql'
end
def self.mysql?
##adapter ||= Rails.configuration.database_configuration[Rails.env]['adapter']
adapter == 'mysql'
end
def self.sqlite?
##adapter ||= Rails.configuration.database_configuration[Rails.env]['adapter']
adapter.include?('sqlite')
end
end
Note that in addition to this, you have to change application.rb such that lib is added to your autoload path:
config.autoload_paths += Dir["#{config.root}/lib/**/"] # include all subdirectories

How to change Rails 3 server default port in develoment?

On my development machine, I use port 10524. So I start my server this way :
rails s -p 10524
Is there a way to change the default port to 10524 so I wouldn't have to append the port each time I start the server?
First - do not edit anything in your gem path! It will influence all projects, and you will have a lot problems later...
In your project edit script/rails this way:
#!/usr/bin/env ruby
# This command will automatically be run when you run "rails" with Rails 3 gems installed from the root of your application.
APP_PATH = File.expand_path('../../config/application', __FILE__)
require File.expand_path('../../config/boot', __FILE__)
# THIS IS NEW:
require "rails/commands/server"
module Rails
class Server
def default_options
super.merge({
:Port => 10524,
:environment => (ENV['RAILS_ENV'] || "development").dup,
:daemonize => false,
:debugger => false,
:pid => File.expand_path("tmp/pids/server.pid"),
:config => File.expand_path("config.ru")
})
end
end
end
# END OF CHANGE
require 'rails/commands'
The principle is simple - you are monkey-patching the server runner - so it will influence just one project.
UPDATE: Yes, I know that the there is simpler solution with bash script containing:
#!/bin/bash
rails server -p 10524
but this solution has a serious drawback - it is boring as hell.
I like to append the following to config/boot.rb:
require 'rails/commands/server'
module Rails
class Server
alias :default_options_alias :default_options
def default_options
default_options_alias.merge!(:Port => 3333)
end
end
end
One more idea for you. Create a rake task that calls rails server with the -p.
task "start" => :environment do
system 'rails server -p 3001'
end
then call rake start instead of rails server
Combining two previous answers, for Rails 4.0.4 (and up, presumably), this suffices at the end of config/boot.rb:
require 'rails/commands/server'
module Rails
class Server
def default_options
super.merge({Port: 10524})
end
end
end
We're using Puma as a web server, and dotenv to set environment variables in development. This means I can set an environment variable for PORT, and reference it in the Puma config.
# .env
PORT=10524
# config/puma.rb
port ENV['PORT']
However, you'll have to start your app with foreman start instead of rails s, otherwise the puma config doesn't get read properly.
I like this approach because the configuration works the same way in development and production, you just change the value of the port if necessary.
Inspired by Radek and Spencer...
On Rails 4(.0.2 - Ruby 2.1.0 ), I was able to append this to config/boot.rb:
# config/boot.rb
# ...existing code
require 'rails/commands/server'
module Rails
# Override default development
# Server port
class Server
def default_options
super.merge(Port: 3100)
end
end
end
All other configuration in default_options are still set, and command-line switches still override defaults.
Solution for Rails 2.3 - script/server:
#!/usr/bin/env ruby
require 'rack/handler'
module Rack::Handler
class << WEBrick
alias_method :old_run, :run
end
class WEBrick
def self.run(app, options={})
options[:Port] = 3010 if options[:Port] == 3000
old_run(app, options)
end
end
end
require File.dirname(__FILE__) + '/../config/boot'
require 'commands/server'
If you're using puma (I'm using this on Rails 6+):
To change default port for all environments:
The "{3000}" part sets the default port if undefined in ENV.
~/config/puma.rb
change:
port ENV.fetch('PORT') { 3000 }
for:
port ENV.fetch('PORT') { 10524 }
To define it depending on the environment, using Figaro gem for credentials/environment variable:
~/application.yml
local_db_username: your_user_name
​local_db_password: your_password
PORT: 10524
You can adapt this to you own environment variable manager.
You could install $ gem install foreman, and use foreman to start your server as defined in your Procfile like:
web: bundle exec rails -p 10524
You can check foreman gem docs here: https://github.com/ddollar/foreman for more info
The benefit of this approach is not only can you set/change the port in the config easily and that it doesn't require much code to be added but also you can add different steps in the Procfile that foreman will run for you so you don't have to go though them each time you want to start you application something like:
bundle: bundle install
web: bundle exec rails -p 10524
...
...
Cheers
For ruby > 3 and For rails > 7
in file app/config/puma.rb, update the port number.
port ENV.fetch("PORT") { 3200 }

Resources