smugglys/translatomatic

View on GitHub
lib/translatomatic/database.rb

Summary

Maintainability
A
25 mins
Test Coverage
require "active_record"

module Translatomatic
  # Database functions
  class Database
    class << self
      # @param options [Hash<Symbol,Object>] Database options
      # @return [boolean] True if we can connect to the database
      def enabled?(options = {})
        new(options).connect
      end
    end

    def initialize(options = {})
      @env = options[:database_env] || DEFAULT_ENV
      @db_config = database_config(@env, options)
      env_config = @db_config
      unless env_config[@env]
        raise t("database.no_environment", env: @env, file: db_config_path)
      end
      @env_config = env_config[@env] || {}
      init_active_record
      create unless exists?
    end

    # Connect to the database
    # @return [boolean] True if the connection was established
    def connect
      ActiveRecord::Base.establish_connection(@env_config)
      migrate
      true
    rescue LoadError
      false
    end

    # Disconnect from the database
    # @return [void]
    def disconnect
      ActiveRecord::Base.remove_connection
    end

    # Test if the database exists
    # @return [Boolean] true if the database exists
    def exists?
      begin
        return true if sqlite_database_exists?
        return false unless connect
        ActiveRecord::Base.connection.tables
      rescue StandardError
        return false
      end
      true
    end

    # Run outstanding migrations against the database
    # @return [void]
    def migrate
      return if @migrated
      migrator = ActiveRecord::MigrationContext.new(MIGRATIONS_PATH)
      migrator.migrate
      ActiveRecord::Base.clear_cache!
      log.debug t("database.migrated")
      @migrated = true
    end

    # Create the database
    # @return [boolean] True if the database was created
    def create
      ActiveRecord::Tasks::DatabaseTasks.create(@env_config)
      log.debug t("database.created")
      true
    rescue LoadError => e
      log.debug t("database.could_not_create")
      log.error e.message
      false
    end

    # Drop the database
    # @return [void]
    def drop
      disconnect
      ActiveRecord::Tasks::DatabaseTasks.drop(@env_config)
      log.debug t("database.deleted")
    end

    # Shortcut to text model class
    # @return [Class] Translatomatic::Model::Text
    def text
      Translatomatic::Model::Text
    end

    # Shortcut to locale model class
    # @return [Class] Translatomatic::Model::Locale
    def locale
      Translatomatic::Model::Locale
    end

    private

    include Translatomatic::Util
    include Translatomatic::DefineOptions

    class << self
      private

      def join_path(*parts)
        File.realpath(File.join(*parts))
      end
    end

    def init_active_record
      ActiveRecord::Base.configurations = @db_config
      ActiveRecord::Tasks::DatabaseTasks.env = @env
      ActiveRecord::Tasks::DatabaseTasks.db_dir = DB_PATH
      ActiveRecord::Tasks::DatabaseTasks.root = DB_PATH
      ActiveRecord::Tasks::DatabaseTasks.database_configuration = @db_config
    end

    def sqlite_database_exists?
      @env_config["adapter"] == "sqlite3" &&
        File.exist?(@env_config["database"])
    end

    DB_PATH = join_path(File.dirname(__FILE__), "..", "..", "db")
    INTERNAL_CONF = File.join(DB_PATH, "database.yml")
    CUSTOM_CONF = File.join(Dir.home, ".translatomatic", "database.yml")
    DEFAULT_CONF = File.exist?(CUSTOM_CONF) ? CUSTOM_CONF : INTERNAL_CONF
    MIGRATIONS_PATH = File.join(DB_PATH, "migrate")
    GEM_ROOT = join_path(File.dirname(__FILE__), "..", "..")
    DEFAULT_ENV = "production".freeze

    define_option :database_config, desc: t("database.config_file"),
                                    default: DEFAULT_CONF, type: :path
    define_option :database_env, desc: t("database.env"),
                                 default: DEFAULT_ENV

    # return path to database config
    def database_config_path(options)
      if options[:database_env] == "test"
        INTERNAL_CONF # used for rspec
      elsif options[:database_config]
        options[:database_config]
      else
        DEFAULT_CONF
      end
    end

    # return database config as a hash
    def database_config(env, options)
      if options[:database_config].is_a?(Hash)
        return { env => options[:database_config] }
      end

      db_config_path = database_config_path(options)
      dbconfig = File.read(db_config_path)
      dbconfig.gsub!(/\$HOME/, Dir.home)
      dbconfig.gsub!(/\$GEM_ROOT/, GEM_ROOT)
      YAML.safe_load(dbconfig) || {}
    end
  end
end