# frozen_string_literal: true module Switchman class Engine < ::Rails::Engine isolate_namespace Switchman # enable Rails 6.1 style connection handling config.active_record.legacy_connection_handling = false config.active_record.writing_role = :primary config.autoload_once_paths << File.expand_path('app/models', config.paths.path) def self.lookup_stores(cache_store_config) result = {} cache_store_config.each do |key, value| next if value.is_a?(String) result[key] = ::ActiveSupport::Cache.lookup_store(value) end cache_store_config.each do |key, value| # rubocop:disable Style/CombinableLoops next unless value.is_a?(String) result[key] = result[value] end result end initializer 'switchman.initialize_cache', before: 'initialize_cache' do require 'switchman/active_support/cache' ::ActiveSupport::Cache.singleton_class.prepend(ActiveSupport::Cache::ClassMethods) # if we haven't already setup our cache map out-of-band, set it up from # config.cache_store now. behaves similarly to Rails' default # initialize_cache initializer, but for each value in the map, rather # than just Rails.cache. if config.cache_store is a flat value, uses it # to fill just the Rails.env entry in the cache map. unless Switchman.config[:cache_map].present? cache_store_config = ::Rails.configuration.cache_store cache_store_config = { ::Rails.env => cache_store_config } unless cache_store_config.is_a?(Hash) Switchman.config[:cache_map] = Engine.lookup_stores(cache_store_config) end # if the configured cache map (either from before, or as populated from # config.cache_store) didn't have an entry for Rails.env, add one using # lookup_store(nil); matches the behavior of Rails' default # initialize_cache initializer when config.cache_store is nil. unless Switchman.config[:cache_map].key?(::Rails.env) value = ::ActiveSupport::Cache.lookup_store(nil) Switchman.config[:cache_map][::Rails.env] = value end middlewares = Switchman.config[:cache_map].values.map do |store| store.middleware if store.respond_to?(:middleware) end.compact.uniq middlewares.each do |middleware| config.middleware.insert_before('Rack::Runtime', middleware) end # prevent :initialize_cache from trying to (or needing to) set # Rails.cache. once our switchman.extend_ar initializer (below) runs # Rails.cache will be overridden to pull appropriate values from the # cache map, but between now and then, Rails.cache should return the # Rails.env entry in the cache map. ::Rails.cache = Switchman.config[:cache_map][::Rails.env] end initializer 'switchman.extend_ar', before: 'active_record.initialize_database' do ::ActiveSupport.on_load(:active_record) do # Switchman requires postgres, so just always load the pg adapter require 'active_record/connection_adapters/postgresql_adapter' require 'switchman/active_record/abstract_adapter' require 'switchman/active_record/association' require 'switchman/active_record/attribute_methods' require 'switchman/active_record/base' require 'switchman/active_record/calculations' require 'switchman/active_record/connection_pool' require 'switchman/active_record/database_configurations' require 'switchman/active_record/database_configurations/database_config' require 'switchman/active_record/finder_methods' require 'switchman/active_record/log_subscriber' require 'switchman/active_record/migration' require 'switchman/active_record/model_schema' require 'switchman/active_record/persistence' require 'switchman/active_record/postgresql_adapter' require 'switchman/active_record/predicate_builder' require 'switchman/active_record/query_cache' require 'switchman/active_record/query_methods' require 'switchman/active_record/reflection' require 'switchman/active_record/relation' require 'switchman/active_record/spawn_methods' require 'switchman/active_record/statement_cache' require 'switchman/active_record/tasks/database_tasks' require 'switchman/active_record/type_caster' require 'switchman/active_record/test_fixtures' require 'switchman/arel' require 'switchman/call_super' require 'switchman/rails' require 'switchman/guard_rail/relation' require 'switchman/standard_error' ::StandardError.include(StandardError) self.default_shard = ::Rails.env.to_sym self.default_role = :primary prepend ActiveRecord::Base include ActiveRecord::AttributeMethods include ActiveRecord::Persistence singleton_class.prepend ActiveRecord::ModelSchema::ClassMethods ::ActiveRecord::StatementCache.prepend(ActiveRecord::StatementCache) ::ActiveRecord::StatementCache.singleton_class.prepend(ActiveRecord::StatementCache::ClassMethods) ::ActiveRecord::StatementCache::BindMap.prepend(ActiveRecord::StatementCache::BindMap) ::ActiveRecord::StatementCache::Substitute.send(:attr_accessor, :primary, :sharded) ::ActiveRecord::Associations::CollectionAssociation.prepend(ActiveRecord::CollectionAssociation) ::ActiveRecord::Associations::HasOneAssociation.prepend(ActiveRecord::ForeignAssociation) ::ActiveRecord::Associations::HasManyAssociation.prepend(ActiveRecord::ForeignAssociation) ::ActiveRecord::PredicateBuilder.singleton_class.prepend(ActiveRecord::PredicateBuilder) prepend(ActiveRecord::AutosaveAssociation) ::ActiveRecord::Associations::Association.prepend(ActiveRecord::Association) ::ActiveRecord::Associations::BelongsToAssociation.prepend(ActiveRecord::BelongsToAssociation) ::ActiveRecord::Associations::CollectionProxy.include(ActiveRecord::CollectionProxy) ::ActiveRecord::Associations::Preloader::Association.prepend(ActiveRecord::Preloader::Association) ::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::AbstractAdapter) ::ActiveRecord::ConnectionAdapters::ConnectionPool.prepend(ActiveRecord::ConnectionPool) ::ActiveRecord::ConnectionAdapters::AbstractAdapter.prepend(ActiveRecord::QueryCache) ::ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.prepend(ActiveRecord::PostgreSQLAdapter) ::ActiveRecord::DatabaseConfigurations.prepend(ActiveRecord::DatabaseConfigurations) ::ActiveRecord::DatabaseConfigurations::DatabaseConfig.prepend(ActiveRecord::DatabaseConfigurations::DatabaseConfig) ::ActiveRecord::LogSubscriber.prepend(ActiveRecord::LogSubscriber) ::ActiveRecord::Migration.prepend(ActiveRecord::Migration) ::ActiveRecord::Migration::Compatibility::V5_0.prepend(ActiveRecord::Migration::Compatibility::V5_0) ::ActiveRecord::MigrationContext.prepend(ActiveRecord::MigrationContext) ::ActiveRecord::Migrator.prepend(ActiveRecord::Migrator) ::ActiveRecord::Reflection::AbstractReflection.include(ActiveRecord::Reflection::AbstractReflection) ::ActiveRecord::Reflection::AssociationReflection.prepend(ActiveRecord::Reflection::AssociationScopeCache) ::ActiveRecord::Reflection::ThroughReflection.prepend(ActiveRecord::Reflection::AssociationScopeCache) ::ActiveRecord::Reflection::AssociationReflection.prepend(ActiveRecord::Reflection::AssociationReflection) ::ActiveRecord::Relation.prepend(ActiveRecord::Calculations) ::ActiveRecord::Relation.include(ActiveRecord::FinderMethods) ::ActiveRecord::Relation.include(ActiveRecord::QueryMethods) ::ActiveRecord::Relation.prepend(GuardRail::Relation) ::ActiveRecord::Relation.prepend(ActiveRecord::Relation) ::ActiveRecord::Relation.include(ActiveRecord::SpawnMethods) ::ActiveRecord::Relation.include(CallSuper) ::ActiveRecord::PredicateBuilder::AssociationQueryValue.prepend(ActiveRecord::PredicateBuilder::AssociationQueryValue) ::ActiveRecord::PredicateBuilder::PolymorphicArrayValue.prepend(ActiveRecord::PredicateBuilder::AssociationQueryValue) ::ActiveRecord::Tasks::DatabaseTasks.singleton_class.prepend(ActiveRecord::Tasks::DatabaseTasks) ::ActiveRecord::TestFixtures.prepend(ActiveRecord::TestFixtures) ::ActiveRecord::TypeCaster::Map.include(ActiveRecord::TypeCaster::Map) ::ActiveRecord::TypeCaster::Connection.include(ActiveRecord::TypeCaster::Connection) ::Rails.singleton_class.prepend(Rails::ClassMethods) ::Arel::Table.prepend(Arel::Table) ::Arel::Visitors::ToSql.prepend(Arel::Visitors::ToSql) end end def self.foreign_key_check(name, type, limit: nil) puts "WARNING: All foreign keys need to be 8-byte integers. #{name} looks like a foreign key. If so, please add the option: `:limit => 8`" if name.to_s =~ /_id\z/ && type.to_s == 'integer' && limit.to_i < 8 end initializer 'switchman.extend_connection_adapters', after: 'active_record.initialize_database' do ::ActiveSupport.on_load(:active_record) do ::ActiveRecord::ConnectionAdapters::AbstractAdapter.descendants.each do |klass| klass.prepend(ActiveRecord::AbstractAdapter::ForeignKeyCheck) end require 'switchman/active_record/table_definition' ::ActiveRecord::ConnectionAdapters::TableDefinition.prepend(ActiveRecord::TableDefinition) Shard.send(:initialize_sharding) end end initializer 'switchman.eager_load' do ::ActiveSupport.on_load(:before_eager_load) do # This needs to be loaded before Switchman::Shard, otherwise it won't autoload it correctly require 'active_record/base' end end initializer 'switchman.extend_guard_rail', before: 'switchman.extend_ar' do ::ActiveSupport.on_load(:active_record) do require 'switchman/guard_rail' ::GuardRail.singleton_class.prepend(GuardRail::ClassMethods) end end initializer 'switchman.extend_controller', after: 'guard_rail.extend_ar' do ::ActiveSupport.on_load(:action_controller) do require 'switchman/action_controller/caching' ::ActionController::Base.include(ActionController::Caching) end end end end