mirror of
https://github.com/discourse/discourse.git
synced 2025-05-31 21:20:07 +08:00
REFACTOR: Code generator for migrations IntemerdiateDB
* Splits the existing script into multiple classes * Adds command for generating IntermediateDB schema (`migrations/bin/cli schema generate`) * Changes the syntax of the IntermediateDB schema config * Adds validation for the schema config * It uses YAML schema aka JSON schema to validate the config file * It generates the SQL schema file and Ruby classes for storing data in the IntermediateDB
This commit is contained in:

committed by
Gerhard Schlager

parent
71a90dcba2
commit
17ba19c7ae
139
migrations/lib/database/schema/model_writer.rb
Normal file
139
migrations/lib/database/schema/model_writer.rb
Normal file
@ -0,0 +1,139 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "rake"
|
||||
require "syntax_tree/rake_tasks"
|
||||
|
||||
module Migrations::Database::Schema
|
||||
class ModelWriter
|
||||
def initialize(namespace, header)
|
||||
@namespace = namespace
|
||||
@header = header.gsub(/^/, "# ")
|
||||
end
|
||||
|
||||
def self.filename_for(table)
|
||||
"#{table.name.singularize}.rb"
|
||||
end
|
||||
|
||||
def self.format_files(path)
|
||||
glob_pattern = File.join(path, "**/*.rb")
|
||||
|
||||
system(
|
||||
"bundle",
|
||||
"exec",
|
||||
"stree",
|
||||
"write",
|
||||
glob_pattern,
|
||||
exception: true,
|
||||
out: File::NULL,
|
||||
err: File::NULL,
|
||||
)
|
||||
rescue StandardError
|
||||
raise "Failed to run `bundle exec stree write '#{glob_pattern}'`"
|
||||
end
|
||||
|
||||
def output_table(table, output_stream)
|
||||
columns = table.sorted_columns
|
||||
|
||||
output_stream.puts "# frozen_string_literal: true"
|
||||
output_stream.puts
|
||||
output_stream.puts @header
|
||||
output_stream.puts
|
||||
output_stream.puts "module #{@namespace}"
|
||||
output_stream.puts " module #{to_singular_classname(table.name)}"
|
||||
output_stream.puts " SQL = <<~SQL"
|
||||
output_stream.puts " INSERT INTO #{escape_identifier(table.name)} ("
|
||||
output_stream.puts column_names(columns)
|
||||
output_stream.puts " )"
|
||||
output_stream.puts " VALUES ("
|
||||
output_stream.puts value_placeholders(columns)
|
||||
output_stream.puts " )"
|
||||
output_stream.puts " SQL"
|
||||
output_stream.puts
|
||||
output_stream.puts " def self.create("
|
||||
output_stream.puts method_parameters(columns)
|
||||
output_stream.puts " )"
|
||||
output_stream.puts " ::Migrations::Database::IntermediateDB.insert("
|
||||
output_stream.puts " SQL,"
|
||||
output_stream.puts insertion_arguments(columns)
|
||||
output_stream.puts " )"
|
||||
output_stream.puts " end"
|
||||
output_stream.puts " end"
|
||||
output_stream.puts "end"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def to_singular_classname(snake_case_string)
|
||||
snake_case_string.singularize.camelize
|
||||
end
|
||||
|
||||
def column_names(columns)
|
||||
columns.map { |c| " #{escape_identifier(c.name)}" }.join(",\n")
|
||||
end
|
||||
|
||||
def value_placeholders(columns)
|
||||
indentation = " "
|
||||
max_length = 100 - indentation.length
|
||||
placeholder = "?, "
|
||||
placeholder_count = columns.size
|
||||
|
||||
current_length = 0
|
||||
placeholders = indentation.dup
|
||||
|
||||
(1..placeholder_count).each do |index|
|
||||
placeholder = "?" if index == placeholder_count
|
||||
|
||||
if current_length + placeholder.length > max_length
|
||||
placeholders.rstrip!
|
||||
placeholders << "\n" << indentation
|
||||
current_length = 0
|
||||
end
|
||||
|
||||
placeholders << placeholder
|
||||
current_length += placeholder.length
|
||||
end
|
||||
|
||||
placeholders
|
||||
end
|
||||
|
||||
def method_parameters(columns)
|
||||
columns
|
||||
.map do |c|
|
||||
default_value = !c.is_primary_key && c.nullable ? " nil" : ""
|
||||
" #{c.name}:#{default_value}"
|
||||
end
|
||||
.join(",\n")
|
||||
end
|
||||
|
||||
def insertion_arguments(columns)
|
||||
columns
|
||||
.map do |c|
|
||||
argument =
|
||||
case c.datatype
|
||||
when :datetime
|
||||
"::Migrations::Database.format_datetime(#{c.name})"
|
||||
when :date
|
||||
"::Migrations::Database.format_date(#{c.name})"
|
||||
when :boolean
|
||||
"::Migrations::Database.format_boolean(#{c.name})"
|
||||
when :inet
|
||||
"::Migrations::Database.format_ip_address(#{c.name})"
|
||||
when :blob
|
||||
"::Migrations::Database.to_blob(#{c.name})"
|
||||
when :json
|
||||
"::Migrations::Database.to_json(#{c.name})"
|
||||
when :float, :integer, :numeric, :text
|
||||
c.name
|
||||
else
|
||||
raise "Unknown dataype: #{type}"
|
||||
end
|
||||
" #{argument},"
|
||||
end
|
||||
.join("\n")
|
||||
end
|
||||
|
||||
def escape_identifier(identifier)
|
||||
::Migrations::Database::Schema.escape_identifier(identifier)
|
||||
end
|
||||
end
|
||||
end
|
Reference in New Issue
Block a user