railsware/global

View on GitHub
lib/global/backend/filesystem.rb

Summary

Maintainability
A
0 mins
Test Coverage
# frozen_string_literal: true

module Global
  module Backend
    # Loads Global configuration from the filesystem
    #
    # Available options:
    # - `path` (required): the directory with config files
    # - `environment` (required): the environment to load
    # - `yaml_whitelist_classes`: the set of classes that are permitted to unmarshal from the configuration files
    #
    # For Rails:
    # - the `path` is optional and defaults to `config/global`
    # - the `environment` is optional and defaults to the current Rails environment
    class Filesystem

      FILE_ENV_SPLIT = '.'
      YAML_EXT = '.yml'

      def initialize(options = {})
        if defined?(Rails)
          @path = options.fetch(:path) { Rails.root.join('config', 'global').to_s }
          @environment = options.fetch(:environment) { Rails.env.to_s }
        else
          @path = options.fetch(:path)
          @environment = options.fetch(:environment)
        end
        @yaml_whitelist_classes = options.fetch(:yaml_whitelist_classes, [])
      end

      def load
        load_from_path(@path)
      end

      private

      def load_from_path(path)
        load_from_file(path).deep_merge(load_from_directory(path))
      end

      def load_from_file(path)
        config = {}

        if File.exist?(file = "#{path}#{YAML_EXT}")
          configurations = load_yml_file(file)
          config = get_config_by_key(configurations, 'default')
          config.deep_merge!(get_config_by_key(configurations, @environment))
          if File.exist?(env_file = "#{path}#{FILE_ENV_SPLIT}#{@environment}#{YAML_EXT}")
            config.deep_merge!(load_yml_file(env_file) || {})
          end
        end

        config
      end

      def get_config_by_key(config, key)
        return {} if config.empty?

        config[key.to_sym] || config[key.to_s] || {}
      end

      def load_yml_file(file)
        file_contents = ERB.new(IO.read(file)).result
        permitted_classes = [Date, Time, DateTime, Symbol].concat(@yaml_whitelist_classes)

        if Gem::Version.new(Psych::VERSION) >= Gem::Version.new('4')
          YAML.safe_load(file_contents, permitted_classes: permitted_classes, aliases: true)
        else
          YAML.safe_load(file_contents, permitted_classes, [], true)
        end
      end

      def load_from_directory(path)
        config = {}

        if File.directory?(path)
          Dir["#{path}/*"].each do |entry|
            namespace = File.basename(entry, YAML_EXT)
            next if namespace.include? FILE_ENV_SPLIT # skip files with dot(s) in name

            file_with_path = File.join(File.dirname(entry), File.basename(entry, YAML_EXT))
            config.deep_merge!(namespace => load_from_path(file_with_path))
          end
        end

        config
      end

    end
  end
end