Skip to content

Commit

Permalink
cleanup & documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
hollow committed Apr 15, 2012
1 parent f6e9c1a commit 359b0bd
Show file tree
Hide file tree
Showing 8 changed files with 168 additions and 59 deletions.
2 changes: 1 addition & 1 deletion .yardopts
Original file line number Original file line Diff line number Diff line change
@@ -1 +1 @@
--no-private --protected --no-private
6 changes: 2 additions & 4 deletions Gemfile
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -2,10 +2,8 @@ source :rubygems


gemspec gemspec


gem 'rake' group :development, :test do
gem 'bundler' gem 'pry'

group :test do
gem 'redcarpet' gem 'redcarpet'
gem 'reek' gem 'reek'
gem 'rspec' gem 'rspec'
Expand Down
3 changes: 0 additions & 3 deletions lib/madvertise-ext.rb

This file was deleted.

166 changes: 117 additions & 49 deletions lib/madvertise/ext/config.rb
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
23 changes: 23 additions & 0 deletions lib/madvertise/ext/environment.rb
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
20 changes: 20 additions & 0 deletions lib/madvertise/ext/hash.rb
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
4 changes: 2 additions & 2 deletions lib/madvertise/ext/mask.reek
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
3 changes: 3 additions & 0 deletions madvertise-ext.gemspec
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ Gem::Specification.new do |gem|
gem.summary = %q{Ruby extensions} gem.summary = %q{Ruby extensions}
gem.homepage = "https://github.com/madvertise/ext" gem.homepage = "https://github.com/madvertise/ext"


gem.add_development_dependency "rake"
gem.add_development_dependency "bundler"

gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
gem.files = `git ls-files`.split("\n") gem.files = `git ls-files`.split("\n")
gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
Expand Down

0 comments on commit 359b0bd

Please sign in to comment.