class ComplexConfig::Provider

Attributes

env[W]
key[W]
master_key_pathname[W]
plugins[R]

Public Class Methods

new() click to toggle source
# File lib/complex_config/provider.rb, line 13
def initialize
  @plugins     = Set.new
  @deep_freeze = true
end

Public Instance Methods

[](name) click to toggle source
# File lib/complex_config/provider.rb, line 127
def [](name)
  config pathname(name), name
end
add_plugin(plugin) click to toggle source
# File lib/complex_config/provider.rb, line 35
def add_plugin(plugin)
  plugins.add plugin
  self
end
apply_plugins(setting, id) click to toggle source
# File lib/complex_config/provider.rb, line 51
def apply_plugins(setting, id)
  plugins.find do |plugin|
    catch :skip do
      value = setting.instance_exec(id, &plugin) and return value
    end
    nil
  end
end
config(pathname, name = nil) click to toggle source
# File lib/complex_config/provider.rb, line 85
def config(pathname, name = nil)
  datas = []
  path_exist = File.exist?(pathname)
  if path_exist
    datas << IO.binread(pathname)
  end
  decrypted, reason, enc_pathname = decrypt_config_case(pathname)
  case reason
  when :ok
    datas << decrypted
  when :key_missing
    datas.empty? and raise ComplexConfig::EncryptionKeyMissing,
      "encryption key for #{enc_pathname.to_s.inspect} is missing"
  when :file_missing
    datas.empty? and raise ComplexConfig::ConfigurationFileMissing,
      "configuration file #{pathname.to_s.inspect} is missing"
  end
  results = datas.map { |d| evaluate(pathname, d) }
  hashes =
    if ::Psych::VERSION < "4"
      results.map { |r| ::YAML.load(r, pathname) }
    else
      results.map { |r| ::YAML.unsafe_load(r, filename: pathname) }
    end
  settings = ComplexConfig::Settings.build(name, hashes.shift)
  hashes.each { |h| settings.attributes_update(h) }
  if shared = settings.shared?
    shared = shared.to_h
    settings.each do |key, value|
      if value.is_a? ComplexConfig::Settings
        value.attributes_update_if_nil(shared)
      elsif value.nil?
        settings[key] = ComplexConfig::Settings.build(nil, shared.dup)
      end
    end
  end
  deep_freeze? and settings.deep_freeze
  settings
rescue ::Psych::SyntaxError => e
  raise ComplexConfig::ComplexConfigError.wrap(:ConfigurationSyntaxError, e)
end
config_dir() click to toggle source
# File lib/complex_config/provider.rb, line 68
def config_dir
  @config_dir || (defined?(Rails) && Rails.respond_to?(:root) && Rails.root || Pathname.pwd) + 'config'
end
config_dir=(dir) click to toggle source
# File lib/complex_config/provider.rb, line 60
def config_dir=(dir)
  if dir.nil?
    @config_dir = nil
  else
    @config_dir = Pathname.new(dir)
  end
end
configure_with(config) click to toggle source
# File lib/complex_config/provider.rb, line 28
def configure_with(config)
  config.configure(self)
  flush_cache
end
decrypt_config(pathname) click to toggle source
# File lib/complex_config/provider.rb, line 76
def decrypt_config(pathname)
  decrypt_config_case(pathname).first
end
deep_freeze=(flag) click to toggle source
# File lib/complex_config/provider.rb, line 40
def deep_freeze=(flag)
  if @deep_freeze && !flag
    mize_cache_clear
  end
  @deep_freeze = flag
end
deep_freeze?() click to toggle source
# File lib/complex_config/provider.rb, line 47
def deep_freeze?
  !!@deep_freeze
end
encrypt_config(pathname, config) click to toggle source
# File lib/complex_config/provider.rb, line 80
def encrypt_config(pathname, config)
  ks = key_source(pathname)
  ComplexConfig::Encryption.new(ks.key_bytes).encrypt(config)
end
env() click to toggle source
# File lib/complex_config/provider.rb, line 192
def env
  @env || defined?(Rails) && Rails.respond_to?(:env) && Rails.env ||
    ENV['RAILS_ENV'] ||
    'development'
end
evaluate(pathname, data) click to toggle source
# File lib/complex_config/provider.rb, line 179
def evaluate(pathname, data)
  erb = ::ERB.new(data, trim_mode: '-')
  erb.filename = pathname.to_s
  erb.result
end
exist?(name) click to toggle source
# File lib/complex_config/provider.rb, line 162
def exist?(name)
  !!config(pathname(name), name)
rescue ComplexConfig::ConfigurationFileMissing, ComplexConfig::EncryptionKeyMissing
  false
