Skip to content

Commit

Permalink
Refactoring Database generators
Browse files Browse the repository at this point in the history
Extract all the DB information (gems, dockerfile packages, devcontainer etc.) into an object. This let's us remove the growing number of case statements in this code.
  • Loading branch information
andrewn617 committed May 13, 2024
1 parent 662c54d commit 1d9c431
Show file tree
Hide file tree
Showing 8 changed files with 317 additions and 214 deletions.
13 changes: 8 additions & 5 deletions railties/lib/rails/generators/app_base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
module Rails
module Generators
class AppBase < Base # :nodoc:
include Database
include Devcontainer
include AppName

Expand Down Expand Up @@ -40,7 +39,7 @@ def self.add_shared_options_for(name)
desc: "Path to some #{name} template (can be a filesystem path or URL)"

class_option :database, type: :string, aliases: "-d", default: "sqlite3",
enum: DATABASES,
enum: Database::DATABASES,
desc: "Preconfigure for selected database"

class_option :skip_git, type: :boolean, aliases: "-G", default: nil,
Expand Down Expand Up @@ -279,7 +278,7 @@ def set_default_accessors! # :doc:
def database_gemfile_entry # :doc:
return if options[:skip_active_record]

gem_name, gem_version = gem_for_database
gem_name, gem_version = database.gem
GemfileEntry.version gem_name, gem_version,
"Use #{options[:database]} as the database for Active Record"
end
Expand Down Expand Up @@ -574,7 +573,7 @@ def dockerfile_base_packages
packages = ["curl"]

# ActiveRecord databases
packages << base_package_for_database unless skip_active_record?
packages << database.base_package unless skip_active_record?

# ActiveStorage preview support
packages << "libvips" unless skip_active_storage?
Expand All @@ -590,7 +589,7 @@ def dockerfile_build_packages
packages = %w(build-essential git pkg-config)

# add database support
packages << build_package_for_database unless skip_active_record?
packages << database.build_package unless skip_active_record?

packages << "unzip" if using_bun?

Expand Down Expand Up @@ -772,6 +771,10 @@ def dockerfile_chown_directories

directories.sort
end

def database
@database ||= Database.build(options[:database])
end
end
end
end
308 changes: 262 additions & 46 deletions railties/lib/rails/generators/database.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,59 +2,84 @@

module Rails
module Generators
module Database # :nodoc:
class Database
DATABASES = %w( mysql trilogy postgresql sqlite3 )

def gem_for_database(database = options[:database])
case database
when "mysql" then ["mysql2", ["~> 0.5"]]
when "trilogy" then ["trilogy", ["~> 2.7"]]
when "postgresql" then ["pg", ["~> 1.1"]]
when "sqlite3" then ["sqlite3", [">= 1.4"]]
else [database, nil]
class << self
def build(database_name)
case database_name
when "mysql" then MySQL.new
when "postgresql" then PostgreSQL.new
when "trilogy" then MariaDB.new
when "sqlite3" then SQLite3.new
else Null.new
end
end
end

def docker_for_database_base(database = options[:database])
case database
when "mysql" then "curl default-mysql-client libvips"
when "trilogy" then "curl libvips"
when "postgresql" then "curl libvips postgresql-client"
when "sqlite3" then "curl libsqlite3-0 libvips"
else nil
def all
@all ||= [
MySQL.new,
PostgreSQL.new,
MariaDB.new,
SQLite3.new,
]
end
end

def docker_for_database_build(database = options[:database])
case database
when "mysql" then "build-essential default-libmysqlclient-dev git"
when "trilogy" then "build-essential git"
when "postgresql" then "build-essential git libpq-dev"
when "sqlite3" then "build-essential git"
else nil
end
def name
raise NotImplementedError
end

def base_package_for_database(database = options[:database])
case database
when "mysql" then "default-mysql-client"
when "postgresql" then "postgresql-client"
when "sqlite3" then "libsqlite3-0"
else nil
end
def service
raise NotImplementedError
end

