cfa 0.4.1 → 0.4.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/cfa/augeas_parser.rb +130 -65
- data/lib/cfa/base_model.rb +45 -9
- data/lib/cfa/matcher.rb +40 -4
- data/lib/cfa/placer.rb +40 -15
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2a5ddfd0e34f669de02849ae3fcbc4ca3dafb664
|
4
|
+
data.tar.gz: b4d6d350b7a072b7168a9250454115c206630e53
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 265417fcbac259f04e42331f29918ebf4e575bc5e8f58d564af944355d12fc4dfd271bae09acb8213fa9cf6b5782189090d1e5f672d39d4b3687d025de9d43d1
|
7
|
+
data.tar.gz: 6862d7929f54bdf1673dd6f3721481fe7953a7a960b00ddc905254bd5dec4d608eec94ac36f388c959228e13fbe4876267eda2b5df3f661da7fb546262aca120
|
data/lib/cfa/augeas_parser.rb
CHANGED
@@ -3,6 +3,25 @@ require "forwardable"
|
|
3
3
|
require "cfa/placer"
|
4
4
|
|
5
5
|
module CFA
|
6
|
+
# A building block for {AugeasTree}.
|
7
|
+
#
|
8
|
+
# Intuitively the tree is made of hashes where keys may be duplicated,
|
9
|
+
# so it is implemented as a sequence of hashes with two keys, :key and :value.
|
10
|
+
#
|
11
|
+
# A `:key` is a String.
|
12
|
+
# The key may have a collection suffix "[]". Note that in contrast
|
13
|
+
# with the underlying {::Augeas} library, an integer index is not present
|
14
|
+
# (which should make it easier to modify collections of elements).
|
15
|
+
#
|
16
|
+
# A `:value` is either a String, or an {AugeasTree},
|
17
|
+
# or an {AugeasTreeValue} (which combines both).
|
18
|
+
#
|
19
|
+
# @return [Hash{Symbol => String, AugeasTree}]
|
20
|
+
#
|
21
|
+
# @todo Unify naming: entry, element
|
22
|
+
class AugeasElement < Hash
|
23
|
+
end
|
24
|
+
|
6
25
|
# Represents list of same config options in augeas.
|
7
26
|
# For example comments are often stored in collections.
|
8
27
|
class AugeasCollection
|
@@ -19,6 +38,7 @@ module CFA
|
|
19
38
|
element = placer.new_element(@tree)
|
20
39
|
element[:key] = augeas_name
|
21
40
|
element[:value] = value
|
41
|
+
# FIXME: load_collection missing here
|
22
42
|
end
|
23
43
|
|
24
44
|
def delete(value)
|
@@ -47,12 +67,12 @@ module CFA
|
|
47
67
|
end
|
48
68
|
end
|
49
69
|
|
50
|
-
# Represents node that
|
51
|
-
# For easier
|
70
|
+
# Represents a node that contains both a value and a subtree below it.
|
71
|
+
# For easier traversal it forwards `#[]` to the subtree.
|
52
72
|
class AugeasTreeValue
|
53
|
-
# value in node
|
73
|
+
# @return [String] the value in the node
|
54
74
|
attr_accessor :value
|
55
|
-
# subtree below node
|
75
|
+
# @return [AugeasTree] the subtree below the node
|
56
76
|
attr_accessor :tree
|
57
77
|
|
58
78
|
def initialize(tree, value)
|
@@ -60,32 +80,58 @@ module CFA
|
|
60
80
|
@value = value
|
61
81
|
end
|
62
82
|
|
63
|
-
|
64
|
-
|
83
|
+
# (see AugeasTree#[])
|
84
|
+
def [](key)
|
85
|
+
tree[key]
|
86
|
+
end
|
87
|
+
|
88
|
+
def ==(other)
|
89
|
+
[:class, :value, :tree].all? do |a|
|
90
|
+
public_send(a) == other.public_send(a)
|
91
|
+
end
|
65
92
|
end
|
93
|
+
|
94
|
+
# For objects of class Object, eql? is synonymous with ==:
|
95
|
+
# http://ruby-doc.org/core-2.3.3/Object.html#method-i-eql-3F
|
96
|
+
alias_method :eql?, :==
|
66
97
|
end
|
67
98
|
|
68
|
-
#
|
99
|
+
# Represents a parsed Augeas config tree with user friendly methods
|
69
100
|
class AugeasTree
|
70
|
-
#
|
101
|
+
# Low level access to Augeas structure
|
102
|
+
#
|
103
|
+
# An ordered mapping, represented by an Array of Hashes
|
104
|
+
# with the keys :key and :value.
|
105
|
+
#
|
106
|
+
# @see AugeasElement
|
107
|
+
#
|
108
|
+
# @return [Array<Hash{Symbol => String, AugeasTree}>]
|
71
109
|
attr_reader :data
|
72
110
|
|
73
111
|
def initialize
|
74
112
|
@data = []
|
75
113
|
end
|
76
114
|
|
115
|
+
# @return [AugeasCollection] collection for *key*
|
77
116
|
def collection(key)
|
78
117
|
AugeasCollection.new(self, key)
|
79
118
|
end
|
80
119
|
|
81
|
-
|
82
|
-
|
120
|
+
# @param [String, Matcher]
|
121
|
+
def delete(matcher)
|
122
|
+
unless matcher.is_a?(CFA::Matcher)
|
123
|
+
matcher = CFA::Matcher.new(key: matcher)
|
124
|
+
end
|
125
|
+
@data.reject!(&matcher)
|
83
126
|
end
|
84
127
|
|
85
|
-
#
|
86
|
-
#
|
87
|
-
#
|
88
|
-
#
|
128
|
+
# Adds the given *value* for *key* in the tree.
|
129
|
+
#
|
130
|
+
# By default an AppendPlacer is used which produces duplicate keys
|
131
|
+
# but ReplacePlacer can be used to replace the *first* duplicate.
|
132
|
+
# @param key [String]
|
133
|
+
# @param value [String,AugeasTree,AugeasTreeValue]
|
134
|
+
# @param placer [Placer] determines where to insert value in tree.
|
89
135
|
# Useful e.g. to specify order of keys or placing comment above of given
|
90
136
|
# key.
|
91
137
|
def add(key, value, placer = AppendPlacer.new)
|
@@ -94,10 +140,10 @@ module CFA
|
|
94
140
|
element[:value] = value
|
95
141
|
end
|
96
142
|
|
97
|
-
#
|
98
|
-
# @
|
99
|
-
#
|
100
|
-
#
|
143
|
+
# Finds given *key* in tree.
|
144
|
+
# @param key [String]
|
145
|
+
# @return [String,AugeasTree,AugeasTreeValue,nil] the first value for *key*,
|
146
|
+
# or `nil` if not found
|
101
147
|
def [](key)
|
102
148
|
entry = @data.find { |d| d[:key] == key }
|
103
149
|
return entry[:value] if entry
|
@@ -105,8 +151,10 @@ module CFA
|
|
105
151
|
nil
|
106
152
|
end
|
107
153
|
|
108
|
-
#
|
109
|
-
#
|
154
|
+
# Replace the first value for *key* with *value*.
|
155
|
+
# Append a new element if *key* did not exist.
|
156
|
+
# @param key [String]
|
157
|
+
# @param value [String, AugeasTree, AugeasTreeValue]
|
110
158
|
def []=(key, value)
|
111
159
|
entry = @data.find { |d| d[:key] == key }
|
112
160
|
if entry
|
@@ -119,12 +167,20 @@ module CFA
|
|
119
167
|
end
|
120
168
|
end
|
121
169
|
|
170
|
+
# @param matcher [Matcher]
|
171
|
+
# @return [Array<AugeasElement>] matching elements
|
122
172
|
def select(matcher)
|
123
173
|
@data.select(&matcher)
|
124
174
|
end
|
125
175
|
|
126
176
|
# @note for internal usage only
|
127
|
-
# @private
|
177
|
+
# @api private
|
178
|
+
#
|
179
|
+
# Initializes {#data} from *prefix* in *aug*.
|
180
|
+
# @param aug [::Augeas]
|
181
|
+
# @param prefix [String] Augeas path prefix
|
182
|
+
# @param keys_cache [AugeasKeysCache]
|
183
|
+
# @return [void]
|
128
184
|
def load_from_augeas(aug, prefix, keys_cache)
|
129
185
|
@data = keys_cache.keys_for_prefix(prefix).map do |key|
|
130
186
|
aug_key = prefix + "/" + key
|
@@ -136,7 +192,12 @@ module CFA
|
|
136
192
|
end
|
137
193
|
|
138
194
|
# @note for internal usage only
|
139
|
-
# @private
|
195
|
+
# @api private
|
196
|
+
#
|
197
|
+
# Saves {#data} to *prefix* in *aug*.
|
198
|
+
# @param aug [::Augeas]
|
199
|
+
# @param prefix [String] Augeas path prefix
|
200
|
+
# @return [void]
|
140
201
|
def save_to_augeas(aug, prefix)
|
141
202
|
arrays = {}
|
142
203
|
|
@@ -145,6 +206,14 @@ module CFA
|
|
145
206
|
end
|
146
207
|
end
|
147
208
|
|
209
|
+
def ==(other)
|
210
|
+
[:class, :data].all? { |a| public_send(a) == other.public_send(a) }
|
211
|
+
end
|
212
|
+
|
213
|
+
# For objects of class Object, eql? is synonymous with ==:
|
214
|
+
# http://ruby-doc.org/core-2.3.3/Object.html#method-i-eql-3F
|
215
|
+
alias_method :eql?, :==
|
216
|
+
|
148
217
|
private
|
149
218
|
|
150
219
|
def save_entry(key, value, arrays, aug, prefix)
|
@@ -199,20 +268,22 @@ module CFA
|
|
199
268
|
end
|
200
269
|
|
201
270
|
# @example read, print, modify and serialize again
|
202
|
-
# require "
|
271
|
+
# require "cfa/augeas_parser"
|
203
272
|
#
|
204
|
-
# parser = CFA::AugeasParser.new("
|
273
|
+
# parser = CFA::AugeasParser.new("Sysconfig.lns")
|
205
274
|
# data = parser.parse(File.read("/etc/default/grub"))
|
206
275
|
#
|
207
276
|
# puts data["GRUB_DISABLE_OS_PROBER"]
|
208
277
|
# data["GRUB_DISABLE_OS_PROBER"] = "true"
|
209
278
|
# puts parser.serialize(data)
|
210
279
|
class AugeasParser
|
280
|
+
# @param lens [String] a lens name, like "Sysconfig.lns"
|
211
281
|
def initialize(lens)
|
212
282
|
@lens = lens
|
213
283
|
end
|
214
284
|
|
215
|
-
#
|
285
|
+
# @param raw_string [String] a string to be parsed
|
286
|
+
# @return [AugeasTree] the parsed data
|
216
287
|
def parse(raw_string)
|
217
288
|
@old_content = raw_string
|
218
289
|
|
@@ -232,7 +303,8 @@ module CFA
|
|
232
303
|
end
|
233
304
|
end
|
234
305
|
|
235
|
-
#
|
306
|
+
# @param data [AugeasTree] the data to be serialized
|
307
|
+
# @return [String] a string to be written
|
236
308
|
def serialize(data)
|
237
309
|
# open augeas without any autoloading and it should not touch disk and
|
238
310
|
# load lenses as needed only
|
@@ -248,13 +320,15 @@ module CFA
|
|
248
320
|
end
|
249
321
|
end
|
250
322
|
|
251
|
-
#
|
323
|
+
# @return [AugeasTree] an empty tree that can be filled
|
324
|
+
# for future serialization
|
252
325
|
def empty
|
253
326
|
AugeasTree.new
|
254
327
|
end
|
255
328
|
|
256
329
|
private
|
257
330
|
|
331
|
+
# @param aug [::Augeas]
|
258
332
|
def report_error(aug)
|
259
333
|
error = aug.error
|
260
334
|
# zero is no error, so problem in lense
|
@@ -267,53 +341,44 @@ module CFA
|
|
267
341
|
raise "Augeas parsing/serializing error: #{msg} at #{location}"
|
268
342
|
end
|
269
343
|
end
|
270
|
-
end
|
271
344
|
|
272
|
-
# Cache that holds all avaiable keys in augeas tree. It is used to
|
273
|
-
# prevent too many aug.match calls which are expensive.
|
274
|
-
class AugeasKeysCache
|
275
|
-
|
276
|
-
STORE_LEN = STORE_PREFIX.size
|
277
|
-
STORE_LEN_1 = STORE_LEN + 1
|
345
|
+
# Cache that holds all avaiable keys in augeas tree. It is used to
|
346
|
+
# prevent too many aug.match calls which are expensive.
|
347
|
+
class AugeasKeysCache
|
348
|
+
STORE_PREFIX = "/store".freeze
|
278
349
|
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
# returns list of keys available on given prefix
|
285
|
-
def keys_for_prefix(prefix)
|
286
|
-
cut = prefix.length > STORE_LEN ? STORE_LEN_1 : STORE_LEN
|
287
|
-
path = prefix[cut..-1]
|
288
|
-
path = path.split("/")
|
289
|
-
matches = path.reduce(@cache) { |a, e| a[e] }
|
350
|
+
# initialize cache from passed augeas object
|
351
|
+
def initialize(aug)
|
352
|
+
fill_cache(aug)
|
353
|
+
end
|
290
354
|
|
291
|
-
|
292
|
-
|
355
|
+
# returns list of keys available on given prefix
|
356
|
+
def keys_for_prefix(prefix)
|
357
|
+
@cache[prefix] || []
|
358
|
+
end
|
293
359
|
|
294
|
-
private
|
360
|
+
private
|
295
361
|
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
362
|
+
def fill_cache(aug)
|
363
|
+
@cache = {}
|
364
|
+
search_path = "#{STORE_PREFIX}/*"
|
365
|
+
loop do
|
366
|
+
matches = aug.match(search_path)
|
367
|
+
break if matches.empty?
|
368
|
+
assign_matches(matches, @cache)
|
303
369
|
|
304
|
-
|
370
|
+
search_path += "/*"
|
371
|
+
end
|
305
372
|
end
|
306
|
-
end
|
307
373
|
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
374
|
+
def assign_matches(matches, cache)
|
375
|
+
matches.each do |match|
|
376
|
+
split_index = match.rindex("/")
|
377
|
+
prefix = match[0..(split_index - 1)]
|
378
|
+
key = match[(split_index + 1)..-1]
|
379
|
+
cache[prefix] ||= []
|
380
|
+
cache[prefix] << key
|
314
381
|
end
|
315
|
-
|
316
|
-
target[leap] = {}
|
317
382
|
end
|
318
383
|
end
|
319
384
|
end
|
data/lib/cfa/base_model.rb
CHANGED
@@ -22,11 +22,28 @@ module CFA
|
|
22
22
|
self.data = parser.empty
|
23
23
|
end
|
24
24
|
|
25
|
+
# Serializes *data* using *parser*
|
26
|
+
# and writes the resulting String using *file_handler*.
|
27
|
+
# @return [void]
|
28
|
+
# @raise a *file_handler* specific error if *file_path* cannot be written
|
29
|
+
# e.g. due to missing permissions or living on a read only device.
|
30
|
+
# @raise a *parser* specific error. If *data* contain invalid values
|
31
|
+
# then *parser* may raise an error.
|
32
|
+
# A properly written BaseModel subclass should prevent that by preventing
|
33
|
+
# insertion of such values in the first place.
|
25
34
|
def save(changes_only: false)
|
26
35
|
merge_changes if changes_only
|
27
36
|
@file_handler.write(@file_path, @parser.serialize(data))
|
28
37
|
end
|
29
38
|
|
39
|
+
# Reads a String using *file_handler*
|
40
|
+
# and parses it with *parser*, storing the result in *data*.
|
41
|
+
# @return [void]
|
42
|
+
# @raise a *file_handler* specific error. If *file_path* does not exist
|
43
|
+
# or permission is not sufficient it may raise an error
|
44
|
+
# depending on the used file handler.
|
45
|
+
# @raise a *parser* specific error. If the parsed String is malformed, then
|
46
|
+
# depending on the used parser it may raise an error.
|
30
47
|
def load
|
31
48
|
self.data = @parser.parse(@file_handler.read(@file_path))
|
32
49
|
@loaded = true
|
@@ -65,21 +82,31 @@ module CFA
|
|
65
82
|
@default_file_handler = value
|
66
83
|
end
|
67
84
|
|
68
|
-
|
69
|
-
|
70
|
-
#
|
85
|
+
# Generates accessors for trivial key-value attributes
|
86
|
+
# @param attrs [Hash{Symbol => String}] mapping of methods to file keys
|
87
|
+
#
|
88
|
+
# @example Usage
|
89
|
+
# class FooModel < CFA::BaseModel
|
90
|
+
# attributes(
|
91
|
+
# server: "server",
|
92
|
+
# read_timeout: "ReadTimeout",
|
93
|
+
# write_timeout: "WriteTimeout"
|
94
|
+
# )
|
95
|
+
# ...
|
96
|
+
# end
|
71
97
|
def self.attributes(attrs)
|
72
|
-
attrs.each_pair do |
|
73
|
-
define_method(
|
74
|
-
generic_get(
|
98
|
+
attrs.each_pair do |method_name, key|
|
99
|
+
define_method(method_name) do
|
100
|
+
generic_get(key)
|
75
101
|
end
|
76
102
|
|
77
|
-
define_method(:"#{
|
78
|
-
generic_set(
|
103
|
+
define_method(:"#{method_name.to_s}=") do |value|
|
104
|
+
generic_set(key, value)
|
79
105
|
end
|
80
106
|
end
|
81
107
|
end
|
82
|
-
|
108
|
+
|
109
|
+
protected
|
83
110
|
|
84
111
|
attr_accessor :data
|
85
112
|
|
@@ -90,6 +117,9 @@ module CFA
|
|
90
117
|
data.merge(new_data)
|
91
118
|
end
|
92
119
|
|
120
|
+
# Modify an **existing** entry and return `true`,
|
121
|
+
# or do nothing and return `false`.
|
122
|
+
# @return [Boolean]
|
93
123
|
def modify(key, value)
|
94
124
|
# if already set, just change value
|
95
125
|
return false unless data[key]
|
@@ -98,14 +128,20 @@ module CFA
|
|
98
128
|
true
|
99
129
|
end
|
100
130
|
|
131
|
+
# Replace a commented out entry and return `true`,
|
132
|
+
# or do nothing and return `false`.
|
133
|
+
# @return [Boolean]
|
101
134
|
def uncomment(key, value)
|
102
135
|
# Try to find if it is commented out, so we can replace line
|
103
136
|
matcher = Matcher.new(
|
104
137
|
collection: "#comment",
|
138
|
+
# FIXME: this assumes a specific "=" syntax, bypassing the lens
|
139
|
+
# FIXME: this will match also "# If you set FOO=bar then..."
|
105
140
|
value_matcher: /(\s|^)#{key}\s*=/
|
106
141
|
)
|
107
142
|
return false unless data.data.any?(&matcher)
|
108
143
|
|
144
|
+
# FIXME: this assumes that *data* is an AugeasTree
|
109
145
|
data.add(key, value, ReplacePlacer.new(matcher))
|
110
146
|
true
|
111
147
|
end
|
data/lib/cfa/matcher.rb
CHANGED
@@ -1,9 +1,42 @@
|
|
1
1
|
module CFA
|
2
|
-
#
|
3
|
-
#
|
4
|
-
#
|
2
|
+
# The Matcher is used as a predicate on {AugeasElement}.
|
3
|
+
#
|
4
|
+
# Being a predicate, it is passed to methods such as Enumerable#select
|
5
|
+
# or Array#index, returning a Boolean meaning whether a match was found.
|
6
|
+
#
|
7
|
+
# Acting on {AugeasElement} means it expects a Hash `e`
|
8
|
+
# containing `e[:key]` and `e[:value]`.
|
9
|
+
#
|
10
|
+
# It is used with the `&` syntax which makes the matcher
|
11
|
+
# act like a block/lambda/Proc (via {Matcher#to_proc}).
|
12
|
+
#
|
13
|
+
# @note The coupling to {AugeasTree}, {AugeasElement} is not a goal.
|
14
|
+
# Once we have more parsers it will go away.
|
15
|
+
#
|
16
|
+
# @example
|
17
|
+
# elements = [
|
18
|
+
# {key: "#comment[]", value: "\"magical\" mostly works"},
|
19
|
+
# {key: "DRIVE", value: "magical"},
|
20
|
+
# {key: "#comment[]", value: "'years' or 'centuries'"},
|
21
|
+
# {key: "PRECISION", value: "years"}
|
22
|
+
# ]
|
23
|
+
# drive_matcher = Matcher.new(key: "DRIVE")
|
24
|
+
# i = elements.index(&drive_matcher) # => 1
|
5
25
|
class Matcher
|
6
|
-
#
|
26
|
+
# The constructor arguments are constraints to match on an element.
|
27
|
+
# All constraints are optional.
|
28
|
+
# All supplied constraints must match, so it is a conjunction.
|
29
|
+
# @param key [Object,nil] if non-nil,
|
30
|
+
# constrain to elements with the name "*key*"
|
31
|
+
# @param collection [Object,nil] if non-nil,
|
32
|
+
# constrain to elements with the name "*collection*[]"
|
33
|
+
# @param value_matcher [Object,Regexp,nil] if non-nil,
|
34
|
+
# constrain to elements whose value is Object or matches Regexp
|
35
|
+
# @yieldparam blk_key [Object]
|
36
|
+
# @yieldparam blk_value [Object]
|
37
|
+
# @yieldreturn [Boolean] if the block is present,
|
38
|
+
# constrain to elements for which the block(*blk_key*, *blk_value*)
|
39
|
+
# returns true
|
7
40
|
def initialize(key: nil, collection: nil, value_matcher: nil, &block)
|
8
41
|
@matcher = lambda do |element|
|
9
42
|
return false unless key_match?(element, key)
|
@@ -14,10 +47,13 @@ module CFA
|
|
14
47
|
end
|
15
48
|
end
|
16
49
|
|
50
|
+
# @return [Proc{AugeasElement=>Boolean}]
|
17
51
|
def to_proc
|
18
52
|
@matcher
|
19
53
|
end
|
20
54
|
|
55
|
+
private
|
56
|
+
|
21
57
|
def key_match?(element, key)
|
22
58
|
return true unless key
|
23
59
|
|
data/lib/cfa/placer.rb
CHANGED
@@ -1,6 +1,21 @@
|
|
1
1
|
module CFA
|
2
|
-
#
|
3
|
-
|
2
|
+
# Places a new {AugeasElement} into an {AugeasTree}.
|
3
|
+
# @abstract Subclasses implement different ways **where**
|
4
|
+
# to place the entry by overriding {#new_element}.
|
5
|
+
class Placer
|
6
|
+
# @param [AugeasTree] tree
|
7
|
+
# @return [AugeasElement,Hash] the new element; it is empty!
|
8
|
+
# Note that the return value is actually a Hash; {AugeasElement}
|
9
|
+
# documents its structure.
|
10
|
+
def new_element(_tree)
|
11
|
+
raise NotImplementedError,
|
12
|
+
"Subclasses of #{Module.nesting.first} must override #{__method__}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# Places the new element at the end of the tree.
|
17
|
+
class AppendPlacer < Placer
|
18
|
+
# (see Placer#new_element)
|
4
19
|
def new_element(tree)
|
5
20
|
res = {}
|
6
21
|
tree.data << res
|
@@ -9,14 +24,18 @@ module CFA
|
|
9
24
|
end
|
10
25
|
end
|
11
26
|
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
15
|
-
|
27
|
+
# Finds a specific element using a {Matcher} and places the new element
|
28
|
+
# **before** it. Appends at the end if a match is not found.
|
29
|
+
#
|
30
|
+
# Useful when a config option should be inserted to a specific location,
|
31
|
+
# or when assigning a comment to an option.
|
32
|
+
class BeforePlacer < Placer
|
33
|
+
# @param [Matcher] matcher
|
16
34
|
def initialize(matcher)
|
17
35
|
@matcher = matcher
|
18
36
|
end
|
19
37
|
|
38
|
+
# (see Placer#new_element)
|
20
39
|
def new_element(tree)
|
21
40
|
index = tree.data.index(&@matcher)
|
22
41
|
|
@@ -30,14 +49,17 @@ module CFA
|
|
30
49
|
end
|
31
50
|
end
|
32
51
|
|
33
|
-
#
|
34
|
-
#
|
35
|
-
#
|
36
|
-
|
52
|
+
# Finds a specific element using a {Matcher} and places the new element
|
53
|
+
# **after** it. Appends at the end if a match is not found.
|
54
|
+
#
|
55
|
+
# Useful when a config option should be inserted to a specific location.
|
56
|
+
class AfterPlacer < Placer
|
57
|
+
# @param [Matcher] matcher
|
37
58
|
def initialize(matcher)
|
38
59
|
@matcher = matcher
|
39
60
|
end
|
40
61
|
|
62
|
+
# (see Placer#new_element)
|
41
63
|
def new_element(tree)
|
42
64
|
index = tree.data.index(&@matcher)
|
43
65
|
|
@@ -51,15 +73,18 @@ module CFA
|
|
51
73
|
end
|
52
74
|
end
|
53
75
|
|
54
|
-
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
|
76
|
+
# Finds a specific element using a {Matcher} and **replaces** it
|
77
|
+
# with the new element. Appends at the end if a match is not found.
|
78
|
+
#
|
79
|
+
# Useful in key-value configuration files where a specific key
|
80
|
+
# needs to be assigned.
|
81
|
+
class ReplacePlacer < Placer
|
82
|
+
# @param [Matcher] matcher
|
59
83
|
def initialize(matcher)
|
60
84
|
@matcher = matcher
|
61
85
|
end
|
62
86
|
|
87
|
+
# (see Placer#new_element)
|
63
88
|
def new_element(tree)
|
64
89
|
index = tree.data.index(&@matcher)
|
65
90
|
res = {}
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cfa
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.4.
|
4
|
+
version: 0.4.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Josef Reidinger
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-11-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: ruby-augeas
|