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.
Files changed (3) hide show
  1. data/bin/er +60 -0
  2. data/lib/rangeclient.rb +163 -3
  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
- def compress(names)
27
- escaped_arg = CGI.escape names.join ","
28
- return RestClient.get "http://#{@options[:host]}:#{@options[:port]}/range/expand?#{escaped_arg}"
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: 25
4
+ hash: 21
5
5
  prerelease:
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 3
10
- version: 0.0.3
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: 2012-05-16 00:00:00 Z
18
+ date: 2013-02-28 00:00:00 Z
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency
21
- name: rdoc
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