Rails: patch ActionCable (add new adapter) - ruby-on-rails

Question refers to this pull request: https://github.com/rails/rails/pull/23211
Since it has not been merged yet, I want to monkey patch this into my application.
I put this subscription adapter to lib/core_extenstions/subsctiption_adapter/test.rb
Now, in initialize monkey_patch.rb I try:
#require 'lib/action_cable/test.rb'
#autoload ActionCable::SubscriptionAdapter::Test
module ActionCable
module SubscriptionAdapter
puts Rails.root.join('lib/core_extensions/action_cable/test.rb').inspect
autoload :Test, Rails.root.join('lib/core_extensions/action_cable/test.rb')
puts Test.inspect
end
end
Then:
MacBook-Pro-Vaceslav:mvp_c slava$ bundle exec rails c test
Running via Spring preloader in process 93788
Loading test environment (Rails 5.0.2)
irb: warn: can't alias context from irb_context.
irb(main):001:0> server = ActionCable.server
=> #<ActionCable::Server::Base:0x007f890e812628 #mutex=#<Monitor:0x007f890e812600 #mon_owner=nil, #mon_count=0, #mon_mutex=#<Thread::Mutex:0x007f890e8125b0>>, #pubsub=nil, #worker_pool=nil, #event_loop=nil, #remote_connections=nil>
irb(main):002:0> server.pubsub
LoadError: Could not load 'action_cable/subscription_adapter/test'. Make sure that the adapter in config/cable.yml is valid. If you use an adapter other than 'postgresql' or 'redis' add the necessary adapter gem to the Gemfile.
So source of issue is here:
https://github.com/rails/rails/blob/master/actioncable/lib/action_cable/server/configuration.rb
def pubsub_adapter
adapter = (cable.fetch("adapter") { "redis" })
path_to_adapter = "action_cable/subscription_adapter/#{adapter}"
begin
require path_to_adapter
rescue Gem::LoadError => e
raise Gem::LoadError, "Specified '#{adapter}' for Action Cable pubsub adapter, but the gem is not loaded. Add `gem '#{e.name}'` to your Gemfile (and ensure its version is at the minimum required by Action Cable)."
rescue LoadError => e
raise LoadError, "Could not load '#{path_to_adapter}'. Make sure that the adapter in config/cable.yml is valid. If you use an adapter other than 'postgresql' or 'redis' add the necessary adapter gem to the Gemfile.", e.backtrace
end
adapter = adapter.camelize
adapter = "PostgreSQL" if adapter == "Postgresql"
"ActionCable::SubscriptionAdapter::#{adapter}".constantize
end
Not sure how to workaround this.

This is quite bad I suppose, but don't see any other way with this implementation other than:
module ActionCable
module Server
class Configuration
def pubsub_adapter
if Rails.env == 'test'
require Rails.root.join('lib/core_extensions/action_cable/test.rb')
ActionCable::SubscriptionAdapter::Test
else
super
end
end
end
end
end
Any improvements are welcome.
EDIT
possibly much better way is to stub it needed test suits:
before do
ActionCable.server.instance_variable_set(:#pubsub, ActionCable::SubscriptionAdapter::Test)
end

Related

How to create an inline / minimal Rails app?

It's possible, for example, to use ActiveRecord inline in a Ruby script. I like this a lot to report bugs, test features and share gists.
I'm wondering if the same could be done for a Rails webservice? (I'm mainly interested in getting the controller layer to work, the rest should be easy to add on demand.) Something along these lines:
begin
require "bundler/inline"
rescue LoadError => e
$stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
raise e
end
gemfile(true) do
source "https://rubygems.org"
gem 'rails', '~> 6.0.0'
end
require 'rails/commands'
APP_PATH = File.expand_path('config/application', __dir__)
Rails::Command.invoke('server')
When toying around with this it did seem that an external entry point (APP_PATH) is required. So alternatively, an acceptable approach would be to cram all config into the single entry point. I couldn't get this to work so far.
The Rails Initialization Process is the best resource I found about this so far.
I produced a minimal Rails app to get started as follows:
rails new min-rails --skip-keeps --skip-action-mailer --skip-action-mailbox --skip-action-text --skip-active-record --skip-active-storage --skip-puma --skip-action-cable --skip-sprockets --skip-spring --skip-listen --skip-javascript --skip-turbolinks --skip-test --skip-system-test --skip-bootsnap --api
cd min-rails
rm -rf app/jobs app/models config/initializers config/locales lib log public tmp vendor config/environments/test.rb config/environments/production.rb config/credentials.yml.enc config/master.key bin/rake bin/setup bin/bundle
I ended up with the following script:
inline-rails.rb
begin
require "bundler/inline"
rescue LoadError => e
$stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
raise e
end
gemfile(true) do
source "https://rubygems.org"
gem 'rails', '~> 6.0.0'
end
require "action_controller/railtie"
class App < Rails::Application
routes.append do
get "/hello/world" => "hello#world"
end
config.consider_all_requests_local = true # display errors
end
class HelloController < ActionController::API
def world
render json: {hello: :world}
end
end
App.initialize!
Rack::Server.new(app: App, Port: 3000).start
Run it as:
ruby inline-rails.rb

