lib/envied.rb in envied-0.7.0 vs lib/envied.rb in envied-0.7.1

- old
+ new

@@ -1,238 +1,56 @@ require 'envied/version' require 'envied/cli' -require 'virtus' +require 'envied/coercer' +require 'envied/variable' +require 'envied/configuration' class ENVied - module Hashable - def to_hash - require 'rack/utils' - ::Rack::Utils.parse_nested_query(self) - end - end - - module Arrayable - def to_a - self.split(/(?<!\\), ?/).map{|i| i.gsub(/\\,/,',') } - end - end - - class Configuration - include Virtus.model - - def self.variable(name, type = :String, options = {}) - options = { default: nil, strict: true, group: self.current_group }.merge(options) - type = Array if type == :Array - attribute(name, type, options) - end - - def self.group(name, &block) - self.current_group = name.to_sym - yield - ensure - self.current_group = :default - end - - def self.enable_defaults - (@enable_defaults ||= false).respond_to?(:call) ? - @enable_defaults.call : - @enable_defaults - end - - def self.enable_defaults!(value = nil, &block) - value ||= block if block_given? - @enable_defaults = value - end - - class << self - alias_method :defaults_enabled?, :enable_defaults - alias_method :enable_defaults=, :enable_defaults! - attr_writer :current_group - end - - def self.current_group - @current_group ||= :default - end - end - - def self.configuration(options = {}, &block) - if block_given? - @configuration = build_configuration(&block).tap do |c| - options.each {|k, v| c.public_send("#{k}=", v) } - end - end - @configuration ||= build_configuration - end - - def self.configure(options = {}, &block) - deprecation_warning "ENVied.configure will be deprecated. Please generate an Envfile instead (see the envied command)." - configuration(options, &block) - end - class << self - attr_accessor :required_groups + attr_reader :env, :config end - def self.build_configuration(&block) - Class.new(Configuration).tap do |c| - c.instance_eval(&block) if block_given? - end - end - def self.require(*groups) - groups.compact! - @instance = nil - if groups.any? - self.required_groups = groups.map(&:to_sym) - else - self.required_groups = [:default] - end - ensure_configured! + @config ||= Configuration.load + @env ||= EnvProxy.new(@config, groups: required_groups(*groups)) + error_on_missing_variables! error_on_uncoercible_variables! - - _required_variables = required_variables - group_configuration = build_configuration do - _required_variables.each do |v| - @attribute_set << v - end - end - @instance = group_configuration.new(env) end - def self.springified_require(*args) - springify { ENVied.require(*args) } - end - - def self.springify(&block) - if defined?(Spring) && Spring.respond_to?(:watcher) - Spring.after_fork(&block) - else - block.call - end - end - - def self.ensure_configured! - # Backward compat: load Envfile only when it's present - configure_via_envfile if envfile_exist? - end - - def self.envfile - File.expand_path('Envfile') - end - - def self.envfile_exist? - File.exist?(envfile) - end - - def self.configure_via_envfile - configuration { eval(File.read(ENVied.envfile)) } - end - def self.error_on_missing_variables! - if missing_variable_names.any? - raise "Please set the following ENV-variables: #{missing_variable_names.sort.join(',')}" - end + names = env.missing_variables.map(&:name) + raise "The following environment variables should be set: #{names * ', '}" if names.any? end def self.error_on_uncoercible_variables! - # TODO default values should have defined type - if non_coercible_variables.any? - single_error = "ENV['%{name}'] ('%{value}' can't be coerced to %{type})" - errors = non_coercible_variables.map do |v| - var_type = v.type.to_s.split("::").last - single_error % { name: v.name, value: env_value_or_default(v), type: var_type } - end.join ", " - - raise "Some ENV-variables are not coercible: #{errors}" + errors = env.uncoercible_variables.map do |v| + "%{name} ('%{value}' can't be coerced to %{type})" % {name: v.name, value: env.value_to_coerce(v), type: v.type } end + raise "The following environment variables are not coercible: #{errors.join(", ")}" if errors.any? end - def self.env_value(variable) - env[variable.name.to_s] + def self.required_groups(*groups) + result = groups.compact + result.any? ? result.map(&:to_sym) : [:default] end - def self.env - @env ||= begin - Hash[ENV.to_hash.map {|k,v| [k, v.dup.extend(Hashable, Arrayable)] }] + def self.springify(&block) + if spring_enabled? + Spring.after_fork(&block) + else + block.call end end - def self.env_value_or_default(variable) - env_value(variable) || default_value(variable) + def self.spring_enabled? + defined?(Spring) && Spring.respond_to?(:watcher) end - # Yields the assigned default for the variable. - # When defaults are disabled, nil is returned. - def self.default_value(variable) - defaults_enabled? ? variable.default_value.value : nil - end - - # A list of all configured variable names. - # - # @example - # ENVied.required_variable_names - # # => [:DATABASE_URL] - # - # @return [Array<Symbol>] the list of variable names - def self.required_variable_names - required_variables.map(&:name).map(&:to_sym) - end - - def self.required_variables - from_required_group = ->(var){ self.required_groups.include?(var.options[:group]) } - configured_variables.to_a.keep_if(&from_required_group) - end - - def self.configured_variables - configuration.attribute_set.dup#.to_a.keep_if(&var_from_required_group) - end - - def self.provided_variable_names - ENV.keys.map(&:to_sym) - end - - def self.non_coercible_variables - required_variables.reject(&method(:variable_coercible?)) - end - - def self.variable_coercible?(variable) - var_value = env_value_or_default(variable) - return true if var_value.respond_to?(:call) - - !variable.coerce(var_value).nil? - rescue Virtus::CoercionError - return false - end - - def self.missing_variable_names - unprovided = required_variable_names - provided_variable_names - unprovided -= names_of_required_variables_with_defaults if defaults_enabled? - unprovided - end - - def self.names_of_required_variables_with_defaults - required_variables_with_defaults.map(&:name).map(&:to_sym) - end - - def self.required_variables_with_defaults - required_variables.map do |v| - v unless v.default_value.value.nil? - end.compact - end - - def self.defaults_enabled? - configuration.enable_defaults - end - def self.method_missing(method, *args, &block) - respond_to_missing?(method) ? @instance.public_send(method, *args, &block) : super + respond_to_missing?(method) ? (env && env[method.to_s]) : super end def self.respond_to_missing?(method, include_private = false) - @instance.respond_to?(method) || super - end - - def self.deprecation_warning(msg) - puts "DEPRECATION WARNING: #{msg}" + (env && env.has_key?(method)) || super end end