require 'sinatra/base' require 'yaml' require 'erb' module Sinatra # = Sinatra::ConfigFile # # Sinatra::ConfigFile is an extension that allows you to load the # application's configuration from YAML files. It automatically detects if # the files contain specific environment settings and it will use those # corresponding to the current one. # # You can access those options through +settings+ within the application. If # you try to get the value for a setting that hasn't been defined in the # config file for the current environment, you will get whatever it was set # to in the application. # # == Usage # # Once you have written your configurations to a YAML file you can tell the # extension to load them. See below for more information about how these # files are interpreted. # # For the examples, lets assume the following config.yml file: # # greeting: Welcome to my file configurable application # # === Classic Application # # require "sinatra" # require "sinatra/config_file" # # config_file 'path/to/config.yml' # # get '/' do # @greeting = settings.greeting # haml :index # end # # # The rest of your classic application code goes here... # # === Modular Application # # require "sinatra/base" # require "sinatra/config_file" # # class MyApp < Sinatra::Base # register Sinatra::ConfigFile # # config_file 'path/to/config.yml' # # get '/' do # @greeting = settings.greeting # haml :index # end # # # The rest of your modular application code goes here... # end # # === Config File Format # # In its most simple form this file is just a key-value list: # # foo: bar # something: 42 # nested: # a: 1 # b: 2 # # But it also can provide specific environment configuration. There are two # ways to do that: at the file level and at the settings level. # # At the settings level (e.g. in 'path/to/config.yml'): # # development: # foo: development # bar: bar # test: # foo: test # bar: bar # production: # foo: production # bar: bar # # Or at the file level: # # foo: # development: development # test: test # production: production # bar: bar # # In either case, settings.foo will return the environment name, and # settings.bar will return "bar". # # If you wish to provide defaults that may be shared among all the # environments, this can be done by using a YAML alias, and then overwriting # values in environments where appropriate: # # default: &common_settings # foo: 'foo' # bar: 'bar' # # production: # <<: *common_settings # bar: 'baz' # override the default value # module ConfigFile # When the extension is registered sets the +environments+ setting to the # traditional environments: development, test and production. def self.registered(base) base.set :environments, %w[test production development] end # Loads the configuration from the YAML files whose +paths+ are passed as # arguments, filtering the settings for the current environment. Note that # these +paths+ can actually be globs. def config_file(*paths) Dir.chdir(root || '.') do paths.each do |pattern| Dir.glob(pattern) do |file| raise UnsupportedConfigType unless ['.yml', '.yaml', '.erb'].include?(File.extname(file)) logger.info "loading config file '#{file}'" if logging? && respond_to?(:logger) document = ERB.new(IO.read(file)).result yaml = begin YAML.load(document, aliases: true) rescue ArgumentError YAML.load(document) end config = config_for_env(yaml) config.each_pair { |key, value| set(key, value) } end end end end class UnsupportedConfigType < Exception def message 'Invalid config file type, use .yml, .yaml or .erb' end end private # Given a +hash+ containing application configuration it returns # settings applicable to the current environment. Note: It gives # precedence to environment settings defined at the root-level. def config_for_env(hash) return from_environment_key(hash) if environment_keys?(hash) hash.each_with_object(IndifferentHash[]) do |(k, v), acc| if environment_keys?(v) acc.merge!(k => v[environment.to_s]) if v.key?(environment.to_s) else acc.merge!(k => v) end end end # Given a +hash+ returns the settings corresponding to the current # environment. def from_environment_key(hash) hash[environment.to_s] || hash[environment.to_sym] || {} end # Returns true if supplied with a hash that has any recognized # +environments+ in its root keys. def environment_keys?(hash) hash.is_a?(Hash) && hash.any? { |k, _| environments.include?(k.to_s) } end end register ConfigFile Delegator.delegate :config_file end