class RailsDbObjects::DbObjectsCreator

Attributes

objects[R]

Public Class Methods

new() click to toggle source
# File lib/rails_db_objects/db_objects_creator.rb, line 5
def initialize
  @objects = {}
end

Public Instance Methods

create_objects() click to toggle source
# File lib/rails_db_objects/db_objects_creator.rb, line 72
def create_objects
  reset_objects_status!
  @objects.keys.each do |object_type|
    @objects[object_type].each do |_name, object|
      create_object(object)
    end
  end
end
drop_objects() click to toggle source
# File lib/rails_db_objects/db_objects_creator.rb, line 63
def drop_objects
  reset_objects_status!
  @objects.keys.each do |object_type|
    @objects[object_type].each do |_name, object|
      drop_object(object)
    end
  end
end
register_files(files) click to toggle source
# File lib/rails_db_objects/db_objects_creator.rb, line 9
def register_files(files)
  files.each do |file|
    object_name = File.basename(file, File.extname(file))
    object_type = File.dirname(file.to_s).to_s.split('/').last.upcase

    @objects[object_type] ||= {}

    content = File.read(file)
    content_lines = content.split("\n")

    # Reject the commented lines from the file
    sql_content = content_lines.reject { |x| x.strip =~ /^--/ || x.strip =~ /^#/ }.join("\n")

    file_obj = {
      name: object_name,
      type: object_type,
      debug: false,
      directives: [],
      path: file,
      sql_content: sql_content,
      status: :none,
      requires: [],
      silent: false,
      keep: false,
      nodrop: false,
      deleted: false,
      nocreate: false,
      dbschema: Rails.configuration.rails_db_objects[:objects_dbschema],
      dropsql: [],
      createsql: [],
      vanilla: false,
      condition: [],
      dropconditionruby: [],
      createconditionruby: [],
      beforedropruby: [],
      beforedropsql: [],
      afterdropruby: [],
      afterdropsql: [],
      beforecreateruby: [],
      beforecreatesql: [],
      aftercreateruby: [],
      aftercreatesql: []
    }

    # Detect directives in commentary
    directives = extract_from_comments(content_lines)

    # puts "directives: #{directives.inspect}"
    directives.each { |directive| prepare_directive(file_obj, directive) }

    @objects[object_type][object_name] = file_obj
  end
end

Private Instance Methods

conditional_ruby(condition, object) click to toggle source
# File lib/rails_db_objects/db_objects_creator.rb, line 136
def conditional_ruby(condition, object)
  interpolated_command = object.instance_eval('"' + condition.gsub(/\"/, '\"') + '"')
  condition = object.instance_eval(interpolated_command)
  if object[:debug]
    puts '=' * 80
    puts "interpolated_command: #{interpolated_command}"
    puts '=' * 80
    puts "condition: #{condition.inspect}"
    puts '=' * 80
  end
  condition ? true : false
end
create_dependencies(object, object_type) click to toggle source

rubocop:enable Metrics/AbcSize

# File lib/rails_db_objects/db_objects_creator.rb, line 308
def create_dependencies(object, object_type)
  object[:requires].each do |requirement|
    requires = requirement.split('/', 2)
    required_object = requires.last
    required_type = requires.first.upcase if requires.length == 2
    required_type ||= object_type
    create_object(@objects[required_type.upcase][required_object])
  end
end
create_object(object) click to toggle source
# File lib/rails_db_objects/db_objects_creator.rb, line 229
def create_object(object)
  return if object[:nocreate]

  return if object[:status] == :loaded

  object_type = object[:type]
  name = object[:name]

  raise "Error: Circular file reference! (#{object_type} #{name})" if object[:status] == :inprogress

  object[:status] = :inprogress
  @dbschema = (object[:dbschema] || []).clone

  full_name = full(name)

  if object[:createconditionruby].compact.any?
    condition = conditional_ruby(object[:createconditionruby].join("\n"), object)
    if condition
      puts "RUBY CONDITION MET FOR #{object_type} #{full_name} / #{condition}"
    else
      puts "RUBY CONDITION NOT MET FOR #{object_type} #{full_name}"
      return
    end
  elsif object[:condition].compact.any?
    condition = ActiveRecord::Base.connection.select_rows(object[:condition].join("\n")).empty?
    if condition
      puts "CONDITION MET FOR #{object_type} #{full_name}"
    else
      puts "CONDITION NOT MET FOR #{object_type} #{full_name}"
      return
    end
  end

  if object[:debug]
    puts '=' * 80
    puts "CREATE OBJECT #{name} / #{object[:path]}"
    puts '=' * 80
    puts object.to_json
    puts '=' * 80
  end
  # skip empty sql content
  if object[:sql_content].strip.blank?
    puts "#{object_type} #{name} EMPTY... SKIPPING"
    return
  end

  create_dependencies(object, object_type)

  sql = if object[:vanilla]
          interpolate_sql(object[:sql_content], object)
        elsif object[:createsql].compact.empty?
          "CREATE #{object_type} #{full_name}\n#{object[:sql_content]}"
        else
          interpolate_sql(object[:createsql].compact.join(";\n"), object)
        end

  begin
    conditional_ruby(object[:beforecreateruby].join("\n"), object) unless object[:beforecreateruby].empty?
    ActiveRecord::Base.connection.execute(object[:beforecreatesql].join("\n")) unless object[:beforecreatesql].empty?
    ActiveRecord::Base.connection.execute(sql.to_s)
    ActiveRecord::Base.connection.execute(object[:aftercreatesql].join("\n")) unless object[:aftercreatesql].empty?
    conditional_ruby(object[:aftercreateruby].join("\n"), object) unless object[:aftercreateruby].empty?
    puts "CREATE #{object_type} #{full_name}... OK"
  rescue StandardError => e
    unless object[:silent]
      puts '#' * 80
      puts e.message.to_s
      # puts '#' * 80
      # puts "#{e.backtrace}"
      puts '#' * 80
      puts "WARNING: CREATE #{object_type} #{full_name}... ERROR"
      puts '#' * 80
    end
  end

  object[:status] = :loaded
end
drop_object(object) click to toggle source

rubocop:disable Metrics/AbcSize

# File lib/rails_db_objects/db_objects_creator.rb, line 154
def drop_object(object)
  return if object[:nodrop]

  return if object[:status] == :loaded

  object_type = object[:type]
  name = object[:name]
  @dbschema = (object[:dbschema] || []).clone

  raise "Error: Circular file reference! (#{object_type} #{name})" if object[:status] == :inprogress

  full_name = full(name)

  if object[:dropconditionruby].compact.any?
    condition = conditional_ruby(object[:dropconditionruby].join("\n"), object)
    if condition
      puts "RUBY CONDITION MET FOR #{object_type} #{full_name} #{condition}"
    else
      puts "RUBY CONDITION NOT MET FOR #{object_type} #{full_name} #{condition}"
      return
    end
  elsif object[:condition].compact.any?
    condition = !ActiveRecord::Base.connection.select_rows(object[:condition].join("\n")).empty?
    if condition
      puts "SQL CONDITION MET FOR #{object_type} #{full_name}"
    else
      puts "SQL CONDITION NOT MET FOR #{object_type} #{full_name}"
      return
    end
  end

  if object[:debug]
    puts '=' * 80
    puts "DROP OBJECT #{name} / #{object[:path]}"
    puts '=' * 80
    puts object.to_json
    puts '=' * 80
  end

  object[:requires].each do |requirement|
    requires = requirement.split('/', 2)
    required_object = requires.last
    required_type = requires.first.upcase if requires.length == 2
    required_type ||= object_type
    drop_object @objects[required_type.upcase][required_object]
  end

  sql = if object[:dropsql].compact.empty? && !object[:vanilla]
          "DROP #{object_type} #{full_name};"
        else
          interpolate_sql(object[:dropsql].compact.join(";\n"), object)
        end

  begin
    conditional_ruby(object[:beforedropruby].join("\n"), object) unless object[:beforedropruby].empty?
    ActiveRecord::Base.connection.execute(object[:beforedropsql].join("\n")) unless object[:beforedropsql].empty?
    ActiveRecord::Base.connection.execute(sql)
    ActiveRecord::Base.connection.execute(object[:afterdropsql].join("\n")) unless object[:afterdropsql].empty?
    puts "DROP #{object_type} #{full_name}... OK"
    conditional_ruby(object[:afterdropruby].join("\n"), object) unless object[:afterdropruby].empty?
  rescue StandardError => e
    unless object[:debug]
      puts '#' * 80
      puts e.message.to_s
      # puts "#"*80
      # puts "#{e.backtrace}"
      puts '#' * 80
      puts "WARNING: #{sql}... ERROR"
      puts '#' * 80
    end
  end

  object[:status] = :loaded
end
extract_from_comments(content_lines) click to toggle source
# File lib/rails_db_objects/db_objects_creator.rb, line 101
def extract_from_comments(content_lines)
  dir_lines = content_lines.select { |x| x.strip =~ /^--/ || x.strip =~ /^#/ }.map(&:strip)
  dir_lines.map { |x| /^--/.match?(x) ? x[2..-1] : x[1..-1] }.select { |x| x =~ /^!/ }
end
full(name) click to toggle source
# File lib/rails_db_objects/db_objects_creator.rb, line 83
def full(name)
  prefix = @dbschema.blank? ? '' : "#{@dbschema}."
  "#{prefix}#{wrap_name(name)}"
end
interpolate_sql(sql, object) click to toggle source
# File lib/rails_db_objects/db_objects_creator.rb, line 149
def interpolate_sql(sql, object)
  object.instance_eval('"' + sql.gsub(/\"/, '\"') + '"')
end
prepare_directive(file_obj, directive) click to toggle source

rubocop:disable Metrics/AbcSize

# File lib/rails_db_objects/db_objects_creator.rb, line 107
def prepare_directive(file_obj, directive)
  file_obj[:debug] = /^!debug/.match?(directive) unless file_obj[:debug]
  file_obj[:directives] << directive
  file_obj[:requires] += directive.split(' ')[1..-1] if /^!require /.match?(directive)
  file_obj[:vanilla] = /^!vanilla/.match?(directive) unless file_obj[:vanilla]
  file_obj[:nocreate] = file_obj[:deleted] = /^!deleted/.match?(directive) unless file_obj[:deleted]
  file_obj[:silent] = /^!silent/.match?(directive) unless file_obj[:silent]
  file_obj[:nodrop] = file_obj[:keep] = /^!keep/.match?(directive) unless file_obj[:keep]
  file_obj[:dbschema] = directive.split(' ')[1..-1] if /^!schema/.match?(directive)
  file_obj[:dropconditionruby] << directive.split(' ')[1..-1].join(' ') if /^!dropconditionruby /.match?(directive)
  file_obj[:createconditionruby] << directive.split(' ')[1..-1].join(' ') if /^!createconditionruby /.match?(directive)
  file_obj[:condition] << directive.split(' ')[1..-1].join(' ') if /^!condition /.match?(directive)
  file_obj[:beforedropsql] << directive.split(' ')[1..-1].join(' ') if /^!beforedropsql /.match?(directive)
  file_obj[:dropsql] << directive.split(' ')[1..-1].join(' ') if /^!dropsql /.match?(directive)
  file_obj[:afterdropsql] << directive.split(' ')[1..-1].join(' ') if /^!afterdropsql /.match?(directive)
  file_obj[:beforecreatesql] << directive.split(' ')[1..-1].join(' ') if /^!beforecreatesql /.match?(directive)
  file_obj[:createsql] << directive.split(' ')[1..-1].join(' ') if /^!createsql /.match?(directive)
  file_obj[:aftercreatesql] << directive.split(' ')[1..-1].join(' ') if /^!aftercreatesql /.match?(directive)
end
reset_objects_status!() click to toggle source

rubocop:enable Metrics/AbcSize

# File lib/rails_db_objects/db_objects_creator.rb, line 128
def reset_objects_status!
  @objects.keys.each do |object_type|
    @objects[object_type].each do |_name, object|
      object[:status] = :none
    end
  end
end
wrap_name(name) click to toggle source
# File lib/rails_db_objects/db_objects_creator.rb, line 88
def wrap_name(name)
  adapter_name = ActiveRecord::Base.connection.adapter_name

  case adapter_name
  when 'PostgreSQL'
    "\"#{name}\""
  when 'MySQL'
    "`#{name}`"
  when 'SQLServer'
    "[#{name}]"
  end
end