# Add to `spec/support/database_state_loader.rb` class DatabaseStateLoader class EnvironmentError < RuntimeError; end def self.load(path) new(path).load end def initialize(path) @path = path end def load # Just double check we're in the right environment raise EnvironmentError.new("This can only be run in development") if not Rails.env.development? puts "Loading #{@path}" data = JSON.parse(File.read(@path)) created_database_config = create_and_switch_to_temporary_database insert_data(data) username = created_database_config['username'] password = created_database_config['password'] host = created_database_config['host'] || "127.0.0.1" port = created_database_config['port'] || "5432" name = created_database_config['database'] database_url = "postgres://#{username}:#{password}@#{host}:#{port}/#{name}" puts "" puts "State data was succesfully inserted into database: #{name} 👍" puts "" puts "You can startup a console to access this database by running:" puts "" puts " DATABASE_URL=#{database_url} DISABLE_SPRING=1 rails console" puts "" puts "When you're done, you can remove the database by running:" puts "" puts " dropdb #{name}" puts "" end private def create_and_switch_to_temporary_database # Parse and load database.yml database_yml_path = Rails.root.join("config", "database.yml") parsed_database_yml = ERB.new(database_yml_path.read).result database_config = YAML.load(parsed_database_yml) test_database_config = database_config['test'] # Create a new database for this state puts "Creating state database..." state_database_name = "#{test_database_config['database']}_state_#{Time.now.to_i}" ActiveRecord::Base.connection.create_database(state_database_name) # Connect to the jdatabase and recreate structure puts "Connecting `#{state_database_name}`" state_database_config = test_database_config.merge("database" => state_database_name, "pool" => 30) ActiveRecord::Base.establish_connection state_database_config ActiveRecord::Base.connection.execute(Rails.root.join("db/structure.sql").read) state_database_config end def insert_data(data) puts "Inserting data into database..." ActiveRecord::Base.transaction do data.each do |(table, rows)| rows.each do |row| columns = [] values = [] row.each do |(key, value)| columns << key values << begin case value when nil "null" when Hash case connection.columns(table).find { |column| column.name == key }.sql_type when "hstore" connection.quote connection.lookup_cast_type("hstore").serialize(value) when "json" connection.quote connection.lookup_cast_type("json").serialize(value) else raise "Not sure how to insert: (#{key}: #{value.inspect})" end when Array if value.empty? "null" else "(#{value.map { |v| connection.quote(v) }.join(", ")})" end else connection.quote value end end end connection.execute(<<~SQL) INSERT INTO #{quote_table_name(table)} (#{columns.map { |column| quote_column_name(column) }.join(", ")}) VALUES (#{values.join(", ")}) SQL end end end end delegate :connection, to: "ActiveRecord::Base" delegate :quote_table_name, :quote_column_name, to: :connection end