configurable 0.1.0 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History +9 -0
- data/MIT-LICENSE +1 -1
- data/README +40 -142
- data/lib/cdoc.rb +413 -0
- data/lib/cdoc/cdoc_html_generator.rb +38 -0
- data/lib/cdoc/cdoc_html_template.rb +42 -0
- data/lib/config_parser.rb +302 -52
- data/lib/config_parser/option.rb +70 -21
- data/lib/config_parser/switch.rb +25 -10
- data/lib/config_parser/utils.rb +41 -27
- data/lib/configurable.rb +64 -40
- data/lib/configurable/class_methods.rb +245 -100
- data/lib/configurable/delegate.rb +18 -2
- data/lib/configurable/delegate_hash.rb +112 -69
- data/lib/configurable/indifferent_access.rb +21 -8
- data/lib/configurable/utils.rb +193 -0
- data/lib/configurable/validation.rb +112 -112
- metadata +16 -15
data/lib/config_parser/option.rb
CHANGED
@@ -1,35 +1,63 @@
|
|
1
1
|
require 'config_parser/utils'
|
2
2
|
|
3
|
-
class ConfigParser
|
3
|
+
class ConfigParser
|
4
|
+
|
5
|
+
# Represents an option registered with ConfigParser.
|
4
6
|
class Option
|
5
|
-
|
7
|
+
|
8
|
+
# A format string used by to_s
|
9
|
+
LINE_FORMAT = "%-36s %-43s"
|
10
|
+
|
11
|
+
# The short switch mapping to self
|
6
12
|
attr_reader :short
|
13
|
+
|
14
|
+
# The long switch mapping to self
|
7
15
|
attr_reader :long
|
16
|
+
|
17
|
+
# The argument name printed by to_s. If arg_name
|
18
|
+
# is nil, no value will be parsed for self.
|
8
19
|
attr_reader :arg_name
|
9
|
-
|
20
|
+
|
21
|
+
# The description printed by to_s
|
22
|
+
attr_reader :desc
|
23
|
+
|
24
|
+
# The block called when one of the switches mapping
|
25
|
+
# to self is parse; block will receive the parsed
|
26
|
+
# argument if arg_name is specified.
|
10
27
|
attr_reader :block
|
11
28
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
29
|
+
# Initializes a new Option using attribute values for :long, :short,
|
30
|
+
# :arg_name, and :desc. The long and short values are transformed
|
31
|
+
# using Utils.longify and Utils.shortify, meaning both bare strings
|
32
|
+
# (ex 'opt', 'o') and full switches ('--opt', '-o') are valid.
|
33
|
+
def initialize(attributes={}, &block)
|
34
|
+
@short = Utils.shortify(attributes[:short])
|
35
|
+
@long = Utils.longify(attributes[:long])
|
36
|
+
@arg_name = attributes[:arg_name]
|
37
|
+
@desc = attributes[:desc]
|
17
38
|
@block = block
|
18
39
|
end
|
19
40
|
|
20
|
-
# Returns an array of non-nil switches mapping to
|
21
|
-
# (ie [long, short]). May be overridden in subclasses.
|
41
|
+
# Returns an array of non-nil switches mapping to self (ie [long, short]).
|
22
42
|
def switches
|
23
43
|
[long, short].compact
|
24
44
|
end
|
25
|
-
|
26
|
-
#
|
27
|
-
#
|
45
|
+
|
46
|
+
# Parse determines how an option is actually parsed from an argv. Parse
|
47
|
+
# recieves the switch mapping to self for cases in which it affects the
|
48
|
+
# outcome (see Switch). By default parse has two modes of action:
|
49
|
+
#
|
50
|
+
# ==== Argument-style option (arg_name is specified)
|
51
|
+
#
|
52
|
+
# If arg_name is set, then parse passes value to the block. If no value
|
53
|
+
# is specified, the next argument in argv is used instead. An error
|
54
|
+
# is raised if no value can be found.
|
55
|
+
#
|
56
|
+
# ==== Flag-style option (no arg_name is specified)
|
57
|
+
#
|
58
|
+
# In this case, parse simply calls the block. If a non-nil value is
|
59
|
+
# specified, parse raises an error.
|
28
60
|
#
|
29
|
-
# Parse is a hook for fancier ways of determining an option
|
30
|
-
# value and/or setting the value in config. Parse recieves
|
31
|
-
# the switch (ie long or short) mapping to self for subclasses
|
32
|
-
# that need it (ex the Switch class).
|
33
61
|
def parse(switch, value, argv)
|
34
62
|
if arg_name
|
35
63
|
unless value
|
@@ -42,11 +70,32 @@ class ConfigParser
|
|
42
70
|
block ? block.call : nil
|
43
71
|
end
|
44
72
|
end
|
45
|
-
|
73
|
+
|
74
|
+
# Formats self as a help string for use on the command line.
|
46
75
|
def to_s
|
47
|
-
|
48
|
-
|
49
|
-
|
76
|
+
lines = Lazydoc::Utils.wrap(desc.to_s, 43)
|
77
|
+
|
78
|
+
header = " #{short_str} #{long_str} #{arg_name}"
|
79
|
+
header = header.length > 36 ? header.ljust(80) : (LINE_FORMAT % [header, lines.shift])
|
80
|
+
|
81
|
+
if lines.empty?
|
82
|
+
header
|
83
|
+
else
|
84
|
+
lines.collect! {|line| LINE_FORMAT % [nil, line] }
|
85
|
+
"#{header}\n#{lines.join("\n")}"
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
# helper returning short formatted for to_s
|
92
|
+
def short_str # :nodoc:
|
93
|
+
short ? short + ',' : ' '
|
94
|
+
end
|
95
|
+
|
96
|
+
# helper returning long formatted for to_s
|
97
|
+
def long_str # :nodoc:
|
98
|
+
long
|
50
99
|
end
|
51
100
|
end
|
52
101
|
end
|
data/lib/config_parser/switch.rb
CHANGED
@@ -1,29 +1,44 @@
|
|
1
|
-
class ConfigParser
|
1
|
+
class ConfigParser
|
2
|
+
|
3
|
+
# Switch represents a special type of Option where both a positive
|
4
|
+
# (--switch) and negative (--no-switch) version of long should
|
5
|
+
# map to self. A short may be specified for Switch; it will always
|
6
|
+
# be treated like the positive switch.
|
2
7
|
class Switch < Option
|
8
|
+
|
9
|
+
# The negative long switch, determined from long.
|
3
10
|
attr_reader :negative_long
|
4
|
-
|
11
|
+
|
12
|
+
# Initializes a new Switch. Raises an error if an arg_name is
|
13
|
+
# specified for self (as switches are intended to be boolean
|
14
|
+
# in nature), or if no long option is specified.
|
5
15
|
def initialize(options={})
|
6
16
|
super
|
7
17
|
raise ArgumentError, "arg_name specified for switch: #{arg_name}" if arg_name
|
8
18
|
raise ArgumentError, "no long specified" unless long
|
9
|
-
@negative_long = Utils.
|
19
|
+
@negative_long = Utils.prefix_long(long, 'no-')
|
10
20
|
end
|
11
|
-
|
21
|
+
|
22
|
+
# Returns an array of non-nil switches mapping to self (ie
|
23
|
+
# [long, negative_long, short]).
|
12
24
|
def switches
|
13
25
|
[long, negative_long, short].compact
|
14
26
|
end
|
15
|
-
|
27
|
+
|
28
|
+
# Calls the block with false if the negative long is specified,
|
29
|
+
# or calls the block with true in all other cases. Raises an
|
30
|
+
# error if a value is specified.
|
16
31
|
def parse(switch, value, argv)
|
17
32
|
raise "value specified for switch" if value
|
18
33
|
value = (switch == negative_long ? false : true)
|
19
34
|
block ? block.call(value) : value
|
20
35
|
end
|
21
36
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
37
|
+
private
|
38
|
+
|
39
|
+
# helper returning long formatted for to_s
|
40
|
+
def long_str # :nodoc:
|
41
|
+
long ? Utils.prefix_long(long, '[no-]') : ''
|
27
42
|
end
|
28
43
|
end
|
29
44
|
end
|
data/lib/config_parser/utils.rb
CHANGED
@@ -1,4 +1,6 @@
|
|
1
1
|
class ConfigParser
|
2
|
+
|
3
|
+
# A medly of methods used throughout the ConfigParser classes.
|
2
4
|
module Utils
|
3
5
|
module_function
|
4
6
|
|
@@ -34,8 +36,8 @@ class ConfigParser
|
|
34
36
|
# an error if the option does not match SHORT_OPTION. Nils
|
35
37
|
# are returned directly.
|
36
38
|
#
|
37
|
-
#
|
38
|
-
#
|
39
|
+
# shortify("-o") # => '-o'
|
40
|
+
# shortify(:o) # => '-o'
|
39
41
|
#
|
40
42
|
def shortify(str)
|
41
43
|
return nil if str == nil
|
@@ -52,9 +54,9 @@ class ConfigParser
|
|
52
54
|
# are converted to hyphens. Raises an error if the option does
|
53
55
|
# not match LONG_OPTION. Nils are returned directly.
|
54
56
|
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
57
|
+
# longify("--opt") # => '--opt'
|
58
|
+
# longify(:opt) # => '--opt'
|
59
|
+
# longify(:opt_ion) # => '--opt-ion'
|
58
60
|
#
|
59
61
|
def longify(str)
|
60
62
|
return nil if str == nil
|
@@ -68,58 +70,70 @@ class ConfigParser
|
|
68
70
|
str
|
69
71
|
end
|
70
72
|
|
71
|
-
#
|
73
|
+
# Adds a prefix onto the last nested segment of a long option.
|
74
|
+
#
|
75
|
+
# prefix_long("--opt", 'no-') # => '--no-opt'
|
76
|
+
# prefix_long("--nested:opt", 'no-') # => '--nested:no-opt'
|
77
|
+
#
|
78
|
+
def prefix_long(switch, prefix, split_char=':')
|
79
|
+
switch = switch[2,switch.length-2] if switch =~ /^--/
|
80
|
+
switch = switch.split(split_char)
|
81
|
+
switch[-1] = "#{prefix}#{switch[-1]}"
|
82
|
+
"--#{switch.join(':')}"
|
83
|
+
end
|
84
|
+
|
85
|
+
# Attributes:
|
72
86
|
#
|
73
87
|
# :long the long key ("--key")
|
74
88
|
# :arg_name the argument name ("KEY")
|
75
89
|
#
|
76
|
-
def setup_option(key,
|
77
|
-
|
78
|
-
|
79
|
-
|
90
|
+
def setup_option(key, attributes={})
|
91
|
+
attributes[:long] ||= "--#{key}"
|
92
|
+
attributes[:long].to_s =~ /^(--)?(.*)$/
|
93
|
+
attributes[:arg_name] ||= $2.upcase
|
80
94
|
|
81
95
|
lambda {|value| config[key] = value }
|
82
96
|
end
|
83
97
|
|
84
|
-
#
|
98
|
+
# Attributes:
|
85
99
|
#
|
86
100
|
# :long the long key ("--key")
|
87
101
|
#
|
88
|
-
def setup_flag(key, default=true,
|
89
|
-
|
102
|
+
def setup_flag(key, default=true, attributes={})
|
103
|
+
attributes[:long] ||= "--#{key}"
|
90
104
|
|
91
105
|
lambda {config[key] = !default }
|
92
106
|
end
|
93
107
|
|
94
|
-
#
|
108
|
+
# Attributes:
|
95
109
|
#
|
96
110
|
# :long the long key ("--[no-]key")
|
97
111
|
#
|
98
|
-
def setup_switch(key, default=true,
|
99
|
-
|
100
|
-
|
101
|
-
|
112
|
+
def setup_switch(key, default=true, attributes={})
|
113
|
+
attributes[:long] ||= "--#{key}"
|
114
|
+
attributes[:long].to_s =~ /^(--)?(\[no-\])?(.*)$/
|
115
|
+
attributes[:long] = "--[no-]#{$3}" unless $2
|
102
116
|
|
103
|
-
lambda {|value| config[key] =
|
117
|
+
lambda {|value| config[key] = value }
|
104
118
|
end
|
105
119
|
|
106
|
-
#
|
120
|
+
# Attributes:
|
107
121
|
#
|
108
122
|
# :long the long key ("--key")
|
109
123
|
# :arg_name the argument name ("KEY" or "A,B,C" for a comma split)
|
110
124
|
# :split the split character
|
111
125
|
#
|
112
|
-
def setup_list(key,
|
113
|
-
|
126
|
+
def setup_list(key, attributes={})
|
127
|
+
attributes[:long] ||= "--#{key}"
|
114
128
|
|
115
|
-
if split =
|
116
|
-
|
129
|
+
if split = attributes[:split]
|
130
|
+
attributes[:arg_name] ||= %w{A B C}.join(split)
|
117
131
|
else
|
118
|
-
|
119
|
-
|
132
|
+
attributes[:long].to_s =~ /^(--)?(.*)$/
|
133
|
+
attributes[:arg_name] ||= $2.upcase
|
120
134
|
end
|
121
135
|
|
122
|
-
n =
|
136
|
+
n = attributes[:n]
|
123
137
|
|
124
138
|
lambda do |value|
|
125
139
|
array = (config[key] ||= [])
|
data/lib/configurable.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'configurable/class_methods'
|
2
2
|
|
3
|
-
# Configurable enables the specification of configurations within a class
|
3
|
+
# Configurable enables the specification of configurations within a class
|
4
|
+
# definition.
|
4
5
|
#
|
5
6
|
# class ConfigClass
|
6
7
|
# include Configurable
|
@@ -9,17 +10,15 @@ require 'configurable/class_methods'
|
|
9
10
|
# config :two, 'two'
|
10
11
|
# config :three, 'three'
|
11
12
|
#
|
12
|
-
# def initialize(overrides={})
|
13
|
-
# initialize_config(overrides)
|
14
|
-
# end
|
15
13
|
# end
|
16
14
|
#
|
17
15
|
# c = ConfigClass.new
|
18
16
|
# c.config.class # => Configurable::DelegateHash
|
19
17
|
# c.config # => {:one => 'one', :two => 'two', :three => 'three'}
|
20
18
|
#
|
21
|
-
#
|
22
|
-
#
|
19
|
+
# Instances have a <tt>config</tt> object that acts like a forwarding hash;
|
20
|
+
# configuration keys delegate to accessors while undeclared key-value pairs
|
21
|
+
# are stored internally:
|
23
22
|
#
|
24
23
|
# c.config[:one] = 'ONE'
|
25
24
|
# c.one # => 'ONE'
|
@@ -32,7 +31,7 @@ require 'configurable/class_methods'
|
|
32
31
|
#
|
33
32
|
# The writer for a configuration can be defined by providing a block to config.
|
34
33
|
# The Validation module provides a number of common validation/transform
|
35
|
-
# blocks
|
34
|
+
# blocks accessible through the class method 'c':
|
36
35
|
#
|
37
36
|
# class SubClass < ConfigClass
|
38
37
|
# config(:one, 'one') {|v| v.upcase }
|
@@ -52,12 +51,39 @@ require 'configurable/class_methods'
|
|
52
51
|
# s.two = nil # !> ValidationError
|
53
52
|
# s.two = 'str' # !> ValidationError
|
54
53
|
#
|
55
|
-
#
|
56
|
-
#
|
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 ClassConfig
|
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 ConfigClass
|
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.
|
57
83
|
#
|
58
|
-
# ===
|
84
|
+
# === Attributes
|
59
85
|
#
|
60
|
-
# Alternative reader and writer methods may be specified as
|
86
|
+
# Alternative reader and writer methods may be specified as config attributes.
|
61
87
|
# When alternate methods are specified, Configurable assumes the methods are
|
62
88
|
# declared elsewhere and will not define accessors.
|
63
89
|
#
|
@@ -66,10 +92,6 @@ require 'configurable/class_methods'
|
|
66
92
|
#
|
67
93
|
# config_attr :sym, 'value', :reader => :get_sym, :writer => :set_sym
|
68
94
|
#
|
69
|
-
# def initialize
|
70
|
-
# initialize_config
|
71
|
-
# end
|
72
|
-
#
|
73
95
|
# def get_sym
|
74
96
|
# @sym
|
75
97
|
# end
|
@@ -89,8 +111,8 @@ require 'configurable/class_methods'
|
|
89
111
|
# alt.set_sym('two')
|
90
112
|
# alt.config[:sym] # => :two
|
91
113
|
#
|
92
|
-
# Idiosyncratically, true, false, and nil may also be provided as
|
93
|
-
#
|
114
|
+
# Idiosyncratically, true, false, and nil may also be provided as reader/writer
|
115
|
+
# options.
|
94
116
|
#
|
95
117
|
# true Same as using the defaults, accessors are defined.
|
96
118
|
#
|
@@ -102,25 +124,39 @@ require 'configurable/class_methods'
|
|
102
124
|
# that does not map to the instance, but will be
|
103
125
|
# present in instance.config
|
104
126
|
#
|
127
|
+
# ==== Non-reader/writer attributes
|
128
|
+
#
|
129
|
+
# Metadata for a config may be specified in attributes as well. Attributes like
|
130
|
+
# :desc, and :type are used by ConfigParser, for instance, to determine how to
|
131
|
+
# represent the configuration on the command line. Attributes are unstructured
|
132
|
+
# so they can accomodate metadata for multiple contexts (ex a web or desktop
|
133
|
+
# interface), as needed.
|
134
|
+
#
|
105
135
|
module Configurable
|
106
|
-
|
136
|
+
autoload(:Utils, 'configurable/utils')
|
137
|
+
|
107
138
|
# Extends including classes with Configurable::ClassMethods
|
108
139
|
def self.included(mod) # :nodoc:
|
109
140
|
mod.extend ClassMethods if mod.kind_of?(Class)
|
110
141
|
end
|
111
142
|
|
112
|
-
# A
|
143
|
+
# A DelegateHash bound to self
|
113
144
|
attr_reader :config
|
145
|
+
|
146
|
+
# Initializes config, if necessary, and then calls super. If initialize
|
147
|
+
# is overridden without calling super, be sure to call initialize_config
|
148
|
+
# manually within the new initialize method.
|
149
|
+
def initialize(*args)
|
150
|
+
initialize_config unless instance_variable_defined?(:@config)
|
151
|
+
super
|
152
|
+
end
|
114
153
|
|
115
|
-
# Reconfigures self with the given overrides. Only the
|
116
|
-
#
|
154
|
+
# Reconfigures self with the given overrides. Only the
|
155
|
+
# specified configs are modified.
|
117
156
|
#
|
118
157
|
# Returns self.
|
119
158
|
def reconfigure(overrides={})
|
120
|
-
overrides
|
121
|
-
config[key] = value
|
122
|
-
end
|
123
|
-
|
159
|
+
config.merge!(overrides)
|
124
160
|
self
|
125
161
|
end
|
126
162
|
|
@@ -129,7 +165,7 @@ module Configurable
|
|
129
165
|
# separate from the original object.
|
130
166
|
def initialize_copy(orig)
|
131
167
|
super
|
132
|
-
initialize_config(orig.config)
|
168
|
+
initialize_config(orig.config.dup)
|
133
169
|
end
|
134
170
|
|
135
171
|
protected
|
@@ -137,19 +173,7 @@ module Configurable
|
|
137
173
|
# Initializes config. Default config values
|
138
174
|
# are overridden as specified by overrides.
|
139
175
|
def initialize_config(overrides={})
|
140
|
-
|
141
|
-
|
142
|
-
# note the defaults could be stored first and overridden
|
143
|
-
# by the overrides, but this is likely more efficient
|
144
|
-
# on average since delegates duplicate default values.
|
145
|
-
store = {}
|
146
|
-
overrides.each_pair do |key, value|
|
147
|
-
store[key] = value
|
148
|
-
end
|
149
|
-
delegates.each_pair do |key, delegate|
|
150
|
-
store[key] = delegate.default unless store.has_key?(key)
|
151
|
-
end
|
152
|
-
|
153
|
-
@config = DelegateHash.new(delegates, store).bind(self)
|
176
|
+
@config = overrides.kind_of?(DelegateHash) ? overrides : DelegateHash.new(self.class.configurations, overrides)
|
177
|
+
@config.bind(self)
|
154
178
|
end
|
155
179
|
end
|