I am building an application in Rails which requires me to access multiple SQL databases. The resources I found online suggested I use ActiveRecord::Base.establish_connection, however, this seems to interfere with my site's models. After connecting to a different database, when I next do <model>.<command>, it gives me Mysql2::Error: Table '<Database I had to access through establish_connection>.<Model's table>' doesn't exist eg: Mysql2::Error: Table 'test.words' doesn't exist , which means rails tries to look for the table associated with its models in the database I had to access through establish_connection instead of the site development database.
Steps to reproduce error:
Here are some steps I found which seem to reproduce the problem;
First, I create a new rails app:
rails new sqldbtest -d mysql
cd sqldbtest
Then I set the config file:
default: &default
adapter: mysql2
encoding: utf8mb4
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
username: root
password: <omitted>
host: localhost
Then create a controller, model, and some data in the mysql database:
rails generate controller test test
rails generate model Word name:string txt:text
rails db:create
rails db:migrate
rails c
cat=Word.new
cat.name="Cat"
cat.txt="Cat."
cat.save
exit
mysql -u root -p # I already have a database called "test".
use test;
create table extst (id int primary key, name varchar(8), txt text);
insert into extst (id,name,txt) values (0,"Bob","Bob.");
quit
I then made the controller and the view:
class TestController < ApplicationController
def test
itemOne=Word.find_by(name:"Cat")
#textOne=itemOne.txt
con=ActiveRecord::Base.establish_connection(adapter: 'mysql2', encoding: 'utf8mb4', username: 'root', password: <omitted>, host: 'localhost', database: 'test').connection
#textTwo=con.execute('select txt from extst where name="Bob"').to_a[0][0]
end
end
I wrote this in the view:
<%= #textOne %><br>
<%= #textTwo %>
added 'root "test#test"' to config/routes.rb
rails s
Result:
When I load the page, it shows "Cat." and "Bob." on separate lines as expected, but when I refresh, It shows the error as described above.
I have tried adding con.close to the controller, but this does not work.
Since this error comes from making a connection to another database, adding ActiveRecord::Base.establish_connection(adapter: 'mysql2', encoding: 'utf8mb4', username: 'root', password: <omitted>, host: 'localhost', database: 'sqldbtest_development').connection to the controller after #textTwo=con.execute('select txt from extst where name="Bob"').to_a[0][0] stops the error from happening, however I don't know if this is the best practice or if it has any side effrcts. For example, the database will need to be changed when moving to test or production.
This solution is most likely just a temporary workaround.
I've been trying to debug my credentials file in my staging server. Whenever I try to edit the credentials on my staging server, I get the following error:
/var/www/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/application.rb:583:in `validate_secret_key_base': `secret_key_base` for staging environment must be a type of String`
My database.yml file looks like the following:
---
default: &default
adapter: postgresql
development:
<<: *default
database: dev_db
host: <%= Rails.application.credentials.database.fetch(:development).fetch(:host) %>
username: <%= Rails.application.credentials.database.fetch(:development).fetch(:username) %>
password: <%= Rails.application.credentials.database.fetch(:development).fetch(:password) %>
secret_key_base: <%= Rails.application.credentials.secret_key_base.fetch(:development) %>
test:
<<: *default
database: test_db
host: <%= Rails.application.credentials.database.fetch(:development).fetch(:host) %>
username: <%= Rails.application.credentials.database.fetch(:development).fetch(:username) %>
password: <%= Rails.application.credentials.database.fetch(:development).fetch(:password) %>
secret_key_base: <%= Rails.application.credentials.secret_key_base.fetch(:development) %>
staging:
<<: *default
database: <%= Rails.application.credentials.database.fetch(:staging).fetch(:name) %>
host: <%= Rails.application.credentials.database.fetch(:staging).fetch(:host) %>
username: <%= Rails.application.credentials.database.fetch(:staging).fetch(:username) %>
password: <%= Rails.application.credentials.database.fetch(:staging).fetch(:password) %>
secret_key_base: <%= Rails.application.credentials.secret_key_base.fetch(:staging) %>
production:
<<: *default
database: <%= Rails.application.credentials.database.fetch(:production).fetch(:name) %>
host: <%= Rails.application.credentials.database.fetch(:production).fetch(:host) %>
username: <%= Rails.application.credentials.database.fetch(:production).fetch(:username) %>
password: <%= Rails.application.credentials.database.fetch(:production).fetch(:password) %>
secret_key_base: <%= Rails.application.credentials.secret_key_base.fetch(:production) %>
I think my staging's secret_key_base is of type String. I generated my secret_key_base using rails secret. Locally, when I bring up the rails console, I can view the secret_key_bases for my staging environment:
[1] pry(main)> Rails.application.credentials.secret_key_base.fetch(:staging)
\=> "generated_using_rails_secret"
It returns a string but I still get the error message above whenever I try to access credentials in my staging environment.
I ended up looking at the stack trace and digging into the railties-5.2.0 gem.
Abbreviated stack trace:
ArgumentError: `secret_key_base` for staging environment must be a type of String`
/var/www/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/application.rb:583:in `validate_secret_key_base'
/var/www/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/application.rb:432:in `secret_key_base'
/var/www/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/application.rb:176:in `key_generator'
/var/www/bundle/ruby/2.5.0/gems/railties-5.2.0/lib/rails/application.rb:205:in `message_verifier'
I ended up looking in railties-5.2.0/lib/rails/application.rb:432: and seeing the following bit of code:
# The secret_key_base is used as the input secret to the application's key generator, which in turn
# is used to create all MessageVerifiers/MessageEncryptors, including the ones that sign and encrypt cookies.
#
# In test and development, this is simply derived as a MD5 hash of the application's name.
#
# In all other environments, we look for it first in ENV["SECRET_KEY_BASE"],
# then credentials.secret_key_base, and finally secrets.secret_key_base. For most applications,
# the correct place to store it is in the encrypted credentials file.
def secret_key_base
if Rails.env.test? || Rails.env.development?
Digest::MD5.hexdigest self.class.name
else
validate_secret_key_base(
ENV["SECRET_KEY_BASE"] || credentials.secret_key_base || secrets.secret_key_base
)
end
end
I had mistakenly thought I could specify a SECRET_KEY_BASE for an individual environment. Instead, I could only specify one secret key base. The secret key base apparently has nothing to do with database.yml. I need to read up on it and what it actually does.
If you run rails credentials:edit from the command line it will decrypt the config/credentials.yml.enc file.
You can then edit this file to add environment based secret keys like you would have previously added to config/secrets.yml.
When you save this file it will be encrypted again with the new information included.
There is no reason to have the "secret_key_base" in your database.yml file as this will not have any impact.
Nice Article on the new Rails credentials
Additionally just because rails now longer generates a config/secrets.yml file for you, as of rails 5.2, adding one will still work appropriately as it has in previous releases.
I'm trying to deploy my Rails 5 application using Postgres to a VPS via Capistrano. It keeps failing, though - giving me a PG::ConnectionBad: FATAL: password authentication failed for user 'sys_user'.
The relevant settings of my database.yml are:
production:
<<: *default
database: <%= ENV['RDS_DB_NAME'] %>
username: <%= ENV['RDS_USERNAME'] %>
password: <%= ENV['RDS_PASSWORD'] %>
host: <%= ENV['RDS_HOSTNAME'] %>
port: <%= ENV['RDS_PORT'] %>
All these environment variables have been set in /etc/environment - this is definitely the case because it's picking up the RDS_USERNAME as sys_user. The password of the database is the same as the variable RDS_PASSWORD. The port, hostname etc. are all the same also.
I'm stumped. Please help.
Where is the Postgres instance running? I'm guessing that you're trying to migrate an application from Elastic Beanstalk to a non-Amazon host. If that's the case, and your Postgres instance is not actually RDS, you might need to enable password authentication in your config file.
Documentation on Postgres configuration can be found here; I suspect you might need to change ident to md5.
I am using rails with figaro for configuration, database user with the name test.
I have a DATABASE_URL in application.yml
DATABASE_URL: "postgresql://localhost/database_name?user=test"
When I run a
rake db:migrate
I get the following error
PG::ConnectionBad: fe_sendauth: no password supplied
Check config/database.yml to ensure your password is wired up for postgresql adapter.
it should look something like this:
default:
adapter: postgresql
database: foo
user: bar
password: <%= ENV['pg_password'] %>
pg_password should be defined in your figaro config, where it becomes accessible as an environment variable.
I just want to emphasize you call the Figaro ENV variables in your .yml using the ERB Syntax :
username: <%= ENV["PG_USERNAME"] %>
password: <%= ENV["PG_PASSWORD"] %>
Hope this helps...!
I need to create one config option for my Rails application. It can be the same for all environments. I found that if I set it in environment.rb, it's available in my views, which is exactly what I want...
environment.rb
AUDIOCAST_URI_FORMAT = http://blablalba/blabbitybla/yadda
Works great.
However, I'm a little uneasy. Is this a good way to do it? Is there a way that's more hip?
For general application configuration that doesn't need to be stored in a database table, I like to create a config.yml file within the config directory. For your example, it might look like this:
defaults: &defaults
audiocast_uri_format: http://blablalba/blabbitybla/yadda
development:
<<: *defaults
test:
<<: *defaults
production:
<<: *defaults
This configuration file gets loaded from a custom initializer in config/initializers:
# Rails 2
APP_CONFIG = YAML.load_file("#{RAILS_ROOT}/config/config.yml")[RAILS_ENV]
# Rails 3+
APP_CONFIG = YAML.load_file(Rails.root.join('config/config.yml'))[Rails.env]
If you're using Rails 3, ensure you don't accidentally add a leading slash to your relative config path.
You can then retrieve the value using:
uri_format = APP_CONFIG['audiocast_uri_format']
See this Railscast for full details.
Rails 3 version of initialiser code is as follows (RAILS_ROOT & RAILS_ENV are deprecated)
APP_CONFIG = YAML.load_file(Rails.root.join('config', 'config.yml'))[Rails.env]
Also, Ruby 1.9.3 uses Psych which makes merge keys case sensitive so you'll need to change your config file to take that into account, e.g.
defaults: &DEFAULTS
audiocast_uri_format: http://blablalba/blabbitybla/yadda
development:
<<: *DEFAULTS
test:
<<: *DEFAULTS
production:
<<: *DEFAULTS
Rails >= 4.2
Just create a YAML file into config/ directory, for example: config/neo4j.yml.
Content of neo4j.yml can be somthing like below(For simplicity, I used default for all environments):
default: &default
host: localhost
port: 7474
username: neo4j
password: root
development:
<<: *default
test:
<<: *default
production:
<<: *default
in config/application.rb:
module MyApp
class Application < Rails::Application
config.neo4j = config_for(:neo4j)
end
end
Now, your custom config is accessible like below:
Rails.configuration.neo4j['host'] #=>localhost
Rails.configuration.neo4j['port'] #=>7474
More info
Rails official API document describes config_for method as:
Convenience for loading config/foo.yml for the current Rails env.
If you do not want to use a yaml file
As Rails official guide says:
You can configure your own code through the Rails configuration object with custom configuration under the config.x property.
Example
config.x.payment_processing.schedule = :daily
config.x.payment_processing.retries = 3
config.x.super_debugger = true
These configuration points are then available through the configuration object:
Rails.configuration.x.payment_processing.schedule # => :daily
Rails.configuration.x.payment_processing.retries # => 3
Rails.configuration.x.super_debugger # => true
Rails.configuration.x.super_debugger.not_set # => nil
Official Reference for config_for method |
Official Rails Guide
Step 1: Create config/initializers/appconfig.rb
require 'ostruct'
require 'yaml'
all_config = YAML.load_file("#{Rails.root}/config/config.yml") || {}
env_config = all_config[Rails.env] || {}
AppConfig = OpenStruct.new(env_config)
Step 2: Create config/config.yml
common: &common
facebook:
key: 'asdjhasxas'
secret : 'xyz'
twitter:
key: 'asdjhasxas'
secret : 'abx'
development:
<<: *common
test:
<<: *common
production:
<<: *common
Step 3: Get constants anywhere in the code
facebook_key = AppConfig.facebook['key']
twitter_key = AppConfig.twitter['key']
I just wanted to update this for the latest cool stuff in Rails 4.2 and 5, you can now do this inside any of your config/**/*.rb files:
config.x.whatever = 42
(and that's a literal x in there, ie. the config.x. literally must be that, and then you can add whatever you want after the x)
...and this will be available in your app as:
Rails.configuration.x.whatever
See more here: http://guides.rubyonrails.org/configuring.html#custom-configuration
Just some extra info on this topic:
APP_CONFIG = YAML.load_file(Rails.root.join('config', 'config.yml'))[Rails.env].with_indifferent_access
".with_indifferent_access" allows you to access the values in the hash using a string key or with an equivalent symbol key.
eg.
APP_CONFIG['audiocast_uri_format'] => 'http://blablalba/blabbitybla/yadda'
APP_CONFIG[:audiocast_uri_format] => 'http://blablalba/blabbitybla/yadda'
Purely a convenience thing, but I prefer to have my keys represented as symbols.
I use something similar to John for Rails 3.0/3.1, but I have erb parse the file first:
APP_CONFIG = YAML.load(ERB.new(File.new(File.expand_path('../config.yml', __FILE__)).read).result)[Rails.env]
This allows me to use ERB in my config if I need to, like reading heroku's redistogo url:
production:
<<: *default
redis: <%= ENV['REDISTOGO_URL'] %>
Rails 4
To create a custom configuration yaml and load it (and make available to your app) similar to how database_configuration.
Create your *.yml, in my case I needed a redis configuration file.
config/redis.yml
default: &default
host: localhost
port: 6379
development:
<<: *default
test:
<<: *default
production:
<<: *default
host: <%= ENV['ELASTICACHE_HOST'] %>
port: <%= ENV['ELASTICACHE_PORT'] %>
Then load the configuration
config/application.rb
module MyApp
class Application < Rails::Application
## http://guides.rubyonrails.org/configuring.html#initialization-events
config.before_initialize do
Rails.configuration.redis_configuration = YAML.load_file("#{Rails.root}/config/redis.yml")
end
end
end
Access the values:
Rails.configuration.redis_configuration[Rails.env] similar to how you can have access to your database.yml by Rails.configuration.database_configuration[Rails.env]
Building on Omer Aslam's elegant solution, I decided to convert the keys into symbols. The only change is:
all_config = YAML.load_file("#{Rails.root}/config/config.yml").with_indifferent_access || {}
This allows you to then reference values by symbols as keys, e.g.
AppConfig[:twitter][:key]
This seems neater to my eyes.
(Posted as an answer as my reputation isn't high enough to comment on Omer's reply)
I like simpleconfig. It allows you to have per environment configuration.
see my response to Where is the best place to store application parameters : database, file, code...?
A variation to what you had in that it's a simple reference to another file. It sees that environment.rb isn't constantly updated and doesn't have a heap of app specific stuff in it.
Though not a specific answer to your question of 'is it the Rails way?', perhaps there'll be some discussion there about that.
I prefer accessing settings through the global application stack. I avoid excess global variables in local scope.
config/initializers/myconfig.rb
MyAppName::Application.define_singleton_method("myconfig") {YAML.load_file("#{Rails.root}/config/myconfig.yml") || {}}
And access it with.
MyAppName::Application.myconfig["yamlstuff"]
My way to load Settings before Rails initialize
Allows you to use settings in Rails initialization and configure settings per environment
# config/application.rb
Bundler.require(*Rails.groups)
mode = ENV['RAILS_ENV'] || 'development'
file = File.dirname(__FILE__).concat('/settings.yml')
Settings = YAML.load_file(file).fetch(mode)
Settings.define_singleton_method(:method_missing) {|name| self.fetch(name.to_s, nil)}
You could get settings in two ways:
Settings['email'] or Settings.email
My best way to custom config, with raise message when setting.yml is missing.
gets loaded from a custom initializer in config/initializers/custom_config.rb
setting_config = File.join(Rails.root,'config','setting.yml')
raise "#{setting_config} is missing!" unless File.exists? setting_config
config = YAML.load_file(setting_config)[Rails.env].symbolize_keys
#APP_ID = config[:app_id]
#APP_SECRET = config[:app_secret]
Create a YAML in config/setting.yml
development:
app_id: 433387212345678
app_secret: f43df96fc4f65904083b679412345678
test:
app_id: 148166412121212
app_secret: 7409bda8139554d11173a32222121212
production:
app_id: 148166412121212
app_secret: 7409bda8139554d11173a32222121212