At what point does ActiveRecord actually connect to the database? - mysql

I have a ruby script that is using ActiveRecord (2.3.12) to access a MySQL database. The flow goes something like, "read database values from a config file", "connect to database", "Create table A if it doesn't exist", "download and parse a file", "save parsed records to A".
The code looks like the following:
ActiveRecord::Base.establish_connection(
:adapter => 'mysql',
:database => database_name,
:username => username,
:password => password,
:host => "localhost",
:port => 3306
)
...
ActiveRecord::Schema.define do
create_table a, :force => true do |t|
t.string :last_name, :limit => 60, :default => "", :null => false
t.string :first_name, :limit => 30, :default => "", :null => false
t.string :middle_initial, :limit => 2, :default => ""
t.string :dob, :limit => 12, :default => "", :null => false
end
end unless A.table_exists?
However, if I put incorrect DB credentials, or a non-existent database name into the establish_connection method, the script doesn't seem to give any errors or throw any exceptions until I actually try to perform some operation on the database (i.e., create table A). I tried a begin-rescue-end around establish_connection, but it never went into the rescue block.
Why does establish_connection seem to not really...well...establish the connection? And for the life of me, I can't figure out what it is even supposed to return. The docs HERE sure don't seem to be any help.
Or am I doing something wrong? Please help!

I usually use ActiveRecord::Base.connection.active? statement to check the whether the ActiveRecord is really connected to database.
def establish_database_connection
begin
ActiveRecord::Base.establish_connection config["database"]
ActiveRecord::Base.connection.active?
logger.info "Connected to Database"
rescue Exception => e
logger.error "Exception db connection : #{e.message} "
raise "Database connection failed"
end
end
Without ActiveRecord::Base.connection.active? statement the above code won't raise any error on invalid credentials.

