lib/figgy/finder.rb
class Figgy
class Finder
def initialize(config)
@config = config
end
# Searches for files defining the configuration key +name+, merging each
# instance found with the previous. In this way, the overlay configuration
# at +production/foo.yml+ can override values in +foo.yml+.
#
# If the contents of the file were a Hash, Figgy will translate it into
# a {Figgy::Hash Figgy::Hash} and perform deep-merging for all overlays. This
# allows you to override only a single key deep within the configuration, and to
# access it using dot-notation, symbol keys or string keys.
#
# @param [String] name the configuration file to load
# @return Whatever was in the config file loaded
# @raise [Figgy::FileNotFound] if no config file could be found for +name+
def load(name)
files = files_for(name)
if files.empty?
raise(Figgy::FileNotFound, "Can't find config files for key: #{name.inspect}")
end
final_result = files.reduce(nil) do |result, file|
object = @config.handler_for(file).call(File.read(file))
if result && result.respond_to?(:merge)
deep_merge(result, object)
else
object
end
end
deep_freeze(to_figgy_hash(final_result))
end
# @param [String] name the configuration key to search for
# @return [Array<String>] the paths to all files to load for configuration key +name+
def files_for(name)
Dir[*file_globs(name)]
end
# @return [Array<String>] the names of all unique configuration keys
def all_key_names
Dir[*file_globs].map { |file| File.basename(file).sub(/\..+$/, '') }.uniq
end
private
def file_globs(name = '*')
globs = extension_globs(name)
@config.overlay_dirs.map { |dir|
globs.map { |glob| File.join(dir, glob) }
}.flatten
end
def extension_globs(name = '*')
@config.extensions.map { |ext| "#{name}.#{ext}" }
end
def to_figgy_hash(obj)
case obj
when ::Hash
obj.each_pair { |k, v| obj[k] = to_figgy_hash(v) }
Figgy::Hash.new(obj)
when Array
obj.map { |v| to_figgy_hash(v) }
else
obj
end
end
def deep_freeze(obj)
return obj unless @config.freeze?
case obj
when ::Hash
obj.each_pair { |k, v| obj[deep_freeze(k)] = deep_freeze(v) }
when Array
obj.map! { |v| deep_freeze(v) }
end
obj.freeze
end
def deep_merge(a, b)
a.merge(b) do |key, oldval, newval|
oldval.respond_to?(:merge) && newval.respond_to?(:merge) ? deep_merge(oldval, newval) : newval
end
end
end
end