continuent-tools-core 0.1.6 → 0.5.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.
- checksums.yaml +4 -4
- data/lib/continuent-tools-core.rb +0 -11
- data/lib/iniparse.rb +66 -0
- data/lib/iniparse/LICENSE +19 -0
- data/lib/iniparse/document.rb +84 -0
- data/lib/iniparse/generator.rb +203 -0
- data/lib/iniparse/line_collection.rb +170 -0
- data/lib/iniparse/lines.rb +327 -0
- data/lib/iniparse/parser.rb +110 -0
- data/lib/tungsten/api.rb +3 -3
- data/lib/tungsten/common.rb +2 -2
- data/lib/tungsten/exec.rb +1 -10
- data/lib/tungsten/iniparse.rb +53 -0
- data/lib/tungsten/install.rb +111 -3
- data/lib/tungsten/properties.rb +11 -19
- data/lib/tungsten/script.rb +124 -2
- data/lib/tungsten/util.rb +94 -1
- metadata +11 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 256816c02fae6a3705e6ebccdff65da094570a94
|
4
|
+
data.tar.gz: 9ce21782909d9e01f599191e51fceb9dd9c942e5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: fbf0c48597aedd4672accc6b5ed353604a488978b27500902a928df6d7c3e2bd859c682daf16a74345524ff08501dfbb3692b2a6573586c8ef025ef38e116241
|
7
|
+
data.tar.gz: 05cd1d15c8ba0bd16d46ac0aaf0564fe5187c1031e418befed059d3ac1329b7413d86487ea049bb914cf368dedfa7fbd72c2c176329d6dad79a51626587fad95
|
@@ -140,17 +140,6 @@ module TungstenScript
|
|
140
140
|
|
141
141
|
def validate
|
142
142
|
orig_validate()
|
143
|
-
|
144
|
-
@option_definitions.each{
|
145
|
-
|option_key,definition|
|
146
|
-
|
147
|
-
if definition[:required] == true
|
148
|
-
if opt(option_key).to_s() == ""
|
149
|
-
arg = definition[:on][0].split(" ")[0]
|
150
|
-
TU.error("Missing value for the #{arg} option")
|
151
|
-
end
|
152
|
-
end
|
153
|
-
}
|
154
143
|
end
|
155
144
|
end
|
156
145
|
|
data/lib/iniparse.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
dir = File.expand_path('iniparse', File.dirname(__FILE__))
|
2
|
+
|
3
|
+
require File.join(dir, 'document')
|
4
|
+
require File.join(dir, 'generator')
|
5
|
+
require File.join(dir, 'line_collection')
|
6
|
+
require File.join(dir, 'lines')
|
7
|
+
require File.join(dir, 'parser')
|
8
|
+
|
9
|
+
module IniParse
|
10
|
+
VERSION = '1.3.2'
|
11
|
+
|
12
|
+
# A base class for IniParse errors.
|
13
|
+
class IniParseError < StandardError; end
|
14
|
+
|
15
|
+
# Raised if an error occurs parsing an INI document.
|
16
|
+
class ParseError < IniParseError; end
|
17
|
+
|
18
|
+
# Raised when an option line is found during parsing before the first
|
19
|
+
# section.
|
20
|
+
class NoSectionError < ParseError; end
|
21
|
+
|
22
|
+
# Raised when a line is added to a collection which isn't allowed (e.g.
|
23
|
+
# adding a Section line into an OptionCollection).
|
24
|
+
class LineNotAllowed < IniParseError; end
|
25
|
+
|
26
|
+
module_function
|
27
|
+
|
28
|
+
# Parse given given INI document source +source+.
|
29
|
+
#
|
30
|
+
# See IniParse::Parser.parse
|
31
|
+
#
|
32
|
+
# ==== Parameters
|
33
|
+
# source<String>:: The source from the INI document.
|
34
|
+
#
|
35
|
+
# ==== Returns
|
36
|
+
# IniParse::Document
|
37
|
+
#
|
38
|
+
def parse(source)
|
39
|
+
IniParse::Parser.new(source).parse
|
40
|
+
end
|
41
|
+
|
42
|
+
# Opens the file at +path+, reads and parses it's contents.
|
43
|
+
#
|
44
|
+
# ==== Parameters
|
45
|
+
# path<String>:: The path to the INI document.
|
46
|
+
#
|
47
|
+
# ==== Returns
|
48
|
+
# IniParse::Document
|
49
|
+
#
|
50
|
+
def open(path)
|
51
|
+
document = IniParse::Parser.new(File.read(path)).parse
|
52
|
+
document.path = path
|
53
|
+
document
|
54
|
+
end
|
55
|
+
|
56
|
+
# Creates a new IniParse::Document using the specification you provide.
|
57
|
+
#
|
58
|
+
# See IniParse::Generator.
|
59
|
+
#
|
60
|
+
# ==== Returns
|
61
|
+
# IniParse::Document
|
62
|
+
#
|
63
|
+
def gen(&blk)
|
64
|
+
IniParse::Generator.new.gen(&blk)
|
65
|
+
end
|
66
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
Copyright (c) 2008-2010 Anthony Williams
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
15
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
16
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
17
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
18
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
19
|
+
SOFTWARE.
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module IniParse
|
2
|
+
# Represents an INI document.
|
3
|
+
class Document
|
4
|
+
include Enumerable
|
5
|
+
|
6
|
+
attr_reader :lines
|
7
|
+
attr_accessor :path
|
8
|
+
|
9
|
+
# Creates a new Document instance.
|
10
|
+
def initialize(path = nil)
|
11
|
+
@path = path
|
12
|
+
@lines = IniParse::SectionCollection.new
|
13
|
+
end
|
14
|
+
|
15
|
+
# Enumerates through each Section in this document.
|
16
|
+
#
|
17
|
+
# Does not yield blank and comment lines by default; if you want _all_
|
18
|
+
# lines to be yielded, pass true.
|
19
|
+
#
|
20
|
+
# ==== Parameters
|
21
|
+
# include_blank<Boolean>:: Include blank/comment lines?
|
22
|
+
#
|
23
|
+
def each(*args, &blk)
|
24
|
+
@lines.each(*args, &blk)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the section identified by +key+.
|
28
|
+
#
|
29
|
+
# Returns nil if there is no Section with the given key.
|
30
|
+
#
|
31
|
+
def [](key)
|
32
|
+
@lines[key.to_s]
|
33
|
+
end
|
34
|
+
|
35
|
+
# Deletes the section whose name matches the given +key+.
|
36
|
+
#
|
37
|
+
# Returns the document.
|
38
|
+
#
|
39
|
+
def delete(*args)
|
40
|
+
@lines.delete(*args)
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns this document as a string suitable for saving to a file.
|
45
|
+
def to_ini
|
46
|
+
string = @lines.to_a.map { |line| line.to_ini }.join($/)
|
47
|
+
string = "#{ string }\n" unless string[-1] == "\n"
|
48
|
+
|
49
|
+
string
|
50
|
+
end
|
51
|
+
|
52
|
+
alias_method :to_s, :to_ini
|
53
|
+
|
54
|
+
# A human-readable version of the document, for debugging.
|
55
|
+
def inspect
|
56
|
+
sections = @lines.select { |l| l.is_a?(IniParse::Lines::Section) }
|
57
|
+
"#<IniParse::Document {#{ sections.map(&:key).join(', ') }}>"
|
58
|
+
end
|
59
|
+
|
60
|
+
# Returns true if a section with the given +key+ exists in this document.
|
61
|
+
def has_section?(key)
|
62
|
+
@lines.has_key?(key.to_s)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Saves a copy of this Document to disk.
|
66
|
+
#
|
67
|
+
# If a path was supplied when the Document was initialized then nothing
|
68
|
+
# needs to be given to Document#save. If Document was not given a file
|
69
|
+
# path, or you wish to save the document elsewhere, supply a path when
|
70
|
+
# calling Document#save.
|
71
|
+
#
|
72
|
+
# ==== Parameters
|
73
|
+
# path<String>:: A path to which this document will be saved.
|
74
|
+
#
|
75
|
+
# ==== Raises
|
76
|
+
# IniParseError:: If your document couldn't be saved.
|
77
|
+
#
|
78
|
+
def save(path = nil)
|
79
|
+
@path = path if path
|
80
|
+
raise IniParseError, 'No path given to Document#save' if @path !~ /\S/
|
81
|
+
File.open(@path, 'w') { |f| f.write(self.to_ini) }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,203 @@
|
|
1
|
+
module IniParse
|
2
|
+
# Generator provides a means for easily creating new INI documents.
|
3
|
+
#
|
4
|
+
# Rather than trying to hack together new INI documents by manually creating
|
5
|
+
# Document, Section and Option instances, it is preferable to use Generator
|
6
|
+
# which will handle it all for you.
|
7
|
+
#
|
8
|
+
# The Generator is exposed through IniParse.gen.
|
9
|
+
#
|
10
|
+
# IniParse.gen do |doc|
|
11
|
+
# doc.section("vehicle") do |vehicle|
|
12
|
+
# vehicle.option("road_side", "left")
|
13
|
+
# vehicle.option("realistic_acceleration", true)
|
14
|
+
# vehicle.option("max_trains", 500)
|
15
|
+
# end
|
16
|
+
#
|
17
|
+
# doc.section("construction") do |construction|
|
18
|
+
# construction.option("build_on_slopes", true)
|
19
|
+
# construction.option("autoslope", true)
|
20
|
+
# end
|
21
|
+
# end
|
22
|
+
#
|
23
|
+
# # => IniParse::Document
|
24
|
+
#
|
25
|
+
# This can be simplified further if you don't mind the small overhead
|
26
|
+
# which comes with +method_missing+:
|
27
|
+
#
|
28
|
+
# IniParse.gen do |doc|
|
29
|
+
# doc.vehicle do |vehicle|
|
30
|
+
# vehicle.road_side = "left"
|
31
|
+
# vehicle.realistic_acceleration = true
|
32
|
+
# vehicle.max_trains = 500
|
33
|
+
# end
|
34
|
+
#
|
35
|
+
# doc.construction do |construction|
|
36
|
+
# construction.build_on_slopes = true
|
37
|
+
# construction.autoslope = true
|
38
|
+
# end
|
39
|
+
# end
|
40
|
+
#
|
41
|
+
# # => IniParse::Document
|
42
|
+
#
|
43
|
+
# If you want to add slightly more complicated formatting to your document,
|
44
|
+
# each line type (except blanks) takes a number of optional parameters:
|
45
|
+
#
|
46
|
+
# :comment::
|
47
|
+
# Adds an inline comment at the end of the line.
|
48
|
+
# :comment_offset::
|
49
|
+
# Indent the comment. Measured in characters from _beginning_ of the line.
|
50
|
+
# See String#ljust.
|
51
|
+
# :indent::
|
52
|
+
# Adds the supplied text to the beginning of the line.
|
53
|
+
#
|
54
|
+
# If you supply +:indent+, +:comment_sep+, or +:comment_offset+ options when
|
55
|
+
# adding a section, the same options will be inherited by all of the options
|
56
|
+
# which belong to it.
|
57
|
+
#
|
58
|
+
# IniParse.gen do |doc|
|
59
|
+
# doc.section("vehicle",
|
60
|
+
# :comment => "Options for vehicles", :indent => " "
|
61
|
+
# ) do |vehicle|
|
62
|
+
# vehicle.option("road_side", "left")
|
63
|
+
# vehicle.option("realistic_acceleration", true)
|
64
|
+
# vehicle.option("max_trains", 500, :comment => "More = slower")
|
65
|
+
# end
|
66
|
+
# end.to_ini
|
67
|
+
#
|
68
|
+
# [vehicle] ; Options for vehicles
|
69
|
+
# road_side = left
|
70
|
+
# realistic_acceleration = true
|
71
|
+
# max_trains = 500 ; More = slower
|
72
|
+
#
|
73
|
+
class Generator
|
74
|
+
attr_reader :context
|
75
|
+
attr_reader :document
|
76
|
+
|
77
|
+
def initialize(opts = {}) # :nodoc:
|
78
|
+
@document = IniParse::Document.new
|
79
|
+
@context = @document
|
80
|
+
|
81
|
+
@in_section = false
|
82
|
+
@opt_stack = [opts]
|
83
|
+
end
|
84
|
+
|
85
|
+
def gen # :nodoc:
|
86
|
+
yield self
|
87
|
+
@document
|
88
|
+
end
|
89
|
+
|
90
|
+
# Creates a new IniParse::Document with the given sections and options.
|
91
|
+
#
|
92
|
+
# ==== Returns
|
93
|
+
# IniParse::Document
|
94
|
+
#
|
95
|
+
def self.gen(opts = {}, &blk)
|
96
|
+
new(opts).gen(&blk)
|
97
|
+
end
|
98
|
+
|
99
|
+
# Creates a new section with the given name and adds it to the document.
|
100
|
+
#
|
101
|
+
# You can optionally supply a block (as detailed in the documentation for
|
102
|
+
# Generator#gen) in order to add options to the section.
|
103
|
+
#
|
104
|
+
# ==== Parameters
|
105
|
+
# name<String>:: A name for the given section.
|
106
|
+
#
|
107
|
+
def section(name, opts = {})
|
108
|
+
if @in_section
|
109
|
+
# Nesting sections is bad, mmmkay?
|
110
|
+
raise LineNotAllowed, "You can't nest sections in INI files."
|
111
|
+
end
|
112
|
+
|
113
|
+
if @document.has_section?(name.to_s())
|
114
|
+
@context = @document[name.to_s()]
|
115
|
+
else
|
116
|
+
@context = Lines::Section.new(name, line_options(opts))
|
117
|
+
@document.lines << @context
|
118
|
+
end
|
119
|
+
|
120
|
+
if block_given?
|
121
|
+
begin
|
122
|
+
@in_section = true
|
123
|
+
with_options(opts) { yield self }
|
124
|
+
@context = @document
|
125
|
+
blank()
|
126
|
+
ensure
|
127
|
+
@in_section = false
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
|
132
|
+
# Adds a new option to the current section.
|
133
|
+
#
|
134
|
+
# Can only be called as part of a section block, or after at least one
|
135
|
+
# section has been added to the document.
|
136
|
+
#
|
137
|
+
# ==== Parameters
|
138
|
+
# key<String>:: The key (name) for this option.
|
139
|
+
# value:: The option's value.
|
140
|
+
# opts<Hash>:: Extra options for the line (formatting, etc).
|
141
|
+
#
|
142
|
+
# ==== Raises
|
143
|
+
# IniParse::NoSectionError::
|
144
|
+
# If no section has been added to the document yet.
|
145
|
+
#
|
146
|
+
def option(key, value, opts = {})
|
147
|
+
@context.lines << Lines::Option.new(
|
148
|
+
key, value, line_options(opts)
|
149
|
+
)
|
150
|
+
rescue LineNotAllowed
|
151
|
+
# Tried to add an Option to a Document.
|
152
|
+
raise NoSectionError,
|
153
|
+
'Your INI document contains an option before the first section is ' \
|
154
|
+
'declared which is not allowed.'
|
155
|
+
end
|
156
|
+
|
157
|
+
# Adds a new comment line to the document.
|
158
|
+
#
|
159
|
+
# ==== Parameters
|
160
|
+
# comment<String>:: The text for the comment line.
|
161
|
+
#
|
162
|
+
def comment(comment, opts = {})
|
163
|
+
@context.lines << Lines::Comment.new(
|
164
|
+
line_options(opts.merge(:comment => comment))
|
165
|
+
)
|
166
|
+
end
|
167
|
+
|
168
|
+
# Adds a new blank line to the document.
|
169
|
+
def blank
|
170
|
+
@context.lines << Lines::Blank.new
|
171
|
+
end
|
172
|
+
|
173
|
+
# Wraps lines, setting default options for each.
|
174
|
+
def with_options(opts = {}) # :nodoc:
|
175
|
+
opts = opts.dup
|
176
|
+
opts.delete(:comment)
|
177
|
+
@opt_stack.push( @opt_stack.last.merge(opts))
|
178
|
+
yield self
|
179
|
+
@opt_stack.pop
|
180
|
+
end
|
181
|
+
|
182
|
+
def method_missing(name, *args, &blk) # :nodoc:
|
183
|
+
if m = name.to_s.match(/(.*)=$/)
|
184
|
+
option(m[1], *args)
|
185
|
+
else
|
186
|
+
section(name.to_s, *args, &blk)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
#######
|
191
|
+
private
|
192
|
+
#######
|
193
|
+
|
194
|
+
# Returns options for a line.
|
195
|
+
#
|
196
|
+
# If the context is a section, we use the section options as a base,
|
197
|
+
# rather than the global defaults.
|
198
|
+
#
|
199
|
+
def line_options(given_opts) # :nodoc:
|
200
|
+
@opt_stack.last.empty? ? given_opts : @opt_stack.last.merge(given_opts)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
@@ -0,0 +1,170 @@
|
|
1
|
+
module IniParse
|
2
|
+
# Represents a collection of lines in an INI document.
|
3
|
+
#
|
4
|
+
# LineCollection acts a bit like an Array/Hash hybrid, allowing arbitrary
|
5
|
+
# lines to be added to the collection, but also indexes the keys of Section
|
6
|
+
# and Option lines to enable O(1) lookup via LineCollection#[].
|
7
|
+
#
|
8
|
+
# The lines instances are stored in an array, +@lines+, while the index of
|
9
|
+
# each Section/Option is held in a Hash, +@indicies+, keyed with the
|
10
|
+
# Section/Option#key value (see LineCollection#[]=).
|
11
|
+
#
|
12
|
+
module LineCollection
|
13
|
+
include Enumerable
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
@lines = []
|
17
|
+
@indicies = {}
|
18
|
+
end
|
19
|
+
|
20
|
+
# Retrive a value identified by +key+.
|
21
|
+
def [](key)
|
22
|
+
has_key?(key) ? @lines[ @indicies[key] ] : nil
|
23
|
+
end
|
24
|
+
|
25
|
+
# Set a +value+ identified by +key+.
|
26
|
+
#
|
27
|
+
# If a value with the given key already exists, the value will be replaced
|
28
|
+
# with the new one, with the new value taking the position of the old.
|
29
|
+
#
|
30
|
+
def []=(key, value)
|
31
|
+
key = key.to_s
|
32
|
+
|
33
|
+
if has_key?(key)
|
34
|
+
@lines[ @indicies[key] ] = value
|
35
|
+
else
|
36
|
+
@lines << value
|
37
|
+
@indicies[key] = @lines.length - 1
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Appends a line to the collection.
|
42
|
+
#
|
43
|
+
# Note that if you pass a line with a key already represented in the
|
44
|
+
# collection, the old item will be replaced.
|
45
|
+
#
|
46
|
+
def <<(line)
|
47
|
+
line.blank? ? (@lines << line) : (self[line.key] = line) ; self
|
48
|
+
end
|
49
|
+
|
50
|
+
alias_method :push, :<<
|
51
|
+
|
52
|
+
# Enumerates through the collection.
|
53
|
+
#
|
54
|
+
# By default #each does not yield blank and comment lines.
|
55
|
+
#
|
56
|
+
# ==== Parameters
|
57
|
+
# include_blank<Boolean>:: Include blank/comment lines?
|
58
|
+
#
|
59
|
+
def each(include_blank = false)
|
60
|
+
@lines.each do |line|
|
61
|
+
if include_blank || ! (line.is_a?(Array) ? line.empty? : line.blank?)
|
62
|
+
yield(line)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
# Removes the value identified by +key+.
|
68
|
+
def delete(key)
|
69
|
+
key = key.key if key.respond_to?(:key)
|
70
|
+
|
71
|
+
unless (idx = @indicies[key]).nil?
|
72
|
+
@indicies.delete(key)
|
73
|
+
@indicies.each { |k,v| @indicies[k] = v -= 1 if v > idx }
|
74
|
+
@lines.delete_at(idx)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns whether +key+ is in the collection.
|
79
|
+
def has_key?(*args)
|
80
|
+
@indicies.has_key?(*args)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Return an array containing the keys for the lines added to this
|
84
|
+
# collection.
|
85
|
+
def keys
|
86
|
+
map { |line| line.key }
|
87
|
+
end
|
88
|
+
|
89
|
+
# Returns this collection as an array. Includes blank and comment lines.
|
90
|
+
def to_a
|
91
|
+
@lines.dup
|
92
|
+
end
|
93
|
+
|
94
|
+
# Returns this collection as a hash. Does not contain blank and comment
|
95
|
+
# lines.
|
96
|
+
def to_hash
|
97
|
+
Hash[ *(map { |line| [line.key, line] }).flatten ]
|
98
|
+
end
|
99
|
+
|
100
|
+
alias_method :to_h, :to_hash
|
101
|
+
end
|
102
|
+
|
103
|
+
# A implementation of LineCollection used for storing (mostly) Option
|
104
|
+
# instances contained within a Section.
|
105
|
+
#
|
106
|
+
# Since it is assumed that an INI document will only represent a section
|
107
|
+
# once, if SectionCollection encounters a Section key already held in the
|
108
|
+
# collection, the existing section is merged with the new one (see
|
109
|
+
# IniParse::Lines::Section#merge!).
|
110
|
+
class SectionCollection
|
111
|
+
include LineCollection
|
112
|
+
|
113
|
+
def <<(line)
|
114
|
+
if line.kind_of?(IniParse::Lines::Option)
|
115
|
+
option = line
|
116
|
+
line = IniParse::Lines::AnonymousSection.new
|
117
|
+
|
118
|
+
line.lines << option if option
|
119
|
+
end
|
120
|
+
|
121
|
+
if line.blank? || (! has_key?(line.key))
|
122
|
+
super # Adding a new section, comment or blank line.
|
123
|
+
else
|
124
|
+
self[line.key].merge!(line)
|
125
|
+
end
|
126
|
+
|
127
|
+
self
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
# A implementation of LineCollection used for storing (mostly) Option
|
132
|
+
# instances contained within a Section.
|
133
|
+
#
|
134
|
+
# Whenever OptionCollection encounters an Option key already held in the
|
135
|
+
# collection, it treats it as a duplicate. This means that instead of
|
136
|
+
# overwriting the existing value, the value is changed to an array
|
137
|
+
# containing the previous _and_ the new Option instances.
|
138
|
+
class OptionCollection
|
139
|
+
include LineCollection
|
140
|
+
|
141
|
+
# Appends a line to the collection.
|
142
|
+
#
|
143
|
+
# If you push an Option with a key already represented in the collection,
|
144
|
+
# the previous Option will not be overwritten, but treated as a duplicate.
|
145
|
+
#
|
146
|
+
# ==== Parameters
|
147
|
+
# line<IniParse::LineType::Line>:: The line to be added to this section.
|
148
|
+
#
|
149
|
+
def <<(line)
|
150
|
+
if line.kind_of?(IniParse::Lines::Section)
|
151
|
+
raise IniParse::LineNotAllowed,
|
152
|
+
"You can't add a Section to an OptionCollection."
|
153
|
+
end
|
154
|
+
|
155
|
+
if line.blank? || (! has_key?(line.key))
|
156
|
+
super # Adding a new option, comment or blank line.
|
157
|
+
else
|
158
|
+
self[line.key] = [self[line.key], line].flatten
|
159
|
+
end
|
160
|
+
|
161
|
+
self
|
162
|
+
end
|
163
|
+
|
164
|
+
# Return an array containing the keys for the lines added to this
|
165
|
+
# collection.
|
166
|
+
def keys
|
167
|
+
map { |line| line.kind_of?(Array) ? line.first.key : line.key }
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|