How to read ActiveRecord configuration from own gem? - ruby-on-rails

I need to call "establish_connection" method from my own gem, but ActiveRecord::Base.configurations is [].
I made work around:
module Bitrix
class Database < ActiveRecord::Base
#config = YAML.load(File.read('config/database.yml'))
self.abstract_class = true
establish_connection #config["bitrix_#{Rails.env}"]
end
end
But why I can't access to AR config without opening file directly? I tried to use establish_connection :"bitrix_#{Rails.env}" and got this error:
/usr/local/var/rbenv/versions/2.1.3/lib/ruby/gems/2.1.0/gems/activerecord-4.1.4/lib/active_record/connection_adapters/connection_specification.rb:257:in `resolve_symbol_connection': 'bitrix_development' database is not configured. Available: [] (ActiveRecord::AdapterNotSpecified)

All config is available in:
Rails.application.config.database_configuration
So you'd have:
db_config = Rails.application.config.database_configuration
db_config['development']

Opening any file is not needed here. ActiveRecord needs however proper configuration to know how to connect to given database, e.g:
{"template"=>"template0", "adapter"=>"postgresql",
"encoding"=>"UTF8", "database"=>"database", "pool"=>50,
"username"=>"rails", "password"=>nil}
Loading database.yml parses yaml into hash with environment names as keys and their configurations as values.
config = YAML.load(File.read('config/database.yml'))
establish_connection config["bitrix_#{Rails.env}"]
returns more or less the same hash with adapter, user and password which are essential to connect to database.

Related

How can I establish a connection to multiple databases, with the same adapter(postgres), using active_record gem?

how are you? I've been having some troubles when I try to establish a connection to two databases in postres. I will try to describe the scenario:
This is my pg.yml:
default: &default
adapter: postgresql
encoding: unicode
pool: 5
host: localhost
user: xxxxx
password: xxxxx
development:
<<: *default
database: not_legacy
legacy:
<<: *default
database: legacy
This is my Legacy::Base class:
#/models/legacy/base.rb
require 'active_record'
require 'erb'
module Legacy
class Base < ActiveRecord::Base
self.abstract_class = true
conf_contents = File.read('config/pg.yml')
conf_evaluated = ::ERB.new(conf_contents).result
conf = YAML.load(conf_evaluated)
ActiveRecord::Base.establish_connection conf['legacy']
end
end
and this is my NotLegacy::Base class:
#/models/not_legacy/base.rb
require 'active_record'
require 'erb'
module NotLegacy
class Base < ActiveRecord::Base
self.abstract_class = true
conf_contents = File.read('config/pg.yml')
conf_evaluated = ::ERB.new(conf_contents).result
conf = YAML.load(conf_evaluated)
ActiveRecord::Base.establish_connection conf['not_legacy']
end
end
Also, I have two classes that inherit from the classes previously described.
This is Legacy::Company:
#/models/legacy/company.rb
require_relative 'base'
module Legacy
class Company < Base
self.table_name = "company"
end
end
and NotLegacy::Company:
#/models/not_legacy/company.rb
require_relative 'base'
module NotLegacy
class Company < Base
self.table_name = "company"
end
end
Now, if I go to the console and do something like(I'm printing conf value):
irb(main):001:0> load 'app/models/legacy/company.rb'
CONFS: {"adapter"=>"postgresql", "encoding"=>"unicode", "pool"=>5, "host"=>"localhost", "user"=>"xxxxx", "password"=>"xxxxx", "database"=>"legacy"}
=> true
irb(main):002:0> Legacy::Company.count
=> 8
irb(main):003:0> load 'app/models/not_legacy/company.rb'
CONFS: {"adapter"=>"postgresql", "encoding"=>"unicode", "pool"=>5, "host"=>"localhost", "user"=>"xxxxx", "password"=>"xxxxx", "database"=>"not_legacy"}
=> true
irb(main):004:0> NotLegacy::Company.count
=> 1
At this point everything seems to work correctly since in the legacy database there are 8 records for company and in the not_legacy database there is only 1 record. But if I call Legacy::Company again, I get:
irb(main):005:0> Legacy::Company.count
=> 1
irb(main):005:0> NotLegacy::Company.count
=> 1
It seems that the second connection (made to the not_legacy database) is overwriting the first one (the one made to the legacy database).
If anyone of you can explain me why this is happening and how to fix it I will be immensely grateful
Thanks.
First I would like to go with basic about how you are going through code flow,
When you load any class, its code is executed & methods defined, your connection is created through static code written & establish_connection is executed.
Single instant of rails app can have connection to single database
You better to write code to switch through different database to have access for different database connectivity.
Legacy::Company or NotLegacy::Company both models have same table name companies from different databases. But when you connect particular database both model (which got loaded) will point to companies table in current database.
So what is current database mean to both model ?
-> The one which is loaded last time by establish_connection.
So this is little tedious but it is working that if you deal with different databases, you have to keep switching from one database to another one.
on config/database.yml
other_base:
adapter: postgresql
encoding: unicode
pool: 5
host: localhost
user: xxxxx
password: xxxxx
on your model
class YourModel < ActiveRecord::Base
self.establish_connection :other_base
end

Using the backup gem how can I get database authentications details from rails database.yml

I'm testing out the backup gem
http://backup.github.io/backup/v4/utilities/
I understand that I've to create a db_backup.rb with the configuration for example
Model.new(:my_backup, 'My Backup') do
database MySQL do |db|
# To dump all databases, set `db.name = :all` (or leave blank)
db.name = "my_database_name"
db.username = "my_username"
db.password = "my_password"
db.host = "localhost"
db.port = 3306
However I'm not able to find out how to get those details from the Rails database.yml. I've tried something like this:
env = defined?(RAILS_ENV) ? RAILS_ENV : 'development'
#settings = YAML.load(File.read(File.join( "config", "database.yml")))
But I guess there should be a better way.
I would do something like this:
env = defined?(RAILS_ENV) ? RAILS_ENV : 'development'
config = YAML.load_file(File.join('config', 'database.yml'))[env]
Model.new(:my_backup, 'My Backup') do
database MySQL do |db|
config.each_pair do |key, value|
db.public_send("#{key}=", value)
end
# ...
Use ActiveRecord's own configuration handling:
require 'active_record'
require 'yaml'
Model.new(:my_backup, 'My Backup') do
database MySQL do |db|
config = {
# these are the default values
host: 'localhost'
port: 3306
}.merge(load_configuration(ENV['RAILS_ENV'] || 'development'))
config.each_pair do |key, value|
db.public_send("#{key}=", value)
end
end
# this loads the configuration from file and memoizes it
def load_configuration(env)
#file_config ||= YAML.load(File.read(File.join( "config", "database.yml")))
#configurations ||= ActiveRecord::ConnectionHandling::MergeAndResolveDefaultUrlConfig.new(file_config).resolve
#configurations[env]
end
end
The key advantage here is that it will merge the values from ENV['DATABASE_URL']. Which is very important since you should avoid adding database credentials to config/database.yml.
A good habit is to specify only the connection adapter and base essentials in database.yml. Use ENV['DATABASE_URL'] for usernames, passwords and everything else.
Env vars are easy to change between deploys without changing any
code; unlike config files, there is little chance of them being
checked into the code repo accidentally; and unlike custom config
files, or other config mechanisms such as Java System Properties, they
are a language- and OS-agnostic standard.
- https://12factor.net/config
See:
Configuring Rails Applications

Using two database in Rails?

is it possible to use 2 types of database in a Rails application?
I am using 2 databases - Postgres to store data which might not change much, and MongoDB
to store data which change dynamically.
is this valid approach? and is it possible to connect between these 2 databases and operate
in a single Rails application?
Please correct, if i am wrong.
regards,
Balan
yes this is possible here is an example from my old code (but here we use mysql for both the DB, but I think you can get an idea)
in database.yml file define two databases
development: &defaults
adapter: mysql
encoding: utf8
database: DB1
enable_call: true
username:
password:
host:
portal_development: &defaults
adapter: mysql
encoding: utf8
database: DB2
enable_call: true
username:
password:
host:
in your models have to base models coupled with above databases
portal_base.rb
class PortalBase < ActiveRecord::Base
self.abstract_class = true
establish_connection "portal_#{Rails.env}"
def self.table_name_prefix
"DB1."
end
end
default_base.rb
class DefaultBase < ActiveRecord::Base
self.abstract_class = true
establish_connection "#{Rails.env}"
def self.table_name_prefix
"DB2."
end
end
and derive your models accordingly
class Client < PortalBase
#code
end
Hope you have an idea now :)
Found solution :
We can use mongoid gem to achieve this.
Steps to install
1) Install mongoid by adding "gem mongoid" in Gemfile and running bundle command
2) Generate mongoid configuration by typing "rails g mongoid:config", this will create a
config file mongoid.yml near database.yml file, you can add the configuration to Mongo server
in this file.
Note : After adding Mongoid, all the models created will be created for MongoDB by default, you can specify --orm option to generate Models for Postgres.

Can't Rails 3 run in MySQL MyISAM mode, without InnoDB?

I have a MySQL server running with InnoDB disabled (for reasons of performance), with this setup I can't seem to be able to use Rails 3 (with the mysql2 adapter).
Here's my test migration:
class CreateTxts < ActiveRecord::Migration
def change
create_table(:txts, :options => 'ENGINE=MyISAM') do |t|
t.timestamps
end
end
end
And here's the error:
>rake db:migrate
rake aborted!
Mysql2::Error: Unknown storage engine 'InnoDB': CREATE TABLE `schema_migrations`
(`version` varchar(255) NOT NULL) ENGINE=InnoDB
Tried the workaround described here, but it doesn't seem to work either (I did modify MysqlAdapter to Mysql2Adapter to match my setup).
Sorry I'm a newb in Rails. Any help will be much appreciated :o
Going to answer my own question. Here's a patch for environment.rb I ended up with that works with the native mysql driver as well as JRuby/JDBC-mysql:
# Load the rails application
require File.expand_path('../application', __FILE__)
# Patch Mysql adapter to default to MyISAM instead of InnoDB
require 'active_record/connection_adapters/mysql_adapter'
module ActiveRecord
module ConnectionAdapters
class MysqlAdapter
def create_table(table_name, options = {}) #:nodoc:
super(table_name, options.reverse_merge(:options => "ENGINE=MyISAM"))
end
end
end
end
# Initialize the rails application
.....
rake db:migrate now succeeds and creates all tables including schema_migrations with TYPE=MyISAM.
Note: For mysql2 adapter, rename mysql_adapter to mysql2_adapter and MysqlAdapter to Mysql2Adapter.
Try creating the table without specifying the type of engine being used like this
class CreateTxts < ActiveRecord::Migration
def change
create_table(:txts) do |t|
t.timestamps
end
end
end
and then in mysql cli, type this
ALTER TABLE txts ENGINE = MYISAM
hope it helps
The error is coming from creating the schema_migrations table (which rails uses to track which migrations have been run) rather than your table. You could create that table yourself (with a single varchar(255) column called version with an index on it).
If you do end up overwriting the create_table method, you need to preserve the method's signature - you're ignoring the block that it yields. I'd try something like
def create_table(name, options={})
super(name, options.merge(...)) {|t| yield t}
end
#rustyx nice one! &here's my slightly tweaked monkey-patch so the engine can be set from within the /config/database.yml configuration:
&Rather than in /config/environment.rb, i put the codes into an initializer, Eg: /config/initializers/mysql2adapter_default_engine.rb.
Either this for brevity:
require 'active_record/connection_adapters/abstract_mysql_adapter'
class ActiveRecord::ConnectionAdapters::Mysql2Adapter < ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter
def create_table(table_name, options = {}) #:nodoc:
super(table_name, #config[:engine].nil? ? options : options.reverse_merge(:options => "ENGINE="+#config[:engine]))
end
end
or this for more clarity:
require 'active_record/connection_adapters/abstract_mysql_adapter'
module ActiveRecord
module ConnectionAdapters
class Mysql2Adapter < AbstractMysqlAdapter
def create_table(table_name, options = {}) #:nodoc:
super(table_name, #config[:engine].nil? ? options : options.reverse_merge(:options => "ENGINE="+#config[:engine]))
end
end
end
end
Then in /config/database.yml we can have something like this:
production:
adapter: mysql2
encoding: utf8
database: ooook
username: ooook
password: ooook
host: ooook.tld
port: 3306
pool: 5
timeout: 5000
engine: MyISAM
If no engine is specified then the mysql2 adapter will use the default (InnoDB or whatever).

Rails - generating .sqlite3-databases

My rails app. uses mysql database and I need to generate .sqlite3.databases. Is it possible to use activerecord and rails models for it?
We are trying now to use models namespaced by Remote:: module but by this way we can't start concurrent generators.
In your remote models, you want to connect to a separate database using #establish_connection:
# config/database.yml
remote_development:
adapter: sqlite3
database: db/development.sqlite3
remote_production:
adapter: sqlite3
database: /usr/local/remote/myapp.sqlite3
# app/models/remote_model.rb
class RemoteModel < ActiveRecord::Base
establish_connection "remote_#{Rails.env}"
self.abstract_class = true
end
# app/models/remote_user.rb
class RemoteUser < RemoteModel
end
Note the abstract_class setter: this means the class in question doesn't have an underlying table: it's used for configuration purposes only.

Resources