Copy between databases in Rails console - ruby-on-rails

I have three databases:
A. mynewapp_psql (Postgres)
B. old_products_psql (Postgres)
C. old_blogposts_mysql (Mysql)
Each is defined in database.yml
I'm using A (mynewapp_psql) as the database for my new app. In this app I want to be able to copy selected material from my two older databases.
My attempt (updated according to response)
old_db = ActiveRecord::Base.establish_connection(:database => 'old_blogposts_mysql'... etc)
posts = old_db.connection.execute("select * from posts'")
posts.each do |p|
NewPost.create(:name => p.name.downcase) #NewPost should add Post in A. (mynewapp_psql)
end
It should take each product from my old database and create a new equivalent in the new database.
I really prefer doing it through the console and I can't copy the database straight over since I need to filter and alter the data.

So your case is one of Lost connection. How about Establishing a new connection to your current DB so that your code becomes like this
old_db = ActiveRecord::Base.establish_connection(:database => 'old_blogposts_mysql'... etc)
posts = old_db.connection.execute("select * from posts'")
new_db = ActiveRecord::Base.establish_connection(:database => 'mynewapp_psql'... etc)
posts.each do |p|
NewPost.create(:name => p.name.downcase) #NewPost should add Post in A. (mynewapp_psql)
end

The console is not the only way to dynamically access your databases. Think about a custom ruby task for your purposes using ActiveRecord connections for each database.
require 'pg'
require 'active_record'
ActiveRecord::Base.establish_connection(
adapter: 'postgresql', # or 'mysql2' or 'sqlite3'
host: 'localhost',
database: 'canvas_test',
username: 'joe',
password: 'shiitake'
)
# EXEC raw SQL
ActiveRecord::Base.connection.execute("SELECT * FROM posts;")
class Post < ActiveRecord::Base
end
# READ all posts
puts Post.count
=> #<Post:0xb96582cc>
=> ...
# CREATE one new post
my_post = Post.new

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

Can I Rebuild db/schema.rb to Include Multiple Databases Without Migrations?

I have a Rails 5 application that uses three different existing databases. No migrations were needed in this application. I want to build db/schema.rb to include all three databases, not just the main database. Executing rake db:schema:dump rebuilds the schema using the main database. I believe there is a way to do this but for some reason the way I've been searching I cannot find anything about this. All of the posts I have found discuss how to use models from different databases, not how to rebuild the schema to include all databases.
I started looking at this again about a month ago. After reviewing several options I found a solution created by #ostinelli that worked really well and easy to implement. This solution creates separate migrations for each database being maintained instead of putting them all in a single schema file. My goal mainly was to have a schema for each database which this accomplishes for me. There are some parts I did not implement because they were not needed.
http://www.ostinelli.net/setting-multiple-databases-rails-definitive-guide/
I created separate database yml files for each database like this:
config/database_stats.yml
development:
adapter: postgresql
encoding: utf8
host: localhost
pool: 10
database: myapp_stats_development
username: postgres
password:
test:
adapter: postgresql
encoding: utf8
host: localhost
pool: 10
database: myapp_stats_test
username: postgres
password:
production:
adapter: postgresql
encoding: utf8
url: <%= ENV["DATABASE_STATS_URL"] %>
pool: <%= ENV["DB_POOL"] || 5 %>
Then I created separate database rake tasks for each database:
lib/tasks/db_stats.rake
task spec: ["stats:db:test:prepare"]
namespace :stats do
namespace :db do |ns|
task :drop do
Rake::Task["db:drop"].invoke
end
task :create do
Rake::Task["db:create"].invoke
end
task :setup do
Rake::Task["db:setup"].invoke
end
task :migrate do
Rake::Task["db:migrate"].invoke
end
task :rollback do
Rake::Task["db:rollback"].invoke
end
task :seed do
Rake::Task["db:seed"].invoke
end
task :version do
Rake::Task["db:version"].invoke
end
namespace :schema do
task :load do
Rake::Task["db:schema:load"].invoke
end
task :dump do
Rake::Task["db:schema:dump"].invoke
end
end
namespace :test do
task :prepare do
Rake::Task["db:test:prepare"].invoke
end
end
# append and prepend proper tasks to all the tasks defined here above
ns.tasks.each do |task|
task.enhance ["stats:set_custom_config"] do
Rake::Task["stats:revert_to_original_config"].invoke
end
end
end
task :set_custom_config do
# save current vars
#original_config = {
env_schema: ENV['SCHEMA'],
config: Rails.application.config.dup
}
# set config variables for custom database
ENV['SCHEMA'] = "db_stats/schema.rb"
Rails.application.config.paths['db'] = ["db_stats"]
Rails.application.config.paths['db/migrate'] = ["db_stats/migrate"]
Rails.application.config.paths['db/seeds'] = ["db_stats/seeds.rb"]
Rails.application.config.paths['config/database'] = ["config/database_stats.yml"]
end
task :revert_to_original_config do
# reset config variables to original values
ENV['SCHEMA'] = #original_config[:env_schema]
Rails.application.config = #original_config[:config]
end
end
Then I created custom migration generators for the additional databases.
lib/generators/stats_migration_generator.rb
require 'rails/generators/active_record/migration/migration_generator'
class StatsMigrationGenerator < ActiveRecord::Generators::MigrationGenerator
source_root File.join(File.dirname(ActiveRecord::Generators::MigrationGenerator.instance_method(:create_migration_file).source_location.first), "templates")
def create_migration_file
set_local_assigns!
validate_file_name!
migration_template #migration_template, "db_stats/migrate/#{file_name}.rb"
end
end
I created database migrations for each database similar to:
rails g stats_migration create_clicks
create db_stats/migrate/20151201191642_create_clicks.rb
rake stats:db:create
rake stats:db:migrate
Then created separate database initializers for the additional databases then modified each model.
config/initializers/db_stats.rb
DB_STATS = YAML::load(ERB.new(File.read(Rails.root.join("config","database_stats.yml"))).result)[Rails.env]
class Click < ActiveRecord::Base
establish_connection DB_STATS
end
I had three databases in my application when I first wrote this question. I'm now adding another one database. I'm so grateful that #ostinelli wrote this. It made my life easier.

