rabbitt-configurator 1.2.4
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 +4 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +27 -0
- data/LICENSE +339 -0
- data/README.md +216 -0
- data/configurator.gemspec +44 -0
- data/lib/configurator/cast.rb +165 -0
- data/lib/configurator/delegated.rb +112 -0
- data/lib/configurator/dsl.rb +139 -0
- data/lib/configurator/errors.rb +56 -0
- data/lib/configurator/extensions.rb +42 -0
- data/lib/configurator/loader.rb +56 -0
- data/lib/configurator/option.rb +281 -0
- data/lib/configurator/section.rb +254 -0
- data/lib/configurator/validation.rb +18 -0
- data/lib/configurator/version.rb +21 -0
- data/lib/configurator.rb +30 -0
- metadata +114 -0
@@ -0,0 +1,281 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2013 Carl P. Corliss
|
3
|
+
|
4
|
+
This program is free software; you can redistribute it and/or modify
|
5
|
+
it under the terms of the GNU General Public License as published by
|
6
|
+
the Free Software Foundation; either version 2 of the License, or
|
7
|
+
(at your option) any later version.
|
8
|
+
|
9
|
+
This program is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License along
|
15
|
+
with this program; if not, write to the Free Software Foundation, Inc.,
|
16
|
+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
17
|
+
=end
|
18
|
+
|
19
|
+
require 'pathname'
|
20
|
+
|
21
|
+
module Configurator
|
22
|
+
class Option
|
23
|
+
attr_accessor :name, :parent
|
24
|
+
attr_reader :type, :default, :caster, :validations, :required
|
25
|
+
private :validations, :required
|
26
|
+
|
27
|
+
UNDEFINED_OPTION = :__undefined__
|
28
|
+
|
29
|
+
def initialize(name, parent, options={})
|
30
|
+
@name = name.to_sym
|
31
|
+
@value = nil
|
32
|
+
@parent = parent
|
33
|
+
@guarding = false
|
34
|
+
|
35
|
+
@default = (options.delete(:default) || UNDEFINED_OPTION).freeze
|
36
|
+
@type = (type = options.delete(:type)).nil? ? compute_type(@default) : type
|
37
|
+
@caster = (cast = options.delete(:cast)).nil? ? Cast::Director[@type] : Cast::Director[cast]
|
38
|
+
|
39
|
+
@required = determine_if_required?(options)
|
40
|
+
@validations = gather_validations(options)
|
41
|
+
|
42
|
+
if options.count > 0
|
43
|
+
warn "#{path_name}: encountered unknown options: #{options.inspect}"
|
44
|
+
end
|
45
|
+
rescue StandardError => e
|
46
|
+
raise OptionInvalid.new("Failed to add option #{parent.path_name}.#{name}: #{e.class.name}: #{e.message}") { |ve|
|
47
|
+
ve.set_backtrace(e.backtrace)
|
48
|
+
}
|
49
|
+
end
|
50
|
+
|
51
|
+
def compute_type(type)
|
52
|
+
case type
|
53
|
+
when UNDEFINED_OPTION then :any
|
54
|
+
when OptionValue then type.type
|
55
|
+
when Bignum, Fixnum then :integer
|
56
|
+
when Float then :float
|
57
|
+
when Symbol then :symbol
|
58
|
+
when FalseClass, TrueClass, /(true|false|yes|no|enabled?|disabled?|on|off)/i then :boolean
|
59
|
+
when String then :string
|
60
|
+
when Pathname then :path
|
61
|
+
when URI then :uri
|
62
|
+
when Hash then :hash
|
63
|
+
when Array then
|
64
|
+
type.size <= 0 ? :array : [compute_type(type.first)]
|
65
|
+
when Proc then
|
66
|
+
with_loop_guard do
|
67
|
+
compute_type(type.call)
|
68
|
+
end rescue :any
|
69
|
+
else :any
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def value=(v)
|
74
|
+
return nil unless validate(v)
|
75
|
+
@value = v
|
76
|
+
end
|
77
|
+
|
78
|
+
def value
|
79
|
+
return nil if @value.nil? && @default == UNDEFINED_OPTION
|
80
|
+
|
81
|
+
value = (@value || @default)
|
82
|
+
|
83
|
+
begin
|
84
|
+
with_loop_guard do
|
85
|
+
if value.respond_to? :call
|
86
|
+
unless value.arity == 0
|
87
|
+
raise OptionInvalidCallableDefault, "#{path_name}: callable defaults must not accept any arguments"
|
88
|
+
end
|
89
|
+
value = value.call
|
90
|
+
end
|
91
|
+
end
|
92
|
+
rescue OptionLoopError
|
93
|
+
raise # bubble up
|
94
|
+
rescue NoMethodError => e
|
95
|
+
method = e.message.match(/undefined method .([^']+)'.+/)[1]
|
96
|
+
raise OptionInvalidCallableDefault, "#{path_name}: bad method/option name #{method.inspect} in callable default."
|
97
|
+
rescue StandardError => e
|
98
|
+
excp = OptionInvalidCallableDefault.new "#{path_name}: error executing callable default: #{e.class.name}: #{e.message}"
|
99
|
+
excp.set_backtrace(e.backtrace)
|
100
|
+
raise excp
|
101
|
+
end
|
102
|
+
|
103
|
+
@caster.convert(value)
|
104
|
+
end
|
105
|
+
|
106
|
+
def include?(data)
|
107
|
+
value.respond_to?(:include?) ? value.include?(data) : false
|
108
|
+
end
|
109
|
+
|
110
|
+
def empty?; value.nil? || value.empty?; end
|
111
|
+
def valid?; validate(value); end
|
112
|
+
def required?; !!@required; end
|
113
|
+
def optional?; !required?; end
|
114
|
+
def path_name; [ parent.path_name, name ].join('.'); end
|
115
|
+
def deprecated?; false; end
|
116
|
+
def renamed?; false; end
|
117
|
+
|
118
|
+
private
|
119
|
+
|
120
|
+
def determine_if_required?(options)
|
121
|
+
if options.key?(:required) && options.key?(:optional)
|
122
|
+
unless !!options[:required] != !!options[:optional]
|
123
|
+
raise OptionInvalidArgument, "#{path_name}: can't be both required and optional at the same time!"
|
124
|
+
else
|
125
|
+
options.delete(:optional)
|
126
|
+
!!options.delete(:required)
|
127
|
+
end
|
128
|
+
elsif options.key?(:required)
|
129
|
+
!!options.delete(:required)
|
130
|
+
elsif options.key?(:optional)
|
131
|
+
not !!options.delete(:optional)
|
132
|
+
else
|
133
|
+
# if there's no default, require option
|
134
|
+
default == :__undefined__ ? true : false
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def gather_validations(options)
|
139
|
+
# XXX: create Validation classes (Expection would derive from Validation) and
|
140
|
+
# move all validation logic into those classes
|
141
|
+
[].tap { |validations|
|
142
|
+
validation = (v = options.delete(:validate)).nil? ? true : v
|
143
|
+
validate_msg = options.delete(:validate_message)
|
144
|
+
|
145
|
+
expectations = options.delete(:expect)
|
146
|
+
expect_msg = options.delete(:expect_messgae)
|
147
|
+
|
148
|
+
type_validator = options.delete(:type_validator)
|
149
|
+
type_validator_msg = options.delete(:type_validation_message)
|
150
|
+
|
151
|
+
if !validation
|
152
|
+
if expectations
|
153
|
+
raise OptionInvalidArgument, "#{path_name}: can't disable validations and set an expectation at the same time!"
|
154
|
+
elsif type_validator
|
155
|
+
raise OptionInvalidArgument, "#{path_name}: can't disable validations and assign a type validator at the same time!"
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
return [] unless validation
|
160
|
+
|
161
|
+
if type_validator
|
162
|
+
validations << lambda { |_value|
|
163
|
+
unless type_validator.call(_value)
|
164
|
+
if type_validator_msg
|
165
|
+
raise ValidationError, "#{path_name}: #{_value.inspect} fails to validate as custom type: #{type_validator_msg}"
|
166
|
+
else
|
167
|
+
raise ValidationError, "#{path_name}: #{_value.inspect} fails to validate as custom type."
|
168
|
+
end
|
169
|
+
end
|
170
|
+
true
|
171
|
+
}
|
172
|
+
else
|
173
|
+
validations << lambda { |_value|
|
174
|
+
unless validate_type(_value)
|
175
|
+
raise ValidationError, "#{path_name}: #{_value.inspect} fails to validate as #{type.inspect}"
|
176
|
+
end
|
177
|
+
true
|
178
|
+
}
|
179
|
+
end
|
180
|
+
|
181
|
+
validations << lambda { |_value|
|
182
|
+
unless validation.call(_value)
|
183
|
+
if validate_msg
|
184
|
+
raise ValidationError, "#{path_name}: #{_value.inspect} fails custom validation rule: #{validate_msg}"
|
185
|
+
else
|
186
|
+
raise ValidationError, "#{path_name}: #{_value.inspect} fails custom validation rule"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
true
|
190
|
+
} if validation.respond_to?(:call)
|
191
|
+
|
192
|
+
unless expectations.nil?
|
193
|
+
if expectations.respond_to? :call
|
194
|
+
validations << lambda { |_value|
|
195
|
+
unless expectations.call(_value)
|
196
|
+
if expect_msg
|
197
|
+
raise ValidationError, "#{path_name}: #{_value.inspect} fails custom expectation: #{expect_msg}"
|
198
|
+
else
|
199
|
+
raise ValidationError, "#{path_name}: #{_value.inspect} fails custom expectation"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
true
|
203
|
+
}
|
204
|
+
else
|
205
|
+
validations << lambda { |_value|
|
206
|
+
unless expectations.include?(_value)
|
207
|
+
raise ValidationError, "#{path_name}: Failed expectation: #{_value.inspect} not in list: #{expectations.collect(&:inspect).join(', ')}"
|
208
|
+
end
|
209
|
+
true
|
210
|
+
}
|
211
|
+
end
|
212
|
+
end
|
213
|
+
}
|
214
|
+
end
|
215
|
+
|
216
|
+
def validate(_value)
|
217
|
+
return true if type == :any && validations.empty?
|
218
|
+
|
219
|
+
begin
|
220
|
+
# try on just the raw value first
|
221
|
+
validations.all? { |validation| validation.call(_value.freeze) }
|
222
|
+
rescue StandardError => initial_exception
|
223
|
+
begin
|
224
|
+
# now try on the converted value
|
225
|
+
cast_value = @caster.convert(_value)
|
226
|
+
validations.all? { |validation| validation.call(cast_value) }
|
227
|
+
rescue ValidationError => e
|
228
|
+
raise ValidationError.new(e.message).tap {|ve| ve.set_backtrace(initial_exception.backtrace) }
|
229
|
+
rescue CastError
|
230
|
+
raise initial_exception
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
def validate_type(_value, validation_type = nil)
|
236
|
+
validation_type ||= type
|
237
|
+
|
238
|
+
case validation_type
|
239
|
+
when :any; true
|
240
|
+
when Array then
|
241
|
+
return _value.is_a?(Array) if validation_type.empty?
|
242
|
+
[*_value].flatten.all? { |v|
|
243
|
+
validate_type(v, validation_type.first)
|
244
|
+
}
|
245
|
+
when :scalar then
|
246
|
+
validate_type(_value, :integer) || validate_type(_value, :float) ||
|
247
|
+
validate_type(_value, :symbol) || validate_type(_value, :string) ||
|
248
|
+
validate_type(_value, :boolean)
|
249
|
+
when :boolean then
|
250
|
+
_value.is_a?(FalseClass) || _value.is_a?(TrueClass)
|
251
|
+
when :float then
|
252
|
+
((Float(_value) rescue nil) == _value.to_f)
|
253
|
+
when :integer then
|
254
|
+
((Float(_value).to_i rescue nil) == _value.to_i)
|
255
|
+
when :path then
|
256
|
+
_value.is_a?(Pathname)
|
257
|
+
when :array then _value.is_a?(Array)
|
258
|
+
when :hash then _value.is_a?(Hash)
|
259
|
+
when :string then _value.is_a? String
|
260
|
+
when :symbol then _value.is_a? Symbol
|
261
|
+
when :uri then !!(URI.parse(_value) rescue false)
|
262
|
+
else
|
263
|
+
warn "unable to validate - no handler for type: #{type.inspect}"
|
264
|
+
true # assume valid
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
268
|
+
def with_loop_guard(&block)
|
269
|
+
begin
|
270
|
+
raise OptionLoopError if @guarding
|
271
|
+
@guarding = true
|
272
|
+
yield
|
273
|
+
rescue OptionLoopError => error
|
274
|
+
raise error.tap { |e| e.stack << path_name }
|
275
|
+
ensure
|
276
|
+
@guarding = false
|
277
|
+
end
|
278
|
+
end
|
279
|
+
|
280
|
+
end
|
281
|
+
end
|
@@ -0,0 +1,254 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2013 Carl P. Corliss
|
3
|
+
|
4
|
+
This program is free software; you can redistribute it and/or modify
|
5
|
+
it under the terms of the GNU General Public License as published by
|
6
|
+
the Free Software Foundation; either version 2 of the License, or
|
7
|
+
(at your option) any later version.
|
8
|
+
|
9
|
+
This program is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License along
|
15
|
+
with this program; if not, write to the Free Software Foundation, Inc.,
|
16
|
+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
17
|
+
=end
|
18
|
+
|
19
|
+
module Configurator
|
20
|
+
class Section
|
21
|
+
attr_accessor :name, :parent
|
22
|
+
attr_reader :table
|
23
|
+
|
24
|
+
def initialize(name, parent = nil, options = {})
|
25
|
+
@table = {}
|
26
|
+
@name = name
|
27
|
+
@parent = parent
|
28
|
+
|
29
|
+
load options
|
30
|
+
end
|
31
|
+
|
32
|
+
def type; :section; end
|
33
|
+
def deprecated?; false; end
|
34
|
+
def renamed?; false; end
|
35
|
+
|
36
|
+
def root; parent.nil? ? self : parent.root; end
|
37
|
+
def path_name; parent.nil? ? name : [ parent.path_name, name ].join('.'); end
|
38
|
+
def required?; options.any? { |k,o| o.required? }; end
|
39
|
+
def optional?; !required?; end
|
40
|
+
|
41
|
+
def include?(option_name)
|
42
|
+
@table.key? option_name.to_sym
|
43
|
+
end
|
44
|
+
|
45
|
+
def [](option_name)
|
46
|
+
@table[option_name.to_sym]
|
47
|
+
end
|
48
|
+
|
49
|
+
def []=(option_name, value)
|
50
|
+
@table[option_name.to_sym].value = value
|
51
|
+
end
|
52
|
+
|
53
|
+
def each(&block)
|
54
|
+
@table.each &block
|
55
|
+
end
|
56
|
+
|
57
|
+
def inject(*args, &block)
|
58
|
+
@table.inject(*args, &block)
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_h
|
62
|
+
inject({}) { |hash,(_name,_option)|
|
63
|
+
hash.tap {|h|
|
64
|
+
unless _option.deprecated? || _option.renamed?
|
65
|
+
h[_name] = _option.to_h rescue _option.value
|
66
|
+
end
|
67
|
+
}
|
68
|
+
}
|
69
|
+
end
|
70
|
+
|
71
|
+
def config() self; end
|
72
|
+
alias :value :config
|
73
|
+
|
74
|
+
def load(data)
|
75
|
+
unless data.is_a? Hash
|
76
|
+
warn "#{path_name}: invalid load data for section (#{data.inspect}) - skipping..."
|
77
|
+
else
|
78
|
+
data.each { |key,value|
|
79
|
+
if not @table.key? key.to_sym
|
80
|
+
warn "#{path_name}: unable to load data for unknown key #{key.inspect} -> #{value.inspect}"
|
81
|
+
next
|
82
|
+
end
|
83
|
+
@table[key.to_sym].value = value
|
84
|
+
}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
alias :value= :load
|
88
|
+
|
89
|
+
def requirements_fullfilled?
|
90
|
+
@table.collect { |k,v|
|
91
|
+
if v.respond_to? :requirements_fullfilled?
|
92
|
+
v.requirements_fullfilled?
|
93
|
+
else
|
94
|
+
next true unless v.required? && v.value.nil? && !(v.deprecated? rescue false)
|
95
|
+
warn "#{v.path_name}: option required but nil value."
|
96
|
+
false
|
97
|
+
end
|
98
|
+
}
|
99
|
+
end
|
100
|
+
|
101
|
+
def option(option_name, *args)
|
102
|
+
_options = args.last.is_a?(Hash) ? args.pop : {}
|
103
|
+
_options.merge!(:type => args.first)
|
104
|
+
|
105
|
+
option_name = option_name.to_sym
|
106
|
+
deprecated = _options.delete(:deprecated)
|
107
|
+
|
108
|
+
option = Option.new(option_name, self, _options)
|
109
|
+
|
110
|
+
if deprecated
|
111
|
+
option = DelegatedOption::Deprecated.new(
|
112
|
+
option.name, option.parent, option, end_of_life
|
113
|
+
)
|
114
|
+
end
|
115
|
+
|
116
|
+
add_option option_name, option
|
117
|
+
end
|
118
|
+
|
119
|
+
def options(*names)
|
120
|
+
names.each do |option_name|
|
121
|
+
option option_name
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def section(option_name, options = {}, &block)
|
126
|
+
option_name = option_name.to_sym
|
127
|
+
deprecated = options.delete(:deprecated)
|
128
|
+
|
129
|
+
section = Section.new(option_name, self).tap { |s|
|
130
|
+
s.instance_eval(&block) if block_given?
|
131
|
+
}
|
132
|
+
|
133
|
+
if deprecated
|
134
|
+
section = DelegatedOption::Deprecated.new(
|
135
|
+
section.name, section.parent, section, end_of_life
|
136
|
+
)
|
137
|
+
end
|
138
|
+
|
139
|
+
add_option(option_name, section)
|
140
|
+
end
|
141
|
+
|
142
|
+
def alias!(orig_path, new_path)
|
143
|
+
orig_path = "root.#{orig_path}" unless orig_path.include? 'root.'
|
144
|
+
new_path = "root.#{new_path}" unless new_path.include? 'root.'
|
145
|
+
|
146
|
+
unless _option = root.get_path(orig_path)
|
147
|
+
raise DeprecateFailed, "Unable to alias #{new_path} to #{orig_path} - option does not appear to be defined."
|
148
|
+
end
|
149
|
+
|
150
|
+
_option = root.get_path(orig_path)
|
151
|
+
_parent, _name = new_path.option_path_split
|
152
|
+
|
153
|
+
_parent = root.get_path(_parent)
|
154
|
+
new_option = AliasedOption.new(_name, _parent, _option)
|
155
|
+
_parent.add_option(_name, new_option)
|
156
|
+
end
|
157
|
+
alias :aliased! :alias!
|
158
|
+
|
159
|
+
def deprecate!(option_paths, end_of_life = nil)
|
160
|
+
[*option_paths].collect {|option_path|
|
161
|
+
option_path = "root.#{option_path}" unless option_path.include? 'root.'
|
162
|
+
|
163
|
+
unless _option = root.get_path(option_path)
|
164
|
+
raise DeprecateFailed, "Unable to deprecated #{option_path} - option does not appear to be defined."
|
165
|
+
end
|
166
|
+
|
167
|
+
_option = DeprecatedOption.new(_option.name, _option.parent, _option, end_of_life)
|
168
|
+
_option.parent.replace_option(_option.name, _option)
|
169
|
+
}
|
170
|
+
end
|
171
|
+
alias :deprecated! :deprecate!
|
172
|
+
|
173
|
+
# like alias but with reversed arguments and a warning on assignment
|
174
|
+
# note: new path must already exist. old_path is created as an alias
|
175
|
+
# to new_path.
|
176
|
+
def rename!(old_path, target_path)
|
177
|
+
old_path = "root.#{old_path}" unless old_path.include? 'root.'
|
178
|
+
target_path = "root.#{target_path}" unless target_path.include? 'root.'
|
179
|
+
|
180
|
+
unless _option = root.get_path(target_path)
|
181
|
+
raise OptionNotExist, "option #{target_path} does not exist - target path must exist for rename."
|
182
|
+
end
|
183
|
+
|
184
|
+
_parent, _name = old_path.option_path_split
|
185
|
+
_section = root.get_path(_parent)
|
186
|
+
|
187
|
+
renamed_option = RenamedOption.new(_name, _section, _option)
|
188
|
+
|
189
|
+
begin
|
190
|
+
_section.add_option(_name, renamed_option)
|
191
|
+
rescue OptionExists => e
|
192
|
+
raise RenameFailed, "Unable to rename #{old_path} -> #{target_path}"
|
193
|
+
end
|
194
|
+
end
|
195
|
+
alias :renamed! :rename!
|
196
|
+
alias :moved! :rename!
|
197
|
+
|
198
|
+
def add_option(option_name, object)
|
199
|
+
option_name = option_name.to_sym
|
200
|
+
if @table.key? option_name
|
201
|
+
raise OptionExists, "Option #{path_name}.#{option_name} already exists"
|
202
|
+
end
|
203
|
+
|
204
|
+
@table[option_name] = object.tap {
|
205
|
+
self.class.class_eval(<<-EOF, __FILE__, __LINE__ + 1)
|
206
|
+
def #{option_name}()
|
207
|
+
OptionValue.new(@table[#{option_name.inspect}])
|
208
|
+
end
|
209
|
+
|
210
|
+
def #{option_name}=(_value)
|
211
|
+
@table[#{option_name.inspect}].value = _value
|
212
|
+
end
|
213
|
+
EOF
|
214
|
+
}
|
215
|
+
end
|
216
|
+
|
217
|
+
def replace_option(name, new_option)
|
218
|
+
name = name.to_sym
|
219
|
+
unless @table.include? name
|
220
|
+
raise OptionNotExist, "#{path_name}.#{name} doesn't exist"
|
221
|
+
end
|
222
|
+
@table[name] = new_option
|
223
|
+
end
|
224
|
+
|
225
|
+
def get_path(path)
|
226
|
+
begin
|
227
|
+
# remove the root - we start there anyway
|
228
|
+
path.gsub!(/^root\.?/, '')
|
229
|
+
current_path = [:root]
|
230
|
+
|
231
|
+
path.split('.').collect(&:to_sym).inject(root) do |option, path_component|
|
232
|
+
current_path << path_component
|
233
|
+
unless option.include? path_component
|
234
|
+
raise InvalidOptionPath, "#{current_path.join('.')}: doesn't exist in the current configuration."
|
235
|
+
else
|
236
|
+
option = option[path_component]
|
237
|
+
end
|
238
|
+
end
|
239
|
+
rescue StandardError => e
|
240
|
+
warn "#{e.class.name}: #{e.message}\n\t#{e.backtrace.join("\n\t")}"
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
alias :original_respond_to? :respond_to?
|
245
|
+
def respond_to?(method, include_private = false)
|
246
|
+
@table.key?(method) || original_respond_to?(method, include_private)
|
247
|
+
end
|
248
|
+
|
249
|
+
def method_missing(method, *args, &block)
|
250
|
+
return super unless respond_to?(method)
|
251
|
+
self[method] if include?(method)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2013 Carl P. Corliss
|
3
|
+
|
4
|
+
This program is free software; you can redistribute it and/or modify
|
5
|
+
it under the terms of the GNU General Public License as published by
|
6
|
+
the Free Software Foundation; either version 2 of the License, or
|
7
|
+
(at your option) any later version.
|
8
|
+
|
9
|
+
This program is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License along
|
15
|
+
with this program; if not, write to the Free Software Foundation, Inc.,
|
16
|
+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
17
|
+
=end
|
18
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2013 Carl P. Corliss
|
3
|
+
|
4
|
+
This program is free software; you can redistribute it and/or modify
|
5
|
+
it under the terms of the GNU General Public License as published by
|
6
|
+
the Free Software Foundation; either version 2 of the License, or
|
7
|
+
(at your option) any later version.
|
8
|
+
|
9
|
+
This program is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License along
|
15
|
+
with this program; if not, write to the Free Software Foundation, Inc.,
|
16
|
+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
17
|
+
=end
|
18
|
+
|
19
|
+
module Configurator
|
20
|
+
VERSION = "1.2.4"
|
21
|
+
end
|
data/lib/configurator.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
=begin
|
2
|
+
Copyright (C) 2013 Carl P. Corliss
|
3
|
+
|
4
|
+
This program is free software; you can redistribute it and/or modify
|
5
|
+
it under the terms of the GNU General Public License as published by
|
6
|
+
the Free Software Foundation; either version 2 of the License, or
|
7
|
+
(at your option) any later version.
|
8
|
+
|
9
|
+
This program is distributed in the hope that it will be useful,
|
10
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
11
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
12
|
+
GNU General Public License for more details.
|
13
|
+
|
14
|
+
You should have received a copy of the GNU General Public License along
|
15
|
+
with this program; if not, write to the Free Software Foundation, Inc.,
|
16
|
+
51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
17
|
+
=end
|
18
|
+
|
19
|
+
require 'configurator/errors'
|
20
|
+
require 'configurator/dsl'
|
21
|
+
require 'configurator/extensions'
|
22
|
+
require 'configurator/delegated'
|
23
|
+
|
24
|
+
module Configurator
|
25
|
+
autoload :Loader, 'configurator/loader'
|
26
|
+
autoload :Section, 'configurator/section'
|
27
|
+
autoload :Option, 'configurator/option'
|
28
|
+
autoload :Cast, 'configurator/cast'
|
29
|
+
autoload :VERSION, 'configurator/version'
|
30
|
+
end
|