configurable 0.7.0 → 1.0.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/Help/Command Line.rdoc +141 -0
- data/Help/Config Syntax.rdoc +229 -0
- data/Help/Config Types.rdoc +143 -0
- data/{History → History.rdoc} +9 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +144 -0
- data/lib/configurable.rb +7 -270
- data/lib/configurable/class_methods.rb +344 -367
- data/lib/configurable/config_classes.rb +3 -0
- data/lib/configurable/config_classes/list_config.rb +26 -0
- data/lib/configurable/config_classes/nest_config.rb +50 -0
- data/lib/configurable/config_classes/scalar_config.rb +91 -0
- data/lib/configurable/config_hash.rb +87 -112
- data/lib/configurable/config_types.rb +6 -0
- data/lib/configurable/config_types/boolean_type.rb +22 -0
- data/lib/configurable/config_types/float_type.rb +11 -0
- data/lib/configurable/config_types/integer_type.rb +11 -0
- data/lib/configurable/config_types/nest_type.rb +39 -0
- data/lib/configurable/config_types/object_type.rb +58 -0
- data/lib/configurable/config_types/string_type.rb +15 -0
- data/lib/configurable/conversions.rb +91 -0
- data/lib/configurable/module_methods.rb +0 -1
- data/lib/configurable/version.rb +1 -5
- metadata +73 -30
- data/README +0 -207
- data/lib/cdoc.rb +0 -413
- data/lib/cdoc/cdoc_html_generator.rb +0 -38
- data/lib/cdoc/cdoc_html_template.rb +0 -42
- data/lib/config_parser.rb +0 -563
- data/lib/config_parser/option.rb +0 -108
- data/lib/config_parser/switch.rb +0 -44
- data/lib/config_parser/utils.rb +0 -177
- data/lib/configurable/config.rb +0 -97
- data/lib/configurable/indifferent_access.rb +0 -35
- data/lib/configurable/nest_config.rb +0 -78
- data/lib/configurable/ordered_hash_patch.rb +0 -85
- data/lib/configurable/utils.rb +0 -186
- data/lib/configurable/validation.rb +0 -768
@@ -1,359 +1,202 @@
|
|
1
|
+
require 'lazydoc'
|
1
2
|
require 'configurable/config_hash'
|
2
|
-
require 'configurable/
|
3
|
-
require 'configurable/validation'
|
4
|
-
|
5
|
-
autoload(:ConfigParser, 'config_parser')
|
3
|
+
require 'configurable/conversions'
|
6
4
|
|
7
5
|
module Configurable
|
8
6
|
|
7
|
+
# Hash of default config types (bool, integer, float, string).
|
8
|
+
DEFAULT_CONFIG_TYPES = {
|
9
|
+
:bool => ConfigTypes::BooleanType,
|
10
|
+
:integer => ConfigTypes::IntegerType,
|
11
|
+
:float => ConfigTypes::FloatType,
|
12
|
+
:string => ConfigTypes::StringType,
|
13
|
+
:nest => ConfigTypes::NestType,
|
14
|
+
:obj => ConfigTypes::ObjectType
|
15
|
+
}
|
16
|
+
|
9
17
|
# ClassMethods extends classes that include Configurable and provides methods
|
10
18
|
# for declaring configurations.
|
11
19
|
module ClassMethods
|
12
|
-
|
20
|
+
include ConfigClasses
|
21
|
+
include ConfigTypes
|
13
22
|
|
14
|
-
# A hash of (key, Config) pairs tracking configs defined on self. See
|
15
|
-
#
|
23
|
+
# A hash of (key, Config) pairs tracking configs defined on self. See the
|
24
|
+
# configs method for all configs declared across all ancestors.
|
16
25
|
attr_reader :config_registry
|
17
26
|
|
18
|
-
|
27
|
+
# A hash of (key, ConfigType) pairs tracking config_types defined on self.
|
28
|
+
# See the config_types method for all config_types declared across all
|
29
|
+
# ancestors.
|
30
|
+
attr_reader :config_type_registry
|
31
|
+
|
32
|
+
def self.initialize(base) # :nodoc:
|
33
|
+
base.reset_configs
|
19
34
|
unless base.instance_variable_defined?(:@config_registry)
|
20
|
-
base.instance_variable_set(:@config_registry,
|
35
|
+
base.instance_variable_set(:@config_registry, {})
|
21
36
|
end
|
22
37
|
|
23
|
-
|
24
|
-
|
38
|
+
base.reset_config_types
|
39
|
+
unless base.instance_variable_defined?(:@config_type_registry)
|
40
|
+
base.instance_variable_set(:@config_type_registry, {})
|
25
41
|
end
|
26
42
|
|
27
|
-
unless base.instance_variable_defined?(:@
|
28
|
-
base.instance_variable_set(:@
|
43
|
+
unless base.instance_variable_defined?(:@config_type_context)
|
44
|
+
base.instance_variable_set(:@config_type_context, base)
|
29
45
|
end
|
30
46
|
end
|
31
47
|
|
32
|
-
#
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
# Same as parse, but removes parsed args from argv.
|
44
|
-
def parse!(argv=ARGV, options={})
|
45
|
-
parser = ConfigParser.new
|
46
|
-
parser.add(configurations)
|
47
|
-
|
48
|
-
args = parser.parse!(argv, options)
|
49
|
-
[args, parser.config]
|
50
|
-
end
|
51
|
-
|
52
|
-
# A hash of (key, Config) pairs representing all configurations defined
|
53
|
-
# on this class or inherited from ancestors. The configurations hash is
|
54
|
-
# generated on each call to ensure it accurately reflects any configs
|
55
|
-
# added on ancestors. This slows down initialization and config access
|
56
|
-
# through instance.config.
|
57
|
-
#
|
58
|
-
# Call cache_configurations after all configs have been declared in order
|
59
|
-
# to prevent regeneration of configurations and to significantly improve
|
60
|
-
# performance.
|
61
|
-
def configurations
|
62
|
-
return @configurations if @configurations
|
48
|
+
# A hash of (key, Config) pairs representing all configs defined on this
|
49
|
+
# class or inherited from ancestors. The configs hash is memoized for
|
50
|
+
# performance. Call reset_configs if configs needs to be recalculated for
|
51
|
+
# any reason.
|
52
|
+
#
|
53
|
+
# Configs is extended with the Conversions module.
|
54
|
+
def configs
|
55
|
+
@configs ||= begin
|
56
|
+
configs = {}
|
63
57
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
else
|
73
|
-
configurations[key] = value
|
58
|
+
ancestors.reverse.each do |ancestor|
|
59
|
+
next unless ancestor.kind_of?(ClassMethods)
|
60
|
+
ancestor.config_registry.each_pair do |key, value|
|
61
|
+
if value.nil?
|
62
|
+
configs.delete(key)
|
63
|
+
else
|
64
|
+
configs[key] = value
|
65
|
+
end
|
74
66
|
end
|
75
67
|
end
|
68
|
+
|
69
|
+
configs.extend Conversions
|
70
|
+
configs
|
76
71
|
end
|
77
|
-
|
78
|
-
configurations
|
79
72
|
end
|
80
73
|
|
81
|
-
#
|
82
|
-
|
83
|
-
|
84
|
-
@configurations = nil
|
85
|
-
@configurations = self.configurations if on
|
74
|
+
# Resets configs such that they will be recalculated.
|
75
|
+
def reset_configs
|
76
|
+
@configs = nil
|
86
77
|
end
|
87
78
|
|
88
|
-
|
89
|
-
|
90
|
-
#
|
91
|
-
#
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
79
|
+
# A hash of (key, ConfigType) pairs representing all config_types defined
|
80
|
+
# on this class or inherited from ancestors. The config_types hash is
|
81
|
+
# memoized for performance. Call reset_config_types if config_types needs
|
82
|
+
# to be recalculated for any reason.
|
83
|
+
def config_types
|
84
|
+
@config_types ||= begin
|
85
|
+
config_types = {}
|
86
|
+
|
87
|
+
registries = []
|
88
|
+
each_registry do |ancestor, registry|
|
89
|
+
registries.unshift(registry)
|
90
|
+
end
|
91
|
+
|
92
|
+
registries.each do |registry|
|
93
|
+
registry.each_pair do |key, value|
|
94
|
+
if value.nil?
|
95
|
+
config_types.delete(key)
|
96
|
+
else
|
97
|
+
config_types[key] = value
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
96
101
|
|
97
|
-
|
98
|
-
@configurations.extend(IndifferentAccess)
|
99
|
-
else
|
100
|
-
@configurations = @configurations.dup
|
102
|
+
config_types
|
101
103
|
end
|
102
104
|
end
|
103
|
-
|
104
|
-
#
|
105
|
-
|
106
|
-
|
107
|
-
#
|
108
|
-
# class SampleClass
|
109
|
-
# include Configurable
|
110
|
-
#
|
111
|
-
# config :str, 'value'
|
112
|
-
# config(:upcase, 'value') {|input| input.upcase }
|
113
|
-
# end
|
114
|
-
#
|
115
|
-
# # An equivalent class to illustrate class-context
|
116
|
-
# class EquivalentClass
|
117
|
-
# attr_accessor :str
|
118
|
-
# attr_reader :upcase
|
119
|
-
#
|
120
|
-
# UPCASE_BLOCK = lambda {|input| input.upcase }
|
121
|
-
#
|
122
|
-
# def upcase=(input)
|
123
|
-
# @upcase = UPCASE_BLOCK.call(input)
|
124
|
-
# end
|
125
|
-
# end
|
126
|
-
#
|
127
|
-
def config(key, value=nil, attributes={}, &block)
|
128
|
-
attributes = merge_attributes(block, attributes)
|
129
|
-
|
130
|
-
if block_given?
|
131
|
-
instance_variable = "@#{key}".to_sym
|
132
|
-
config_attr(key, value, attributes) do |input|
|
133
|
-
instance_variable_set(instance_variable, yield(input))
|
134
|
-
end
|
135
|
-
else
|
136
|
-
config_attr(key, value, attributes)
|
137
|
-
end
|
105
|
+
|
106
|
+
# Resets config_types such that they will be recalculated.
|
107
|
+
def reset_config_types
|
108
|
+
@config_types = nil
|
138
109
|
end
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
#
|
145
|
-
#
|
146
|
-
#
|
147
|
-
#
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
# config_attr :str, 'value'
|
152
|
-
# config_attr(:upcase, 'value') {|input| @upcase = input.upcase }
|
153
|
-
# end
|
154
|
-
#
|
155
|
-
# # An equivalent class to illustrate instance-context
|
156
|
-
# class EquivalentClass
|
157
|
-
# attr_accessor :str
|
158
|
-
# attr_reader :upcase
|
159
|
-
#
|
160
|
-
# def upcase=(input)
|
161
|
-
# @upcase = input.upcase
|
162
|
-
# end
|
163
|
-
# end
|
164
|
-
#
|
165
|
-
# === Attributes
|
166
|
-
#
|
167
|
-
# Several attributes may be specified to modify how a config is constructed.
|
168
|
-
# Attribute keys should be specified as symbols.
|
169
|
-
#
|
170
|
-
# Attribute:: Description
|
171
|
-
# init:: When set to false the config will not initialize
|
172
|
-
# during initialize_config. (default: true)
|
173
|
-
# reader:: The method used to read the configuration.
|
174
|
-
# (default: key)
|
175
|
-
# writer:: The method used to write the configuration
|
176
|
-
# (default: "#{key}=")
|
177
|
-
#
|
178
|
-
# Neither reader nor writer may be set to nil, but they may be set to
|
179
|
-
# non-default values. In that case, config_attr will register the method
|
180
|
-
# names as provided, but it will not define the methods themselves.
|
181
|
-
# Specifying true defines the default methods. Specifying false makes
|
182
|
-
# the config expect the default method name, but does not define the method
|
183
|
-
# itself.
|
184
|
-
#
|
185
|
-
# Any additional attributes are registered with the configuration.
|
186
|
-
def config_attr(key, value=nil, attributes={}, &block)
|
187
|
-
attributes = merge_attributes(block, attributes)
|
110
|
+
|
111
|
+
protected
|
112
|
+
|
113
|
+
attr_accessor :config_type_context
|
114
|
+
|
115
|
+
# Defines and registers an instance of config_class with the specified key
|
116
|
+
# and attrs. Unless attrs specifies a :reader or :writer, the
|
117
|
+
# corresponding attr accessors will be defined for the config name (which
|
118
|
+
# by default is the key).
|
119
|
+
def define_config(key, attrs={}, config_class=ScalarConfig)
|
120
|
+
reader = attrs[:reader]
|
121
|
+
writer = attrs[:writer]
|
188
122
|
|
189
|
-
|
190
|
-
reader = define_attribute_method(:reader, attributes, key) do |attribute|
|
191
|
-
attr_reader(attribute)
|
192
|
-
public(attribute)
|
193
|
-
end
|
123
|
+
config = config_class.new(key, attrs)
|
194
124
|
|
195
|
-
|
196
|
-
|
197
|
-
|
125
|
+
unless reader
|
126
|
+
attr_reader(config.name)
|
127
|
+
public(config.name)
|
198
128
|
end
|
199
129
|
|
200
|
-
|
201
|
-
|
202
|
-
public(
|
130
|
+
unless writer
|
131
|
+
attr_writer(config.name)
|
132
|
+
public("#{config.name}=")
|
203
133
|
end
|
204
134
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
config_registry[key] = Config.new(reader, writer, value, attributes, init, dup)
|
135
|
+
config_registry[config.key] = config
|
136
|
+
reset_configs
|
137
|
+
config
|
209
138
|
end
|
210
139
|
|
211
|
-
#
|
212
|
-
#
|
213
|
-
#
|
214
|
-
#
|
215
|
-
#
|
216
|
-
#
|
217
|
-
#
|
218
|
-
#
|
219
|
-
#
|
220
|
-
#
|
221
|
-
#
|
222
|
-
#
|
223
|
-
#
|
224
|
-
#
|
225
|
-
#
|
226
|
-
#
|
227
|
-
#
|
228
|
-
#
|
229
|
-
#
|
230
|
-
#
|
231
|
-
#
|
232
|
-
#
|
233
|
-
#
|
234
|
-
#
|
235
|
-
#
|
236
|
-
|
237
|
-
|
238
|
-
# a.config.to_hash # => {:key => 'one', :nest => {:key => 2}}
|
239
|
-
# a.nest.config.to_hash # => {:key => 2}
|
240
|
-
# a.nest.class # => A::Nest
|
241
|
-
#
|
242
|
-
# An existing configurable class may be provided instead of using the block
|
243
|
-
# to define a new configurable class. Recursive nesting is supported.
|
244
|
-
#
|
245
|
-
# class B
|
246
|
-
# include Configurable
|
247
|
-
#
|
248
|
-
# config :key, 1, &c.integer
|
249
|
-
# nest :nest do
|
250
|
-
# config :key, 2, &c.integer
|
251
|
-
# nest :nest do
|
252
|
-
# config :key, 3, &c.integer
|
253
|
-
# end
|
254
|
-
# end
|
255
|
-
# end
|
256
|
-
#
|
257
|
-
# class C
|
258
|
-
# include Configurable
|
259
|
-
# nest :a, A
|
260
|
-
# nest :b, B
|
261
|
-
# end
|
262
|
-
#
|
263
|
-
# c = C.new
|
264
|
-
# c.b.key = 7
|
265
|
-
# c.b.nest.key = "8"
|
266
|
-
# c.config[:b][:nest][:nest][:key] = "9"
|
267
|
-
#
|
268
|
-
# c.config.to_hash
|
269
|
-
# # => {
|
270
|
-
# # :a => {
|
271
|
-
# # :key => 'one',
|
272
|
-
# # :nest => {:key => 'two'}
|
273
|
-
# # },
|
274
|
-
# # :b => {
|
275
|
-
# # :key => 7,
|
276
|
-
# # :nest => {
|
277
|
-
# # :key => 8,
|
278
|
-
# # :nest => {:key => 9}
|
279
|
-
# # }
|
280
|
-
# # }}
|
281
|
-
#
|
282
|
-
# === Attributes
|
283
|
-
#
|
284
|
-
# Nest uses the same attributes as config_attr, with a couple additions:
|
285
|
-
#
|
286
|
-
# Attribute:: Description
|
287
|
-
# const_name:: Determines the constant name of the configurable
|
288
|
-
# class within the nesting class. May be nil.
|
289
|
-
# (default: key.to_s.capitalize)
|
290
|
-
# type:: By default set to :nest.
|
291
|
-
#
|
292
|
-
def nest(key, configurable_class=nil, attributes={}, &block)
|
293
|
-
attributes = merge_attributes(block, attributes)
|
294
|
-
attributes = {
|
295
|
-
:reader => true,
|
296
|
-
:writer => true,
|
297
|
-
:type => :nest
|
298
|
-
}.merge(attributes)
|
140
|
+
# Defines a config after guessing or setting some standard values into
|
141
|
+
# attrs. Specifically:
|
142
|
+
#
|
143
|
+
# * :default is the default
|
144
|
+
# * :caster is the caster block (if provided)
|
145
|
+
# * :desc is set using Lazydoc (unless already set)
|
146
|
+
# * :list is set to true for array defaults (unless already set)
|
147
|
+
#
|
148
|
+
# In addition config also guesses the type of a config (if not manually
|
149
|
+
# specified by :type) and merges in any attributes for the corresponding
|
150
|
+
# config_type. The class of the config is guessed from the attrs, based
|
151
|
+
# on the :list and :options attributes using this logic:
|
152
|
+
#
|
153
|
+
# :list :otions config_class
|
154
|
+
# ---------------------------
|
155
|
+
# false false Config
|
156
|
+
# true false List
|
157
|
+
# false true Select
|
158
|
+
# true true ListSelect
|
159
|
+
#
|
160
|
+
# == Usage Note
|
161
|
+
#
|
162
|
+
# Config is meant to be a convenience method. It gets most things right
|
163
|
+
# but if the attrs logic is too convoluted (and at times it is) then
|
164
|
+
# define configs manually with the define_config method.
|
165
|
+
def config(key, default=nil, attrs={}, &block)
|
166
|
+
orig_attrs = attrs.dup
|
299
167
|
|
300
|
-
|
301
|
-
|
302
|
-
if block_given?
|
303
|
-
configurable_class = Class.new(configurable_class)
|
304
|
-
configurable_class.class_eval(&block)
|
305
|
-
end
|
306
|
-
else
|
307
|
-
configurable_class = Class.new { include Configurable }
|
308
|
-
configurable_class.class_eval(&block) if block_given?
|
168
|
+
if nest_class = guess_nest_class(default, block)
|
169
|
+
default = nest_class.new
|
309
170
|
end
|
310
171
|
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
else
|
315
|
-
key.to_s.capitalize
|
172
|
+
if default.kind_of?(Configurable)
|
173
|
+
attrs[:configurable] = default
|
174
|
+
default = default.config.to_hash
|
316
175
|
end
|
317
176
|
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
|
322
|
-
const_set(const_name, configurable_class)
|
323
|
-
end
|
324
|
-
end
|
325
|
-
const_name = nil
|
177
|
+
attrs[:default] = default
|
178
|
+
attrs[:type] = guess_config_type(attrs).new(attrs)
|
179
|
+
attrs[:metadata] = guess_config_metadata(Lazydoc.register_caller).merge!(orig_attrs)
|
180
|
+
config_class = guess_config_class(attrs)
|
326
181
|
|
327
|
-
|
328
|
-
reader = define_attribute_method(:reader, attributes, key) do |attribute|
|
329
|
-
attr_reader attribute
|
330
|
-
public(attribute)
|
331
|
-
end
|
182
|
+
config = define_config(key, attrs, config_class)
|
332
183
|
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
define_method(attribute) do |value|
|
338
|
-
Validation.validate(value, [configurable_class])
|
339
|
-
instance_variable_set(instance_variable, value)
|
184
|
+
if nest_class
|
185
|
+
const_name = attrs[:const_name] || guess_nest_const_name(config)
|
186
|
+
unless const_defined?(const_name)
|
187
|
+
const_set(const_name, nest_class)
|
340
188
|
end
|
341
|
-
public(attribute)
|
342
189
|
end
|
343
|
-
|
344
|
-
|
345
|
-
|
346
|
-
config_registry[key] = NestConfig.new(configurable_class, reader, writer, attributes, init)
|
347
|
-
check_infinite_nest(configurable_class)
|
348
|
-
end
|
190
|
+
|
191
|
+
config
|
192
|
+
end
|
349
193
|
|
350
|
-
# Removes a
|
351
|
-
#
|
352
|
-
#
|
353
|
-
#
|
354
|
-
# Setting :reader or :writer to false in the options prevents those methods
|
355
|
-
# from being removed.
|
194
|
+
# Removes a config much like remove_method removes a method. The reader
|
195
|
+
# and writer for the config are likewise removed. Nested configs can be
|
196
|
+
# removed using this method.
|
356
197
|
#
|
198
|
+
# Setting :reader or :writer to false in the options prevents those
|
199
|
+
# methods from being removed.
|
357
200
|
def remove_config(key, options={})
|
358
201
|
unless config_registry.has_key?(key)
|
359
202
|
raise NameError.new("#{key.inspect} is not a config on #{self}")
|
@@ -365,18 +208,20 @@ module Configurable
|
|
365
208
|
}.merge(options)
|
366
209
|
|
367
210
|
config = config_registry.delete(key)
|
368
|
-
|
211
|
+
reset_configs
|
369
212
|
|
370
|
-
|
371
|
-
|
213
|
+
remove_method(config.reader) if options[:reader]
|
214
|
+
remove_method(config.writer) if options[:writer]
|
215
|
+
|
216
|
+
config
|
372
217
|
end
|
373
218
|
|
374
|
-
# Undefines a
|
219
|
+
# Undefines a config much like undef_method undefines a method. The
|
375
220
|
# reader and writer for the config are likewise undefined. Nested configs
|
376
221
|
# can be undefined using this method.
|
377
222
|
#
|
378
|
-
# Setting :reader or :writer to false in the options prevents those
|
379
|
-
# from being undefined.
|
223
|
+
# Setting :reader or :writer to false in the options prevents those
|
224
|
+
# methods from being undefined.
|
380
225
|
#
|
381
226
|
# ==== Implementation Note
|
382
227
|
#
|
@@ -384,12 +229,9 @@ module Configurable
|
|
384
229
|
# Deleting the config is not sufficient because the registry needs to
|
385
230
|
# convey to self and subclasses to not inherit the config from ancestors.
|
386
231
|
#
|
387
|
-
# This is unlike remove_config where the config is simply deleted from
|
388
|
-
#
|
389
|
-
#
|
232
|
+
# This is unlike remove_config where the config is simply deleted from the
|
233
|
+
# config_registry.
|
390
234
|
def undef_config(key, options={})
|
391
|
-
# temporarily cache as an optimization
|
392
|
-
configs = configurations
|
393
235
|
unless configs.has_key?(key)
|
394
236
|
raise NameError.new("#{key.inspect} is not a config on #{self}")
|
395
237
|
end
|
@@ -401,77 +243,212 @@ module Configurable
|
|
401
243
|
|
402
244
|
config = configs[key]
|
403
245
|
config_registry[key] = nil
|
404
|
-
|
246
|
+
reset_configs
|
405
247
|
|
406
248
|
undef_method(config.reader) if options[:reader]
|
407
249
|
undef_method(config.writer) if options[:writer]
|
250
|
+
|
251
|
+
config
|
408
252
|
end
|
409
253
|
|
410
|
-
|
411
|
-
|
412
|
-
|
254
|
+
def define_config_type(name, config_type)
|
255
|
+
config_type_registry[name] = config_type
|
256
|
+
reset_config_types
|
257
|
+
config_type
|
413
258
|
end
|
414
|
-
|
259
|
+
|
260
|
+
def config_type(name, *matchers, &caster)
|
261
|
+
config_type = StringType.subclass(*matchers).cast(&caster)
|
262
|
+
const_name = guess_config_type_const_name(name)
|
263
|
+
|
264
|
+
unless const_defined?(const_name)
|
265
|
+
const_set(const_name, config_type)
|
266
|
+
end
|
267
|
+
|
268
|
+
define_config_type(name, config_type)
|
269
|
+
end
|
270
|
+
|
271
|
+
# Removes a config_type much like remove_method removes a method.
|
272
|
+
def remove_config_type(name)
|
273
|
+
unless config_type_registry.has_key?(name)
|
274
|
+
raise NameError.new("#{name.inspect} is not a config_type on #{self}")
|
275
|
+
end
|
276
|
+
|
277
|
+
config_type = config_type_registry.delete(name)
|
278
|
+
reset_config_types
|
279
|
+
config_type
|
280
|
+
end
|
281
|
+
|
282
|
+
# Undefines a config_type much like undef_method undefines a method.
|
283
|
+
#
|
284
|
+
# ==== Implementation Note
|
285
|
+
#
|
286
|
+
# ConfigClasses are undefined by setting the key to nil in the registry.
|
287
|
+
# Deleting the config_type is not sufficient because the registry needs to
|
288
|
+
# convey to self and subclasses to not inherit the config_type from
|
289
|
+
# ancestors.
|
290
|
+
#
|
291
|
+
# This is unlike remove_config_type where the config_type is simply
|
292
|
+
# deleted from the config_type_registry.
|
293
|
+
def undef_config_type(name)
|
294
|
+
unless config_types.has_key?(name)
|
295
|
+
raise NameError.new("#{name.inspect} is not a config_type on #{self}")
|
296
|
+
end
|
297
|
+
|
298
|
+
config_type = config_type_registry[name]
|
299
|
+
config_type_registry[name] = nil
|
300
|
+
reset_config_types
|
301
|
+
config_type
|
302
|
+
end
|
303
|
+
|
415
304
|
private
|
416
305
|
|
417
306
|
def inherited(base) # :nodoc:
|
418
|
-
|
419
|
-
|
307
|
+
ClassMethods.initialize(base)
|
308
|
+
super
|
309
|
+
end
|
310
|
+
|
311
|
+
def guess_nest_class(base, block) # :nodoc:
|
312
|
+
unless base.kind_of?(Hash) || block
|
313
|
+
return nil
|
314
|
+
end
|
315
|
+
|
316
|
+
nest_class = Class.new { include Configurable }
|
317
|
+
nest_class.config_type_context = self
|
318
|
+
|
319
|
+
if base.kind_of?(Hash)
|
320
|
+
base.each_pair do |key, value|
|
321
|
+
nest_class.send(:config, key, value)
|
322
|
+
end
|
323
|
+
end
|
324
|
+
|
325
|
+
if block
|
326
|
+
nest_class.class_eval(&block)
|
327
|
+
end
|
328
|
+
|
329
|
+
check_infinite_nest(nest_class)
|
330
|
+
nest_class
|
331
|
+
end
|
332
|
+
|
333
|
+
# helper to recursively check for an infinite nest
|
334
|
+
def check_infinite_nest(klass) # :nodoc:
|
335
|
+
raise "infinite nest detected" if klass == self
|
336
|
+
|
337
|
+
klass.configs.each_value do |config|
|
338
|
+
if config.type.kind_of?(NestType)
|
339
|
+
check_infinite_nest(config.type.configurable.class)
|
340
|
+
end
|
341
|
+
end
|
420
342
|
end
|
421
343
|
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
|
426
|
-
|
344
|
+
def each_registry # :nodoc:
|
345
|
+
# yield the registry for self first to take account of times when
|
346
|
+
# config_type_context.ancestors does not include self (otherwise
|
347
|
+
# config types defined on self are missed)
|
348
|
+
yield self, config_type_registry
|
427
349
|
|
428
|
-
|
429
|
-
|
430
|
-
|
431
|
-
|
432
|
-
|
433
|
-
|
434
|
-
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
350
|
+
config_type_context.ancestors.each do |ancestor|
|
351
|
+
case
|
352
|
+
when ancestor == self
|
353
|
+
next
|
354
|
+
|
355
|
+
when ancestor.kind_of?(ClassMethods)
|
356
|
+
yield ancestor, ancestor.config_type_registry
|
357
|
+
|
358
|
+
when ancestor == Configurable
|
359
|
+
yield ancestor, Configurable::DEFAULT_CONFIG_TYPES
|
360
|
+
break
|
361
|
+
|
362
|
+
else next
|
363
|
+
end
|
441
364
|
end
|
442
|
-
# ... all other values specify what the method should be,
|
443
|
-
# and lets the user define the method.
|
444
|
-
|
445
|
-
attribute.to_sym
|
446
365
|
end
|
447
366
|
|
448
|
-
|
449
|
-
|
450
|
-
def merge_attributes(block, attributes) # :nodoc:
|
451
|
-
defaults = DEFAULT_ATTRIBUTES[nil].dup
|
452
|
-
defaults.merge!(DEFAULT_ATTRIBUTES[block]) if block
|
453
|
-
defaults.merge!(attributes)
|
367
|
+
def guess_config_type_by_name(name) # :nodoc:
|
368
|
+
return name if name.nil?
|
454
369
|
|
455
|
-
|
456
|
-
|
370
|
+
each_registry do |ancestor, registry|
|
371
|
+
if registry.has_key?(name)
|
372
|
+
return registry[name]
|
373
|
+
end
|
374
|
+
end
|
457
375
|
|
458
|
-
|
376
|
+
raise "no such config type: #{type.inspect}"
|
459
377
|
end
|
460
378
|
|
461
|
-
|
462
|
-
|
463
|
-
|
379
|
+
def guess_config_type_by_value(value) # :nodoc:
|
380
|
+
each_registry do |ancestor, registry|
|
381
|
+
guesses = registry.values.select {|config_type| config_type.matches?(value) }
|
382
|
+
|
383
|
+
case guesses.length
|
384
|
+
when 0 then next
|
385
|
+
when 1 then return guesses.at(0)
|
386
|
+
else raise "multiple guesses for config type: #{guesses.inspect} (in: #{ancestor} default: #{default.inspect})"
|
387
|
+
end
|
388
|
+
end
|
464
389
|
|
465
|
-
|
466
|
-
|
467
|
-
|
390
|
+
ObjectType
|
391
|
+
end
|
392
|
+
|
393
|
+
def guess_config_type(attrs) # :nodoc:
|
394
|
+
if attrs.has_key?(:type)
|
395
|
+
guess_config_type_by_name attrs[:type]
|
396
|
+
else
|
397
|
+
value = attrs[:default]
|
398
|
+
value = value.at(0) if value.kind_of?(Array)
|
399
|
+
guess_config_type_by_value value
|
400
|
+
end
|
401
|
+
end
|
402
|
+
|
403
|
+
def guess_config_class(attrs) # :nodoc:
|
404
|
+
if attrs.has_key?(:class)
|
405
|
+
attrs[:class]
|
406
|
+
else
|
407
|
+
case attrs[:default]
|
408
|
+
when Array then ListConfig
|
409
|
+
when Hash then NestConfig
|
410
|
+
else ScalarConfig
|
468
411
|
end
|
469
412
|
end
|
470
413
|
end
|
414
|
+
|
415
|
+
def guess_config_metadata(comment) # :nodoc:
|
416
|
+
Hash.new do |hash, key|
|
417
|
+
comment.resolve
|
418
|
+
|
419
|
+
if trailer = comment.trailer
|
420
|
+
flags, desc = trailer.split(':', 2)
|
421
|
+
|
422
|
+
if desc.nil?
|
423
|
+
flags, desc = '', flags
|
424
|
+
elsif flags.strip.empty?
|
425
|
+
hash[:hidden] = true
|
426
|
+
else
|
427
|
+
hash[:long] = nil
|
428
|
+
hash[:short] = nil
|
429
|
+
hash[:arg_name] = nil
|
430
|
+
end
|
431
|
+
|
432
|
+
argv = flags.split(',').collect! {|arg| arg.strip }
|
433
|
+
argv << desc.strip
|
434
|
+
|
435
|
+
comment_attrs = ConfigParser::Utils.parse_attrs(argv)
|
436
|
+
comment_attrs.each_pair do |attr_key, attr_value|
|
437
|
+
hash[attr_key] = attr_value
|
438
|
+
end
|
439
|
+
end
|
440
|
+
|
441
|
+
hash[:help] = comment.content
|
442
|
+
hash.has_key?(key) ? hash[key] : nil
|
443
|
+
end
|
444
|
+
end
|
445
|
+
|
446
|
+
def guess_nest_const_name(config) # :nodoc:
|
447
|
+
config.name.gsub(/(?:^|_)(.)/) { $1.upcase }
|
448
|
+
end
|
449
|
+
|
450
|
+
def guess_config_type_const_name(name) # :nodoc:
|
451
|
+
"#{name}Type".gsub(/(?:^|_)(.)/) { $1.upcase }
|
452
|
+
end
|
471
453
|
end
|
472
|
-
end
|
473
|
-
|
474
|
-
# Apply the ordered hash patch if necessary
|
475
|
-
if RUBY_VERSION < '1.9'
|
476
|
-
require 'configurable/ordered_hash_patch'
|
477
454
|
end
|