confuse 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ vendor
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 1.8.7-p370
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in configuration.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Tom Chipchase
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Configuration
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'configuration'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install configuration
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/TODO ADDED
@@ -0,0 +1,4 @@
1
+ * Make the :type attriute for a config item actually do something. (e.g.some
2
+ basic type checking)
3
+ * Write some more tests
4
+ * Pick a good name that hasn't already been claimed on rubygems.
data/confuse.gemspec ADDED
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'confuse/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'confuse'
8
+ spec.version = Confuse::VERSION
9
+ spec.authors = ['Tom Chipchase']
10
+ spec.email = ['tom@mediasp.com']
11
+ spec.description = %q{Add nested configuration to an application}
12
+ spec.summary = %q{}
13
+ spec.homepage = ''
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files`.split($RS)
17
+ spec.executables = spec.files.grep(/^bin/) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(/^(test|spec|features)/)
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'inifile'
22
+
23
+ spec.add_development_dependency 'bundler', '~> 1.3'
24
+ spec.add_development_dependency 'minitest'
25
+ end
@@ -0,0 +1,3 @@
1
+ [default]
2
+ foo = 10
3
+ bar = 20
@@ -0,0 +1,18 @@
1
+ require 'configuration/configurable'
2
+
3
+ # An example of adding configuration options to a class.
4
+ class SimpleExample
5
+ configurable
6
+
7
+ config_path 'example/001_simple_example.ini'
8
+
9
+ define :foo do
10
+ description "How many Foo's are there?"
11
+ type :integer
12
+ default 1
13
+ end
14
+
15
+ end
16
+
17
+ example = SimpleExample.new
18
+ puts example.foo
@@ -0,0 +1,29 @@
1
+ require 'configuration/configurable'
2
+
3
+ # An example of adding configuration options to a class, seperated by
4
+ # namespaces.
5
+ class NamespaceExample
6
+ configurable
7
+
8
+ config_path 'example/002_namespace_example.ini'
9
+
10
+ define :bar do
11
+ description 'foo_bar'
12
+ type :integer
13
+ default 1
14
+ end
15
+
16
+ namespace :foo do
17
+ define :bar do
18
+ description 'bar_foo'
19
+ type :string
20
+ default 'Hello, world!'
21
+ end
22
+ end
23
+ end
24
+
25
+ example = NamespaceExample.new
26
+ puts example.config.inspect
27
+ puts example.bar
28
+ puts example.config[:foo_bar]
29
+ puts example.foo[:bar]
@@ -0,0 +1,9 @@
1
+ require 'configuration'
2
+
3
+ class ExampleConfig < Configuration::ConfigBase
4
+ define :foo do
5
+ default 10
6
+ end
7
+ end
8
+
9
+ puts ExampleConfig.new.foo
@@ -0,0 +1,26 @@
1
+ require 'configuration/configurable'
2
+
3
+ module ExampleConfig
4
+ extend Configuration::ConfigMixin
5
+ extend Configuration::DSL
6
+
7
+ define :conf do
8
+ default 2
9
+ end
10
+ end
11
+
12
+ class Foo
13
+ configured_by ExampleConfig
14
+
15
+ default_namespace :foo
16
+
17
+ define :bar do
18
+ default 1
19
+ end
20
+
21
+ end
22
+
23
+ foo = Foo.new
24
+ puts foo.config[:conf]
25
+ puts foo.bar
26
+
@@ -0,0 +1,24 @@
1
+ require 'confuse/config_mixin'
2
+ require 'confuse/dsl'
3
+
4
+ module Confuse
5
+ # The default module used for configuration.
6
+ module Config
7
+ extend ConfigMixin
8
+ end
9
+
10
+ # Super class for configuration in order to have multiple instances.
11
+ class ConfigBase
12
+ include ConfigMixin
13
+ extend DSL
14
+
15
+ def initialize
16
+ load_namespaces(self.class.namespaces)
17
+ read_files(self.class.config_path)
18
+ end
19
+
20
+ def config
21
+ self
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,33 @@
1
+ module Confuse
2
+ # A {ConfigItem} is a class for storing a single piece of config. It has a
3
+ # key, a type, a description, and a default value.
4
+ class ConfigItem
5
+ attr_reader :key
6
+ attr_writer :value
7
+
8
+ def initialize(name, &block)
9
+ @key = name
10
+ instance_eval(&block)
11
+ end
12
+
13
+ def description(description = nil)
14
+ @description = description unless description.nil?
15
+ @description
16
+ end
17
+
18
+ def type(type = nil)
19
+ @type = type unless type.nil?
20
+ @type
21
+ end
22
+
23
+ def default(default = nil)
24
+ @default = default unless default.nil?
25
+ @default
26
+ end
27
+
28
+ def value
29
+ @value || @default
30
+ end
31
+ end
32
+ end
33
+
@@ -0,0 +1,117 @@
1
+ require 'confuse/namespace'
2
+ require 'inifile'
3
+ require 'yaml'
4
+
5
+ module Confuse
6
+ # Mixin for configuration.
7
+ module ConfigMixin
8
+ def namespaces
9
+ @namespaces ||= {}
10
+ end
11
+
12
+ def load_namespaces(new_namespaces)
13
+ new_namespaces.each do |key, value|
14
+ existing = namespaces[key]
15
+ existing ? existing.merge!(value) : namespaces[key] = value
16
+ end
17
+ end
18
+
19
+ def read_files(file_paths)
20
+ Array(file_paths).map do |path|
21
+ if File.directory?(path)
22
+ load_config_dir(path)
23
+ elsif File.exists?(path)
24
+ load_config_file(path)
25
+ end
26
+ end.flatten.compact.each { |c| mixin_config!(c) }
27
+ end
28
+
29
+ def [](key)
30
+ namespace = find_namespace(key) || :default
31
+ rest_of_key = rest_of_key(key, namespace)
32
+ ns = namespaces[namespace]
33
+ return ns unless rest_of_key
34
+ ns[rest_of_key]
35
+ end
36
+
37
+ # We allow the namespace and the key to be concatenated with an '_', so this
38
+ # method is to search the possible substrings that could make up the
39
+ # namespace for a key.
40
+ #
41
+ # This does not guarentee that the suffix of the namespace that is found is
42
+ # a valid key in that namespace.
43
+ #
44
+ # @param [Symbol] key to search
45
+ def find_namespace(key)
46
+ until key.to_s.empty? || @namespaces[key.to_sym]
47
+ key = (s = key.to_s)[0, s.rindex('_') || 0]
48
+ end
49
+ key.to_s.empty? ? nil : key.to_sym
50
+ end
51
+
52
+ # Once we've found the namespace, we want to find the rest of the string
53
+ # to use as the rest of the key. If the namespace isn't a substring of the
54
+ # key (e.g. :default), we return the key unaltered.
55
+ #
56
+ # @param [Symbol] key The full key
57
+ # @param [Symbol] namespace The substring of the key that is the
58
+ # namespace.
59
+ def rest_of_key(key, namespace)
60
+ key_s = key.to_s
61
+ namespace_s = namespace.to_s
62
+ return nil if key_s == namespace_s
63
+ index = key_s.index(namespace_s) && (namespace_s.length + 1)
64
+ key_s[index || 0, key_s.length].to_sym
65
+ end
66
+
67
+ def load_config_dir(config_dir)
68
+ Dir[config_dir + '/*.{ini,conf,yaml}'].map do |conf_file|
69
+ load_config_file(conf_file)
70
+ end
71
+ end
72
+
73
+ def load_config_file(conf_file)
74
+ conf = load_ini(conf_file) || load_yaml(conf_file)
75
+ raise "Can't parse #{conf_file}" unless conf
76
+ conf
77
+ end
78
+
79
+ def mixin_config!(config)
80
+ config.each do |key, value|
81
+ namespace = @namespaces[find_namespace(key) || :default]
82
+ if value.respond_to? :keys
83
+ # if its a hash, set each key in the hash as a config item in the
84
+ # namespace
85
+ value.each do |k, v|
86
+ namespace[k] = v
87
+ end
88
+ else
89
+ # otherwise, set it directly in the namespace
90
+ namespace[key] = value
91
+ end
92
+ end
93
+ end
94
+
95
+ def load_ini(file)
96
+ begin
97
+ # make sure they keys are ruby symbols
98
+ symbolise_hash_keys(IniFile.load(file).to_h)
99
+ rescue IniFile::Error
100
+ nil
101
+ end
102
+ end
103
+
104
+ def symbolise_hash_keys(hash)
105
+ hash.reduce({}) do |memo, (key, val)|
106
+ memo[key.to_sym] =
107
+ val.respond_to?(:reduce) ? symbolise_hash_keys(val) : val
108
+ memo
109
+ end
110
+ end
111
+
112
+ def load_yaml(file)
113
+ YAML.load(File.read(file))
114
+ end
115
+
116
+ end
117
+ end
@@ -0,0 +1,15 @@
1
+ require 'configuration'
2
+
3
+ # Monkey patch class to easily allow a class to be configurable.
4
+ class Class
5
+ def configurable
6
+ extend Confuse::DSL
7
+ include Confuse::InstanceMethods
8
+ end
9
+
10
+ def configured_by(config)
11
+ configurable
12
+ @configured_by = config
13
+ end
14
+ end
15
+
@@ -0,0 +1,68 @@
1
+ require 'confuse/config'
2
+
3
+ module Confuse
4
+ # DSL for setting up a configurable class
5
+ module DSL
6
+ attr_reader :configured_by
7
+
8
+ def configured_by(config = Config)
9
+ @configured_by ||= config
10
+ end
11
+
12
+ def default_namespace(namespace = :default)
13
+ @default_namespace ||= namespace
14
+ end
15
+
16
+ def config_path(*paths)
17
+ @config_path ||= []
18
+ @config_path.concat paths
19
+ end
20
+
21
+ def namespaces
22
+ @namespaces ||= {
23
+ default_namespace => Namespace.new(&(Proc.new { }))
24
+ }
25
+ end
26
+
27
+ def define(name, &block)
28
+ namespaces[default_namespace].define(name, &block)
29
+ getter(name, default_namespace)
30
+ end
31
+
32
+ def namespace(name, &block)
33
+ new_namespace = Namespace.new(&block)
34
+ if namespaces[name.to_sym]
35
+ namespaces[name.to_sym].merge! new_namespace
36
+ else
37
+ namespaces[name.to_sym] = new_namespace
38
+ end
39
+ getter(name)
40
+ end
41
+
42
+ def getter(name, namespace = nil)
43
+ class_eval do
44
+ if namespace.nil?
45
+ define_method(name) do
46
+ config[name]
47
+ end
48
+ else
49
+ define_method(name) do
50
+ config[namespace][name]
51
+ end
52
+ end
53
+ end
54
+ end
55
+
56
+ end
57
+
58
+ # Instance methods for getting the defined configuration from an instance of
59
+ # a configured class.
60
+ module InstanceMethods
61
+ # Lazilly create the config object when it is first requested.
62
+ def config
63
+ self.class.configured_by.load_namespaces(self.class.namespaces)
64
+ self.class.configured_by.read_files(self.class.config_path)
65
+ self.class.configured_by
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,56 @@
1
+ require 'confuse/config_item'
2
+
3
+ module Confuse
4
+ # A {Namespace} is a container to keep configuration data seperate from the
5
+ # rest of the config.
6
+ class Namespace
7
+
8
+ attr_reader :items, :supress_warnings_flag, :strict_flag
9
+
10
+ def initialize(&block)
11
+ @items = {}
12
+ @supress_warnings = false
13
+ @strict = false
14
+ instance_eval(&block)
15
+ end
16
+
17
+ def define(name, &block)
18
+ @items[name] = ConfigItem.new(name, &block)
19
+ end
20
+
21
+ def supress_warnings
22
+ @supress_warnings_flag = true
23
+ end
24
+
25
+ def strict
26
+ @strict_flag = true
27
+ end
28
+
29
+ def [](key)
30
+ (i = get_item(key)) && i.value
31
+ end
32
+
33
+ def []=(key, value)
34
+ item = get_item(key) || create_new_key(key, value)
35
+ item && item.value = value
36
+ end
37
+
38
+ def create_new_key(key, value)
39
+ if @supress_warnings_flag
40
+ puts "Warning: config includes unknown option '#{key}'"
41
+ end
42
+ @items[key] = ConfigItem.new(key, &(Proc.new {})) unless @strict_flag
43
+ end
44
+
45
+ def get_item(key)
46
+ @items[key]
47
+ end
48
+
49
+ def merge!(namespace)
50
+ @strict_flag = namespace.strict
51
+ @supress_warnings_flag = namespace.supress_warnings
52
+ @items.merge! namespace.items
53
+ end
54
+
55
+ end
56
+ end
@@ -0,0 +1,3 @@
1
+ module Confuse
2
+ VERSION = '0.0.1'
3
+ end
data/lib/confuse.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'confuse/version'
2
+
3
+ require 'confuse/config'
4
+ require 'confuse/config_item'
5
+ require 'confuse/config_mixin'
6
+ require 'confuse/dsl'
7
+ require 'confuse/namespace'
8
+
9
+ module Confuse
10
+ end
11
+
@@ -0,0 +1,50 @@
1
+ require 'minitest/autorun'
2
+ require 'confuse'
3
+
4
+ # Test instance methods
5
+ class TestConfig < MiniTest::Unit::TestCase
6
+ def setup
7
+ namespace = Confuse::Namespace.new do
8
+ define :baz do
9
+ default 1
10
+ end
11
+ end
12
+ @config = Confuse::Config
13
+ @config.load_namespaces({ :foo_bar => namespace })
14
+ end
15
+
16
+ def test_find_namespace
17
+ assert_equal :foo_bar, @config.find_namespace(:foo_bar_baz)
18
+ end
19
+
20
+ def test_fine_namespace_no_sub_key
21
+ assert_equal :foo_bar, @config.find_namespace(:foo_bar)
22
+ end
23
+
24
+ def test_find_namespace_doesnt_exist
25
+ assert_equal nil, @config.find_namespace(:bar)
26
+ end
27
+
28
+ def test_rest_of_key
29
+ assert_equal :bar, @config.rest_of_key(:foo_bar, :foo)
30
+ end
31
+
32
+ def test_rest_of_key_default_namespace
33
+ assert_equal :bar, @config.rest_of_key(:bar, :default)
34
+ end
35
+
36
+ def test_load_namespaces
37
+ namespace = Confuse::Namespace.new do
38
+ define :foo do
39
+ default 1
40
+ end
41
+ end
42
+
43
+ @config.load_namespaces({ :foo_bar => namespace })
44
+
45
+ assert @config[:foo_bar][:foo]
46
+ assert @config[:foo_bar][:baz]
47
+ end
48
+
49
+ end
50
+
@@ -0,0 +1,25 @@
1
+ require 'minitest/autorun'
2
+ require 'confuse'
3
+
4
+ class TestNamespace < MiniTest::Unit::TestCase
5
+ def setup
6
+ @namespace = Confuse::Namespace.new do
7
+ define :foo do
8
+ default 1
9
+ end
10
+ end
11
+ end
12
+
13
+ def test_merge!
14
+ namespace = Confuse::Namespace.new do
15
+ define :bar do
16
+ default 1
17
+ end
18
+ end
19
+
20
+ @namespace.merge!(namespace)
21
+
22
+ assert @namespace[:foo]
23
+ assert @namespace[:bar]
24
+ end
25
+ end
metadata ADDED
@@ -0,0 +1,132 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: confuse
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Tom Chipchase
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2013-10-01 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: inifile
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ">="
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 0
31
+ version: "0"
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ - !ruby/object:Gem::Dependency
35
+ name: bundler
36
+ prerelease: false
37
+ requirement: &id002 !ruby/object:Gem::Requirement
38
+ none: false
39
+ requirements:
40
+ - - ~>
41
+ - !ruby/object:Gem::Version
42
+ hash: 9
43
+ segments:
44
+ - 1
45
+ - 3
46
+ version: "1.3"
47
+ type: :development
48
+ version_requirements: *id002
49
+ - !ruby/object:Gem::Dependency
50
+ name: minitest
51
+ prerelease: false
52
+ requirement: &id003 !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ type: :development
62
+ version_requirements: *id003
63
+ description: Add nested configuration to an application
64
+ email:
65
+ - tom@mediasp.com
66
+ executables: []
67
+
68
+ extensions: []
69
+
70
+ extra_rdoc_files: []
71
+
72
+ files:
73
+ - .gitignore
74
+ - .ruby-version
75
+ - Gemfile
76
+ - LICENSE.txt
77
+ - README.md
78
+ - Rakefile
79
+ - TODO
80
+ - confuse.gemspec
81
+ - example/001_simple_example.ini
82
+ - example/001_simple_example.rb
83
+ - example/002_namespace_example.rb
84
+ - example/003_config_subclass_instance_example.rb
85
+ - example/004_configured_by.rb
86
+ - lib/confuse.rb
87
+ - lib/confuse/config.rb
88
+ - lib/confuse/config_item.rb
89
+ - lib/confuse/config_mixin.rb
90
+ - lib/confuse/configurable.rb
91
+ - lib/confuse/dsl.rb
92
+ - lib/confuse/namespace.rb
93
+ - lib/confuse/version.rb
94
+ - test/test_config.rb
95
+ - test/test_namespace.rb
96
+ homepage: ""
97
+ licenses:
98
+ - MIT
99
+ post_install_message:
100
+ rdoc_options: []
101
+
102
+ require_paths:
103
+ - lib
104
+ required_ruby_version: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ">="
108
+ - !ruby/object:Gem::Version
109
+ hash: 3
110
+ segments:
111
+ - 0
112
+ version: "0"
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ none: false
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ hash: 3
119
+ segments:
120
+ - 0
121
+ version: "0"
122
+ requirements: []
123
+
124
+ rubyforge_project:
125
+ rubygems_version: 1.8.25
126
+ signing_key:
127
+ specification_version: 3
128
+ summary: ""
129
+ test_files:
130
+ - test/test_config.rb
131
+ - test/test_namespace.rb
132
+ has_rdoc: