configit 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.DS_Store +0 -0
- data/.bnsignore +16 -0
- data/History.txt +4 -0
- data/README.txt +77 -0
- data/Rakefile +31 -0
- data/bin/configit +8 -0
- data/configit.gemspec +37 -0
- data/lib/configit.rb +51 -0
- data/lib/configit/attribute_definition.rb +38 -0
- data/lib/configit/base.rb +168 -0
- data/lib/configit/exceptions.rb +5 -0
- data/spec/configit/base_spec.rb +222 -0
- data/spec/configit_spec.rb +7 -0
- data/spec/spec.opts +7 -0
- data/spec/spec_helper.rb +16 -0
- data/tasks/ann.rake +80 -0
- data/tasks/bones.rake +20 -0
- data/tasks/gem.rake +201 -0
- data/tasks/git.rake +40 -0
- data/tasks/notes.rake +27 -0
- data/tasks/post_load.rake +34 -0
- data/tasks/rdoc.rake +51 -0
- data/tasks/rubyforge.rake +55 -0
- data/tasks/setup.rb +292 -0
- data/tasks/spec.rake +54 -0
- data/tasks/svn.rake +47 -0
- data/tasks/test.rake +40 -0
- data/tasks/zentest.rake +36 -0
- data/test/test_configit.rb +0 -0
- metadata +97 -0
data/.DS_Store
ADDED
Binary file
|
data/.bnsignore
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# The list of files that should be ignored by Mr Bones.
|
2
|
+
# Lines that start with '#' are comments.
|
3
|
+
#
|
4
|
+
# A .gitignore file can be used instead by setting it as the ignore
|
5
|
+
# file in your Rakefile:
|
6
|
+
#
|
7
|
+
# PROJ.ignore_file = '.gitignore'
|
8
|
+
#
|
9
|
+
# For a project with a C extension, the following would be a good set of
|
10
|
+
# exclude patterns (uncomment them if you want to use them):
|
11
|
+
# *.[oa]
|
12
|
+
# *~
|
13
|
+
announcement.txt
|
14
|
+
coverage
|
15
|
+
doc
|
16
|
+
pkg
|
data/History.txt
ADDED
data/README.txt
ADDED
@@ -0,0 +1,77 @@
|
|
1
|
+
configit
|
2
|
+
by Kris Rasmussen
|
3
|
+
http://www.aptana.com
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
The purpose of this gem is to make it very easy to consume configuration files
|
8
|
+
and define them.
|
9
|
+
|
10
|
+
== FEATURES/PROBLEMS:
|
11
|
+
|
12
|
+
* Easilly define config attributes directly on the class
|
13
|
+
* Provides validation of configs
|
14
|
+
* Automatically converts attributes to the correct type
|
15
|
+
* Enables you to access attribute values as attributes on the class instance
|
16
|
+
* Load config from files, strings, or IO
|
17
|
+
* Can turn on or off ERB evaluation before loading from yaml
|
18
|
+
* Treats strings / symbol keys as equivalent from yaml config
|
19
|
+
|
20
|
+
== SYNOPSIS:
|
21
|
+
|
22
|
+
class MyConfig << Configit::Base
|
23
|
+
attribute :foo, :required => true
|
24
|
+
attribute :bar, "Some description", :type => :integer, :default => 10
|
25
|
+
attribute :log_level, "Set the log level", :default => :debug, :type => :symbol
|
26
|
+
end
|
27
|
+
|
28
|
+
config = MyConfig.load_from_file("/etc/myconfig")
|
29
|
+
|
30
|
+
config.foo
|
31
|
+
config.bar
|
32
|
+
config.log_level
|
33
|
+
|
34
|
+
config.foo = "new value"
|
35
|
+
|
36
|
+
config.valid # true or false
|
37
|
+
config.errors.each do |error|
|
38
|
+
puts error.message
|
39
|
+
puts error.attribute.name
|
40
|
+
end
|
41
|
+
|
42
|
+
== REQUIREMENTS:
|
43
|
+
|
44
|
+
== TODO:
|
45
|
+
|
46
|
+
* Implement type validation
|
47
|
+
* Enable easy printing of errors
|
48
|
+
* Add colorization to error messages
|
49
|
+
|
50
|
+
== INSTALL:
|
51
|
+
|
52
|
+
sudo gem install krisr-configit --source gems.github.com
|
53
|
+
|
54
|
+
== LICENSE:
|
55
|
+
|
56
|
+
(The MIT License)
|
57
|
+
|
58
|
+
Copyright (c) 2009 Kris Rasmussen
|
59
|
+
|
60
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
61
|
+
a copy of this software and associated documentation files (the
|
62
|
+
'Software'), to deal in the Software without restriction, including
|
63
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
64
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
65
|
+
permit persons to whom the Software is furnished to do so, subject to
|
66
|
+
the following conditions:
|
67
|
+
|
68
|
+
The above copyright notice and this permission notice shall be
|
69
|
+
included in all copies or substantial portions of the Software.
|
70
|
+
|
71
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
72
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
73
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
74
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
75
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
76
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
77
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Look in the tasks/setup.rb file for the various options that can be
|
2
|
+
# configured in this Rakefile. The .rake files in the tasks directory
|
3
|
+
# are where the options are used.
|
4
|
+
|
5
|
+
begin
|
6
|
+
require 'bones'
|
7
|
+
Bones.setup
|
8
|
+
rescue LoadError
|
9
|
+
begin
|
10
|
+
load 'tasks/setup.rb'
|
11
|
+
rescue LoadError
|
12
|
+
raise RuntimeError, '### please install the "bones" gem ###'
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
ensure_in_path 'lib'
|
17
|
+
require 'configit'
|
18
|
+
|
19
|
+
task :default => 'spec:run'
|
20
|
+
|
21
|
+
PROJ.name = 'configit'
|
22
|
+
PROJ.authors = 'Kris Rasmussen'
|
23
|
+
PROJ.email = 'kris@aptana.com'
|
24
|
+
PROJ.url = 'http://www.aptana.com'
|
25
|
+
PROJ.version = Configit::VERSION
|
26
|
+
PROJ.rubyforge.name = 'configit'
|
27
|
+
PROJ.ignore_file = '.gitignore'
|
28
|
+
|
29
|
+
PROJ.spec.opts << '--color'
|
30
|
+
|
31
|
+
# EOF
|
data/bin/configit
ADDED
data/configit.gemspec
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{configit}
|
5
|
+
s.version = "0.1.0"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Kris Rasmussen"]
|
9
|
+
s.date = %q{2009-10-08}
|
10
|
+
s.default_executable = %q{configit}
|
11
|
+
s.description = %q{The purpose of this gem is to make it very easy to consume configuration files
|
12
|
+
and define them.}
|
13
|
+
s.email = %q{kris@aptana.com}
|
14
|
+
s.executables = ["configit"]
|
15
|
+
s.extra_rdoc_files = ["History.txt", "README.txt", "bin/configit"]
|
16
|
+
s.files = [".DS_Store", ".bnsignore", "History.txt", "README.txt", "Rakefile", "bin/configit", "lib/configit.rb", "lib/configit/attribute_definition.rb", "lib/configit/base.rb", "lib/configit/exceptions.rb", "spec/configit/base_spec.rb", "spec/configit_spec.rb", "spec/spec.opts", "spec/spec_helper.rb", "test/test_configit.rb"]
|
17
|
+
s.homepage = %q{http://www.aptana.com}
|
18
|
+
s.rdoc_options = ["--main", "README.txt"]
|
19
|
+
s.require_paths = ["lib"]
|
20
|
+
s.rubyforge_project = %q{configit}
|
21
|
+
s.rubygems_version = %q{1.3.5}
|
22
|
+
s.summary = %q{The purpose of this gem is to make it very easy to consume configuration files and define them}
|
23
|
+
s.test_files = ["test/test_configit.rb"]
|
24
|
+
|
25
|
+
if s.respond_to? :specification_version then
|
26
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
27
|
+
s.specification_version = 3
|
28
|
+
|
29
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
30
|
+
s.add_development_dependency(%q<bones>, [">= 2.5.1"])
|
31
|
+
else
|
32
|
+
s.add_dependency(%q<bones>, [">= 2.5.1"])
|
33
|
+
end
|
34
|
+
else
|
35
|
+
s.add_dependency(%q<bones>, [">= 2.5.1"])
|
36
|
+
end
|
37
|
+
end
|
data/lib/configit.rb
ADDED
@@ -0,0 +1,51 @@
|
|
1
|
+
|
2
|
+
module Configit
|
3
|
+
|
4
|
+
# :stopdoc:
|
5
|
+
VERSION = '0.1.0'
|
6
|
+
LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
|
7
|
+
PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
|
8
|
+
# :startdoc:
|
9
|
+
|
10
|
+
# Returns the version string for the library.
|
11
|
+
#
|
12
|
+
def self.version
|
13
|
+
VERSION
|
14
|
+
end
|
15
|
+
|
16
|
+
# Returns the library path for the module. If any arguments are given,
|
17
|
+
# they will be joined to the end of the libray path using
|
18
|
+
# <tt>File.join</tt>.
|
19
|
+
#
|
20
|
+
def self.libpath( *args )
|
21
|
+
args.empty? ? LIBPATH : ::File.join(LIBPATH, args.flatten)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Returns the lpath for the module. If any arguments are given,
|
25
|
+
# they will be joined to the end of the path using
|
26
|
+
# <tt>File.join</tt>.
|
27
|
+
#
|
28
|
+
def self.path( *args )
|
29
|
+
args.empty? ? PATH : ::File.join(PATH, args.flatten)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Utility method used to require all files ending in .rb that lie in the
|
33
|
+
# directory below this file that has the same name as the filename passed
|
34
|
+
# in. Optionally, a specific _directory_ name can be passed in such that
|
35
|
+
# the _filename_ does not have to be equivalent to the directory.
|
36
|
+
#
|
37
|
+
def self.require_all_libs_relative_to( fname, dir = nil )
|
38
|
+
dir ||= ::File.basename(fname, '.*')
|
39
|
+
search_me = ::File.expand_path(
|
40
|
+
::File.join(::File.dirname(fname), dir, '**', '*.rb'))
|
41
|
+
|
42
|
+
Dir.glob(search_me).sort.each {|rb| require rb}
|
43
|
+
end
|
44
|
+
|
45
|
+
end # module Configit
|
46
|
+
|
47
|
+
require Configit.libpath('configit/exceptions')
|
48
|
+
require Configit.libpath('configit/attribute_definition')
|
49
|
+
require Configit.libpath('configit/base')
|
50
|
+
|
51
|
+
# EOF
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Configit
|
2
|
+
# The definition of an attribute in a config.
|
3
|
+
class AttributeDefinition
|
4
|
+
attr_reader :name
|
5
|
+
attr_reader :desc
|
6
|
+
attr_reader :default
|
7
|
+
attr_reader :type
|
8
|
+
|
9
|
+
# See Configit::Base.attribute
|
10
|
+
def initialize(name, desc, options={})
|
11
|
+
# Clone them so we can delete from the hash
|
12
|
+
options = options.clone
|
13
|
+
|
14
|
+
raise ArgumentError, "Name must be a symbol" if not Symbol === name
|
15
|
+
|
16
|
+
@name = name
|
17
|
+
@desc = desc
|
18
|
+
@required = options.delete(:required) || false
|
19
|
+
@type = options.delete(:type) || :string
|
20
|
+
@default = options.delete(:default)
|
21
|
+
|
22
|
+
if options.any?
|
23
|
+
raise ArgumentError, "Invalid options #{options.keys.join(',')}"
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def required?
|
28
|
+
@required
|
29
|
+
end
|
30
|
+
|
31
|
+
# Returns an error string if the value is not valid per this AttributeDefinition
|
32
|
+
def validate(value)
|
33
|
+
return "#{name} is a required attribute" if required? && value == nil || value == ""
|
34
|
+
# TODO: add type validation here
|
35
|
+
nil
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,168 @@
|
|
1
|
+
require 'erb'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Configit
|
5
|
+
# The base class the custom configuration classes should derive from.
|
6
|
+
#
|
7
|
+
# === Example
|
8
|
+
#
|
9
|
+
# class FooConfig < Configit::Base
|
10
|
+
# attribute :name, "The name of the user", :required => true
|
11
|
+
# attribute :port, :required => true, :type => :integer, :default => 80
|
12
|
+
# attribute :log_level, :type => :symbol, :default => :debug
|
13
|
+
# end
|
14
|
+
class Base
|
15
|
+
@@converters ||= {
|
16
|
+
:string => lambda {|v| v.to_s},
|
17
|
+
:integer => lambda {|v| v.to_i},
|
18
|
+
:float => lambda {|v| v.to_f},
|
19
|
+
:symbol => lambda {|v| v.to_sym}
|
20
|
+
}
|
21
|
+
|
22
|
+
# Returns the attributes defined for this class.
|
23
|
+
def attributes
|
24
|
+
@attributes ||= {}
|
25
|
+
end
|
26
|
+
|
27
|
+
def errors
|
28
|
+
@errors ||= []
|
29
|
+
end
|
30
|
+
|
31
|
+
def clear_errors
|
32
|
+
@errors = []
|
33
|
+
end
|
34
|
+
|
35
|
+
def ensure_valid!
|
36
|
+
if !valid?
|
37
|
+
message = "#{self.class.name} config invalid. #{self.errors.first}"
|
38
|
+
raise ArgumentError, message
|
39
|
+
end
|
40
|
+
true
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns true if there are no errors, false otherwise
|
44
|
+
def valid?
|
45
|
+
clear_errors
|
46
|
+
|
47
|
+
unknown_attributes = attributes.keys - schema.keys
|
48
|
+
unknown_attributes.each do |key|
|
49
|
+
errors << "#{key} is not a valid attribute name"
|
50
|
+
end
|
51
|
+
|
52
|
+
schema.values.each do |attribute|
|
53
|
+
if error = attribute.validate(attributes[attribute.name])
|
54
|
+
errors << error
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
errors.empty?
|
59
|
+
end
|
60
|
+
|
61
|
+
def schema
|
62
|
+
self.class.schema
|
63
|
+
end
|
64
|
+
|
65
|
+
class << self
|
66
|
+
# Returns a hash of Configit::AttributeDefinition's keyed by attribute name.
|
67
|
+
def schema
|
68
|
+
@schema ||= {}
|
69
|
+
end
|
70
|
+
|
71
|
+
def evaluate_erb=(value)
|
72
|
+
raise ArgumentError unless value == true || value == false
|
73
|
+
@evaluate_erb = value
|
74
|
+
end
|
75
|
+
|
76
|
+
# Loads the config from a YAML string.
|
77
|
+
#
|
78
|
+
# Unrecognized attributes are placed into the errors list.
|
79
|
+
def load_from_string(string)
|
80
|
+
config = self.new
|
81
|
+
string = ERB.new(string).result unless @evaluate_erb == false
|
82
|
+
(YAML.load(string) || {}).each do |key,value|
|
83
|
+
key = key.to_sym
|
84
|
+
config.attributes[key] = value
|
85
|
+
end
|
86
|
+
return config
|
87
|
+
end
|
88
|
+
|
89
|
+
# Load the config from a file.
|
90
|
+
def load_from_file(filename)
|
91
|
+
raise ArgumentError, "File #{filename} does not exist" unless File.exists?(filename)
|
92
|
+
raise ArgumentError, "File #{filename} is not readable" unless File.readable?(filename)
|
93
|
+
|
94
|
+
return load_from_string(IO.read(filename))
|
95
|
+
end
|
96
|
+
|
97
|
+
# Same as load_from_string except it will raise an ArgumentError if the
|
98
|
+
# config is not valid
|
99
|
+
def load_from_string!(string)
|
100
|
+
config = load_from_string(string)
|
101
|
+
config.ensure_valid!
|
102
|
+
config
|
103
|
+
end
|
104
|
+
|
105
|
+
# Same as load_from_file except it will raise an ArgumentError if the
|
106
|
+
# config is not valid.
|
107
|
+
def load_from_file!(filename)
|
108
|
+
config = load_from_file(filename)
|
109
|
+
config.ensure_valid!
|
110
|
+
config
|
111
|
+
end
|
112
|
+
|
113
|
+
# Defines a new attribute on the config.
|
114
|
+
#
|
115
|
+
# The first argument should be the name of the attribute.
|
116
|
+
#
|
117
|
+
# If the next argument is a string it will be interpreted as the
|
118
|
+
# description of the argument.
|
119
|
+
#
|
120
|
+
# The last argument should be a valid options hash.
|
121
|
+
#
|
122
|
+
# === Valid options
|
123
|
+
#
|
124
|
+
# [:required]
|
125
|
+
# Determines if the option is required or not. Should be either
|
126
|
+
# true or false
|
127
|
+
# [:type]
|
128
|
+
# The type of the attribute. Should be one of :integer, :string
|
129
|
+
# :symbol, :float
|
130
|
+
def attribute(name, desc=nil, options={})
|
131
|
+
raise AttributeAlreadyDefined, name if schema.has_key? name
|
132
|
+
|
133
|
+
if options == {} && Hash === desc
|
134
|
+
options = desc
|
135
|
+
desc = nil
|
136
|
+
end
|
137
|
+
|
138
|
+
attr = AttributeDefinition.new(name, desc, options)
|
139
|
+
schema[name] = attr
|
140
|
+
|
141
|
+
|
142
|
+
@attribute_module ||= begin
|
143
|
+
m = Module.new
|
144
|
+
include m
|
145
|
+
m
|
146
|
+
end
|
147
|
+
|
148
|
+
@attribute_module.class_eval do
|
149
|
+
define_method name do
|
150
|
+
value = attributes[name]
|
151
|
+
if value != nil
|
152
|
+
@@converters[attr.type].call(value)
|
153
|
+
else
|
154
|
+
value
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
define_method "#{name}=" do |value|
|
159
|
+
attributes[name] = value
|
160
|
+
value
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
return attr
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|