Skip to content

Instantly share code, notes, and snippets.

@a-bx
Forked from masciugo/multi-database_pattern.md
Created October 12, 2016 03:37
Show Gist options
  • Save a-bx/58e8b52fb9e934c84dd0b9a8ab31bdde to your computer and use it in GitHub Desktop.
Save a-bx/58e8b52fb9e934c84dd0b9a8ab31bdde to your computer and use it in GitHub Desktop.
Rails multi-database pattern

Rails multi-database pattern

The parent class for models that are persisted on a different database app/models/public_database_model.rb:

# Public: Provide a base class for accessing the public database.
# All models inheriting from this class will use by default the data store DB.
class PublicDatabaseModel < ActiveRecord::Base

  # Tell Active Record (AR) to not look up for a table called like this class,
  # since this class is only used to add custom config for AR
  self.abstract_class = true

  databases = YAML::load(ERB.new(IO.read('config/database_public.yml')).result)
  establish_connection(databases[Rails.env])

end

Hack to have extra tasks for the second datatbase. lib/tasks/databases.rake:

# Public: This Rake file tries to add what rails provides on the
# databases.rake but for building on top of custom databases.
# Basically we get the nice db:migrate but for using it on a different DB than
# the default, by calling it with the namespace defined here.
#
# In order to be able to use the default rails rake commands but on a different
# DB, we are first updating the Rails.application.config.paths and then
# calling the original rake task. Rails.application.config.paths is getting
# loaded and available as soon as we call rake since the rakefile in a rails
# project declares that. Look at Rakefile in the project root for more details.


# Public: Access to the same commands you do for DB operations,
# like db:drop, db:migrate but using the public namespace, this will
# execute on top of the public DB.
namespace :public do

  # Configure the variables that rails need in order to look up for the db configuration in a different folder
  task :set_custom_db_config_paths do
    # This is the minimum required to tell rails to use a different location
    # for all the files related to the database.
    ENV['SCHEMA'] = 'db_public/schema.rb'
    ENV['DB_STRUCTURE'] = 'db_public/structure.sql'
    Rails.application.config.paths['db'] = ['db_public']
    Rails.application.config.paths['db/migrate'] = ['db_public/migrate']
    Rails.application.config.paths['db/seeds.rb'] = ['db_public/seeds.rb']
    Rails.application.config.paths['config/database'] = ['config/database_public.yml']
  end

  namespace :db do
    scope_name = 'db'
    # for all db tasks ...
    Rake.application.tasks.find_all{|t| t.name.starts_with?(scope_name)}.map{|t| t.name[(scope_name.size+1)..-1] }.each do |taskname|

      task_to_run = Rake::Task["db:#{taskname}"]

      if description = task_to_run.comment
        description.gsub!('db:','public:db:')
        description.gsub!('db/','db_public/')
        description.gsub!('database.yml','database_public.yml')
      end

      desc description
      task taskname => :set_custom_db_config_paths do
        task_to_run.invoke
      end
    end
  end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment