Class: ConfigReader

Inherits:
Object
  • Object
show all
Defined in:
lib/config_reader.rb,
lib/config_reader/version.rb,
lib/config_reader/config_hash.rb

Defined Under Namespace

Classes: ConfigHash, Configuration

Constant Summary collapse

VERSION =
"3.1.0"

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.envsObject (readonly)

Returns the value of attribute envs.



13
14
15
# File 'lib/config_reader.rb', line 13

def envs
  @envs
end

Class Method Details

.[](key) ⇒ Object



15
16
17
# File 'lib/config_reader.rb', line 15

def [](key)
  config[key.to_sym]
end

.configObject



19
20
21
22
# File 'lib/config_reader.rb', line 19

def config
  @config = nil unless defined?(@config)
  @config ||= reload
end

.configurationObject



24
25
26
# File 'lib/config_reader.rb', line 24

def configuration
  @configuration ||= Configuration.new
end

.configure {|configuration| ... } ⇒ Object

Yields:



28
29
30
31
# File 'lib/config_reader.rb', line 28

def configure
  self.configuration ||= Configuration.new
  yield(configuration)
end

.deep_merge(hash, other_hash) ⇒ Object



33
34
35
36
37
38
39
40
41
# File 'lib/config_reader.rb', line 33

def deep_merge(hash, other_hash)
  hash.merge(other_hash) do |_key, this_val, other_val|
    if this_val.is_a?(Hash) && other_val.is_a?(Hash)
      deep_merge(this_val, other_val)
    else
      other_val
    end
  end
end

.dig(*args) ⇒ Object



47
48
49
50
51
52
53
54
55
# File 'lib/config_reader.rb', line 47

def dig(*args)
  if args.respond_to?(:map!)
    args.map! do |arg|
      (arg.respond_to?(:to_sym) && !arg.is_a?(Integer)) ? arg.to_sym : arg
    end
  end

  config.dig(*args)
end

.dig_path(path, separator: ".") ⇒ Object



43
44
45
# File 'lib/config_reader.rb', line 43

def dig_path(path, separator: ".")
  dig(*parse_path(path, separator: separator))
end

.find_configObject



70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/config_reader.rb', line 70

def find_config
  return nil unless configuration.config_file

  return configuration.config_file if File.exist?(configuration.config_file)

  %w[. config].each do |dir|
    config_file = File.join(dir, configuration.config_file)
    return config_file if File.exist?(config_file)
  end

  nil
end

.inspectObject



83
84
85
# File 'lib/config_reader.rb', line 83

def inspect
  config.inspect
end

.load_configObject



87
88
89
90
91
92
93
94
95
# File 'lib/config_reader.rb', line 87

def load_config
  raise "No config file set" unless find_config

  conf = load_yaml

  raise "No config found" unless conf

  conf
end

.load_sekretsObject



97
98
99
100
101
102
103
104
105
106
107
# File 'lib/config_reader.rb', line 97

def load_sekrets
  if configuration.sekrets_file
    if sekrets_available?
      ::Sekrets.settings_for(configuration.sekrets_file) ||
        raise("No sekrets found")
    else
      raise ArgumentError,
            "You specified a sekrets_file, but the sekrets gem isn't available."
    end
  end
end

.load_yamlObject



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/config_reader.rb', line 118

def load_yaml
  permitted_classes = configuration.permitted_classes.to_a + [Symbol]

  if defined?(ERB)
    Psych.safe_load(
      ERB.new(File.read(find_config)).result,
      aliases: true,
      permitted_classes: permitted_classes
    )
  else
    Psych.safe_load_file(
      find_config,
      aliases: true,
      permitted_classes: permitted_classes
    )
  end
end

.merge_all_configs(conf, defaults, sekrets) ⇒ Object



136
137
138
139
140
141
142
143
144
145
146
147
148
# File 'lib/config_reader.rb', line 136

def merge_all_configs(conf, defaults, sekrets)
  @envs = {}

  conf.keys.each do |env|
    env_hash = deep_merge(defaults, conf[env] || {})
    env_hash = deep_merge(env_hash, sekrets[env] || {}) if sekrets

    @envs[env] = ConfigHash.convert_hash(
      env_hash,
      configuration.ignore_missing_keys
    )
  end
end

.merge_configs(conf, sekrets) ⇒ Object



150
151
152
153
154
155
156
157
158
159
160
161
162
# File 'lib/config_reader.rb', line 150

def merge_configs(conf, sekrets)
  defaults = conf["defaults"]
  raise "No defaults config found" unless defaults

  if sekrets&.[]("defaults")
    defaults = deep_merge(defaults, sekrets["defaults"])
  end

  merge_all_configs(conf, defaults, sekrets)

  @envs[configuration.environment] ||
    raise("No config found for environment \"#{configuration.environment}\"")
end

.method_missing(key, *_args, &_block) ⇒ Object



164
165
166
167
168
169
170
# File 'lib/config_reader.rb', line 164

def method_missing(key, *_args, &_block)
  if key.to_s.end_with?("=")
    raise ArgumentError.new("ConfigReader is immutable")
  end

  config[key]
end

.normalize_array_path_segment(segment) ⇒ Object



180
181
182
183
184
185
186
187
188
# File 'lib/config_reader.rb', line 180

def normalize_array_path_segment(segment)
  if segment.is_a?(String) || segment.is_a?(Symbol)
    segment.to_sym
  elsif segment.is_a?(Integer)
    segment
  else
    raise ArgumentError, "Path segments must be Strings, Symbols, or Integers"
  end
end

.normalize_string_path_segment(segment) ⇒ Object



190
191
192
193
194
195
196
# File 'lib/config_reader.rb', line 190

def normalize_string_path_segment(segment)
  if segment.is_a?(String) && segment.match?(/\A\d+\z/)
    segment.to_i
  else
    normalize_array_path_segment(segment)
  end
end

.parse_path(path, separator: ".") ⇒ Object



57
58
59
60
61
62
63
64
65
66
67
68
# File 'lib/config_reader.rb', line 57

def parse_path(path, separator: ".")
  case path
  when String
    raise ArgumentError, "Path must not be blank" if path.strip.empty?

    path.split(separator).map { |segment| normalize_string_path_segment(segment) }
  when Array
    path.map { |segment| normalize_array_path_segment(segment) }
  else
    raise ArgumentError, "Path must be a String or Array"
  end
end

.reloadObject



172
173
174
# File 'lib/config_reader.rb', line 172

def reload
  @config = merge_configs(load_config, load_sekrets)
end

.respond_to_missing?(m, include_private = false) ⇒ Boolean

Returns:

  • (Boolean)


176
177
178
# File 'lib/config_reader.rb', line 176

def respond_to_missing?(m, include_private = false)
  super || config.key?(m)
end

.sekrets_available?Boolean

Returns:

  • (Boolean)


109
110
111
112
113
114
115
116
# File 'lib/config_reader.rb', line 109

def sekrets_available?
  return true if defined?(::Sekrets)

  require "sekrets"
  true
rescue LoadError
  false
end