-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
8 changed files
with
168 additions
and
59 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -1 +1 @@ | |||
--no-private | --protected --no-private |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -1,80 +1,148 @@ | |||
require 'yaml' | require 'yaml' | ||
require 'madvertise/ext/hash' | |||
|
|
||
## | |||
# The Configuration class provides a simple interface to configuration stored | |||
# inside of YAML files. | |||
# | |||
class Configuration < Section | |||
|
|||
# Create a new {Configuration} object. | |||
# | |||
# @param [Symbol] mode The mode to load from the configurtion file | |||
# (production, development, etc) | |||
def initialize(mode = :development) | |||
@mode = mode | |||
yield self if block_given? | |||
end | |||
|
|||
# Load given mixins from +path+. | |||
# | |||
# @param [String] path The path to mixin files. | |||
# @param [Array] mixins_to_use A list of mixins to load from +path+. | |||
# @return [void] | |||
def load_mixins(path, mixins_to_use) | |||
mixins_to_use.map do |mixin_name| | |||
File.join(path, "#{mixin_name}.yml") | |||
end.each do |mixin_file| | |||
mixin(mixin_file) | |||
end | |||
end | |||
end | |||
|
|||
## | |||
# A {Configuration} consists of one or more Sections. A section is a hash-like | |||
# object that responds to all keys in the hash as if they were methods: | |||
# | |||
# > s = Section.from_hash({:v1 => 2, :nested => {:v2 => 1}}) | |||
# > s.v1 | |||
# => 2 | |||
# > s.nested.v2 | |||
# => 1 | |||
# | |||
class Section < Hash | class Section < Hash | ||
class << self | class << self | ||
|
|||
# Create a new section from the given hash-like object. | |||
# | |||
# @param [Hash] hsh The hash to convert into a section. | |||
# @return [Section] The new {Section} object. | |||
def from_hash(hsh) | def from_hash(hsh) | ||
new.tap do |result| | result = new.tap do |result| | ||
hsh.each do |key, value| | hsh.each do |key, value| | ||
value = if value.is_a?(Hash) | result[key.to_sym] = from_value(value) | ||
from_hash(value) | end | ||
elsif value.is_a?(Array) | end | ||
value.map do |item| | end | ||
from_hash(item) | |||
end | |||
else | |||
value | |||
end | |||
|
|
||
result[key.to_sym] = value | # Convert the given value into a Section, list of Sections or the pure | ||
# value. Used to recursively build the Section hash. | |||
# | |||
# @private | |||
def from_value(value) | |||
case value | |||
when Hash | |||
from_hash(value) | |||
when Array | |||
value.map do |item| | |||
from_hash(item) | |||
end | end | ||
else | |||
value | |||
end | end | ||
end | end | ||
end | end | ||
|
|
||
def deep_merge(other_hash) | # Mixin a configuration snippet into the current section. | ||
self.merge(other_hash) do |key, oldval, newval| | # | ||
oldval = oldval.to_hash if oldval.respond_to?(:to_hash) | # @param [Hash, String] value A hash to merge into the current | ||
newval = newval.to_hash if newval.respond_to?(:to_hash) | # configuration. If a string is given a filename | ||
oldval.is_a?(Hash) && newval.is_a?(Hash) ? oldval.deep_merge(newval) : newval | # is assumed and the given file is expected to | ||
# contain a YAML hash. | |||
# @return [void] | |||
def mixin(value) | |||
unless value.is_a?(Hash) | |||
value = Section.from_hash(YAML.load(File.read(file))) | |||
end | end | ||
end | |||
|
|
||
def deep_merge!(other_hash) | self.deep_merge!(value[:default]) if value.has_key?(:default) | ||
replace(deep_merge(other_hash)) | self.deep_merge!(value[:generic]) if value.has_key?(:generic) | ||
|
|||
if value.has_key?(@mode) | |||
self.deep_merge!(value[@mode]) | |||
else | |||
self.deep_merge!(value) | |||
end | |||
end | end | ||
|
|
||
# Build the call chain including NilSections. | |||
# | |||
# @private | |||
def method_missing(name, *args) | def method_missing(name, *args) | ||
if name.to_s[-1] == ?= | if name.to_s =~ /(.*)=$/ | ||
self[name.to_s[0..-2].to_sym] = args.first | self[$1.to_sym] = Section.from_value(args.first) | ||
else | else | ||
value = self[name] | value = self[name] | ||
self[name] = value.call if value.is_a?(Proc) | value = value.call if value.is_a?(Proc) | ||
self[name] | value = NilSection.new if value.nil? | ||
self[name] = value | |||
end | end | ||
end | end | ||
end | end | ||
|
|
||
class Configuration < Section | ## | ||
def initialize(mode = :development) | # A NilSection is returned for all missing/empty values in the config file. This | ||
@mode = mode | # allows for terse code when accessing values that have not been configured by | ||
yield self if block_given? | # the user. | ||
# | |||
# Consider code like this: | |||
# | |||
# config.server.listen.tap do |listen| | |||
# open_socket(listen.host, listen.port) | |||
# end | |||
# | |||
# Given that your server component is optional and does not appear in the | |||
# configuration file at all, +config.server.listen+ will return a NilSection | |||
# that does not call the block given to tap _at all_. | |||
# | |||
class NilSection | |||
def nil? | |||
true | |||
end | end | ||
|
|
||
def mixin(value) | def empty? | ||
unless value.is_a?(Hash) | true | ||
value = Section.from_hash(YAML.load(File.read(file))) | end | ||
end | |||
|
|||
if value.has_key?(:default) | |||
self.deep_merge!(value[:default]) | |||
end | |||
|
|
||
if value.has_key?(:generic) | def present? | ||
self.deep_merge!(value[:generic]) | false | ||
end | end | ||
|
|
||
if value.has_key?(@mode) | def tap | ||
self.deep_merge!(value[@mode]) | self | ||
else | |||
self.deep_merge!(value) | |||
end | |||
end | end | ||
|
|
||
def load_mixins(path, mixins_to_use) | def method_missing(*args, &block) | ||
mixins_to_use.map do |mixin_name| | self | ||
File.join(path, "#{mixin_name}.yml") | |||
end.each do |mixin_file| | |||
mixin(mixin_file) | |||
end | |||
end | end | ||
end | end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -1,33 +1,56 @@ | |||
## | |||
# A simple convenience class to support multiple environments in which a | |||
# program can run (e.g. development, production, etc). | |||
# | |||
class Environment | class Environment | ||
attr_accessor :key | attr_accessor :key | ||
|
|
||
# Create a new Environment instance with the corresponding +key+ in the +ENV+ | |||
# hash. | |||
# | |||
# @param [String] key The key in +ENV+ to contain the current program | |||
# environment. | |||
# | |||
def initialize(key=nil) | def initialize(key=nil) | ||
@key = key | @key = key | ||
end | end | ||
|
|
||
# Retreive the current environment mode. | |||
# | |||
# @return [String] The current environment mode. | |||
def mode | def mode | ||
ENV[@key] || 'development' | ENV[@key] || 'development' | ||
end | end | ||
|
|
||
# Retrieve the current environment mode and convert it to a symbol. | |||
# | |||
# @return [Symbol] The current environment mode. | |||
def to_sym | def to_sym | ||
mode.to_sym | mode.to_sym | ||
end | end | ||
|
|
||
# Return true if the current environment is +production+. | |||
def prod? | def prod? | ||
to_sym == :production | to_sym == :production | ||
end | end | ||
|
|
||
# Return true if the current environment is +development+. | |||
def dev? | def dev? | ||
to_sym == :development | to_sym == :development | ||
end | end | ||
|
|
||
# Return true if the current environment is +test+. | |||
def test? | def test? | ||
to_sym == :test | to_sym == :test | ||
end | end | ||
|
|
||
# Set the environment mode. | |||
# | |||
# @param [String] The new environment mode. | |||
def set(value) | def set(value) | ||
ENV[@key] = value.to_s | ENV[@key] = value.to_s | ||
end | end | ||
end | end | ||
|
|
||
# Global instance of {Environment}. | |||
Env = Environment.new | Env = Environment.new |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -0,0 +1,20 @@ | |||
## | |||
# Various Hash extensions. | |||
# | |||
class Hash | |||
|
|||
# Recursively merge +other_hash+ into +self+ and return the new hash. | |||
def deep_merge(other_hash) | |||
self.merge(other_hash) do |key, oldval, newval| | |||
oldval = oldval.to_hash if oldval.respond_to?(:to_hash) | |||
newval = newval.to_hash if newval.respond_to?(:to_hash) | |||
oldval.is_a?(Hash) && newval.is_a?(Hash) ? oldval.deep_merge(newval) : newval | |||
end | |||
end | |||
|
|||
# Recursively merge and replace +other_hash+ into +self. | |||
def deep_merge!(other_hash) | |||
replace(deep_merge(other_hash)) | |||
end | |||
|
|||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Original file line | Diff line number | Diff line change |
---|---|---|---|
@@ -1,7 +1,7 @@ | |||
--- | --- | ||
NestedIterators: | NestedIterators: | ||
exclude: | exclude: | ||
- Configuration#build_config | - Section#from_hash | ||
FeatureEnvy: | FeatureEnvy: | ||
exclude: | exclude: | ||
- Section#deep_merge | - Hash#deep_merge |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters