Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 830
xxx Avatar asked Oct 29 '22 18:10

xxx


1 Answers

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.

like image 134
xxx Avatar answered Nov 13 '22 00:11

xxx