Rails view not displaying the updated database information, information is retrieved like normal in rails console

I have a Student model that has_many CustomField (polymorphic) rows. After adding a couple CustomFields to a student in the rails console, I then started up a rails server and navigated to the page that is supposed to display this information, however it is not displaying any of the updated information.
I have a method get_custom_fields_with_values that returns a Hash of field names and values.
In the rails console:
student = Student.all.first
field_attrs = FieldAttributes.create(name: 'Favorite Color') # => true
student.custom_fields.build(field_attributes: field_attrs, value: 'Blue').save # => true
student.get_custom_fields_with_values # => { 'Favorite Color' => 'Blue' }
However, in the view when I make the call to the get_custom_fields_with_values, all that is returned is an empty Hash.
I'm using a Postgres database, and inspecting the data inside it shows that all the rows are there, and all the foreign keys are correct.
This is the query that is made when the page is requested:
CustomField Load (0.3ms) SELECT "custom_fields".* FROM "custom_fields"
WHERE "custom_fields"."custom_fieldable_id" = $1
AND "custom_fields"."custom_fieldable_type" = $2
[["custom_fieldable_id", "34df8e09-148d-5660-bb6c-dd5ca7c9e0ff"],
["custom_fieldable_type", "Student"]]
The database rows:
I've restarted the server multiple times, and still only get an empty hash or empty ActiveRecord::CollectionProxy object. However, upon restarting the console, it is able to retrieve the fields just fine and I get the expected Hash back. And running the query that is run when loading the page also returns the expected behavior.
I've cleared my browser cache, restarted the browser, and tried multiple browsers all to the same effect. Restarted the server multiple times, and restarted the console multiple times.
The HAML:
= bootstrap_panel do |panel|
- panel.content = capture do
.panel-heading
%h2 Custom Fields
.panel-body
= #student.get_custom_fields_with_values
The function:
def get_custom_fields_with_values
custom_fields.inject(Hash.new) do |h, cf|
field_name = cf.field_attributes.name
h[field_name] = cf.value; h
end
end
And all that is being displayed in the panel body:
{}
But the method call in the console returns:
student.get_custom_fields_with_values
=> {"Favorite Car"=>"Prius", "Favorite Color"=>"Blue"}
config/database.yml
default: &default
adapter: postgresql
database: <%= Rails.application.secrets.database %>
host: <%= Rails.application.secrets.database_host %>
username: <%= Rails.application.secrets.database_username %>
password: <%= Rails.application.secrets.database_password %>
port: 5432
pool: 10
development:
<<: *default
test:
<<: *default
staging:
<<: *default
production:
<<: *default
SOLVED
The problem was my database is multi tenanted and rails was looking in the wrong tenant. I added it to the excluded models so it'd look in public, and it fixed the issue.
The issue was that my database is multi tenanted, so rails was looking in the current schema when it should have been looking in the public schema. I just added the CustomFields model to the exclusion list so rails would look in the public schema and that fixed my issue.

Establish a connection to another database only in a block?

In a rails application, I have this code in pure ruby :
class LinkCreator
attr_accessor :animal
def initialize(animal:)
#animal = animal
end
def call
"something#{link_id}"
end
private
def link_id
connection.execute(sql_request).first.first
end
def sql_request
"SELECT field FROM table WHERE field_id = '#{field_id}' LIMIT 1"
end
def field_id
animal.field_id
end
def connection
ActiveRecord::Base.establish_connection(
adapter: "mysql",
host: ENV["MYSQL_HOST"],
username: ENV["MYSQL_USERNAME"],
password: ENV["MYSQL_PASSWORD"],
database: ENV["MYSQL_DB_NAME"]
).connection
end
end
As you can see, this is not a model but only a simple class. The problem is than the connection of activerecord is changed and the other requests, later, are executed on the new connection.
Is it possible to establish a connection only in a block and go back to the old connection. I know I can establish another connection but this is very bad for performance.
It would be nice if you keep all database connections in database.yml
development:
adapter: mysql2
other stuff...
db_2:
adapter: mysql2
other stuff..
other_envs:
.....
Then create a class
class OtherDB < ActiveRecord::Base
establish_connection(:db_2)
end
From your controller you can access just like
OtherDB.table_name = "table_name"
OtherDB.first
Check my blog here http://imnithin.github.io/multiple-database.html
Have found most short example in Rails codebase at activerecord/test/support/connection_helper.rb and slightly adapted:
def with_another_db(another_db_config)
original_connection = ActiveRecord::Base.remove_connection
ActiveRecord::Base.establish_connection(another_db_config)
yield
ensure
ActiveRecord::Base.establish_connection(original_connection)
end
Usage (given that you have another_db: section in your database.yml):
with_another_db(ActiveRecord::Base.configurations['another_db']) do
ActiveRecord::Base.connection.execute("SELECT 'Look ma, I have changed DB!';")
end
You can perform some queries within a block. First, define some module which will extend ActiveRecord, as below. This is a part of code used in production to change db connection per each request as well as to temporarily switch db to perform some queries within another database.
# RAILS_ROOT/lib/connection_switch.rb
module ConnectionSwitch
def with_db(connection_spec_name)
current_conf = ActiveRecord::Base.connection_config
begin
ActiveRecord::Base.establish_connection(db_configurations[connection_spec_name]).tap do
Rails.logger.debug "\e[1;35m [ActiveRecord::Base switched database] \e[0m #{ActiveRecord::Base.connection.current_database}"
end if database_changed?(connection_spec_name)
yield
ensure
ActiveRecord::Base.establish_connection(current_conf).tap do
Rails.logger.debug "\e[1;35m [ActiveRecord::Base switched database] \e[0m #{ActiveRecord::Base.connection.current_database}"
end if database_changed?(connection_spec_name, current_conf)
end
end
private
def database_changed?(connection_spec_name, current_conf = nil)
current_conf = ActiveRecord::Base.connection_config unless current_conf
current_conf[:database] != db_configurations[connection_spec_name].try(:[], :database)
end
def db_configurations
#db_config ||= begin
file_name = "#{Rails.root}/config/database.yml"
if File.exists?(file_name) || File.symlink?(file_name)
config ||= HashWithIndifferentAccess.new(YAML.load(ERB.new(File.read(file_name)).result))
else
config ||= HashWithIndifferentAccess.new
end
config
end
end
end
ActiveRecord.send :extend, ConnectionSwitch
Now you can use it as below:
ActiveRecord.with_db("db_connection_name") do
# some queries to another db
end
If you want to connect to postgres sql, you can use pg ruby gem & add the below code inside the block.
postgres = PG.connect :host => <host_name>, :port => <port>, :dbname => <database_name>, :user => <user>, :password => <password>
tables = postgres.exec(query)
tables.num_tuples.times do |i|
print tables[i]
end
To connect to mysql db inside a block, use mysql2 ruby gem & add the below code inside the block.
db = Mysql2::Client.new ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(<database_url>).to_hash
data = db.query(<<-SQL)
select * from students
SQL
print data
I use environment variables taken from Heroku's DATABASE_URL to connect to different databases:
class Database
def self.development!
ActiveRecord::Base.establish_connection(:development)
end
def self.production!
ActiveRecord::Base.establish_connection(ENV['PRODUCTION_DATABASE'])
end
def self.staging!
ActiveRecord::Base.establish_connection(ENV['STAGING_DATABASE'])
end
end
e.g.:
Database.production!; puts User.all.map(&:name)
Database.staging!; puts User.all.map(&:name)
It might help to use an instance variable to store the connection. Something like this:
def connection
#connection ||= ActiveRecord::Base.establish_connection(
adapter: "mysql",
host: ENV["MYSQL_HOST"],
username: ENV["MYSQL_USERNAME"],
password: ENV["MYSQL_PASSWORD"],
database: ENV["MYSQL_DB_NAME"]
).connection
end
That way the existing connection is retrieved on future connection attempts, rather than establishing a new one.
Try active_record_slave gem:
Ruby gem: active_record_slave

