class Blufin::YmlSqlStructureWriter

Public Class Methods

new(site, schema_data) click to toggle source
# File lib/core/yml_writers/yml_sql_structure_writer.rb, line 5
def initialize(site, schema_data)

    @schema_data = schema_data

    raise RuntimeError, 'Could not find valid @schema_data.' if @schema_data.nil? || !@schema_data.is_a?(Hash)

    @site          = Blufin::SiteResolver::validate_site(site)
    @site_name     = Blufin::SiteResolver::get_site_name(@site)
    @site_domain   = Blufin::SiteResolver::get_site_domain(@site)
    @site_location = "#{Blufin::SiteResolver::get_site_location(@site)}/"

    @yml_enum_scanner = Blufin::ScannerJavaEnums.new(@site)

end

Public Instance Methods

write() click to toggle source
# File lib/core/yml_writers/yml_sql_structure_writer.rb, line 20
def write

    # Remove ALL previous structure files.
    Blufin::YmlSchemaValidator::VALID_SCHEMAS_GENERATE.each do |schema|
        sfs = get_structure_files(schema)
        next if sfs.nil? # Skip if user doesn't have the PATH_TO_RUBY config value.
        structure_file   = sfs[0]
        foreign_key_file = sfs[1]
        Blufin::Files::delete_file(structure_file)
        Blufin::Files::delete_file(foreign_key_file)
    end

    @schema_data.each do |schema, schema_data|

        # This filters out the "mock" schema used for testing.
        next if schema == Blufin::YmlSchemaValidator::MOCK

        fk_count  = 0
        idx_count = 0

        @structure         = []
        @alter_table_lines = []
        @fks               = []
        @link_tables       = []

        schema_data.each_with_index do |(table, table_data), idx|

            if table_data.length > 0

                primary_key      = nil
                indexes          = []
                indexes_unique   = []
                indexes_fulltext = []
                foreign_keys     = []

                @structure << "-- Create #{table} table"
                @structure << "CREATE TABLE `#{table}` ("

                table_data.each do |column_name, column_data|

                    # Skip Placeholders
                    next if column_name =~ /\A(#{Blufin::YmlSchemaValidator::VALID_SCHEMAS_REGEX})\.[a-z_]+\[\]\z/ || column_name =~ /\A(#{Blufin::YmlSchemaValidator::VALID_SCHEMAS_REGEX})\.[a-z_]+\z/

                    @type = column_data[Blufin::YmlSchemaValidator::TYPE]
                    @fkey = !column_data[Blufin::YmlSchemaValidator::FKEY].nil?

                    # Skip Transient Objects.
                    next if [Blufin::ScannerJavaEmbeddedObjects::OBJECT].include?(@type)

                    # Create FKEY LINK tables
                    if column_name =~ /\A[a-z_.]+\[#{Blufin::YmlSchemaValidator::LINK}\]/
                        @link_tables << [
                            "#{schema}.#{table}",
                            column_name.dup.gsub("[#{Blufin::YmlSchemaValidator::LINK}]", '')
                        ]
                        next
                    end

                    # Get the flags and check for indexes.
                    if column_data[Blufin::YmlSchemaValidator::FLAG].nil?
                        @flags = nil
                        indexes << "  INDEX `#{schema}_#{table}_#{column_name}_idx` (`#{column_name}`)," if @type =~ Blufin::YmlSchemaValidator::REGEX_ENUM
                    else
                        @flags      = Blufin::YmlCommon::extract_flags(column_data[Blufin::YmlSchemaValidator::FLAG])[0]
                        # See if this is a PRIMARY KEY
                        primary_key = column_name if @flags.primary_key
                        # See if there are any INDEXES
                        if @flags.index
                            if [Blufin::YmlSchemaValidator::TYPE_TEXT, Blufin::YmlSchemaValidator::TYPE_TEXT_LONG].include?(@type)
                                indexes_fulltext << "  FULLTEXT `#{schema}_#{table}_#{column_name}_idx_fulltext` (`#{column_name}`),"
                            else
                                indexes << "  INDEX `#{schema}_#{table}_#{column_name}_idx` (`#{column_name}`)," if @flags.unique.nil?
                                indexes_unique << "  UNIQUE INDEX `#{schema}_#{table}_#{column_name}_uniq_idx` (`#{column_name}`)," if @flags.unique
                            end
                        end
                    end

                    # Check for FKs
                    unless column_data[Blufin::YmlSchemaValidator::FKEY].nil?
                        fk_count          = fk_count + 1
                        fk_ref            = column_data[Blufin::YmlSchemaValidator::FKEY].split('.') # The target in form -> app.ebay_user.id
                        foreign_key_lines = []
                        foreign_key_lines << "ADD CONSTRAINT `fk_#{fk_count}`"
                        foreign_key_lines << "    FOREIGN KEY (`#{column_name}`)"
                        foreign_key_lines << "    REFERENCES `#{fk_ref[1]}` (`#{fk_ref[2]}`)"
                        foreign_key_lines << '    ON DELETE RESTRICT'
                        foreign_key_lines << '    ON UPDATE CASCADE,'
                        foreign_keys << foreign_key_lines
                    end

                    column_definition = "  `#{column_name}`"

                    determine_type(column_definition, column_data)
                    determine_not_null(column_definition, column_data)
                    determine_auto_increment(column_definition)
                    determine_comment(column_data[Blufin::YmlSchemaValidator::DESCRIPTION], column_definition)

                    @structure << "#{column_definition},"

                end

                # Add primary key (if any) ..
                @structure << "  PRIMARY KEY (`#{primary_key}`)," if primary_key != nil

                # Add indexes (if any) ..
                indexes_fulltext.each { |index_fulltext| @structure << index_fulltext } if indexes_fulltext.any?
                indexes_unique.each { |index_unique| @structure << index_unique } if indexes_unique.any?
                indexes.each { |index| @structure << index } if indexes.any?

                # Add FKs (if any) ..
                if foreign_keys.any?
                    @alter_table_lines << "-- Add #{table} FKs"
                    @alter_table_lines << "ALTER TABLE `#{table}`"
                    foreign_keys.each do |foreign_key_lines|
                        foreign_key_lines.each do |foreign_key_line|
                            @alter_table_lines << foreign_key_line
                        end
                    end
                    @alter_table_lines[@alter_table_lines.length - 1] = @alter_table_lines[@alter_table_lines.length - 1][0...-1]
                    @alter_table_lines[@alter_table_lines.length - 1] << ";\n"
                end

                @structure[@structure.length - 1] = @structure[@structure.length - 1][0...-1]
                @structure << ") ENGINE = InnoDB;#{idx == (schema_data.length - 1) ? '' : "\n"}"

            end

        end

        # Add LINK tables (if any) ..
        if @link_tables.any?
            @link_tables.each_with_index do |link_data, idx|
                source_table    = link_data[0].split('.')
                target_table    = link_data[1].split('.')
                link_table_name = Blufin::YmlCommon::get_link_table_name(source_table[1], link_data[1])

                # Create structure.
                @structure << "-- Add table to link '#{source_table[1]}' with '#{target_table[1]}'"
                @structure << "CREATE TABLE `#{link_table_name}` ("
                @structure << "  `#{source_table[1]}_id` INT NOT NULL,"
                @structure << "  `#{target_table[1]}_id` INT NOT NULL"
                @structure << ") ENGINE = InnoDB;#{idx == (@link_tables.length - 1) ? '' : "\n"}"

                # Create FKs and indexes.
                @fks << "ALTER TABLE `#{link_table_name}`"
                idx_count = idx_count + 1
                @fks << "  ADD INDEX `idx_#{idx_count}` (`#{source_table[1]}_id` DESC),"
                idx_count = idx_count + 1
                @fks << "  ADD INDEX `idx_#{idx_count}` (`#{target_table[1]}_id` DESC),"
                @fks << "  ADD UNIQUE INDEX `#{source_table[1]}_to_#{target_table[1]}_uniq_idx` (`#{source_table[1]}_id` DESC, `#{target_table[1]}_id` DESC),"
                fk_count = fk_count + 1
                @fks << "  ADD CONSTRAINT `fk_#{fk_count}`"
                @fks << "    FOREIGN KEY (`#{source_table[1]}_id`)"
                @fks << "    REFERENCES `#{source_table[1]}` (`id`)"
                @fks << '    ON DELETE RESTRICT'
                @fks << '    ON UPDATE RESTRICT,'
                fk_count = fk_count + 1
                @fks << "  ADD CONSTRAINT `fk_#{fk_count}`"
                @fks << "    FOREIGN KEY (`#{target_table[1]}_id`)"
                @fks << "    REFERENCES `#{target_table[1]}` (`id`)"
                @fks << '    ON DELETE RESTRICT'
                @fks << '    ON UPDATE RESTRICT;'
                @fks << '' unless idx == @link_tables.length - 1
            end
        end

        sfs = get_structure_files(schema)
        next if sfs.nil? # Skip if user doesn't have the PATH_TO_RUBY config value.
        structure_file   = sfs[0]
        foreign_key_file = sfs[1]

        Blufin::Files.write_file(structure_file, @structure).gsub(@site_location, '') if @structure.any?
        Blufin::Files.write_file(foreign_key_file, @alter_table_lines + @fks).gsub(@site_location, '') if @alter_table_lines.any? || @fks.any?

    end

end

Private Instance Methods

determine_auto_increment(column_definition) click to toggle source

@return void

# File lib/core/yml_writers/yml_sql_structure_writer.rb, line 254
def determine_auto_increment(column_definition)
    column_definition << ' AUTO_INCREMENT' if @type == Blufin::YmlSchemaValidator::TYPE_INT_AUTO
end
determine_comment(description, column_definition) click to toggle source

@return void

# File lib/core/yml_writers/yml_sql_structure_writer.rb, line 259
def determine_comment(description, column_definition)

    if description != nil
        desc = description
    else
        desc = ''
    end

    desc = Blufin::YmlCommon::description_without_formatting(desc)

    if @type =~ Blufin::YmlSchemaValidator::REGEX_ENUM
        desc << " \xe2\x80\x94 " unless desc == '' || desc.nil?
        desc << '['
        Blufin::YmlCommon::enum_value_extractor(@type, @site).each do |enum_value|
            desc << "#{enum_value}|"
        end
        desc = desc[0...-1]
        desc << ']'
    end

    column_definition << " COMMENT '#{desc.gsub("'", %q(\\\'))}'" if desc != nil && desc.to_s != ''

end
determine_not_null(column_definition, column_data) click to toggle source

@return void

# File lib/core/yml_writers/yml_sql_structure_writer.rb, line 246
def determine_not_null(column_definition, column_data)

    ((@flags != nil && @flags.nullable) || column_data.has_key?(Blufin::YmlSchemaValidator::REQUIRED_IF)) ?
        column_definition << ' NULL' :
        column_definition << ' NOT NULL'
end
determine_type(column_definition, column_data) click to toggle source

@return void

# File lib/core/yml_writers/yml_sql_structure_writer.rb, line 201
def determine_type(column_definition, column_data)

    if @type == Blufin::YmlSchemaValidator::TYPE_DATE
        # Must come before DATETIME_TYPES.include?
        column_definition << ' DATE'
    elsif Blufin::YmlSchemaValidator::DATETIME_TYPES.include?(@type)
        column_definition << ' DATETIME(3)'
    elsif @type == Blufin::YmlSchemaValidator::TYPE_INT_TINY
        column_definition << ' TINYINT'
    elsif @type == Blufin::YmlSchemaValidator::TYPE_INT_SMALL
        column_definition << ' SMALLINT'
    elsif @type == Blufin::YmlSchemaValidator::TYPE_INT_BIG
        column_definition << ' BIGINT'
    elsif Blufin::YmlSchemaValidator::INT_TYPES.include?(@type)
        column_definition << ' INT'
    elsif @type == Blufin::YmlSchemaValidator::TYPE_BOOLEAN
        column_definition << ' TINYINT(1)'
    elsif @type == Blufin::YmlSchemaValidator::TYPE_TEXT
        column_definition << ' TEXT'
    elsif @type == Blufin::YmlSchemaValidator::TYPE_TEXT_LONG
        column_definition << ' LONGTEXT'
    elsif @type =~ Blufin::YmlSchemaValidator::REGEX_DECIMAL || @type =~ Blufin::YmlSchemaValidator::REGEX_VARCHAR
        if column_data[Blufin::YmlSchemaValidator::ENCRYPTED].nil?
            column_definition << " #{@type}"
        else
            # Encrypted VARCHAR(30) would become VARCHAR(60).
            varchar_amount = Blufin::Strings::extract_using_regex(@type, /\(\d+\)\z/, %w{( )})
            column_definition << " #{Blufin::YmlSchemaValidator::TYPE_VARCHAR}(#{varchar_amount.to_i * 2})"
        end
    elsif @type =~ Blufin::YmlSchemaValidator::REGEX_ENUM
        enum_values = Blufin::YmlCommon::enum_value_extractor(@type, @site)
        column_definition << " VARCHAR(#{enum_values.max_by(&:length).length})"
    elsif @type =~ Blufin::YmlSchemaValidator::REGEX_ENUM_CUSTOM
        enum_values = @yml_enum_scanner.get_enum_custom_values_for(Blufin::YmlCommon::enum_name_extractor(@type))
        column_definition << " VARCHAR(#{enum_values.max_by(&:length).length})"
    elsif @type =~ Blufin::YmlSchemaValidator::REGEX_ENUM_SYSTEM
        enum_values = @yml_enum_scanner.get_enum_system_values_for(Blufin::YmlCommon::enum_name_extractor(@type))
        column_definition << " VARCHAR(#{enum_values.max_by(&:length).length})"
    else
        raise RuntimeError, "Unrecognized type in #{__FILE__}: #{@type}"
    end

end
get_structure_files(schema) click to toggle source

@return Array

# File lib/core/yml_writers/yml_sql_structure_writer.rb, line 284
def get_structure_files(schema)

    if schema == Blufin::YmlSchemaValidator::CONFIG
        path_to_blufin_ruby = Blufin::Config::get_path('Paths', 'BlufinRuby')
        return nil if path_to_blufin_ruby.nil?
        structure_file   = "#{path_to_blufin_ruby}/#{App::Opt::BLUFIN}#{App::Opt::OPT_PATH_SQL}/structure/#{App::MySQL::CONFIG_FILENAME_STRUCTURE}"
        foreign_key_file = "#{path_to_blufin_ruby}/#{App::Opt::BLUFIN}#{App::Opt::OPT_PATH_SQL}/structure/#{App::MySQL::CONFIG_FILENAME_STRUCTURE_FKS}"
    elsif schema == Blufin::YmlSchemaValidator::MOCK
        path_to_blufin_ruby = Blufin::Config::get_path('Paths', 'BlufinRuby')
        return nil if path_to_blufin_ruby.nil?
        structure_file   = "#{path_to_blufin_ruby}/#{App::Opt::BLUFIN}#{App::Opt::OPT_PATH_SQL}/structure/#{App::MySQL::MOCK_FILENAME_STRUCTURE}"
        foreign_key_file = "#{path_to_blufin_ruby}/#{App::Opt::BLUFIN}#{App::Opt::OPT_PATH_SQL}/structure/#{App::MySQL::MOCK_FILENAME_STRUCTURE_FKS}"
    else
        structure_file   = "#{get_base_path(@site)}/#{Blufin::Site::PATH_TO_SQL_STRUCTURE}/#{Blufin::SiteResolver::get_site_name(@site)}-#{schema}.sql"
        foreign_key_file = "#{get_base_path(@site)}/#{Blufin::Site::PATH_TO_SQL_STRUCTURE}/#{Blufin::SiteResolver::get_site_name(@site)}-fks-#{schema}.sql"
    end

    [structure_file, foreign_key_file]

end