configurable 0.7.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|