configru 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source "http://rubygems.org"
2
+
3
+ group :development do
4
+ gem "riot", ">= 0"
5
+ gem "bundler", "~> 1.0.0"
6
+ end
7
+
8
+ # Specify your gem's dependencies in configru.gemspec
9
+ gemspec
data/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # Configru
2
+
3
+ Versatile configuration file loader for Ruby
4
+
5
+ ## Desired Example Usage
6
+
7
+ ```yaml
8
+ server: irc.ninthbit.net
9
+ port: 6667
10
+ nick: awesomeface
11
+ powerlevel: 9
12
+ awesome: omgyes
13
+ ```
14
+
15
+ ```ruby
16
+ require 'configru'
17
+
18
+ Configru.load do
19
+ cascade '~/foo.yml', '/etc/foo.yml' # Set files to look in
20
+ defaults do
21
+ server 'irc.freenode.net'
22
+ port 6667
23
+ nick 'bot'
24
+ powerlevel 1
25
+ awesome 'not at all'
26
+ end
27
+ verify do
28
+ server /\S+/ # Must match regex
29
+ nick /\S+/
30
+ port Fixnum # Must be an instance of
31
+ powerlevel 1..10 # Must be in range
32
+ awesome ['not at all', 'omgyes', 'meh'] # Must be one of
33
+ end
34
+ end
35
+
36
+ puts "Connecting to #{Configru.server}:#{Configru.port}"
37
+ ```
38
+
39
+ ## License
40
+
41
+ Copyright (c) 2011, Curtis McEnroe <programble@gmail.com>
42
+
43
+ Permission to use, copy, modify, and/or distribute this software for any
44
+ purpose with or without fee is hereby granted, provided that the above
45
+ copyright notice and this permission notice appear in all copies.
46
+
47
+ THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
48
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
49
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
50
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
51
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
52
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
53
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
54
+
data/Rakefile ADDED
@@ -0,0 +1,15 @@
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rake'
4
+ require 'rake/testtask'
5
+
6
+ desc "Run all tests"
7
+ task :test do
8
+ Rake::TestTask.new do |t|
9
+ t.libs << "test"
10
+ t.pattern = "test/**/*_test.rb"
11
+ t.verbose = false
12
+ end
13
+ end
14
+
15
+ task :default => :test
data/configru.gemspec ADDED
@@ -0,0 +1,18 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "configru/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "configru"
7
+ s.version = Configru::VERSION
8
+ s.authors = ["Curtis McEnroe"]
9
+ s.email = ["programble@gmail.com"]
10
+ s.homepage = "https://github.com/programble/configru"
11
+ s.summary = %q{Versatile configuration file loader}
12
+ s.description = s.summary
13
+
14
+ s.files = `git ls-files`.split("\n")
15
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
16
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
17
+ s.require_paths = ["lib"]
18
+ end
@@ -0,0 +1,31 @@
1
+ module Configru
2
+ class ConfigHash < Hash
3
+ def initialize(hash={})
4
+ super
5
+ merge!(hash)
6
+ end
7
+
8
+ def merge!(hash)
9
+ hash.each do |key, value|
10
+ if value.is_a?(Hash) && self[key].is_a?(ConfigHash)
11
+ self[key].merge!(value)
12
+ elsif value.is_a?(Hash)
13
+ self[key] = ConfigHash.new(value)
14
+ else
15
+ self[key] = value
16
+ end
17
+ end
18
+ end
19
+
20
+ def [](key)
21
+ key = key.to_s if key.is_a?(Symbol)
22
+ # For some reason, super(key) returns {} instead of nil when the key
23
+ # doesn't exist :\
24
+ super(key) if self.include?(key)
25
+ end
26
+
27
+ def method_missing(key, *args)
28
+ self[key]
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,59 @@
1
+ module Configru
2
+ module DSL
3
+ class LoadDSL
4
+ attr_reader :defaults_hash, :verify_hash, :files_array, :load_method
5
+
6
+ def initialize(block)
7
+ @defaults_hash = {}
8
+ @verify_hash = {}
9
+ @files_array = []
10
+ @load_method = :first
11
+ instance_eval(&block)
12
+ end
13
+
14
+ def first_of(*args)
15
+ @load_method = :first
16
+ @files_array = args
17
+ end
18
+
19
+ def cascade(*args)
20
+ @load_method = :cascade
21
+ @files_array = args
22
+ end
23
+
24
+ def defaults(hash=nil, &block)
25
+ if hash
26
+ @defaults_hash = hash
27
+ elsif block
28
+ @defaults_hash = HashDSL.new(block).hash
29
+ end
30
+ end
31
+
32
+ def verify(hash=nil, &block)
33
+ if hash
34
+ @verify_hash = hash
35
+ elsif block
36
+ @verify_hash = HashDSL.new(block).hash
37
+ end
38
+ end
39
+ end
40
+
41
+ class HashDSL
42
+ attr_reader :hash
43
+
44
+ def initialize(block)
45
+ @hash = {}
46
+ instance_eval(&block)
47
+ end
48
+
49
+ def method_missing(method, *args, &block)
50
+ key = method.to_s
51
+ if block
52
+ @hash[key] = HashDSL.new(block).hash
53
+ else
54
+ @hash[key] = args[0]
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,3 @@
1
+ module Configru
2
+ VERSION = "0.0.1"
3
+ end
data/lib/configru.rb ADDED
@@ -0,0 +1,77 @@
1
+ $: << File.dirname(__FILE__) unless $:.include?(File.dirname(__FILE__))
2
+
3
+ require 'configru/confighash'
4
+ require 'configru/dsl'
5
+
6
+ require 'yaml'
7
+
8
+ module Configru
9
+ class ConfigurationError < RuntimeError; end
10
+
11
+ def self.load(load_method=:first, files=[], defaults={}, verify={}, &block)
12
+ if block
13
+ dsl = DSL::LoadDSL.new(block)
14
+ @load_method = dsl.load_method
15
+ @files = dsl.files_array.map {|x| File.expand_path(x)}
16
+ @defaults = dsl.defaults_hash
17
+ @verify = dsl.verify_hash
18
+ else
19
+ @load_method = load_method
20
+ @files = files
21
+ @defaults = defaults
22
+ @verify = verify
23
+ end
24
+ self.reload
25
+ end
26
+
27
+ def self.reload
28
+ @config = ConfigHash.new(@defaults)
29
+
30
+ case @load_method
31
+ when :first
32
+ if file = @files.find {|file| File.file?(file)} # Intended
33
+ @config.merge!(YAML.load_file(file) || {})
34
+ end
35
+ when :cascade
36
+ @files.reverse_each do |file|
37
+ @config.merge!(YAML.load_file(file) || {}) if File.file?(file)
38
+ end
39
+ end
40
+
41
+ self.verify(@config, @verify)
42
+ end
43
+
44
+ @verify_stack = []
45
+ def self.verify(hash, criteria)
46
+ hash.each do |key, value|
47
+ next unless criteria[key]
48
+ @verify_stack.unshift(key)
49
+
50
+ result = case criteria[key]
51
+ when Hash
52
+ self.verify(value, criteria[key])
53
+ true
54
+ when Class
55
+ value.is_a?(criteria[key])
56
+ when Array
57
+ criteria[key].include?(value)
58
+ else
59
+ criteria[key] === value
60
+ end
61
+
62
+ raise ConfigurationError, "configuration option '#{@verify_stack.reverse.join('.')}' is invalid" unless result
63
+
64
+ @verify_stack.shift
65
+ end
66
+ end
67
+
68
+ def self.[](key)
69
+ @config[key]
70
+ end
71
+
72
+ def self.method_missing(key, *args)
73
+ # Simulate NoMethodError if it looks like they really wanted a method
74
+ raise NoMethodError, "undefined method `#{key.to_s}' for #{self.inspect}:#{self.class}" unless args.empty?
75
+ self[key]
76
+ end
77
+ end
@@ -0,0 +1,53 @@
1
+ require 'teststrap'
2
+
3
+ context 'ConfigHash - ' do
4
+ setup { Configru::ConfigHash.new }
5
+
6
+ context 'empty' do
7
+ asserts('[:quux]') { topic[:quux] }.nil
8
+ asserts(:quux).nil
9
+ end
10
+
11
+ context 'non-empty' do
12
+ hookup do
13
+ topic.merge!({'foo' => 1, 'bar' => 2, 'baz' => 3})
14
+ end
15
+
16
+ # Testing access methods
17
+ asserts("['foo']") { topic['foo'] }.equals(1)
18
+ asserts('[:foo]') { topic[:foo] }.equals(1)
19
+ asserts(:foo).equals(1)
20
+ end
21
+
22
+ context 'overwriting a value by merge' do
23
+ hookup do
24
+ topic.merge!({'foo' => 1, 'bar' => 2, 'baz' => 3})
25
+ topic.merge!({'bar' => 4})
26
+ end
27
+ asserts(:bar).equals(4)
28
+ end
29
+
30
+ context 'merging a nested Hash' do
31
+ hookup do
32
+ topic.merge!({'foo' => 1, 'bar' => 2, 'baz' => 3})
33
+ topic.merge!({'bar' => 4})
34
+ topic.merge!({'baz' => {'quux' => 5}})
35
+ end
36
+
37
+ asserts(:baz).kind_of(Configru::ConfigHash)
38
+ asserts('baz.quux') { topic.baz.quux }.equals(5)
39
+ end
40
+
41
+ context 'recursively merging a nested Hash' do
42
+ hookup do
43
+ topic.merge!({'foo' => 1, 'bar' => 2, 'baz' => 3})
44
+ topic.merge!({'bar' => 4})
45
+ topic.merge!({'baz' => {'quux' => 5}})
46
+ topic.merge!({'baz' => {'apple' => 6}})
47
+ end
48
+
49
+ asserts(:baz).kind_of(Configru::ConfigHash)
50
+ asserts('baz.apple') { topic.baz.apple }.equals(6)
51
+ asserts('baz.quux') { topic.baz.quux }.equals(5)
52
+ end
53
+ end
data/test/teststrap.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'bundler'
2
+ begin
3
+ Bundler.setup(:default, :development)
4
+ rescue Bundler::BundlerError => e
5
+ $stderr.puts e.message
6
+ $stderr.puts "Run `bundle install` to install missing gems"
7
+ exit e.status_code
8
+ end
9
+ require 'riot'
10
+ require 'configru'
11
+
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: configru
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Curtis McEnroe
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2011-07-14 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Versatile configuration file loader
22
+ email:
23
+ - programble@gmail.com
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - .gitignore
32
+ - Gemfile
33
+ - README.md
34
+ - Rakefile
35
+ - configru.gemspec
36
+ - lib/configru.rb
37
+ - lib/configru/confighash.rb
38
+ - lib/configru/dsl.rb
39
+ - lib/configru/version.rb
40
+ - test/confighash_test.rb
41
+ - test/teststrap.rb
42
+ has_rdoc: true
43
+ homepage: https://github.com/programble/configru
44
+ licenses: []
45
+
46
+ post_install_message:
47
+ rdoc_options: []
48
+
49
+ require_paths:
50
+ - lib
51
+ required_ruby_version: !ruby/object:Gem::Requirement
52
+ none: false
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ required_rubygems_version: !ruby/object:Gem::Requirement
60
+ none: false
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ segments:
65
+ - 0
66
+ version: "0"
67
+ requirements: []
68
+
69
+ rubyforge_project:
70
+ rubygems_version: 1.3.7
71
+ signing_key:
72
+ specification_version: 3
73
+ summary: Versatile configuration file loader
74
+ test_files:
75
+ - test/confighash_test.rb
76
+ - test/teststrap.rb