How can I use the methods in ActiveRecord::ConnectionAdapters::SchemaStatements in isolation?

I am writing a script which I can write in postgresql but would like to write using ActiveRecord. Most of the methods that I would like to use are located in ActiveRecord::ConnectionAdapters::SchemaStatements. Because this is a module how can I use these methods in an ActiveRecord::Base.transaction block. I've already tried calling the methods directly like so:
ActiveRecord::ConnectionAdapters::SchemStatements.drop_table etc.
This doesn't seem to work. Is it even possible to use ActiveRecord like this?
You need to require active_record, establish a connection to the database and then you can use all the methods through the connection method:
# test.rb
require 'active_record'
require 'pg'
# Change the following to reflect your database settings
ActiveRecord::Base.establish_connection(
adapter: 'postgresql',
host: 'localhost',
database: 'database',
username: 'username',
password: 'passwd'
)
puts ActiveRecord::Base.connection.table_exists?('users')
Test run (when the users table indeed exists in my database):
$ ruby test.rb
true
I recently had to do the exact same thing, and, yes, this is possible. I started with a regular Rails project (rails new app) and stripped it down to fit my needs.
My final project structure has only the following folders and files (everything else was deleted):
/app/models/*
/bin/bundle
/bin/rake
/bin/spring
/config/application.rb
/config/boot.rb
/config/database.yml
/config/environment.rb
/config/environments/*
/db/schema.rb
/config.ru
/Gemfile
/Gemfile.lock
/Rakefile
My Gemfile contains:
source 'https://rubygems.org'
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
gem 'rails', '< 5.0.0'
# Use mysql as the database for Active Record
gem 'mysql2' # <-- change this to PostgreSQL
I also have a script app.rb, which I placed at the root of the project:
# ensure that all dependent gems are installed before running
require_relative 'config/boot'
# include dependent libraries
require 'active_record'
require 'active_model'
require 'mysql2' # <-- change this to PostgreSQL
require 'yaml'
# set up environment and connect to the database
environment = ENV['RAILS_ENV'] || 'development'
database = YAML.load_file('config/database.yml')
ActiveRecord::Base.establish_connection database[environment]
# set up logging so you can use $logger.debug or $logger.info anywhere in your code
$logger = Logger.new(File.new("log/#{environment}.log", 'w'))
$logger.level = environment == 'development' ? Logger::DEBUG : Logger::INFO
# include dependent classes in same directory
# this step is optional as the required files can be included as needed
Dir['app/models/*.rb'].each { |file| require_relative file }
Now, suppose you have a model called User defined in /app/models/user.rb as
class User < ActiveRecord::Base
end
You can then write statements like the one below in any file that is included from app.rb:
#execute code as a single transaction
ActiveRecord::Base.transaction do
user1 = User.create!(first_name: 'Richard', last_name: 'Rahl')
user2 = User.create!(first_name: 'Kahlan', last_name: 'Amnell')
end
You can even use statements like has_many, belongs_to, etc in your models without any problems.
Hopefully, this will be enough to get you started. Let me know if you need more details.

Configure Redis connection on initialize

I'm using Predictor gem and when I attempt to start the gem shows:
"redis not configured! - Predictor.redis = Redis.new" (RuntimeError)
So, how to configure Redis Connection on initialize?
thank's
This is how Redis is initialized in general.
Firstly, a good practice would be adding this to your config/environments/[environment_name].rb. So you can maintain different locations for Redis when you change environments.
config.redis_host = "localhost"
Then in your application's config/initializers path create redis.rb and place the code below to initialize Redis.
require 'redis'
## Added rescue condition if Redis connection is failed
begin
$redis = Redis.new(:host => Rails.configuration.redis_host, :port => 6379)
rescue Exception => e
puts e
end
Then you'll be able to use the global variable $redis within your application for Redis-related commands.
$redis.hset "my_hash", item.id, business.id
Here is a helpful article with more details.
Now in your case as this documentation suggests, here is what you should do:
In config/initializers/predictor.rb,
Predictor.redis = Redis.new(:url => ENV["PREDICTOR_REDIS"])
Or, to improve performance, add hiredis as your driver (you'll need to install the hiredis gem first)
Predictor.redis = Redis.new(:url => ENV["PREDICTOR_REDIS"], :driver => :hiredis)
Then, be sure to include include Predictor::Base in all models you want to use it,
class CourseRecommender
include Predictor::Base
...
end
Here is the code responsible for the error you getting.

Mock Sequel connections to Oracle database

I'm trying to test a Rails application that connects to a remote Oracle database using the Sequel gem. Since the user needs to be logged in in order to use the site, I'm using WebMock. However, because WebMock stops all requests to outside sources, I get the error Sequel::DatabaseConnectionError: OCIError: ORA-12541: TNS:no listener every time I run my tests. How would I mock the database connection? Should I try something else instead?
I'm not sure what code to provide, so here are some snippets that may relate to possible solutions:
database_connection.rb:
class DatabaseConnection
##db = nil
def self.get_db
##db ||= Sequel.connect(Settings.db.main.to_hash)
end
def self.db_query(query)
get_db[query]
end
end
In spec_helper.rb:
require 'webmock/rspec'
WebMock.disable_net_connect!(allow_localhost: true)
RSpec.configure do |config|
config.before(:each) do
stub_request(:post, "/path/to/third/party").
with(:body => "request body").
to_return(:status => 200, :body => "", :headers => {})
end
# ... rest of the code
end
Relevant gems from Gemfile:
gem 'rails', '4.0.2'
gem 'ruby-oci8', git: 'https://github.com/kubo/ruby-oci8.git'
group :development, :test do
gem 'rspec-rails', '~> 3.2.0'
end
group :test do
gem 'webmock' # 1.21.0
gem 'capybara' # 2.4.4
end
Sequel ships with a mock adapter for exactly this purpose:
##db ||= Sequel.connect('mock://oracle')
See the documentation for details about how to use the mocked database:
http://sequel.jeremyevans.net/rdoc-adapters/classes/Sequel/Mock/Database.html
http://sequel.jeremyevans.net/rdoc-adapters/classes/Sequel/Mock/Dataset.html

cucumber working fine without spork, but spork giving me "uninitialized constant Cucumber::Rails"

I've got existing rspecs and cucumber features all running fine.
I'm installing spork (spork-rails in fact) to give me some re-run speed up.
I've got rspec running fine with spork.
I've just modified the env.rb as per instructions (very similar to the mods to spec_helper.rb), but I get uninitialized constant Cucumber::Rails when I try to run bundle exec cucubmer --drb.
Rails 3.2 by the way
Any ideas?
Here's my env.rb:
require 'rubygems'
require 'spork'
#uncomment the following line to use spork with the debugger
require 'spork/ext/ruby-debug'
if Spork.using_spork?
Spork.prefork do
require 'rails'
require 'cucumber/rails'
Capybara.default_selector = :css
begin
DatabaseCleaner.strategy = :transaction
rescue NameError
raise "You need to add database_cleaner to your Gemfile (in the :test group) if you wish to use it."
end
end
Spork.each_run do
# This code will be run each time you run your specs.
require 'cucumber/rails'
Cucumber::Rails::Database.javascript_strategy = :truncation
ActionController::Base.allow_rescue = false
module NavigationHelpers
def path_to(page_name)
case page_name
when /the home page/
root_path
# Add more page name => path mappings here
else
if path = match_rails_path_for(page_name)
path
else
raise "Can't find mapping from \"#{page_name}\" to a path.\n" +
"Now, go and add a mapping in features/support/paths.rb"
end
end
end
def match_rails_path_for(page_name)
if page_name.match(/the (.*) page/)
return send "#{$1.gsub(" ", "_")}_path" rescue nil
end
end
end
World(NavigationHelpers)
end
else
#omitted
end
For future reference noting down what I did to fix this. In the end I think it was an odd symptom of having referenced cucumber-rails slightly wrongly in the Gemfile.
I was getting errors as well saying:
WARNING: Cucumber-rails required outside of env.rb.
The rest of loading is being defered until env.rb is called.
To avoid this warning, move 'gem cucumber-rails' under only
group :test in your Gemfile
Following the instructions in https://github.com/cucumber/cucumber/issues/249, I fixed this by adding require: false to my Gemfile as follows:
group :test do
gem 'cucumber-rails', require:false
#....
end

Resources