end
flush_cache() click to toggle source
# File lib/complex_config/provider.rb, line 173
def flush_cache
  mize_cache_clear
  self
end
key(pathname = nil) click to toggle source
# File lib/complex_config/provider.rb, line 210
def key(pathname = nil)
  key_source(pathname).ask_and_send(:key)
end
key_source(pathname = nil) click to toggle source
# File lib/complex_config/provider.rb, line 200
def key_source(pathname = nil)
  [
    ComplexConfig::KeySource.new(var: @key),
    ComplexConfig::KeySource.new(pathname: pathname),
    ComplexConfig::KeySource.new(env_var: 'COMPLEX_CONFIG_KEY'),
    ComplexConfig::KeySource.new(env_var: 'RAILS_MASTER_KEY'),
    ComplexConfig::KeySource.new(master_key_pathname: master_key_pathname),
  ].find(&:key)
end
master_key_pathname() click to toggle source
# File lib/complex_config/provider.rb, line 20
def master_key_pathname
  if @master_key_pathname
    @master_key_pathname
  else
    config_dir + 'master.key'
  end
end
new_key() click to toggle source
# File lib/complex_config/provider.rb, line 218
def new_key
  SecureRandom.hex(16)
end
pathname(name) click to toggle source
# File lib/complex_config/provider.rb, line 72
def pathname(name)
  config_dir + "#{name}.yml"
end
prepare_output(value) click to toggle source
# File lib/complex_config/provider.rb, line 156
def prepare_output(value)
  value.each_with_object({}) do |(k, v), h|
    h[k.to_s] = v
  end.to_yaml
end
proxy(env = nil) click to toggle source
# File lib/complex_config/provider.rb, line 168
def proxy(env = nil)
  ComplexConfig::Proxy.new(env)
end
valid_key?(key) click to toggle source
# File lib/complex_config/provider.rb, line 222
def valid_key?(key)
  ks = ComplexConfig::KeySource.new(var: key)
  ComplexConfig::Encryption.new(ks.key_bytes)
  ks
rescue
  false
end
write_config(name, value: nil, encrypt: false, store_key: false) click to toggle source
# File lib/complex_config/provider.rb, line 132
def write_config(name, value: nil, encrypt: false, store_key: false)
  name, value = interpret_name_value(name, value)
  config_pathname = pathname(name).to_s
  if encrypt
    ks = provide_key_source(config_pathname, encrypt)
    File.secure_write(config_pathname + '.enc') do |out|
      out.write ComplexConfig::Encryption.new(ks.key_bytes).encrypt(prepare_output(value))
    end
    if store_key
      File.secure_write(config_pathname + '.key') do |out|
        out.write ks.key
      end
    end
    ks.key
  else
    File.secure_write(config_pathname) do |out|
      out.puts prepare_output(value)
    end
    true
  end
ensure
  flush_cache
end

Private Instance Methods

decrypt_config_case(pathname) click to toggle source
# File lib/complex_config/provider.rb, line 232
def decrypt_config_case(pathname)
  enc_pathname = pathname.to_s + '.enc'
  my_ks        = key_source(pathname)
  if File.exist?(enc_pathname)
    if my_ks.ask_and_send(:key)
      text = IO.binread(enc_pathname)
      decrypted = ComplexConfig::Encryption.new(my_ks.key_bytes).decrypt(text)
      return decrypted, :ok, enc_pathname
    else
      return nil, :key_missing, enc_pathname
    end
  end
  return nil, :file_missing, enc_pathname
end
interpret_name_value(name, value) click to toggle source
# File lib/complex_config/provider.rb, line 247
def interpret_name_value(name, value)
  if ComplexConfig::Settings === name
    if value
      name = name.name_prefix
    else
      value = name.to_h
      name  = name.name_prefix
    end
  elsif name.respond_to?(:to_sym)
    value = value.to_h
  else
    raise ArgumentError, "name has to be either string/symbol or ComplexConfig::Settings"
  end
  return name, value
end
provide_key_source(pathname, encrypt) click to toggle source
# File lib/complex_config/provider.rb, line 263
def provide_key_source(pathname, encrypt)
  ks =
    case encrypt
    when :random
      ComplexConfig::KeySource.new(var: new_key)
    when true
      key_source(pathname)
    when String
      if encrypt =~ /\A\h{32}\z/
        ComplexConfig::KeySource.new(var: encrypt)
      else
        raise ComplexConfig::EncryptionKeyInvalid,
          "encryption key has wrong format, has to be hex number of length "\
          "32, was #{encrypt.inspect}"
      end
    end
  ks or raise ComplexConfig::EncryptionKeyInvalid, "encryption key is missing"
end