hostlist_expression 1.0.3 → 1.0.4
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/hostlist_expression.rb +57 -82
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: b8ced6089230749389cf14d25a15b05432435942e3ff8b8011da3a0b9891659e
|
|
4
|
+
data.tar.gz: d613f8364b2cf83aa8beecf3a14566da30680353d305b5e9127a1c419d15f9b2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: e8baf7b857a06fff1093c00f4113c3bebb2660c717f9a864c49fa25877b2dd32e856e5e95e54bf0f053ff9ac352d4b48b93a59bb531ea98a1b124d9cc5bb122f
|
|
7
|
+
data.tar.gz: 4ec08df27a097d36b323ca285bb93dd96c04c3a6bd26ab11aa2e4f6edc585db164911258bd8b3b62e52629dbb31dc854933725dfa7ace7e47b45750216c15df8
|
data/lib/hostlist_expression.rb
CHANGED
|
@@ -4,101 +4,76 @@
|
|
|
4
4
|
# License: MIT
|
|
5
5
|
# Home: https://github.com/udondan/hostlist_expression-ruby
|
|
6
6
|
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
#
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
#
|
|
15
|
-
#
|
|
16
|
-
|
|
17
|
-
#
|
|
18
|
-
#
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
raise "Error: Range separator must be either of type String or Array. given: #{separator.class}"
|
|
24
|
-
elsif separator.empty?
|
|
25
|
-
raise "Error: Range separator is empty"
|
|
26
|
-
end
|
|
27
|
-
|
|
28
|
-
# Prepare separator for use in regular expressions
|
|
29
|
-
separator = Regexp.escape(separator)
|
|
7
|
+
# Expansion logic behind {#hostlist_expression}.
|
|
8
|
+
module HostlistExpression
|
|
9
|
+
class << self
|
|
10
|
+
# Expand hostlist expression. See {#hostlist_expression}.
|
|
11
|
+
def expand(expression, separator = [":", "-"])
|
|
12
|
+
separator = separator_pattern(separator)
|
|
13
|
+
|
|
14
|
+
# Return input as single host, if this is not a hostlist expression
|
|
15
|
+
return [expression] unless expression.match?(/\[(?:[\da-z]+(?:[#{separator}][\da-z]+)?,?)+\]/i)
|
|
16
|
+
|
|
17
|
+
# Replace each bracket group, e.g. [0:10] or [A,C-E], in every partial host
|
|
18
|
+
expression.scan(/\[([\da-z#{separator},]+)\]/i).reduce([expression]) do |hosts, (group)|
|
|
19
|
+
replacements = group.split(",").flat_map { |range| expand_range(range, separator, expression) }
|
|
20
|
+
hosts.flat_map { |host| replacements.map { |replacement| host.sub("[#{group}]", replacement) } }
|
|
21
|
+
end.uniq
|
|
22
|
+
end
|
|
30
23
|
|
|
31
|
-
|
|
32
|
-
return [expression] unless expression.match(/\[(?:[\da-z]+(?:[#{separator}][\da-z]+)?,?)+\]/i)
|
|
24
|
+
private
|
|
33
25
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
26
|
+
# Validate the separator and escape it for use in a regexp character class
|
|
27
|
+
def separator_pattern(separator)
|
|
28
|
+
separator = separator.join if separator.is_a?(Array)
|
|
29
|
+
unless separator.is_a?(String)
|
|
30
|
+
raise "Error: Range separator must be either of type String or Array. given: #{separator.class}"
|
|
31
|
+
end
|
|
32
|
+
raise "Error: Range separator is empty" if separator.empty?
|
|
37
33
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
# Will hold replacements for each match
|
|
41
|
-
replacements = []
|
|
34
|
+
Regexp.escape(separator)
|
|
35
|
+
end
|
|
42
36
|
|
|
43
|
-
#
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
range_items = range.split(/[#{separator}]/)
|
|
37
|
+
# Expand a single sequence item, e.g. "1-3" or "A", into its values
|
|
38
|
+
def expand_range(range, separator, expression)
|
|
39
|
+
items = range.split(/[#{separator}]/)
|
|
47
40
|
|
|
48
41
|
# If it's not really a range, duplicate the single item
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
# Numeric range
|
|
52
|
-
if range_items.all? { |item| item.match(/^[0-9]+$/) }
|
|
53
|
-
isnum = true
|
|
54
|
-
from, to = range_items.map(&:to_i).sort
|
|
55
|
-
|
|
56
|
-
# Uppercase alphabetic range
|
|
57
|
-
elsif range_items.all? { |item| item.match(/^[A-Z]$/) }
|
|
58
|
-
alphabet = ("A".."Z").to_a
|
|
59
|
-
from, to = range_items.map { |item| alphabet.index(item) }.sort
|
|
60
|
-
|
|
61
|
-
# Lowercase alphabetic range
|
|
62
|
-
elsif range_items.all? { |item| item.match(/^[a-z]$/) }
|
|
63
|
-
alphabet = ("a".."z").to_a
|
|
64
|
-
from, to = range_items.map { |item| alphabet.index(item) }.sort
|
|
42
|
+
items.push(items[0]) if items.length == 1
|
|
65
43
|
|
|
44
|
+
if items.all?(/\A[0-9]+\z/)
|
|
45
|
+
numeric_range(items)
|
|
46
|
+
elsif items.all?(/\A[A-Z]\z/) || items.all?(/\A[a-z]\z/)
|
|
47
|
+
from, to = items.sort
|
|
48
|
+
(from..to).to_a
|
|
66
49
|
else
|
|
67
50
|
raise "Error: Invalid host range definition #{expression}"
|
|
68
51
|
end
|
|
69
|
-
|
|
70
|
-
# Pad numbers only if a bound has leading zeros, to the width of the longest bound
|
|
71
|
-
padding = range_items.any? { |item| item.match(/^0[0-9]/) } ? range_items.map(&:length).max : 0
|
|
72
|
-
|
|
73
|
-
# Iterate over all hosts and store the resolved patterns in "replacements"
|
|
74
|
-
hosts.each do |_host|
|
|
75
|
-
# Iterate over the range
|
|
76
|
-
(from..to).each do |i|
|
|
77
|
-
if isnum
|
|
78
|
-
# Formatting number with leading zeros
|
|
79
|
-
replacements.push(i.to_s.rjust(padding, "0"))
|
|
80
|
-
else
|
|
81
|
-
# Select correct letter from alphabet
|
|
82
|
-
replacements.push(alphabet[i])
|
|
83
|
-
end
|
|
84
|
-
end
|
|
85
|
-
end
|
|
86
52
|
end
|
|
87
53
|
|
|
88
|
-
#
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
# Iterate over previously stored replacements
|
|
95
|
-
replacements.each do |replacement|
|
|
96
|
-
# Adding replacement to hosts array
|
|
97
|
-
hosts.push(host.sub(/\[#{match[0]}\]/, replacement))
|
|
98
|
-
end
|
|
54
|
+
# Pad numbers only if a bound has leading zeros, to the width of the longest bound
|
|
55
|
+
def numeric_range(items)
|
|
56
|
+
from, to = items.map(&:to_i).sort
|
|
57
|
+
padding = items.any?(/\A0[0-9]/) ? items.map(&:length).max : 0
|
|
58
|
+
(from..to).map { |i| i.to_s.rjust(padding, "0") }
|
|
99
59
|
end
|
|
100
60
|
end
|
|
61
|
+
end
|
|
101
62
|
|
|
102
|
-
|
|
103
|
-
|
|
63
|
+
# Expand hostlist expression
|
|
64
|
+
#
|
|
65
|
+
# @param [ String ] expression A host list expression.
|
|
66
|
+
# @param [ String, Array<String> ] separator - Character(s) for separating ranges.
|
|
67
|
+
#
|
|
68
|
+
# @example
|
|
69
|
+
# hostlist_expression("your-host-[1-3].com")
|
|
70
|
+
# # => ["your-host-1.com", "your-host-2.com", "your-host-3.com"]
|
|
71
|
+
#
|
|
72
|
+
# @return [ Array<String> ] A list of expanded hosts.
|
|
73
|
+
#
|
|
74
|
+
# @raise [ RuntimeError ] If the separator or a range definition is invalid.
|
|
75
|
+
#
|
|
76
|
+
# @since 0.1.0
|
|
77
|
+
def hostlist_expression(expression, separator = [":", "-"])
|
|
78
|
+
HostlistExpression.expand(expression, separator)
|
|
104
79
|
end
|