def build_package_for_database(database = options[:database])
case database
when "mysql" then "default-libmysqlclient-dev"
when "postgresql" then "libpq-dev"
else nil
end
def port
raise NotImplementedError
end

def feature_name
raise NotImplementedError
end

private
def mysql_socket
@mysql_socket ||= [
def gem
raise NotImplementedError
end

def docker_base
raise NotImplementedError
end

def docker_build
raise NotImplementedError
end

def base_package
raise NotImplementedError
end

def build_package
raise NotImplementedError
end

def socket; end
def host; end

def feature
return unless feature_name

{ feature_name => {} }
end

def volume
return unless service

"#{name}-data"
end

module MySqlSocket
def socket
@socket ||= [
"/tmp/mysql.sock", # default
"/var/run/mysqld/mysqld.sock", # debian/gentoo
"/var/tmp/mysql.sock", # freebsd
Expand All @@ -67,13 +92,204 @@ def mysql_socket
].find { |f| File.exist?(f) } unless Gem.win_platform?
end

def mysql_database_host
if options[:skip_devcontainer]
"localhost"
else
"<%= ENV.fetch(\"DB_HOST\") { \"localhost\" } %>"
end
def host
"localhost"
end
end

class MySQL < Database
include MySqlSocket

def name
"mysql"
end

def service
{
"image" => "mysql/mysql-server:8.0",
"restart" => "unless-stopped",
"environment" => {
"MYSQL_ALLOW_EMPTY_PASSWORD" => "true",
"MYSQL_ROOT_HOST" => "%"
},
"volumes" => ["mysql-data:/var/lib/mysql"],
"networks" => ["default"],
}
end

def port
3306
end

def gem
["mysql2", ["~> 0.5"]]
end

def docker_base
"curl default-mysql-client libvips"
end

def docker_build
"build-essential default-libmysqlclient-dev git"
end

def base_package
"default-mysql-client"
end

def build_package
"default-libmysqlclient-dev"
end

def feature_name
"ghcr.io/rails/devcontainer/features/mysql-client"
end
end

class PostgreSQL < Database
def name
"postgres"
end

def service
{
"image" => "postgres:16.1",
"restart" => "unless-stopped",
"networks" => ["default"],
"volumes" => ["postgres-data:/var/lib/postgresql/data"],
"environment" => {
"POSTGRES_USER" => "postgres",
"POSTGRES_PASSWORD" => "postgres"
}
}
end

def port
5432
end

def gem
["pg", ["~> 1.1"]]
end

def docker_base
"curl libvips postgresql-client"
end

def docker_build
"build-essential git libpq-dev"
end

def base_package
"postgresql-client"
end

def build_package
"libpq-dev"
end

def feature_name
"ghcr.io/rails/devcontainer/features/postgres-client"
end
end

class MariaDB < Database
include MySqlSocket

def name
"mariadb"
end

def service
{
"image" => "mariadb:10.5",
"restart" => "unless-stopped",
"networks" => ["default"],
"volumes" => ["mariadb-data:/var/lib/mysql"],
"environment" => {
"MARIADB_ALLOW_EMPTY_ROOT_PASSWORD" => "true",
},
}
end

def port
3306
end

def gem
["trilogy", ["~> 2.7"]]
end

def docker_base
"curl libvips"
end

def docker_build
"build-essential git"
end

def base_package
nil
end

def build_package
nil
end

def feature_name
nil
end
end

class SQLite3 < Database
def name
"sqlite3"
end

def service
nil
end

def port
nil
end

def gem
["sqlite3", [">= 1.4"]]
end

def docker_base
"curl libsqlite3-0 libvips"
end

def docker_build
"build-essential git"
end

def base_package
"libsqlite3-0"
end

def build_package
nil
end

def feature_name
"ghcr.io/rails/devcontainer/features/sqlite3"
end
end

class Null < Database
def name; end
def service; end
def port; end
def volume; end
def docker_base; end
def docker_build; end
def base_package; end
def build_package; end
def feature_name; end
end
end
end
end

0 comments on commit 1d9c431

Please sign in to comment.