class Figgy class Configuration # The directories in which to search for configuration files attr_reader :roots # The list of defined overlays attr_reader :overlays # Whether to reload a configuration file each time it is accessed attr_accessor :always_reload # Whether to load all configuration files upon creation # @note This does not prevent +:always_reload+ from working. attr_accessor :preload # Whether to freeze all loaded objects. Useful in production environments. attr_accessor :freeze # Constructs a new {Figgy::Configuration Figgy::Configuration} instance. # # By default, uses a +root+ of the current directory, and defines handlers # for +.yml+, +.yaml+, +.yml.erb+, +.yaml.erb+, and +.json+. def initialize @roots = [Dir.pwd] @handlers = [] @overlays = [] @always_reload = false @preload = false @freeze = false define_handler 'yml', 'yaml' do |contents| YAML.load(contents) end define_handler 'yml.erb', 'yaml.erb' do |contents| erb = ERB.new(contents).result YAML.load(erb) end define_handler 'json' do |contents| JSON.parse(contents) end end def root=(path) @roots = [File.expand_path(path)] end def add_root(path) @roots.unshift File.expand_path(path) end # @see #always_reload= def always_reload? !!@always_reload end # @see #preload= def preload? !!@preload end # @see #freeze= def freeze? !!@freeze end # Adds an overlay named +name+, found at +value+. # # If a block is given, yields to the block to determine +value+. # # @param name an internal name for the overlay # @param value the value of the overlay # @example An environment overlay # config.define_overlay(:environment) { Rails.env } def define_overlay(name, value = nil) value = yield if block_given? @overlays << [name, value] end # Adds an overlay using the combined values of other overlays. # # @example Searches for files in 'production_US' # config.define_overlay :environment, 'production' # config.define_overlay :country, 'US' # config.define_combined_overlay :environment, :country def define_combined_overlay(*names) combined_name = names.join("_").to_sym value = names.map { |name| overlay_value(name) }.join("_") @overlays << [combined_name, value] end # @return [Array] the list of directories to search for config files def overlay_dirs return @roots if @overlays.empty? overlay_values.map { |overlay| @roots.map { |root| overlay ? File.join(root, overlay) : root } }.flatten.uniq end # Adds a new handler for files with any extension in +extensions+. # # @example Adding an XML handler # config.define_handler 'xml' do |body| # Hash.from_xml(body) # end def define_handler(*extensions, &block) @handlers += extensions.map { |ext| [ext, block] } end # @return [Array] the list of recognized extensions def extensions @handlers.map { |ext, handler| ext } end # @return [Proc] the handler for a given filename def handler_for(filename) match = @handlers.find { |ext, handler| filename =~ /\.#{ext}$/ } match && match.last end private def overlay_value(name) overlay = @overlays.find { |n, v| name == n } raise "No such overlay: #{name.inspect}" unless overlay overlay.last end def overlay_values @overlays.map(&:last) end end end