Created
May 6, 2017 10:36
-
-
Save ronin/4f1b38fcbfd7532e1add7e0c8b71c05b to your computer and use it in GitHub Desktop.
Simple example showing how to monkey patch ActiveRecord to allow database switching
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Define some required dependencies | |
require "bundler/inline" | |
gemfile(false) do | |
source "https://rubygems.org" | |
gem "activerecord", "~> 4.2.8" | |
gem "sqlite3" | |
end | |
require "active_record" | |
class User < ActiveRecord::Base | |
end | |
DatabaseModel = Struct.new(:name) do | |
def superclass | |
ActiveRecord::Base | |
end | |
end | |
# Setup database connections and create databases if not present | |
connection_handler = ActiveRecord::ConnectionAdapters::ConnectionHandler.new | |
resolver = ActiveRecord::ConnectionAdapters::ConnectionSpecification::Resolver.new({ | |
"users_shard_1" => { adapter: "sqlite3", database: "users_shard_1.sqlite3" }, | |
"users_shard_2" => { adapter: "sqlite3", database: "users_shard_2.sqlite3" } | |
}) | |
databases = %w{users_shard_1 users_shard_2} | |
databases.each do |database| | |
filename = "#{database}.sqlite3" | |
ActiveRecord::Base.establish_connection({ | |
adapter: "sqlite3", | |
database: filename | |
}) | |
spec = resolver.spec(database.to_sym) | |
connection_handler.establish_connection(DatabaseModel.new(database), spec) | |
next if File.exists?(filename) | |
ActiveRecord::Schema.define(version: 1) do | |
create_table :users do |t| | |
t.string :name | |
t.string :email | |
end | |
end | |
end | |
# Create custom connection handler | |
class ShardHandler | |
def initialize(original_handler) | |
@original_handler = original_handler | |
end | |
def use_database(name) | |
@model= DatabaseModel.new(name) | |
end | |
def retrieve_connection_pool(klass) | |
@original_handler.retrieve_connection_pool(@model) | |
end | |
def retrieve_connection(klass) | |
pool = retrieve_connection_pool(klass) | |
raise ConnectionNotEstablished, "No connection pool for #{klass}" unless pool | |
conn = pool.connection | |
raise ConnectionNotEstablished, "No connection for #{klass} in connection pool" unless conn | |
puts "Using database \"#{conn.instance_variable_get("@config")[:database]}\" (##{conn.object_id})" | |
conn | |
end | |
end | |
User.connection_handler = ShardHandler.new(connection_handler) | |
User.connection_handler.use_database("users_shard_1") | |
User.create(name: "John Doe", email: "[email protected]") | |
puts User.count | |
User.connection_handler.use_database("users_shard_2") | |
User.create(name: "Jane Doe", email: "[email protected]") | |
puts User.count | |
User.connection_handler.use_database("users_shard_1") | |
puts User.count |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment