configit 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,4 @@
1
+ == 1.0.0 / 2009-08-05
2
+
3
+ * 1 major enhancement
4
+ * Birthday!
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
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require File.expand_path(
4
+ File.join(File.dirname(__FILE__), %w[.. lib configit]))
5
+
6
+ # Put your code here
7
+
8
+ # EOF
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