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
data/{History → History.rdoc}
RENAMED
@@ -1,3 +1,12 @@
|
|
1
|
+
== 1.0.0 / 2011-07-12
|
2
|
+
|
3
|
+
Significant rewrite of all internals. Configurations are easier to declare and
|
4
|
+
have a more formal type system that allows almost everything to be inferred
|
5
|
+
from the default value. The rewrite is designed to be flexible and to handle
|
6
|
+
usage in a variety of interfaces, but only time will tell.
|
7
|
+
|
8
|
+
Currently passes all tests on: 1.8.6, 1.8.7, 1.9.2, rbx, jruby
|
9
|
+
|
1
10
|
== 0.7.0 / 2010-05-02
|
2
11
|
|
3
12
|
* Utils#load_file now properly raises error for non-existant files
|
data/MIT-LICENSE
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Copyright (c) 2008-2009, Regents of the University of Colorado.
|
2
2
|
|
3
|
-
Copyright (c) 2009-
|
3
|
+
Copyright (c) 2009-2011, Simon Chiang.
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.rdoc
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
= Configurable
|
2
|
+
|
3
|
+
Class configurations for the command line and web.
|
4
|
+
|
5
|
+
== Description
|
6
|
+
|
7
|
+
Configurable adds methods to declare class configurations. Configurations are
|
8
|
+
inheritable, delegate to methods, and have hash-like access. Configurable
|
9
|
+
constructs configs such that they easily map to config files, web forms, and
|
10
|
+
the command line.
|
11
|
+
|
12
|
+
== Usage
|
13
|
+
|
14
|
+
Declare configurations using the config method. Config generates accessors
|
15
|
+
that initialize with the default value.
|
16
|
+
|
17
|
+
class ConfigClass
|
18
|
+
include Configurable
|
19
|
+
config :flag, false # a flag
|
20
|
+
config :switch, true # an on/off switch
|
21
|
+
config :num, 3.14 # a number
|
22
|
+
config :lst, [1,2,3] # a list of integers
|
23
|
+
config :str, 'one' # a string
|
24
|
+
end
|
25
|
+
|
26
|
+
c = ConfigClass.new
|
27
|
+
c.str # => 'one'
|
28
|
+
|
29
|
+
Configs may also be accessed through config (a kind of delegating hash):
|
30
|
+
|
31
|
+
c.str = 'two'
|
32
|
+
c.config[:str] # => 'two'
|
33
|
+
c.config[:str] = 'three'
|
34
|
+
c.str # => 'three'
|
35
|
+
|
36
|
+
c.config.to_hash
|
37
|
+
# => {
|
38
|
+
# :flag => false,
|
39
|
+
# :switch => true,
|
40
|
+
# :num => 3.14,
|
41
|
+
# :lst => [1, 2, 3],
|
42
|
+
# :str => 'three'
|
43
|
+
# }
|
44
|
+
|
45
|
+
Configs may be imported and exported as simple objects which easily translate
|
46
|
+
to and from user interfaces, be they config files, web forms, or the command
|
47
|
+
line.
|
48
|
+
|
49
|
+
Config files:
|
50
|
+
|
51
|
+
c.config.import(
|
52
|
+
'flag' => true,
|
53
|
+
'num' => 6.022
|
54
|
+
)
|
55
|
+
|
56
|
+
c.config.export
|
57
|
+
# => {
|
58
|
+
# 'flag' => true,
|
59
|
+
# 'switch' => true,
|
60
|
+
# 'num' => 6.022,
|
61
|
+
# 'lst' => [1, 2, 3],
|
62
|
+
# 'str' => 'three'
|
63
|
+
# }
|
64
|
+
|
65
|
+
Web forms:
|
66
|
+
|
67
|
+
params = {
|
68
|
+
'flag' => 'true', # checkbox
|
69
|
+
'switch' => 'true', # radio button
|
70
|
+
'num' => '2.71', # text input
|
71
|
+
'lst' => ['2', '6'] # list input (lst[]=2&lst[]=6)
|
72
|
+
}
|
73
|
+
|
74
|
+
c.config.import(params).to_hash
|
75
|
+
# => {
|
76
|
+
# :flag => true,
|
77
|
+
# :switch => true,
|
78
|
+
# :num => 2.71,
|
79
|
+
# :lst => [2, 6],
|
80
|
+
# :str => 'three'
|
81
|
+
# }
|
82
|
+
|
83
|
+
Command Line:
|
84
|
+
|
85
|
+
c.config.parse %w{a --flag --no-switch --num 6.022 --lst 7 --lst 8,9 b c}
|
86
|
+
# => ['a', 'b', 'c']
|
87
|
+
|
88
|
+
c.config.to_hash
|
89
|
+
# => {
|
90
|
+
# :flag => true,
|
91
|
+
# :switch => false,
|
92
|
+
# :num => 6.022,
|
93
|
+
# :lst => [7, 8, 9],
|
94
|
+
# :str => 'three'
|
95
|
+
# }
|
96
|
+
|
97
|
+
stdout = []
|
98
|
+
parser = c.config.parser do |psr|
|
99
|
+
psr.on '-h', '--help', 'print help' do
|
100
|
+
stdout << "options:"
|
101
|
+
stdout << psr
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
parser.parse('--help')
|
106
|
+
"\n" + stdout.join("\n")
|
107
|
+
# => %q{
|
108
|
+
# options:
|
109
|
+
# --flag a flag
|
110
|
+
# -h, --help print help
|
111
|
+
# --lst LST... a list of integers (1,2,3)
|
112
|
+
# --num NUM a number (3.14)
|
113
|
+
# --str STR a string (one)
|
114
|
+
# --[no-]switch an on/off switch
|
115
|
+
# }
|
116
|
+
|
117
|
+
Configurable supports custom data types, nested configs, and config modules.
|
118
|
+
|
119
|
+
See the help documentation for more details:
|
120
|
+
|
121
|
+
* {Command Line Usage}[link:files/Help/Command%20Line_rdoc.html]
|
122
|
+
* {Config Syntax}[link:files/Help/Config%20Syntax_rdoc.html]
|
123
|
+
* {Config Types}[link:files/Help/Config%20Types_rdoc.html]
|
124
|
+
|
125
|
+
== Installation
|
126
|
+
|
127
|
+
Configurable is available as a gem[http://rubygems.org/gems/configurable].
|
128
|
+
|
129
|
+
% gem install configurable
|
130
|
+
|
131
|
+
== Development
|
132
|
+
|
133
|
+
To get started, checkout the code from GitHub[http://github.com/thinkerbot/configurable] and run the tests:
|
134
|
+
|
135
|
+
git clone git://github.com/thinkerbot/configurable.git
|
136
|
+
cd configurable
|
137
|
+
rake test
|
138
|
+
|
139
|
+
Please report any issues {here}[http://github.com/thinkerbot/configurable/issues].
|
140
|
+
|
141
|
+
== Info
|
142
|
+
|
143
|
+
Developer:: {Simon Chiang}[http://github.com/thinkerbot]
|
144
|
+
License:: {MIT-Style}[link:files/MIT-LICENSE.html]
|
data/lib/configurable.rb
CHANGED
@@ -1,8 +1,7 @@
|
|
1
|
-
require 'configurable/version'
|
2
1
|
require 'configurable/module_methods'
|
3
2
|
|
4
3
|
# Configurable enables the specification of configurations within a class
|
5
|
-
# definition.
|
4
|
+
# definition. Include and declare configs as below.
|
6
5
|
#
|
7
6
|
# class ConfigClass
|
8
7
|
# include Configurable
|
@@ -13,184 +12,10 @@ require 'configurable/module_methods'
|
|
13
12
|
#
|
14
13
|
# c = ConfigClass.new
|
15
14
|
# c.config.class # => Configurable::ConfigHash
|
16
|
-
# c.config
|
17
|
-
#
|
18
|
-
# Instances have a <tt>config</tt> object that acts like a forwarding hash;
|
19
|
-
# configuration keys delegate to accessors while undeclared key-value pairs
|
20
|
-
# are stored internally:
|
21
|
-
#
|
22
|
-
# c.config[:one] = 'ONE'
|
23
|
-
# c.one # => 'ONE'
|
24
|
-
#
|
25
|
-
# c.one = 1
|
26
|
-
# c.config # => {:one => 1, :two => 'two', :three => 'three'}
|
27
|
-
#
|
28
|
-
# c.config[:undeclared] = 'value'
|
29
|
-
# c.config.store # => {:undeclared => 'value'}
|
30
|
-
#
|
31
|
-
# The writer for a configuration can be defined by providing a block to config.
|
32
|
-
# The Validation module provides a number of common validation/transform
|
33
|
-
# blocks accessible through the class method 'c':
|
34
|
-
#
|
35
|
-
# class ValidationClass
|
36
|
-
# include Configurable
|
37
|
-
# config(:one, 'one') {|v| v.upcase }
|
38
|
-
# config :two, 2, &c.integer
|
39
|
-
# end
|
40
|
-
#
|
41
|
-
# c = ValidationClass.new
|
42
|
-
# c.config # => {:one => 'ONE', :two => 2}
|
43
|
-
#
|
44
|
-
# c.one = 'aNothER'
|
45
|
-
# c.one # => 'ANOTHER'
|
46
|
-
#
|
47
|
-
# c.two = -2
|
48
|
-
# c.two # => -2
|
49
|
-
# c.two = "3"
|
50
|
-
# c.two # => 3
|
51
|
-
# c.two = nil # !> ValidationError
|
52
|
-
# c.two = 'str' # !> ValidationError
|
53
|
-
#
|
54
|
-
# Note that config blocks are defined in class-context and will have access
|
55
|
-
# to variables outside the block (as you would expect). For instance, these
|
56
|
-
# are analagous declarations:
|
57
|
-
#
|
58
|
-
# class ExampleClass
|
59
|
-
# config :key, 'value' do |input|
|
60
|
-
# input.upcase
|
61
|
-
# end
|
62
|
-
# end
|
63
|
-
#
|
64
|
-
# class AnalagousClass
|
65
|
-
# block = lambda {|input| input.upcase}
|
66
|
-
#
|
67
|
-
# define_method(:key=) do |input|
|
68
|
-
# @key = block.call(input)
|
69
|
-
# end
|
70
|
-
# end
|
71
|
-
#
|
72
|
-
# To have the block literally define the writer, use the config_attr method.
|
73
|
-
# Blocks provided to config_attr will have instance context and must set
|
74
|
-
# the instance variable themselves.
|
75
|
-
#
|
76
|
-
# class LiteralClass
|
77
|
-
# config_attr :key, 'value' do |input|
|
78
|
-
# @key = input.upcase
|
79
|
-
# end
|
80
|
-
# end
|
81
|
-
#
|
82
|
-
# Configurations are inherited and may be overridden in subclasses. They may
|
83
|
-
# also be included from a module:
|
84
|
-
#
|
85
|
-
# module A
|
86
|
-
# include Configurable
|
87
|
-
# config :a, 'a'
|
88
|
-
# config :b, 'b'
|
89
|
-
# end
|
90
|
-
#
|
91
|
-
# class B
|
92
|
-
# include A
|
93
|
-
# end
|
94
|
-
#
|
95
|
-
# class C < B
|
96
|
-
# config :b, 'B'
|
97
|
-
# config :c, 'C'
|
98
|
-
# end
|
99
|
-
#
|
100
|
-
# B.new.config.to_hash # => {:a => 'a', :b => 'b'}
|
101
|
-
# C.new.config.to_hash # => {:a => 'a', :b => 'B', :c => 'C'}
|
102
|
-
#
|
103
|
-
# Lastly, configurable classes may be nested through the nest method. Nesting
|
104
|
-
# creates a configurable class with the configs defined in the nest block;
|
105
|
-
# nested configs may be accessed by chaining method calls, or through nested
|
106
|
-
# calls to config.
|
107
|
-
#
|
108
|
-
# class NestingClass
|
109
|
-
# include Configurable
|
110
|
-
# config :one, 'one'
|
111
|
-
# nest :two do
|
112
|
-
# config :three, 'three'
|
113
|
-
# end
|
114
|
-
# end
|
115
|
-
#
|
116
|
-
# c = NestingClass.new
|
117
|
-
# c.config.to_hash # => {:one => 'one', :two => {:three => 'three'}}
|
118
|
-
#
|
119
|
-
# c.two.three = 'THREE'
|
120
|
-
# c.config[:two][:three] # => 'THREE'
|
121
|
-
#
|
122
|
-
# === Attributes
|
123
|
-
#
|
124
|
-
# Alternative reader and writer methods may be specified as config attributes.
|
125
|
-
# When alternate methods are specified, Configurable assumes the methods are
|
126
|
-
# declared elsewhere and will not define accessors.
|
127
|
-
#
|
128
|
-
# class AlternativeClass
|
129
|
-
# include Configurable
|
130
|
-
#
|
131
|
-
# config_attr :sym, 'value', :reader => :get_sym, :writer => :set_sym
|
132
|
-
#
|
133
|
-
# def get_sym
|
134
|
-
# @sym
|
135
|
-
# end
|
136
|
-
#
|
137
|
-
# def set_sym(input)
|
138
|
-
# @sym = input.to_sym
|
139
|
-
# end
|
140
|
-
# end
|
141
|
-
#
|
142
|
-
# alt = AlternativeClass.new
|
143
|
-
# alt.respond_to?(:sym) # => false
|
144
|
-
# alt.respond_to?(:sym=) # => false
|
145
|
-
#
|
146
|
-
# alt.config[:sym] = 'one'
|
147
|
-
# alt.get_sym # => :one
|
148
|
-
#
|
149
|
-
# alt.set_sym('two')
|
150
|
-
# alt.config[:sym] # => :two
|
151
|
-
#
|
152
|
-
# Idiosyncratically, true and false may also be provided as reader/writer
|
153
|
-
# values.
|
154
|
-
#
|
155
|
-
# true:: Same as using the defaults, accessors are defined.
|
156
|
-
# false:: Sets the default reader/writer but does not define
|
157
|
-
# the accessors (think 'define reader/writer' => false).
|
158
|
-
#
|
159
|
-
# Nil is not allowed as a value.
|
160
|
-
#
|
161
|
-
# ==== Non-reader/writer attributes
|
162
|
-
#
|
163
|
-
# Attributes provide metadata for how to use configurations in various contexts.
|
164
|
-
# In general, attributes can be used to set any metadata an application
|
165
|
-
# needs. A few attributes are used internally by Configurable.
|
166
|
-
#
|
167
|
-
# Attribute:: Use::
|
168
|
-
# init:: When set to false, the config will not initialize itself.
|
169
|
-
# Specify when you manually initialize a config.
|
170
|
-
# type:: Specifies the type of option ConfigParser generates for this
|
171
|
-
# Config (ex: :switch, :flag, :list, :hidden)
|
172
|
-
# desc:: The description string used in the ConfigParser help
|
173
|
-
# long:: The long option (default: key)
|
174
|
-
# short:: The short option.
|
175
|
-
#
|
176
|
-
# Validation blocks have default attributes already assigned to them (ex type).
|
177
|
-
# In cases where a user-defined block gets used multiple times, it may be useful
|
178
|
-
# to register default attributes for that block. To do so, use this pattern:
|
179
|
-
#
|
180
|
-
# class AttributesClass
|
181
|
-
# include Configurable
|
182
|
-
# block = c.register(:type => :upcase) {|v| v.upcase }
|
183
|
-
#
|
184
|
-
# config :a, 'A', &block
|
185
|
-
# config :b, 'B', &block
|
186
|
-
# end
|
187
|
-
#
|
188
|
-
# AttributesClass.configurations[:a][:type] # => :upcase
|
189
|
-
# AttributesClass.configurations[:b][:type] # => :upcase
|
15
|
+
# c.config.to_hash # => {:one => 'one', :two => 'two', :three => 'three'}
|
190
16
|
#
|
191
17
|
module Configurable
|
192
|
-
|
193
|
-
|
18
|
+
|
194
19
|
# A ConfigHash bound to self. Accessing configurations through config
|
195
20
|
# is much slower (although sometimes more convenient) than through the
|
196
21
|
# config accessors.
|
@@ -203,106 +28,18 @@ module Configurable
|
|
203
28
|
initialize_config unless instance_variable_defined?(:@config)
|
204
29
|
super
|
205
30
|
end
|
206
|
-
|
207
|
-
# Reconfigures self with the given overrides. Only the specified configs
|
208
|
-
# are modified.
|
209
|
-
#
|
210
|
-
# Returns self.
|
211
|
-
def reconfigure(overrides={})
|
212
|
-
config.merge!(overrides)
|
213
|
-
self
|
214
|
-
end
|
215
|
-
|
31
|
+
|
216
32
|
# Reinitializes configurations in the copy such that the new object has
|
217
33
|
# it's own set of configurations, separate from the original object.
|
218
34
|
def initialize_copy(orig)
|
219
35
|
super
|
220
|
-
@config = ConfigHash.new(
|
36
|
+
@config = ConfigHash.new(orig.config.store.dup, self)
|
221
37
|
end
|
222
|
-
|
38
|
+
|
223
39
|
protected
|
224
40
|
|
225
|
-
# Opens the file specified by io and yield it to the block. If io is an
|
226
|
-
# IO, it will be yielded immediately, and the mode is ignored. Nil io are
|
227
|
-
# simply ignored.
|
228
|
-
#
|
229
|
-
# === Usage
|
230
|
-
#
|
231
|
-
# open_io is used to compliment the io validation, to ensure that if a file
|
232
|
-
# is specified, it will be closed.
|
233
|
-
#
|
234
|
-
# class IoSample
|
235
|
-
# include Configurable
|
236
|
-
# config :output, $stdout, &c.io # can be an io or filepath
|
237
|
-
#
|
238
|
-
# def say_hello
|
239
|
-
# open_io(output, 'w') do |io|
|
240
|
-
# io << 'hello!'
|
241
|
-
# end
|
242
|
-
# end
|
243
|
-
# end
|
244
|
-
#
|
245
|
-
# In short, this method provides a way to responsibly handle IO and file
|
246
|
-
# configurations.
|
247
|
-
def open_io(io, mode='r')
|
248
|
-
case io
|
249
|
-
when String
|
250
|
-
dir = File.dirname(io)
|
251
|
-
FileUtils.mkdir_p(dir) unless File.directory?(dir)
|
252
|
-
File.open(io, mode) {|file| yield(file) }
|
253
|
-
when Integer
|
254
|
-
# note this does not close the io because, as far as I understand,
|
255
|
-
# valid integer file descriptors point to files that are already
|
256
|
-
# open and presumably managed elsewhere
|
257
|
-
yield IO.open(io, mode)
|
258
|
-
when nil then nil
|
259
|
-
else yield(io)
|
260
|
-
end
|
261
|
-
end
|
262
|
-
|
263
41
|
# Initializes config. Default config values are overridden as specified.
|
264
42
|
def initialize_config(overrides={})
|
265
|
-
@config = ConfigHash.new(
|
266
|
-
|
267
|
-
# cache as configs (equivalent to self.class.configurations)
|
268
|
-
# as an optimization
|
269
|
-
configs = @config.configs
|
270
|
-
|
271
|
-
# hash overrides by delegate so they may be set
|
272
|
-
# in the correct order below
|
273
|
-
initial_values = {}
|
274
|
-
overrides.each_key do |key|
|
275
|
-
if config = configs[key]
|
276
|
-
|
277
|
-
# check that the config may be initialized
|
278
|
-
unless config.init?
|
279
|
-
key = configs.keys.find {|k| configs[k] == config }
|
280
|
-
raise "initialization values are not allowed for: #{key.inspect}"
|
281
|
-
end
|
282
|
-
|
283
|
-
# check that multiple overrides are not specified for a
|
284
|
-
# single config, as may happen with indifferent access
|
285
|
-
# (ex 'key' and :key)
|
286
|
-
if initial_values.has_key?(config)
|
287
|
-
key = configs.keys.find {|k| configs[k] == config }
|
288
|
-
raise "multiple values map to config: #{key.inspect}"
|
289
|
-
end
|
290
|
-
|
291
|
-
# since overrides are used as the ConfigHash store,
|
292
|
-
# the overriding values must be removed, not read
|
293
|
-
initial_values[config] = overrides.delete(key)
|
294
|
-
end
|
295
|
-
end
|
296
|
-
|
297
|
-
# now initialize configs in order
|
298
|
-
configs.each_pair do |key, config|
|
299
|
-
next unless config.init?
|
300
|
-
|
301
|
-
if initial_values.has_key?(config)
|
302
|
-
config.set(self, initial_values[config])
|
303
|
-
else
|
304
|
-
config.init(self)
|
305
|
-
end
|
306
|
-
end
|
43
|
+
@config = ConfigHash.new(overrides).bind(self)
|
307
44
|
end
|
308
45
|
end
|