configtoolkit 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +3 -0
- data/LICENSE +20 -0
- data/README.txt +67 -0
- data/Rakefile +16 -0
- data/lib/configtoolkit.rb +2 -0
- data/lib/configtoolkit/baseconfig.rb +555 -0
- data/lib/configtoolkit/toolkit.rb +16 -0
- data/lib/configtoolkit/types.rb +57 -0
- data/lib/configtoolkit/yamlreader.rb +20 -0
- data/lib/configtoolkit/yamlwriter.rb +61 -0
- data/test/bad_config.yaml +3 -0
- data/test/common.rb +5 -0
- data/test/contained_sample.yaml +22 -0
- data/test/firewall.yaml +11 -0
- data/test/machines.yaml +100 -0
- data/test/readerwritertest.rb +122 -0
- data/test/sample.ruby_yaml_types.yaml +34 -0
- data/test/sample.standard_yaml_types.yaml +21 -0
- data/test/test_baseconfig.rb +292 -0
- data/test/test_yaml.rb +76 -0
- data/test/webserver.yaml +9 -0
- metadata +85 -0
@@ -0,0 +1,16 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
module ConfigToolkit
|
3
|
+
|
4
|
+
#
|
5
|
+
# All exceptions thrown from the Toolkit descend from
|
6
|
+
# this one; at some point, an exception class hierarchy
|
7
|
+
# rooted in this class might be developed.
|
8
|
+
#
|
9
|
+
class Error < RuntimeError; end
|
10
|
+
|
11
|
+
VERSION = '1.0.0'
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'configtoolkit/types'
|
16
|
+
require 'configtoolkit/baseconfig'
|
@@ -0,0 +1,57 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
module ConfigToolkit
|
3
|
+
|
4
|
+
#
|
5
|
+
# Since Ruby does not have a boolean type (TrueClass and FalseClass classes
|
6
|
+
# exist, but they descend from object), define an empty marker class that
|
7
|
+
# can be passed into the BaseConfig methods to add new parameters in order
|
8
|
+
# to indicate that the parameter is a boolean (the value of the parameter will
|
9
|
+
# be FalseClass or TrueClass, however).
|
10
|
+
#
|
11
|
+
class Boolean; end
|
12
|
+
|
13
|
+
#
|
14
|
+
# The ConstrainedArray models an Array with a specified size
|
15
|
+
# and with all elements being a specified type (basically, it models
|
16
|
+
# the arrays generally offerred by static typing languages).
|
17
|
+
# A ConstrainedArray has:
|
18
|
+
# 1.) A minimum number of elements
|
19
|
+
# 2.) A maximum number of elements
|
20
|
+
# 3.) An element type
|
21
|
+
#
|
22
|
+
# A future enhancement might be to allow users to specify a different
|
23
|
+
# type for each element. Note that the Constrained Array also allows
|
24
|
+
# elements to be instances of a child class of its element type, not
|
25
|
+
# just instances of its element type. Thus, a ConstrainedArray containing
|
26
|
+
# type Object essentially would have no type constraints.
|
27
|
+
#
|
28
|
+
# class ConstrainedArray actually is a class generator, similar to Struct.
|
29
|
+
# Its new() method does *not* return a ConstrainedArray instance, but
|
30
|
+
# instead returns a new class with the specified constraints that
|
31
|
+
# descends from ConstrainedArray. The class returned by new() is meant
|
32
|
+
# to be used in the BaseConfig methods to add new parameters. The
|
33
|
+
# value of one of these parameters actually will be a native Ruby
|
34
|
+
# Array, just one that satisifes the constraints contained in
|
35
|
+
# the ConstrainedArray class.
|
36
|
+
#
|
37
|
+
class ConstrainedArray
|
38
|
+
#
|
39
|
+
# Define an accessor for some class instance variables used to
|
40
|
+
# store the constraints.
|
41
|
+
#
|
42
|
+
class << self
|
43
|
+
attr_reader :element_class
|
44
|
+
attr_reader :min_num_elements
|
45
|
+
attr_reader :max_num_elements
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.new(element_class, min_num_elements = nil, max_num_elements = nil)
|
49
|
+
return Class.new(ConstrainedArray) do
|
50
|
+
@element_class = element_class
|
51
|
+
@min_num_elements = min_num_elements
|
52
|
+
@max_num_elements = max_num_elements
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module ConfigToolkit
|
5
|
+
|
6
|
+
class YAMLReader
|
7
|
+
def initialize(stream)
|
8
|
+
if(stream.class == String)
|
9
|
+
@stream = File.open(stream, "r")
|
10
|
+
else
|
11
|
+
@stream = stream
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def read
|
16
|
+
return YAML::load(@stream)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
@@ -0,0 +1,61 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module ConfigToolkit
|
5
|
+
|
6
|
+
class YAMLWriter
|
7
|
+
def initialize(stream, only_output_standard_yaml_types)
|
8
|
+
if(stream.class == String)
|
9
|
+
@stream = File.open(stream, "w")
|
10
|
+
else
|
11
|
+
@stream = stream
|
12
|
+
end
|
13
|
+
|
14
|
+
@only_output_standard_yaml_types = only_output_standard_yaml_types
|
15
|
+
end
|
16
|
+
|
17
|
+
def convert_value_to_standard_yaml_type(param_value)
|
18
|
+
if(param_value.class == Hash)
|
19
|
+
return convert_hash_values_to_standard_yaml_types(param_value)
|
20
|
+
elsif(param_value.class == Array)
|
21
|
+
new_param_value = []
|
22
|
+
|
23
|
+
param_value.each do |element|
|
24
|
+
new_param_value.push(convert_value_to_standard_yaml_type(element))
|
25
|
+
end
|
26
|
+
|
27
|
+
return new_param_value
|
28
|
+
elsif((param_value.class <= Integer) ||
|
29
|
+
(param_value.class <= Float) ||
|
30
|
+
(param_value.class == TrueClass) ||
|
31
|
+
(param_value.class == FalseClass) ||
|
32
|
+
(param_value.class == String))
|
33
|
+
return param_value
|
34
|
+
else
|
35
|
+
return param_value.to_s
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def convert_hash_values_to_standard_yaml_types(config_hash)
|
40
|
+
standard_yaml_typed_config_hash = {}
|
41
|
+
|
42
|
+
config_hash.each do |param_name, param_value|
|
43
|
+
standard_yaml_typed_config_hash[param_name] = convert_value_to_standard_yaml_type(param_value)
|
44
|
+
end
|
45
|
+
|
46
|
+
return standard_yaml_typed_config_hash
|
47
|
+
end
|
48
|
+
|
49
|
+
def write(config_hash)
|
50
|
+
if(@only_output_standard_yaml_types)
|
51
|
+
output_config_hash = convert_hash_values_to_standard_yaml_types(config_hash)
|
52
|
+
else
|
53
|
+
output_config_hash = config_hash
|
54
|
+
end
|
55
|
+
|
56
|
+
YAML::dump(output_config_hash, @stream)
|
57
|
+
@stream.flush()
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
data/test/common.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
---
|
2
|
+
contained_sample:
|
3
|
+
opt_integer: 42
|
4
|
+
req_uri: http://www.designingpatterns.com
|
5
|
+
req_float: 5.678
|
6
|
+
req_pathname: /usr/designingpatterns
|
7
|
+
req_integer: 5
|
8
|
+
req_string: sample string
|
9
|
+
req_boolean: false
|
10
|
+
req_symbol: sample_symbol
|
11
|
+
req_big_integer: 10000000000
|
12
|
+
nested_config:
|
13
|
+
req_nested_array:
|
14
|
+
- req_second_nested_pathname: /usr/designingpatterns/Applications
|
15
|
+
req_second_nested_array:
|
16
|
+
- 5
|
17
|
+
- 19
|
18
|
+
- req_second_nested_pathname: /usr/designingpatterns/Applications/etc
|
19
|
+
req_second_nested_array:
|
20
|
+
- 200
|
21
|
+
- 201
|
22
|
+
req_nested_integer: 33
|
data/test/firewall.yaml
ADDED
data/test/machines.yaml
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
name: apple
|
2
|
+
architecture: powerpc
|
3
|
+
os: AIX
|
4
|
+
num_cpus: 32
|
5
|
+
behind_firewall: no
|
6
|
+
contains_sensitive_data: no
|
7
|
+
addresses:
|
8
|
+
- http://default.designingpatterns.com
|
9
|
+
- http://apple.designingpatterns.com
|
10
|
+
|
11
|
+
production:
|
12
|
+
name: orange
|
13
|
+
architecture: ultrasparc
|
14
|
+
os: Solaris
|
15
|
+
num_cpus: 64
|
16
|
+
contains_sensitive_data: yes
|
17
|
+
addresses:
|
18
|
+
- http://production.designingpatterns.com
|
19
|
+
- http://orange.designingpatterns.com
|
20
|
+
|
21
|
+
test:
|
22
|
+
name: bananna
|
23
|
+
architecture: itanium
|
24
|
+
os: Linux
|
25
|
+
num_cpus: 16
|
26
|
+
contains_sensitive_data: no
|
27
|
+
addresses:
|
28
|
+
- http://test.designingpatterns.com
|
29
|
+
- http://bananna.designingpatterns.com
|
30
|
+
|
31
|
+
bad_containing_object: 3
|
32
|
+
|
33
|
+
missing_num_cpus:
|
34
|
+
name: bananna
|
35
|
+
architecture: itanium
|
36
|
+
os: Linux
|
37
|
+
contains_sensitive_data: no
|
38
|
+
addresses:
|
39
|
+
- http://test.designingpatterns.com
|
40
|
+
- http://bananna.designingpatterns.com
|
41
|
+
|
42
|
+
missing_name_and_num_cpus:
|
43
|
+
architecture: itanium
|
44
|
+
os: Linux
|
45
|
+
contains_sensitive_data: no
|
46
|
+
addresses:
|
47
|
+
- http://test.designingpatterns.com
|
48
|
+
- http://bananna.designingpatterns.com
|
49
|
+
|
50
|
+
bad_num_cpus:
|
51
|
+
name: bananna
|
52
|
+
architecture: itanium
|
53
|
+
os: Linux
|
54
|
+
num_cpus: -2
|
55
|
+
contains_sensitive_data: no
|
56
|
+
addresses:
|
57
|
+
- http://test.designingpatterns.com
|
58
|
+
- http://bananna.designingpatterns.com
|
59
|
+
|
60
|
+
bad_addresses_element_type:
|
61
|
+
name: bananna
|
62
|
+
architecture: itanium
|
63
|
+
os: Linux
|
64
|
+
num_cpus: -2
|
65
|
+
contains_sensitive_data: no
|
66
|
+
addresses:
|
67
|
+
- 2
|
68
|
+
- http://bananna.designingpatterns.com
|
69
|
+
|
70
|
+
bad_addresses_max_num_elements:
|
71
|
+
name: bananna
|
72
|
+
architecture: itanium
|
73
|
+
os: Linux
|
74
|
+
num_cpus: -2
|
75
|
+
contains_sensitive_data: no
|
76
|
+
addresses:
|
77
|
+
- http://test.designingpatterns.com
|
78
|
+
- http://bananna.designingpatterns.com
|
79
|
+
- http://fruitsalad.designingpatterns.com
|
80
|
+
- http://parfait.designingpatterns.com
|
81
|
+
|
82
|
+
bad_addresses_min_num_elements:
|
83
|
+
name: bananna
|
84
|
+
architecture: itanium
|
85
|
+
os: Linux
|
86
|
+
num_cpus: -2
|
87
|
+
contains_sensitive_data: no
|
88
|
+
addresses:
|
89
|
+
- http://bananna.designingpatterns.com
|
90
|
+
|
91
|
+
bad_contains_sensitive_data_behind_firewall_combo:
|
92
|
+
name: bananna
|
93
|
+
architecture: itanium
|
94
|
+
os: Linux
|
95
|
+
num_cpus: 4
|
96
|
+
contains_sensitive_data: yes
|
97
|
+
behind_firewall: no
|
98
|
+
addresses:
|
99
|
+
- http://test.designingpatterns.com
|
100
|
+
- http://bananna.designingpatterns.com
|
@@ -0,0 +1,122 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'configtoolkit'
|
4
|
+
|
5
|
+
require 'pathname'
|
6
|
+
require 'singleton'
|
7
|
+
require 'uri'
|
8
|
+
|
9
|
+
class SampleSecondNestedConfig < ConfigToolkit::BaseConfig
|
10
|
+
add_required_param(:req_second_nested_pathname, Pathname)
|
11
|
+
add_required_param(:req_second_nested_array,
|
12
|
+
ConfigToolkit::ConstrainedArray.new(Integer, 2, 2))
|
13
|
+
end
|
14
|
+
|
15
|
+
class SampleNestedConfig < ConfigToolkit::BaseConfig
|
16
|
+
add_required_param(:req_nested_integer, Integer)
|
17
|
+
add_required_param(:req_nested_array,
|
18
|
+
ConfigToolkit::ConstrainedArray.new(SampleSecondNestedConfig))
|
19
|
+
end
|
20
|
+
|
21
|
+
class SampleConfig < ConfigToolkit::BaseConfig
|
22
|
+
add_required_param(:req_integer, Integer)
|
23
|
+
add_required_param(:req_big_integer, Integer)
|
24
|
+
add_required_param(:req_float, Float)
|
25
|
+
add_required_param(:req_string, String)
|
26
|
+
add_required_param(:req_boolean, ConfigToolkit::Boolean)
|
27
|
+
add_required_param(:req_pathname, Pathname)
|
28
|
+
add_required_param(:req_uri, URI)
|
29
|
+
add_required_param(:req_symbol, Symbol)
|
30
|
+
add_required_param(:nested_config, SampleNestedConfig)
|
31
|
+
add_optional_param(:opt_integer, Fixnum, 123454321)
|
32
|
+
end
|
33
|
+
|
34
|
+
REFERENCE_SAMPLE_CONFIG = SampleConfig.new()
|
35
|
+
REFERENCE_SAMPLE_CONFIG.req_integer = 6
|
36
|
+
REFERENCE_SAMPLE_CONFIG.req_big_integer = 90000000000
|
37
|
+
REFERENCE_SAMPLE_CONFIG.req_float = 3.14
|
38
|
+
REFERENCE_SAMPLE_CONFIG.req_string = "sample string2"
|
39
|
+
REFERENCE_SAMPLE_CONFIG.req_boolean = true
|
40
|
+
REFERENCE_SAMPLE_CONFIG.req_pathname = Pathname.new("/usr/designingpatterns/bin")
|
41
|
+
REFERENCE_SAMPLE_CONFIG.req_uri = URI("http://blogs.designingpatterns.com")
|
42
|
+
REFERENCE_SAMPLE_CONFIG.req_symbol = :sample_symbol2
|
43
|
+
REFERENCE_SAMPLE_CONFIG.nested_config = SampleNestedConfig.new()
|
44
|
+
REFERENCE_SAMPLE_CONFIG.nested_config.req_nested_integer = 34
|
45
|
+
REFERENCE_SAMPLE_CONFIG.nested_config.req_nested_array =
|
46
|
+
[
|
47
|
+
SampleSecondNestedConfig.new(),
|
48
|
+
SampleSecondNestedConfig.new()
|
49
|
+
]
|
50
|
+
REFERENCE_SAMPLE_CONFIG.nested_config.req_nested_array[0].req_second_nested_pathname = Pathname.new("/usr/designingpatterns/Applications/pattmake")
|
51
|
+
REFERENCE_SAMPLE_CONFIG.nested_config.req_nested_array[0].req_second_nested_array = [6, 20]
|
52
|
+
REFERENCE_SAMPLE_CONFIG.nested_config.req_nested_array[1].req_second_nested_pathname = Pathname.new("/usr")
|
53
|
+
REFERENCE_SAMPLE_CONFIG.nested_config.req_nested_array[1].req_second_nested_array = [205, 206]
|
54
|
+
REFERENCE_SAMPLE_CONFIG.opt_integer = 56
|
55
|
+
|
56
|
+
REFERENCE_CONTAINED_SAMPLE_CONFIG = SampleConfig.new()
|
57
|
+
REFERENCE_CONTAINED_SAMPLE_CONFIG.req_integer = 5
|
58
|
+
REFERENCE_CONTAINED_SAMPLE_CONFIG.req_big_integer = 10000000000
|
59
|
+
REFERENCE_CONTAINED_SAMPLE_CONFIG.req_float = 5.678
|
60
|
+
REFERENCE_CONTAINED_SAMPLE_CONFIG.req_string = "sample string"
|
61
|
+
REFERENCE_CONTAINED_SAMPLE_CONFIG.req_boolean = false
|
62
|
+
REFERENCE_CONTAINED_SAMPLE_CONFIG.req_pathname = Pathname.new("/usr/designingpatterns")
|
63
|
+
REFERENCE_CONTAINED_SAMPLE_CONFIG.req_uri = URI("http://www.designingpatterns.com")
|
64
|
+
REFERENCE_CONTAINED_SAMPLE_CONFIG.req_symbol = :sample_symbol
|
65
|
+
REFERENCE_CONTAINED_SAMPLE_CONFIG.nested_config = SampleNestedConfig.new()
|
66
|
+
REFERENCE_CONTAINED_SAMPLE_CONFIG.nested_config.req_nested_integer = 33
|
67
|
+
REFERENCE_CONTAINED_SAMPLE_CONFIG.nested_config.req_nested_array =
|
68
|
+
[
|
69
|
+
SampleSecondNestedConfig.new(),
|
70
|
+
SampleSecondNestedConfig.new()
|
71
|
+
]
|
72
|
+
REFERENCE_CONTAINED_SAMPLE_CONFIG.nested_config.req_nested_array[0].req_second_nested_pathname = Pathname.new("/usr/designingpatterns/Applications")
|
73
|
+
REFERENCE_CONTAINED_SAMPLE_CONFIG.nested_config.req_nested_array[0].req_second_nested_array = [5, 19]
|
74
|
+
REFERENCE_CONTAINED_SAMPLE_CONFIG.nested_config.req_nested_array[1].req_second_nested_pathname = Pathname.new("/usr/designingpatterns/Applications/etc")
|
75
|
+
REFERENCE_CONTAINED_SAMPLE_CONFIG.nested_config.req_nested_array[1].req_second_nested_array = [200, 201]
|
76
|
+
REFERENCE_CONTAINED_SAMPLE_CONFIG.opt_integer = 42
|
77
|
+
REFERENCE_CONTAINED_SAMPLE_CONFIG.instance_variable_set(:@containing_object_name, "contained_sample")
|
78
|
+
|
79
|
+
module ReaderWriterTest
|
80
|
+
def run_reader_test(reader_class,
|
81
|
+
containing_object_name,
|
82
|
+
reference_config,
|
83
|
+
*reader_constructor_args)
|
84
|
+
reader = reader_class.new(*reader_constructor_args)
|
85
|
+
config = SampleConfig.load(reader, containing_object_name)
|
86
|
+
assert_equal(reference_config, config)
|
87
|
+
end
|
88
|
+
|
89
|
+
def get_sorted_file_contents(file_name)
|
90
|
+
file_contents = File.open(file_name, "r") do |file|
|
91
|
+
file.read()
|
92
|
+
end
|
93
|
+
|
94
|
+
return file_contents.split("\n").sort().join("\n").to_s()
|
95
|
+
end
|
96
|
+
|
97
|
+
def run_writer_test(writer_class,
|
98
|
+
reference_config,
|
99
|
+
reference_file_name,
|
100
|
+
dump_file_name,
|
101
|
+
*writer_constructor_args)
|
102
|
+
writer = writer_class.new(*writer_constructor_args)
|
103
|
+
reference_config.dump(writer)
|
104
|
+
|
105
|
+
assert_equal(get_sorted_file_contents(reference_file_name),
|
106
|
+
get_sorted_file_contents(dump_file_name))
|
107
|
+
end
|
108
|
+
|
109
|
+
def run_reader_writer_test(reader_class,
|
110
|
+
writer_class,
|
111
|
+
reference_config,
|
112
|
+
reader_constructor_args,
|
113
|
+
writer_constructor_args)
|
114
|
+
writer = writer_class.new(*writer_constructor_args)
|
115
|
+
reference_config.dump(writer)
|
116
|
+
|
117
|
+
reader = reader_class.new(*reader_constructor_args)
|
118
|
+
config = SampleConfig.load(reader, reference_config.containing_object_name)
|
119
|
+
|
120
|
+
assert_equal(reference_config, config)
|
121
|
+
end
|
122
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
---
|
2
|
+
opt_integer: 56
|
3
|
+
req_uri: !ruby/object:URI::HTTP
|
4
|
+
fragment:
|
5
|
+
host: blogs.designingpatterns.com
|
6
|
+
opaque:
|
7
|
+
password:
|
8
|
+
path: ""
|
9
|
+
port: 80
|
10
|
+
query:
|
11
|
+
registry:
|
12
|
+
scheme: http
|
13
|
+
user:
|
14
|
+
req_float: 3.14
|
15
|
+
req_pathname: !ruby/object:Pathname
|
16
|
+
path: /usr/designingpatterns/bin
|
17
|
+
req_symbol: :sample_symbol2
|
18
|
+
req_boolean: true
|
19
|
+
req_string: sample string2
|
20
|
+
req_integer: 6
|
21
|
+
req_big_integer: 90000000000
|
22
|
+
nested_config:
|
23
|
+
req_nested_array:
|
24
|
+
- req_second_nested_pathname: !ruby/object:Pathname
|
25
|
+
path: /usr/designingpatterns/Applications/pattmake
|
26
|
+
req_second_nested_array:
|
27
|
+
- 6
|
28
|
+
- 20
|
29
|
+
- req_second_nested_pathname: !ruby/object:Pathname
|
30
|
+
path: /usr
|
31
|
+
req_second_nested_array:
|
32
|
+
- 205
|
33
|
+
- 206
|
34
|
+
req_nested_integer: 34
|