RSK's solution will leave a connection checked out for the current thread. If you don't want that, try this (adapted from https://gist.github.com/limhoff-r7/71ee6b1568b604e131a8, which is for Postgres only):
ActiveRecord::Base.establish_connection
# Check if the spec passed to `ActiveRecord::Base.establish_connection` can connect to the database.
#
# #return [true] if an active connection can be made to the database using the current config.
# #return [false] if an active connection cannot be made to the database.
def connection_established?
begin
# use with_connection so the connection doesn't stay pinned to the thread.
ActiveRecord::Base.connection_pool.with_connection {
ActiveRecord::Base.connection.active?
}
rescue Exception
false
end
end

I agree with #Luke Imhoff: Connections that are manually checked out from ActiveRecord's connection-pool, have to manually be returned to the pool.
As a note, however, I suggest to use the connection that ActiveRecord yields to the block
ActiveRecord::Base.connection_pool.with_connection { |con| con.active? }
Referring to the documentation of :with_connection:

I'm not an expert, but I always assumed that establish_connection was more of a connection definition, whereas the actual connection is made when it is used, in this case, when the table_exists? runs

Related

Rails connect to remote db

How to properly connect to remote db?
Now i have
def db_params
{:adapter => "mysql2",
:host => "host",
:username => "name",
:password => "pass",
:database => "mydb"}
end
def connect_to_remote_db
ActiveRecord::Base.establish_connection(db_params)
end
When i write connect_to_remote_db it seems ok
I know that remote db has table 'Team'
but when i write Team
in console it returns me uninitialized constant Team
How to handle it properly?
When you call Team ActiveRecord's primary connection is looked up, hence the error.
You could probably wrap that in class.
Since I had dealt with similar situation, you could have that connection in database.ymlitself and use.
development:
adapter: mysql2
other stuff...
db_2:
adapter: mysql2
other stuff..
Then create a class
class Team < ActiveRecord::Base
establish_connection(:db_2)
self.table_name = "teams"
end
from - https://stackoverflow.com/a/26574386/2231236
You need to create model in your application ( of that remote db table) and establish connection. Example:
team.rb
class Team< ActiveRecord::Base
establish_connection "remote_db"
end
If you have multiple table you want to use from that remote db, you can make module and just include it in every model for remote db table.
Module example:
module RemoteConnection
extend ActiveSupport::Concern
included do
establish_connection "remote_db"
end
end
and than
class Team< ActiveRecord::Base
include RemoteConnection
end
Use database.yml file to store connections:
...
remote_db:
:adapter => "mysql2",
:host => "host",
:username => "name",
:password => "pass",
:database => "mydb"
...

How to establish connection from a rake task?

I need to create a rake task that gets the table rows from a MySQL database, parse the data, and insert into an Oracle database. The databases are on two different hosts.
My current attempt:
namespace :import_from_mysql do
class MySQLConnection < ActiveRecord::Base
self.abstract_class = true
establish_connection({
:adapter => 'mysql',
:host => 'xxx.xxx.com',
:database => 'sample_database',
:username => 'username',
:password => 'password'
})
end
class MySQLTable < MySQLConnection
self.table_name = "users"
self.primary_key = "id"
self.inheritance_column = "something_unique_here"
end
desc "Parse data before inserting to oracle database"
task :insert_to_oracle => :environment do |t|
puts "Rake task has begun...\n\n"
puts "Parsing data from MYSQL...\n\n"
MySQLTable.establish_connection
puts "Rake task has completed!"
end
end
But the MySQLTable.establish_connection establishes a connection to my local database which is sqlite even though I'm trying to connect to a mysql_adapter.
When I tried to establish a connection using the command below, I was able to connect to a MySQL adapter but I don't know how I can access my tables after the connection was established:
ActiveRecord::Base.establish_connection({:adapter => "mysql", :database => "sample_database", :host => "xxx.xxx.com", :username => "username", :password => "password" })
Any idea on why it keeps on connecting to sqlite? And after successfully establishing a connection to mysql, how do I select table rows after the MySQLTable.establish_connection statement?
With the connection generated using ActiveRecord::Base you can execute SQL statements against whatever database you connect to. Like so:
connection = ActiveRecord::Base.establish_connection(
:adapter => "mysql",
:host => "faraway",
:username => "myuser",
:password => "mypass",
:database => "somedatabase"
)
connection.execute('SELECT * FROM users')
Once established, the connection can also be referenced from ActiveRecord::Base class.
ActiveRecord::Base.establish_connection(...)
ActiveRecord::Base.connection.execute(...)
You can use mysql2 gem (https://github.com/brianmario/mysql2) inside your rake task.
client = Mysql2::Client.new(:host => "localhost", :username => "username", :database => "sample_database", :password => "password")
users = client.query("SELECT * FROM users")
Thought it might be helpful for someone else. The following worked for me!
connection = ActiveRecord::Base.establish_connection(
:adapter => "mysql",
:host => "faraway",
:username => "myuser",
:password => "mypass",
:database => "somedatabase"
)
#connection = ActiveRecord::Base.connection
result = #connection.exec_query('SELECT * FROM users')
result.each do |row|
puts row
end
Here, users is an already existing table in the "somedatabase".
although I am a noob with connections and "database back office" my trivial approach works like a charm, and there is no need with parameters in rake task that is for updating "domain tables" that contain only defining data like legal or help texts:
pre_db = ActiveRecord::Base.establish_connection('preproduction').connection
prod_db = ActiveRecord::Base.establish_connection('online').connection
and in database.yml I defined the databases. ("preproduction" is a step between dev and prod in my case and a real environment like "development". where 'online' is almost the same as 'production' (I made a different entry only for security reasons - not to destroy production if database.yml gets uploaded)

Dynamic second database Ruby

I want to add a second read-only database to my application based on a variable aside the SQLite file I'm developing with.
So, there should be three databases.
1. Local SQLite file
2. Production Read-only MySQL database
3. Test Read-only MySQL database
This test database has new columns added and will be pushed as production database soon.
From this SO-Q, I do not understand what the solution is.
I already use establish_connection.
establish_connection(
:adapter => 'mysql2',
:database => "db1",
:username => "username",
:password => "p*ssw*rd",
:host => "ho.st.com"
)
This db1 is the production db without the extra table columns.
db2 is the test db with the extra table columns.
establish_connection(
:adapter => 'mysql2',
:database => "db2",
:username => "username",
:password => "p*ssw*rd",
:host => "ho.st.com"
)
I thought this could be a solution:
In the models of the tables from the MySQL databases:
config = 'test'
case config
when 'test'
establish_connection(
:adapter => 'mysql2',
:database => "db1",
:username => "username",
:password => "p*ssw*rd",
:host => "ho.st.com"
)
else
establish_connection(
:adapter => 'mysql2',
:database => "db2",
:username => "username",
:password => "p*ssw*rd",
:host => "ho.st.com"
)
end
But now I have to edit all 'affected' models when I change the status. Is there a way to variabilise (is that even a word?) this so I only have to edit this config once?
I've tried via Application_controller.rb, but this didn't work.
# Application_controller.rb
def get_config_status
return 'test'
end
# model.rb
config = get_config_status() #=> undefined method `get_config_status' for Regio(Table doesn't exist):Class
# or
config = Application.get_config_status #=> uninitialized constant Regio::Application
Or should I consider totally something else?
To be honest, I don't like putting usernames and passwords in Models. Is this a possible thread for hackers?
Summary
What I'm trying to accomplish is:
Set one variable so my application uses db2 instead of db1.
The octopus gem might be helpful.
https://github.com/tchandy/octopus
You can select the database at query level,
User.where(name: 'Sam').using(:db2)
Or you can select the database per controller action,
class ApplicationController < ActionController::Base
around_filter :select_shard
def select_shard(&block)
Octopus.using(:db1, &block)
end
end
Check out the readme on Github for more examples.

Is Ruby on Rails appropriate for what I am trying to achieve? Creating CRUD scaffolds for existing MySQL database

I have previously created localhost-only RoR applications where models were created manually with scaffolds generated for them, creating a nice, quick, easy to use CRUD interface for the data. I did this in Netbeans using SQLite.
Now I have a server with a MySQL database and wish to create a quick CRUD application which provides a quick and easy way to view data in the database and give a few options like edit/delete. As my database is already specified by MySQL this seems to open up a massive can of worms, making this a lot less straight forward in comparison to my Netbeans venture into scaffold-generated crud web apps in RoR.
I have looked into dumping the schema of my current database, db:raking it, then generating the scaffolds. Is this the correct approach? I have also read about Magic Model Generator. Is this the best way to go about getting the MySQL database to a RoR model format? http://magicmodels.rubyforge.org/magic_model_generator/
I guess what I'm asking is, for my database structure, is RoR appropriate to mock-up a quick CRUD web app so that the data can have basic manipulation performed on it?
Below is the schema.rb for my MySQL database. There is a FK relationship on userId and attachmentId.
ActiveRecord::Schema.define(:version => 0) do
create_table "attachments", :primary_key => "attachmentId", :force => true do |t|
t.string "attachmentName", :null => false
t.string "fileType", :limit => 30, :null => false
t.binary "content", :null => false
t.string "printCode"
end
create_table "emails", :primary_key => "emailId", :force => true do |t|
t.integer "userId", :limit => 11, :null => false
t.integer "attachmentId", :limit => 11, :null => false
t.string "body", :limit => 1000
t.string "subject"
end
add_index "emails", ["attachmentId"], :name => "attachmentId", :unique => true
add_index "emails", ["userId"], :name => "userId"
create_table "printUsers", :primary_key => "userId", :force => true do |t|
t.string "email", :null => false
end
add_index "printUsers", ["email"], :name => "email", :unique => true
end
There seem to be several approaches to using rails with an existing database. See Connect rails to existing postgres DB - models not seen by console and controllers, and especially the links from it: http://magicmodels.rubyforge.org/magic_model_generator/ and http://blog.aizatto.com/2007/05/21/activerecord-without-rails/
It's good to point out that you don't need to use ActiveRecord with rails. Your model can be any Object, so it might be easier to use something like sequel or arel to access the data.
Another approach would be to migrate the data out of your existing database and into a new one that you generate by scaffolding your models.

RoR MySQL boolean value

Im having an issue with MySql.Created a boolean column(Via a Database Migration) in MySql Database,toggled the value to "true".I Have a method in my rails App that checks if the value in that column is true,but it always returns false(even when called from a ruby console). Tried using the Same Migration on a SQLite Database and the same code returns true on the same Column. Observed MySQL uses tinyint for boolean values(1 == true) .
Please how can i correct this?
My Migration:
def self.up
create_table :users do |t|
t.string :name
t.string :email
t.boolean :admin , default => false
...
t.timestamps
end
end
...
Then i used the Faker Gem to Populate my Database, toggling one user in it setting it's value to true
Sample Data Via Faker Gem
admin = User.create!(:name => "sample",
:email => "sample#sample.com",
:password => "sample",
:password_confirmation => "sample")
admin.toggle!(:admin) #toggling value of admin to true just for this user