Ruby ActiveRecord Dynamic Model creation - ruby

I am trying to establish a multiple DB connection with ActiveRecord.Currently I need to insert data into total 2 Databases. There is a possibility for increase in the No.Of databases.
So I created 2 classes dynamically which will Extend from ActiveRecord::Base
Object.const_set("Connection1",Class.new(ActiveRecord::Base) do
self.abstract_class = true
self.establish_connection({
:host=>"localhost", :username=>"root", :password=>"root", :database=>"db1", :encoding=>"utf8", :adapter=>"mysql2"})
end)
Object.const_set("Connection2",Class.new(ActiveRecord::Base) do
self.abstract_class = true
self.establish_connection({
:host=>"localhost", :username=>"root", :password=>"root", :database=>"db2", :encoding=>"utf8", :adapter=>"mysql2"})
end)
Then I created Dynamic models extends from each class accordingly
Object.const_set("ConnectionUser1",Class.new(Connection1) do
self.table_name = 'user'
def self.foo
all.count
end
end)
Object.const_set("ConnectionUser2",Class.new(Connection2) do
self.table_name = 'user'
def self.foo
all.count
end
end)
Then when I tried to call foo method
p ConnectionUser1.foo
p ConnectionUser2.foo
It gives me ActiveRecord::ConnectionNotEstablished Error.
I heard that if the model doesn't have connection ActiveRecord will take connection of their parent.
So according to this ConnectionUser1 should use the connection of Connection1 and ConnectionUser2 use the connection of Connection2.
Then why ActiveRecord fails to Establish Connection?
Any help will be appreciated.
Thank you.

Take a look at below link which shows that how to use multiple database with ActiveRecord.
How do i work with two different databases in rails with active records?

Related

How to implement Application Record in ruby without rails

I've been looking at this repository
https://github.com/stungeye/ActiveRecord-without-Rails to understand how can I implement activerecord without rails.I got some problems. At first I got this error when I tried to run this class:
require 'active_record'
ActiveRecord::Base.establish_connection(adapter: 'mysql2', database: 'rbuserroom')
# Can override table name and primary key
class User < ActiveRecord::Base
self.primary_key = 'user_id'
def initialize(id, email)
#user_id = id
#user_email = email
#user_room
end
def create()
self.save
end
# accessor get and set method
attr_accessor :user_room
attr_reader :user_id, :user_email
end
usr = User.new(1, "user#user")
usr.create()
but I got this error:
Traceback (most recent call last):
1: from -:25:in `<main>'
/home/felipe/.rbenv/versions/2.7.5/lib/ruby/gems/2.7.0/gems/activerecord-6.1.4.1/lib/active_record/inheritance.rb:52:in `new': wrong number of arguments (given 2, expected 0..1) (ArgumentError)
it seems that active record doesn't accept the parameters in the creation of the class, in fact after that i noticed that the classes in this example don't contain anything inside, how would active record define the columns of the tables?
i'm used to java jpa and springboot that i have to define all the attributes of the class.
besides i don't know if the active record is really working.
I just want that when I create a new user with my user class, the information persists in the database as an insert, or that it updates when I make a change to my object attribute value.
With ActiveRecord you don't need to specify the column names. It detects them from the DB.
You can just write:
require 'active_record'
ActiveRecord::Base.establish_connection(adapter: 'mysql2', database: 'rbuserroom')
class User < ActiveRecord::Base
self.primary_key = 'user_id'
end
usr = User.create(user_id: 1, user_email: "user#user")
You can read more about creating models in the docs. Especially in 3 Creating Active Record Models

How to JOIN tables of two different databases in Ruby on Rails [duplicate]

