configit 0.1.0

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/.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