configtoolkit 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/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
|