My environment: Ruby 1.9.2p290, Rails 3.0.9 and RubyGem 1.8.8
unfortunately I have an issue when come across multiple database.
The situation is this: I have two model connect with two different database and also establishing association between each other.
database connection specifying in each model, look likes
class Visit < ActiveRecord::Base
self.establish_connection "lab"
belongs_to :patient
end
class Patient < ActiveRecord::Base
self.establish_connection "main"
has_many :visits
end
I got an error when meet following scenario
#visits = Visit.joins(:patient)
Errors: Mysql2::Error: Table 'lab.patients' doesn't exist: SELECT visits.* FROM visits INNER JOIN patients ON patients.id IS NULL
Here 'patients' table is in 'main' database and 'visits' table in 'lab' database
I doubt when executing the code, that Rails is considering 'patients' table is part of 'lab' database [which holds 'visits' table].
Well, I don't know if this is the most elegant solution, but I did get this to work by defining self.table_name_prefix to explicitly return the database name.
class Visit < ActiveRecord::Base
def self.table_name_prefix
renv = ENV['RAILS_ENV'] || ENV['RACK_ENV']
(renv.empty? ? "lab." : "lab_#{renv}.")
end
self.establish_connection "lab"
belongs_to :patient
end
class Patient < ActiveRecord::Base
def self.table_name_prefix
renv = ENV['RAILS_ENV'] || ENV['RACK_ENV']
(renv.empty? ? "main." : "main_#{renv}.")
end
self.establish_connection "main"
has_many :visits
end
I'm still working through all the details when it comes to specifying the join conditions, but I hope this helps.
Might be cleaner to do something like this:
def self.table_name_prefix
"#{Rails.configuration.database_configuration["#{Rails.env}"]['database']}."
end
That will pull the appropriate database name from your database.yml file
Or even
def self.table_name_prefix
self.connection.current_database+'.'
end
Is your 2nd database on another machine? You can always do as suggested in this other question:
MySQL -- Joins Between Databases On Different Servers Using Python?
I'd use the self.table_name_prefix as proposed by others, but you can define it a little more cleanly like this:
self.table_name_prefix "#{Rails.configuration.database_configuration["#{Rails.env}"]['database']}."
alternatively you could also use this:
self.table_name_prefix "#{connection.current_database}."
You have to keep in mind that the latter will execute a query SELECT DATABASE() as db the first time that class is loaded.

ruby sequel and postgreSQL - too many clients (connections)

I'm using the sequel gem inside a DB class that is used across my app (rack app) and it's instantiated only once.
The DB class initialises sequel once and has some methods I call, mainly read-only:
def initialize
#psql ||= Sequel.connect('postgres://localhost/mydb')
end
def query_example
#psql[:users].order(:time)
end
The app is basically an API. Something like:
class API < Grape::API
format :json
before do
#db = Db.new
end
get '/' do
#db.query_example
end
This works until I reach 100 connections in postgreSQL. I assume sequel is using some sort of connection pool but somehow is not freeing up the connections? I can see the 100 'selects' in the pg_stat_activity table with a status of 'idle'. However every new request fails with the following error:
Sequel::DatabaseConnectionError: PG::ConnectionBad: FATAL: sorry, too many clients already
/Users/lopezj2/.rvm/gems/ruby-2.1.2/gems/sequel-4.22.0/lib/sequel/adapters/postgres.rb:236:in `initialize'
/Users/lopezj2/.rvm/gems/ruby-2.1.2/gems/sequel-4.22.0/lib/sequel/adapters/postgres.rb:236:in `new'
/Users/lopezj2/.rvm/gems/ruby-2.1.2/gems/sequel-4.22.0/lib/sequel/adapters/postgres.rb:236:in `connect'
/Users/lopezj2/.rvm/gems/ruby-2.1.2/gems/sequel-4.22.0/lib/sequel/connection_pool.rb:101:in `make_new'
It looks like Sequel is trying to create a new connection in the pool, however, the app is not particularly chatty.
You should create connection pool only once, and checkout a connection from the pool for each request, but in your code, you just create a new pool for each request.
You can change your DB class like this:
class DB
class << self
attr_reader :psql
end
# Note that #psql is a class instance variable
#psql = Sequel.connect('postgres://localhost/mydb')
def query_example
DB.psql[:users].order(:time)
end
end

Sharing a class instance between two classes