RSS feed from MySQL table using either Ruby or Rails or Gems

I have a MySQL table and I want to pick certain columns to create RSS Feed from it. How to do it using Ruby or Rails or Gems?
Depending on what I was trying to do, I would probably just go for a simple Ruby script. I would use ActiveRecord so I didn't have to write any SQL. Then I would use either Builder or RubyRSS to generate the feed.
Connecting ActiveRecord to a MySQL server directly is as simple as:
require 'active_record'
ActiveRecord::Base.establish_connection(
:adapter => "mysql",
:host => "localhost",
:username => "myusername",
:password => "mypassword",
:database => "mydb"
)
Then you are free to define ActiveRecord models like you would in a regular Rails app.
There are RSS generator examples on the RubyRSS website and a Builder one on the Railscasts website.
Hernan is right...here is are the full steps you'll need to get data from the database (I moved the code to below the steps for easier formatting:
Install ActiveRecord: sudo gem install activerecord
Establish the connection per hernan43's recommendation (preferrably in a separate file, let's call it "connection.rb"
Create a class that uses that connection by inheriting from ActiveRecord
Retrieve record(s) from the table, and use it/them to populate your RSS generator
You don't have to separate everything out into a file...you could put everything below in one file, and remove the 'requires' in files 2 and 3, but it's a convention to separate out your concerns in a manner similar to what I've done.
#1: file connection.rb
require 'rubygems'
require 'active_record'
ActiveRecord::Base.establish_connection(
:adapter => "mysql",
:host => "localhost",
:database => "appdb",
:username => "appuser",
:password => "secret"
)
#2 filename: singural_rss_table_name.rb
require 'connection'
class SingularRSSTableName < ActiveRecord::Base
set_table_name 'real_database_table_name' #if the table name is the lowercase, underscore plural of the class name, then you don't need this line.
end
#3 filename: rss_creator_file.rb
require 'singular_rss_table_name'
# Here you retrieve the rows from your database table,
# based on the condition that the column 'title' is exactly the text 'article_title'
# there are a lot more options for
# conditions in ActiveRecord, and you'll probably want to look them up.
records_for_rss_entries = SingularRssTableName.find(:all, :conditions => {:title => 'article_title'})
rss_object = RSS::Maker.new(version) do |feed|
feed.channel.title = "Example Ruby RSS feed"
records_for_rss_entries.each do |entry_record| # here we're iterating through
# each of the rows we found.
entry = feed.items.new_item
entry.title = entry_record.title # at this point, each 'entry_record' is a row in your db table
# use the dot (.) operator to access columns in the table.
...
end
end
The contents of this answer were partially answered from:
http://rubyrss.com/
and
http://www.agileadvisor.com/2008/01/using-activerecord-outside-rails.html

Resources