middleman/middleman-core/lib/middleman-core/configuration.rb
Ben Hollis d6110e2ff1 Add a metadata page for inspecting configuration.
This is another metadata page that uses the new configuration system to
show all the available options, their current values, and their defaults.
This is still unstyled, but the info is there. I still need to add on a
method for extensions to register their configuration with the global
config so they show up (stuff like blog) but this is a start.
2013-02-10 16:43:37 -08:00

239 lines
7.2 KiB
Ruby

module Middleman
module Configuration
# Access to a global configuration manager for the whole Middleman project,
# plus backwards compatibility mechanisms for older Middleman projects.
module Global
def self.included(app)
app.send :extend, ClassMethods
end
module ClassMethods
# Global configuration for the whole Middleman project.
# @return [ConfigurationManager]
def config
@_config ||= ConfigurationManager.new
end
# Set attributes (global variables)
#
# @deprecated Prefer accessing settings through "config".
#
# @param [Symbol] key Name of the attribue
# @param value Attribute value
# @return [void]
def set(key, default=nil, &block)
config.define_setting(key, default)
@inst.set(key, default, &block) if @inst
end
# Access global settings as methods, to preserve compatibility with
# old Middleman.
#
# @deprecated Prefer accessing settings through "config".
def method_missing(method, *args)
if config.defines_setting? method
config[method]
else
super
end
end
# Needed so that method_missing makes sense
def respond_to?(method, include_private = false)
super || config.defines_setting?(method)
end
end
def config
self.class.config
end
# Backwards compatibilty with old Sinatra template interface
#
# @deprecated Prefer accessing settings through "config".
#
# @return [ConfigurationManager]
alias :settings :config
# Set attributes (global variables)
#
# @deprecated Prefer accessing settings through "config".
#
# @param [Symbol] key Name of the attribue
# @param value Attribute value
# @return [void]
def set(key, value=nil, &block)
value = block if block_given?
config[key] = value
end
# Access global settings as methods, to preserve compatibility with
# old Middleman.
#
# @deprecated Prefer accessing settings through "config".
def method_missing(method, *args)
if config.defines_setting? method
config[method]
else
super
end
end
# Needed so that method_missing makes sense
def respond_to?(method, include_private = false)
super || config.defines_setting?(method)
end
end
# A class that manages a collection of documented settings.
# Can be used by extensions as well as the main Middleman
# application. Extensions should probably finalize their instance
# after defining all the settings they want to expose.
class ConfigurationManager
def initialize
# A hash from setting key to ConfigSetting instance.
@settings = {}
@finalized = false
end
# Get all settings, sorted by key, as ConfigSetting objects.
# @return [Array<ConfigSetting>]
def all_settings
@settings.values.sort_by(&:key)
end
# Get a full ConfigSetting object for the setting with the give key.
# @return [ConfigSetting]
def setting(key)
@settings[key]
end
# Get the value of a setting by key. Returns nil if there is no such setting.
# @return [Object]
def [](key)
setting = @settings[key]
setting ? setting.value : nil
end
# Set the value of a setting by key. Creates the setting if it doesn't exist.
# @param [Symbol] key
# @param [Object] val
def []=(key, val)
setting = @settings[key] || define_setting(key)
setting.value = val
end
# Allow configuration settings to be read and written via methods
def method_missing(method, *args)
if defines_setting?(method) && args.size == 0
self[method]
elsif method =~ /^(\w+)=$/ && args.size == 1
self[$1.to_sym] = args[0]
else
super
end
end
# Needed so that method_missing makes sense
def respond_to?(method, include_private = false)
super || defines_setting?(method) || (method =~ /^(\w+)=$/ && defines_setting?($1))
end
# Does this configuration manager know about the setting identified by key?
# @param [Symbol] key
# @return [Boolean]
def defines_setting?(key)
@settings.has_key?(key)
end
# Define a new setting, with optional default and user-friendly description.
# Once the configuration manager is finalized, no new settings may be defined.
#
# @param [Symbol] key
# @param [Object] default
# @param [String] description
# @return [ConfigSetting]
def define_setting(key, default=nil, description=nil)
raise "Setting #{key} doesn't exist" if @finalized
raise "Setting #{key} already defined" if @settings.has_key?(key)
raise "Setting key must be a Symbol" unless key.is_a? Symbol
@settings[key] = ConfigSetting.new(key, default, description)
end
# Switch the configuration manager is finalized, it switches to read-only
# mode and no new settings may be defined.
def finalize!
@finalized = true
end
# Deep duplicate of the configuration manager
def dup
copy = ConfigurationManager.new
@settings.each do |key, setting|
copy_setting = copy.define_setting(setting.key, setting.default, setting.description)
copy_setting.value = setting.value if setting.value_set?
end
copy
end
# Load in a list of settings
def load_settings(other_settings)
other_settings.each do |setting|
new_setting = define_setting(setting.key, setting.default, setting.description)
new_setting.value = setting.value if setting.value_set?
end
end
def to_h
hash = {}
@settings.each do |key, setting|
hash[key] = setting.value
end
hash
end
def to_s
to_h.inspect
end
end
# An individual configuration setting, with an optional default and description.
# Also models whether or not a value has been set.
class ConfigSetting
# The name of this setting
attr_accessor :key
# The default value for this setting
attr_accessor :default
# A human-friendly description of the setting
attr_accessor :description
def initialize(key, default, description)
@value_set = false
self.key = key
self.default = default
self.description = description
end
# The user-supplied value for this setting, overriding the default
def value=(value)
@value = value
@value_set = true
end
# The effective value of the setting, which may be the default
# if the user has not set a value themselves. Note that even if the
# user sets the value to nil it will override the default.
def value
value_set? ? @value : default
end
# Whether or not there has been a value set beyond the default
def value_set?
@value_set
end
end
end
end