rabbitt-configurator 1.2.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|