I have two different classes that both represent objects that need to be persisted to my database and now I want to share the database client object between the two classes. I want to avoid instantiating the client object more than once.
Currently I do this by using a global variable
$client = Mysql2::Client.new(:database => "myDb", :user => "user", :password => "password", :host => "localhost")
class Person
def save
$client.query("INSERT INTO persons")
end
end
class Car
def save
$client.query("INSERT INTO cars")
end
end
This works, but I am wondering if there are more correct ways to do this and why they are more correct?
You can inherit from a parent class. This allows you to share common functionality across objects and follows DRY (do not repeat yourself) programming principles. It will also allow you to protect your DB connection with locks, resuces, queues, pools, and whatever else you may want to do without having to worry about it in your children classes
class Record
#table_name = nil
##client = Mysql2::Client.new(:database => "myDb", :user => "user", :password => "password", :host => "localhost")
def save
##client.query("INSERT INTO #{#table_name}") if #table_name
end
end
class Person < Record
#table_name = "persons"
end
class Car < Record
#table_name = "cars"
end
While we are on the subject, you should look at using ActiveRecord for handling your database models and connections. It already does pretty much anything you'll need and will be more compatible with other gems already out there. It can be used without rails.
As an alternative on using inheritance, why not consider a simple Singleton pattern? This could make your models cleaner, by separating the responsibility outside your classes. And eliminating the need for inheritance.
The example below illustrates this. Only one, single instance of the DataManager class can exist. So, you'll only instantiate it once - but can use it everywhere:
require 'singleton'
class DataManager
include Singleton
attr_accessor :last_run_query
def initialize()
if #client.nil?
p "Initialize the Mysql client here - note that this'll only be called once..."
end
end
def query(args)
# do your magic here
#last_run_query = args
end
end
Next, calling it using the .instance accessor is a breeze - and will always point to one single instance, like so:
# Fetch, or create a new singleton instance
first = DataManager.instance
first.query('drop table mother')
p first.last_run_query
# Again, fetch or create a new instance
# this'll actually just fetch the first instance from above
second = DataManager.instance
p second.last_run_query
# last line prints: "drop table mother"
For the record, the Singleton pattern can have some downsides and using it frequently results in a never-ending debate on whether you should use it or not. But in my opinion it's a decent alternative to your specific question.

Why does my Ruby program not initialize multiple database connections?

I have an issue with my database connections overwriting each other.
I create two individual connections, but when I call $db1.execute_sql, $db2.execute_sql is actually what gets called.
Here is what I have:
servers.yml:
db1:
adapter: jdbc
driver: oracle.jdbc.driver.OracleDriver
url: db_1_url
username: my_username
password: m_password
db2:
adapter: jdbc
driver: oracle.jdbc.driver.OracleDriver
url: db_2_url
username: my_username
password: m_password
ServerContext class:
class ServerContext
def initialize (env)
connect(env)
end
def connect(env)
begin
config = YAML.load_file("features/config/servers.yml")
rescue
puts "cannot load config/servers.yml"
end
#connection = ActiveRecord::Base
#connection.establish_connection(config[env])
end
def execute_sql(sql_string)
#connection.connection.execute(sql_string)
end
Database setup:
def connect_databases
$db1 = ServerContext.new('db1')
$db2 = ServerContext.new('db2')
end
The current connection is a thread local variable so you can only set it to one thing at a time - http://api.rubyonrails.org/classes/ActiveRecord/ConnectionAdapters/ConnectionPool.html
You need two separate classes that inherit from ActiveRecord::Base to do what you want
You're doing
#connection = ActiveRecord::Base
I think as you're using the same static method of the class two times for the connection, the second time it simply rewrites the previous connection.
Create a class that extends from ActiveRecord::Base for each connection:
class DB1 < ActiveRecord::Base
self.abstract_class = true
end
class DB2 < ActiveRecord::Base
self.abstract_class = true
end
Then, in your connect method:
case config[env]
when 'db1'
#connection = DB1
when 'db2'
#connection = DB2
end
So the new activeRecord stores connections in a hash based on Class name. So each time I was overwriting the connection in ActiveRecord (not in the class itself). See http://api.rubyonrails.org/classes/ActiveRecord/Base.html and look under Connection to multiple databases in different models.

Resources