rangeclient 0.0.3 → 0.0.5
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/bin/er +60 -0
- data/lib/rangeclient.rb +163 -3
- metadata +6 -19
data/bin/er
ADDED
@@ -0,0 +1,60 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'rangeclient'
|
5
|
+
|
6
|
+
require 'optparse'
|
7
|
+
require 'ostruct'
|
8
|
+
options = {
|
9
|
+
:timeout => 60,
|
10
|
+
}
|
11
|
+
optparse = OptionParser.new do |opts|
|
12
|
+
opts.on('-e', '--expand', 'Print one element per line') do |arg|
|
13
|
+
options[:expand] = arg
|
14
|
+
end
|
15
|
+
opts.on('-c', '--count', 'Print the count of range elements, not the range itself') do |arg|
|
16
|
+
options[:count] = arg
|
17
|
+
end
|
18
|
+
opts.on('-v', '--vip range:80', 'Which host or host:port to query') do |arg|
|
19
|
+
options[:vip] = arg
|
20
|
+
end
|
21
|
+
opts.on('-p', '--port 80', 'Port to use when connecting') do |arg|
|
22
|
+
options[:port] = arg
|
23
|
+
end
|
24
|
+
opts.on('-t', '--timeout', "Wait this long for a response ") do |arg|
|
25
|
+
options[:timeout] = arg
|
26
|
+
end
|
27
|
+
opts.on('-h', '--help', "Usage ") do |arg|
|
28
|
+
options[:verbose] = arg
|
29
|
+
end
|
30
|
+
end
|
31
|
+
optparse.parse!
|
32
|
+
|
33
|
+
range = Range::Client.new
|
34
|
+
|
35
|
+
in_range = ARGV.join ','
|
36
|
+
|
37
|
+
raise "No range given" if in_range.empty?
|
38
|
+
|
39
|
+
if in_range == '-'
|
40
|
+
in_range = STDIN.readlines.map { |l| l.chomp }.join ','
|
41
|
+
end
|
42
|
+
|
43
|
+
nodes = range.expand(in_range)
|
44
|
+
|
45
|
+
if options[:count]
|
46
|
+
# if counting, return count
|
47
|
+
puts nodes.size
|
48
|
+
elsif not nodes.size.zero?
|
49
|
+
# if we got a result, display it accordingly
|
50
|
+
if options[:expand]
|
51
|
+
nodes.each do |node|
|
52
|
+
puts node
|
53
|
+
end
|
54
|
+
else
|
55
|
+
puts range.compress(nodes)
|
56
|
+
end
|
57
|
+
else
|
58
|
+
# no nodes returned.
|
59
|
+
raise "No nodes specified"
|
60
|
+
end
|
data/lib/rangeclient.rb
CHANGED
@@ -6,6 +6,13 @@ require 'cgi'
|
|
6
6
|
|
7
7
|
class Range::Client
|
8
8
|
|
9
|
+
# used to split hostnames into component parts for compression
|
10
|
+
@@NodeRegx = /
|
11
|
+
([-\w.]*?) # $1 - prefix
|
12
|
+
(\d+) # $2 - start of range
|
13
|
+
(\.[-A-Za-z\d.]*[-A-Za-z]+[-A-Za-z\d.]*)? # optional domain
|
14
|
+
/x;
|
15
|
+
|
9
16
|
def initialize(options = {})
|
10
17
|
default_host = 'range'
|
11
18
|
default_host = ENV['RANGE_HOST'] if ENV.has_key?('RANGE_HOST')
|
@@ -23,10 +30,163 @@ class Range::Client
|
|
23
30
|
return res.split "\n"
|
24
31
|
end
|
25
32
|
|
26
|
-
|
27
|
-
|
28
|
-
|
33
|
+
|
34
|
+
# Keep this extremely basic code for reference
|
35
|
+
# def compress(nodes)
|
36
|
+
# escaped_arg = CGI.escape nodes.join ","
|
37
|
+
# return RestClient.get "http://#{@options[:host]}:#{@options[:port]}/range/expand?#{escaped_arg}"
|
38
|
+
# end
|
39
|
+
|
40
|
+
# Take a page from the Perl Seco::Data::Range and perform this locally -- more efficient in both speed/size
|
41
|
+
# This was ported over from the Perl version, so it's not quite idiomatic ruby
|
42
|
+
|
43
|
+
def compress(nodes)
|
44
|
+
domain_tbl = {}
|
45
|
+
no_domain_list = []
|
46
|
+
nodes.each do |n|
|
47
|
+
# If this is a quoted range, just compress it without collapsing
|
48
|
+
return _simple_compress(nodes) if n =~ /^(?:"|q\()/
|
49
|
+
|
50
|
+
# Break out host and key by domain, to enable {foo1,foo3}.bar.com grouping
|
51
|
+
host, domain = n.split('.', 2)
|
52
|
+
if domain
|
53
|
+
domain_tbl[domain] ||= []
|
54
|
+
domain_tbl[domain] << host
|
55
|
+
else
|
56
|
+
no_domain_list << host
|
57
|
+
end
|
58
|
+
end
|
59
|
+
result = []
|
60
|
+
# Range elements with no domain component do not group
|
61
|
+
# just return
|
62
|
+
if not no_domain_list.empty?
|
63
|
+
result << _simple_compress(no_domain_list)
|
64
|
+
end
|
65
|
+
|
66
|
+
domain_tbl.keys.sort.each do |domain|
|
67
|
+
r = _extra_compress(domain_tbl[domain])
|
68
|
+
r.gsub!(/\.#{domain},/) {","}
|
69
|
+
r.gsub!(/\.#{domain}$/) {""}
|
70
|
+
if r=~ /,/
|
71
|
+
r = "{#{r}}"
|
72
|
+
end
|
73
|
+
result << "#{r}.#{domain}"
|
74
|
+
end
|
75
|
+
return result.join ","
|
29
76
|
end
|
77
|
+
|
78
|
+
def _extra_compress(nodes)
|
79
|
+
domains = {}
|
80
|
+
nodes = nodes.dup
|
81
|
+
nodes.each do |node|
|
82
|
+
node.gsub!(/^([a-z]+)(\d+)([a-z]\w+)\./) { "#{$1}#{$2}.UNDOXXX#{$3}." }
|
83
|
+
end
|
84
|
+
result = _simple_compress(nodes)
|
85
|
+
result.each do |r|
|
86
|
+
r.gsub!(/(\d+\.\.\d+)\.UNDOXXX/) {"{#{$1}}"}
|
87
|
+
r.gsub!(/(\d+)\.UNDOXXX/) {"#{$1}"}
|
88
|
+
end
|
89
|
+
return result
|
90
|
+
end
|
91
|
+
|
92
|
+
def _simple_compress(nodes)
|
93
|
+
# dedup nodes
|
94
|
+
set = {}
|
95
|
+
nodes.each do |node|
|
96
|
+
set[node] = true
|
97
|
+
end
|
98
|
+
nodes = set.keys
|
99
|
+
nodes = _sort_nodes(nodes)
|
100
|
+
|
101
|
+
result = []
|
102
|
+
prev_prefix, prev_digits, prev_suffix = "", nil, ""
|
103
|
+
prev_n = nil
|
104
|
+
count = 0
|
105
|
+
|
106
|
+
nodes.each do |n|
|
107
|
+
if n =~ /\A#{@@NodeRegx}\z/
|
108
|
+
# foo100abc => foo 100 abc
|
109
|
+
prefix, digits, suffix = $1, $2, $3
|
110
|
+
prefix = "" if prefix.nil?
|
111
|
+
suffix = "" if suffix.nil?
|
112
|
+
else
|
113
|
+
prefix, digits, suffix = n, nil, nil
|
114
|
+
end
|
115
|
+
if (not digits.to_i.zero?) and
|
116
|
+
(prefix == prev_prefix) and
|
117
|
+
(suffix == prev_suffix) and
|
118
|
+
(not prev_digits.nil?) and
|
119
|
+
(digits.to_i == prev_digits.to_i + count + 1)
|
120
|
+
count += 1
|
121
|
+
next
|
122
|
+
end
|
123
|
+
|
124
|
+
if prev_n
|
125
|
+
if count > 0
|
126
|
+
result << _get_group(prev_prefix, prev_digits, count, prev_suffix)
|
127
|
+
else
|
128
|
+
result << prev_n
|
129
|
+
end
|
130
|
+
end
|
131
|
+
prev_n = n
|
132
|
+
prev_prefix = prefix
|
133
|
+
prev_digits = digits
|
134
|
+
prev_suffix = suffix
|
135
|
+
count = 0
|
136
|
+
end #nodes.each
|
137
|
+
|
138
|
+
if count > 0
|
139
|
+
result << _get_group(prev_prefix, prev_digits, count, prev_suffix)
|
140
|
+
else
|
141
|
+
result << prev_n
|
142
|
+
end
|
143
|
+
return result.join ","
|
144
|
+
end
|
145
|
+
|
146
|
+
def _sort_nodes(nodes)
|
147
|
+
sorted = nodes.map { |n|
|
148
|
+
# decorate-sort-undecorate
|
149
|
+
# FIXME can this all be pushed into sort_by?
|
150
|
+
n =~ /\A#{@@NodeRegx}\z/
|
151
|
+
|
152
|
+
[ n,
|
153
|
+
$1.nil? ? "" : $1,
|
154
|
+
$2.nil? ? 0 : $2.to_i,
|
155
|
+
$3.nil? ? "" : $3,
|
156
|
+
]
|
157
|
+
}.sort_by { |e|
|
158
|
+
[ e[1], e[3], e[2], e[0] ]
|
159
|
+
}.map { |n|
|
160
|
+
n[0]
|
161
|
+
}
|
162
|
+
return sorted
|
163
|
+
end
|
164
|
+
|
165
|
+
def _get_group(prefix, digits, count, suffix)
|
166
|
+
prefix = "" if prefix.nil?
|
167
|
+
group = sprintf("%s%0*d..%s",
|
168
|
+
prefix,
|
169
|
+
digits.to_s.length,
|
170
|
+
digits,
|
171
|
+
_ignore_common_prefix(digits, (digits.to_i + count).to_s)
|
172
|
+
)
|
173
|
+
suffix = "" if suffix.nil?
|
174
|
+
return group + suffix
|
175
|
+
end
|
176
|
+
|
177
|
+
def _ignore_common_prefix(start_pos, end_pos)
|
178
|
+
len_start = start_pos.to_s.length
|
179
|
+
return end_pos if len_start < end_pos.to_s.length
|
180
|
+
pick = 0
|
181
|
+
len_start.times do |i|
|
182
|
+
pick = i
|
183
|
+
# find the point at which the two strings deviate
|
184
|
+
break if (start_pos[0..i] != end_pos[0..i])
|
185
|
+
end
|
186
|
+
# and return that substring prior to deviation
|
187
|
+
return end_pos[pick..-1]
|
188
|
+
end
|
189
|
+
|
30
190
|
end
|
31
191
|
|
32
192
|
if __FILE__ == $0
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: rangeclient
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 21
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 0
|
9
|
-
-
|
10
|
-
version: 0.0.
|
9
|
+
- 5
|
10
|
+
version: 0.0.5
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Evan Miller
|
@@ -15,10 +15,10 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date:
|
18
|
+
date: 2013-02-28 00:00:00 Z
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|
21
|
-
name:
|
21
|
+
name: rest-client
|
22
22
|
prerelease: false
|
23
23
|
requirement: &id001 !ruby/object:Gem::Requirement
|
24
24
|
none: false
|
@@ -31,20 +31,6 @@ dependencies:
|
|
31
31
|
version: "0"
|
32
32
|
type: :runtime
|
33
33
|
version_requirements: *id001
|
34
|
-
- !ruby/object:Gem::Dependency
|
35
|
-
name: rest-client
|
36
|
-
prerelease: false
|
37
|
-
requirement: &id002 !ruby/object:Gem::Requirement
|
38
|
-
none: false
|
39
|
-
requirements:
|
40
|
-
- - ">="
|
41
|
-
- !ruby/object:Gem::Version
|
42
|
-
hash: 3
|
43
|
-
segments:
|
44
|
-
- 0
|
45
|
-
version: "0"
|
46
|
-
type: :runtime
|
47
|
-
version_requirements: *id002
|
48
34
|
description: Use with ranged from https://github.com/ytoolshed/range
|
49
35
|
email:
|
50
36
|
- evan@squareup.com
|
@@ -56,6 +42,7 @@ extra_rdoc_files:
|
|
56
42
|
- LICENSE.md
|
57
43
|
files:
|
58
44
|
- lib/rangeclient.rb
|
45
|
+
- bin/er
|
59
46
|
- README.md
|
60
47
|
- LICENSE.md
|
61
48
|
homepage: https://github.com/square/prodeng/tree/master/ruby_rangeclient
|