confuse 0.1.8 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,12 +1,15 @@
1
- # Configuration
1
+ # Confuse
2
2
 
3
- TODO: Write a gem description
3
+ This gem is used to simplify specifying and reading configuration.
4
+
5
+ You can define what you expect to be configured, and this can be read from ini
6
+ files, yaml files or environment variables.
4
7
 
5
8
  ## Installation
6
9
 
7
10
  Add this line to your application's Gemfile:
8
11
 
9
- gem 'configuration'
12
+ gem 'confuse'
10
13
 
11
14
  And then execute:
12
15
 
@@ -18,7 +21,112 @@ Or install it yourself as:
18
21
 
19
22
  ## Usage
20
23
 
21
- TODO: Write usage instructions here
24
+ Basic usage:
25
+
26
+ config = Confuse.config do |conf|
27
+ conf.add_item :foo, :description => 'Foo', :default => 1
28
+ end
29
+
30
+ config[:foo]
31
+ config.foo
32
+
33
+ Types:
34
+
35
+ You can choose where and your config is stored by passing a :path variable to
36
+ Confuse.config:
37
+
38
+ config = Confuse.config :path => 'config.ini' do |conf|
39
+ conf.add_item :foo, :description => 'Foo', :default => 1
40
+ end
41
+
42
+ Confuse will attempt to work out the type of file from the extension (Supports
43
+ .ini for ini files, and .yml or .yaml for yaml files). You can override this by
44
+ passing in a :type option:
45
+
46
+ config = Confuse.config :path => 'foo.conf' :type => :yaml do |conf|
47
+ conf.add_item :foo, :description => 'Foo', :default => 1
48
+ end
49
+
50
+ If no type or path is given, it will default to environment variables.
51
+
52
+ Environment variables have one extra feature, which is the ability to provide a
53
+ prefix:
54
+
55
+
56
+ config = Confuse.config :prefix => 'FOO' :type => :yaml do |conf|
57
+ conf.add_item :foo, :description => 'Foo', :default => 1
58
+ end
59
+
60
+ This means Confuse will lookup environment varialbles with that prefix with an
61
+ underscore followed by the configuration name. If none is given, it will lookup
62
+ environment variables as is.
63
+
64
+ Namespaces:
65
+
66
+ You can separate your configuration into different namespaces:
67
+
68
+ config = Confuse.config :type => :env do |conf|
69
+ conf.add_namespace :foo do |ns|
70
+ ns.add_item :foo, :description => 'Foo', :default => 1
71
+ end
72
+ end
73
+
74
+ For simplicity, you can fetch these items using a single []:
75
+
76
+ config[:foo_bar]
77
+
78
+ Or a method call:
79
+
80
+ config.foo_bar
81
+
82
+ However, beware of adding an item at the top level with the same name.
83
+
84
+ Check:
85
+
86
+ If you want to make sure all your configuration items are in place before
87
+ running the program you can call .check on it.
88
+
89
+ config = Confuse.config do |conf|
90
+ conf.add_item :foo, :description => 'Foo', :default => 1
91
+ end
92
+
93
+ config.check
94
+
95
+ If any of the items haven't been set, and don't have a default value, an
96
+ exception will be thrown. If you don't care about certain variables, you can
97
+ pass :required => false to them (or give them a default value).
98
+
99
+ Use a different source:
100
+
101
+ If you want to use a source that isn't an ini file or yaml file, or environment
102
+ variable. It is very easy to create your own one. It simply needs to be a class
103
+ that has an initialize that takes a hash, and a [] method that takes a
104
+ namespace and a key argument.
105
+
106
+ class MyOwnSource
107
+ def initialize(options = {})
108
+ ...
109
+ end
110
+
111
+ def [](namespace, key)
112
+ ...
113
+ end
114
+ end
115
+
116
+ Confuse::Source.register(:foo, MyOwnSource)
117
+
118
+ definition = Confuse.config, :type => :foo do |conf|
119
+ conf.add_item :foo
120
+ end
121
+
122
+ config = Confuse::Config.new(definition, my_own_source)
123
+
124
+ If your class takes a :path variable, it will autodetect the extension for
125
+ whatever you register. You can register more than one extension if you wish.
126
+
127
+ You could use this to store data in whatever fancy file format you like, or
128
+ perhaps even a database table, or one of those fancy NoSQL datastores, you
129
+ could even write your config in Ruby code.
22
130
 
23
131
  ## Contributing
24
132
 
data/Rakefile CHANGED
@@ -1 +1,8 @@
1
1
  require "bundler/gem_tasks"
2
+ require 'rake/testtask'
3
+
4
+ Rake::TestTask.new do |t|
5
+ t.libs.push 'lib'
6
+ t.test_files = FileList['test/test_*.rb']
7
+ t.verbose = true
8
+ end
@@ -22,4 +22,5 @@ Gem::Specification.new do |spec|
22
22
 
23
23
  spec.add_development_dependency 'bundler', '~> 1.3'
24
24
  spec.add_development_dependency 'minitest'
25
+ spec.add_development_dependency 'rake'
25
26
  end
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ require 'confuse'
5
+
6
+ config = Confuse.config :prefix => 'CONF' do |conf|
7
+ conf.add_item :foo, :description => "How many Foo's are there?", :default => 1
8
+ end
9
+
10
+ puts config.foo
@@ -0,0 +1 @@
1
+ foo = 10
@@ -0,0 +1 @@
1
+ :foo: 10
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ require 'confuse'
5
+
6
+ config = Confuse.config :path => 'example/example.ini' do |conf|
7
+ conf.add_item :foo, :description => "How many Foo's are there?", :default => 1
8
+ end
9
+
10
+ puts config.foo
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # coding: utf-8
3
+
4
+ require 'confuse'
5
+
6
+ config = Confuse.config :path => 'example/example.yaml' do |conf|
7
+
8
+ conf.add_item :foo, :description => "How many Foo's are there?", :default => 1
9
+ end
10
+
11
+ puts config.foo
@@ -1,11 +1,30 @@
1
+ # coding: utf-8
2
+
1
3
  require 'confuse/version'
2
4
 
5
+ require 'confuse/key_splitter'
3
6
  require 'confuse/config'
4
- require 'confuse/config_item'
5
- require 'confuse/config_mixin'
6
- require 'confuse/dsl'
7
+ require 'confuse/item'
8
+ require 'confuse/definition'
7
9
  require 'confuse/namespace'
10
+ require 'confuse/source'
11
+ require 'confuse/errors'
8
12
 
13
+ # Top level namespace for confuse gem
9
14
  module Confuse
10
- end
15
+ class << self
16
+ def define(&block)
17
+ Definition.new(&block)
18
+ end
11
19
 
20
+ def source(options = {})
21
+ Source.create(options)
22
+ end
23
+
24
+ def config(options = {}, &block)
25
+ definition = Definition.new(&block)
26
+ source = Source.create(options)
27
+ Config.new(definition, source)
28
+ end
29
+ end
30
+ end
@@ -1,48 +1,37 @@
1
- require 'confuse/config_mixin'
2
- require 'confuse/dsl'
1
+ # coding: utf-8
3
2
 
4
3
  module Confuse
5
- # The default module used for configuration.
6
- module Config
7
- extend ConfigMixin
8
- end
4
+ class Config
5
+ def initialize(definition, source)
6
+ @definition = definition
7
+ @source = source
8
+ end
9
9
 
10
- # Super class for configuration in order to have multiple instances.
11
- class ConfigBase
12
- include ConfigMixin
13
- extend DSL
10
+ def respond_to?(name)
11
+ @definition.defines?(name) || super
12
+ end
14
13
 
15
- def namespaces
16
- @namespaces ||= {}
14
+ def method_missing(name, *_args)
15
+ self[name] if respond_to?(name)
17
16
  end
18
17
 
19
- def initialize(options = {})
20
- load_namespaces(self.class.namespaces.clone)
21
- paths = options[:paths] || []
22
- if paths.flatten.empty?
23
- read_files(self.class.config_path.flatten)
24
- else
25
- read_files(paths.flatten)
26
- end
27
- options[:conf].tap { |conf| conf && mixin_config!(conf) }
18
+ def [](name)
19
+ namespace, key = @definition.namespace_and_key(name)
20
+ lookup(namespace, key)
28
21
  end
29
22
 
30
- def config
31
- self
23
+ def lookup(namespace, key)
24
+ @source[namespace, key] || @definition.default(namespace, key)
32
25
  end
33
26
 
34
- def self.params_hash
35
- namespaces.reduce({}) do |memo, (name, namespace)|
36
- namespace.keys.each do |key|
37
- item = namespace.get_item(key)
38
- memo[:"#{name}_#{key}"] = {
39
- :type => item.type,
40
- :doc => item.description,
41
- :default => item.default_value }
27
+ # check items have a value. Will raise Undefined error if a required item
28
+ # has no value.
29
+ def check
30
+ @definition.namespaces.each do |(namespace, ns)|
31
+ ns.items.each do |key, _|
32
+ lookup(namespace, key)
42
33
  end
43
- memo
44
34
  end
45
35
  end
46
-
47
36
  end
48
37
  end
@@ -0,0 +1,52 @@
1
+ # coding: utf-8
2
+
3
+ module Confuse
4
+ class Definition
5
+ def initialize(&block)
6
+ @default_namespace = :default
7
+ block.call(self)
8
+ end
9
+
10
+ def defines?(name)
11
+ !!find_item_by_name(name)
12
+ end
13
+
14
+ def default(namespace, key)
15
+ (item = find_item(namespace, key)) && item.default
16
+ end
17
+
18
+ def add_namespace(name, &block)
19
+ new_namespace = Namespace.new(&block)
20
+ namespaces[name.to_sym] = new_namespace
21
+ end
22
+
23
+ def add_item(name, opts = {})
24
+ namespaces[@default_namespace].add_item(name, opts)
25
+ end
26
+
27
+ def namespaces
28
+ @namespaces ||= {
29
+ @default_namespace => Namespace.new(&(Proc.new {}))
30
+ }
31
+ end
32
+
33
+ def namespace_and_key(name)
34
+ KeySplitter.new(name).split.find { |(ns, _)| namespaces[ns] } ||
35
+ [nil, name]
36
+ end
37
+
38
+ private
39
+
40
+ def find_item_by_name(name)
41
+ namespace, key = namespace_and_key(name)
42
+ end
43
+
44
+ def find_item(namespace, key)
45
+ (ns = find_namespace(namespace)) && ns[key]
46
+ end
47
+
48
+ def find_namespace(name)
49
+ namespaces[name || @default_namespace]
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,12 @@
1
+ # coding: utf-8
2
+
3
+ module Confuse
4
+ module Errors
5
+ class Undefined < StandardError
6
+ def initialize(name)
7
+ @name = name
8
+ super
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,20 @@
1
+ # coding: utf-8
2
+
3
+ module Confuse
4
+ # An {Item} is a class for storing a description piece of config.
5
+ class Item
6
+ def initialize(key, opts = {})
7
+ @key = key
8
+ @default, @description = opts.values_at(:default, :description)
9
+ @required = opts.key?(:required) ? opts[:required] : true
10
+ end
11
+
12
+ attr_reader :description, :required
13
+
14
+ def default
15
+ res = @default
16
+ raise Errors::Undefined.new(@key) if @required && !res
17
+ res
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,40 @@
1
+ # coding: utf-8
2
+
3
+ module Confuse
4
+ # This class encapsulates the code required to support searching for items
5
+ # with a single key item even for nested items.
6
+ # config[:foo_bar] instead of config[:foo][:bar]
7
+ class KeySplitter
8
+ def initialize(key)
9
+ @key = key
10
+ end
11
+
12
+ def split
13
+ possible_namespaces.map do |ns|
14
+ [ns, rest_of_key(ns)]
15
+ end
16
+ end
17
+
18
+ # Returns an array of possible namespaces based on splitting the key at
19
+ # every underscore.
20
+ def possible_namespaces
21
+ namespaces = []
22
+ key = @key.to_s
23
+ while (index = key.rindex('_'))
24
+ key = key[0, index]
25
+ namespaces << key.to_sym
26
+ end
27
+ namespaces
28
+ end
29
+
30
+ # Returns the rest of the key for a given namespace
31
+ def rest_of_key(namespace)
32
+ return nil if @key == namespace
33
+
34
+ key = @key.to_s
35
+
36
+ index = key.index(namespace.to_s) && (namespace.to_s.length + 1)
37
+ key[index, key.length].to_sym if index
38
+ end
39
+ end
40
+ end
@@ -1,75 +1,22 @@
1
- require 'confuse/config_item'
1
+ # coding: utf-8
2
2
 
3
3
  module Confuse
4
4
  # A {Namespace} is a container to keep configuration data seperate from the
5
5
  # rest of the config.
6
6
  class Namespace
7
-
8
- attr_reader :items, :supress_warnings_flag, :strict_flag
7
+ attr_reader :items
9
8
 
10
9
  def initialize(&block)
11
10
  @items = {}
12
- @supress_warnings_flag = false
13
- @strict_flag = 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, config = nil)
30
- value = (i = get_item(key)) && i.value
31
- if value.respond_to?(:call) && !config.nil?
32
- value.call(config)
33
- else
34
- value
35
- end
11
+ block.call(self) if block_given?
36
12
  end
37
13
 
38
- def []=(key, value)
39
- item = get_item(key) || create_new_key(key, value)
40
- item && item.value = value
14
+ def add_item(name, opts = {})
15
+ @items[name] = Item.new(name, opts)
41
16
  end
42
17
 
43
- def keys
44
- @items.keys
45
- end
46
-
47
- def create_new_key(key, value)
48
- if @supress_warnings_flag
49
- puts "Warning: config includes unknown option '#{key}'"
50
- end
51
- @items[key] = ConfigItem.new(key, &nil) unless @strict_flag
52
- end
53
-
54
- def get_item(key)
18
+ def [](key)
55
19
  @items[key]
56
20
  end
57
-
58
- def merge!(namespace)
59
- @strict_flag = namespace.strict
60
- @supress_warnings_flag = namespace.supress_warnings
61
- @items.merge! namespace.clone.items
62
- end
63
-
64
- def clone
65
- c = super
66
- items = @items.reduce({}) do |m, (k, v)|
67
- m[k] = v.clone
68
- m
69
- end
70
- c.instance_variable_set(:"@items", items)
71
- c
72
- end
73
-
74
21
  end
75
22
  end