ip-ranges 0.1.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.
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source :rubygems
2
+ gem "netaddr"
3
+ gem "rdoc"
4
+ gem "rspec"
data/Gemfile.lock ADDED
@@ -0,0 +1,22 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.3)
5
+ netaddr (1.5.0)
6
+ rdoc (3.9.4)
7
+ rspec (2.6.0)
8
+ rspec-core (~> 2.6.0)
9
+ rspec-expectations (~> 2.6.0)
10
+ rspec-mocks (~> 2.6.0)
11
+ rspec-core (2.6.4)
12
+ rspec-expectations (2.6.0)
13
+ diff-lcs (~> 1.1.2)
14
+ rspec-mocks (2.6.0)
15
+
16
+ PLATFORMS
17
+ ruby
18
+
19
+ DEPENDENCIES
20
+ netaddr
21
+ rdoc
22
+ rspec
data/README.markdown ADDED
@@ -0,0 +1,57 @@
1
+ Compare IP ranges
2
+ =================
3
+
4
+ This gem allows you to take multiple IP ranges, which may be defined in several ways (as a single IP number, a dotted string or a CIDR range), and compare them against each other to check for uniqueness, overlaps, equivalence or one range containing another.
5
+
6
+
7
+ Example1 - Range class
8
+ ======================
9
+
10
+ r1 = IpRanges::Range.new :range => '1.2.3.4..1.2.3.5'
11
+ r2 = IpRanges::Range.new :range => '1.2.3.0/24'
12
+
13
+ r2.overlaps_range?(r1) => true
14
+ r2.contains_range?(r1) => true
15
+ r2 == r1 => false
16
+
17
+
18
+ Example2 - Check for overlaps
19
+ =============================
20
+
21
+ require 'rubygems'
22
+ require 'lib/ip-ranges'
23
+
24
+ include IpRanges
25
+
26
+ list = [
27
+ '1.2.3.4..1.2.3.5',
28
+ '1.2.3.0/24'
29
+ ]
30
+
31
+ puts check_for_overlaps(list)
32
+
33
+ # outputs: 1.2.3.4..1.2.3.5 is contained by range 1.2.3.0/24
34
+
35
+
36
+ MIT License
37
+ ===========
38
+
39
+ (c) David Salgado 2011, or whenever
40
+
41
+ Permission is hereby granted, free of charge, to any person obtaining a copy
42
+ of this software and associated documentation files (the "Software"), to deal
43
+ in the Software without restriction, including without limitation the rights
44
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
45
+ copies of the Software, and to permit persons to whom the Software is
46
+ furnished to do so, subject to the following conditions:
47
+
48
+ The above copyright notice and this permission notice shall be included in
49
+ all copies or substantial portions of the Software.
50
+
51
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
52
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
53
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
54
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
55
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
56
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
57
+ THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,82 @@
1
+ require "rubygems/package_task"
2
+ require "rdoc/task"
3
+ require "rspec"
4
+ require "rspec/core/rake_task"
5
+ require 'rake/testtask'
6
+
7
+ $stdout.sync = true
8
+
9
+ task :default => :spec
10
+
11
+ task :spec do
12
+ system "bundle exec rspec spec/*_spec.rb spec/**/*_spec.rb"
13
+ end
14
+
15
+ # This builds the actual gem. For details of what all these options
16
+ # mean, and other ones you can add, check the documentation here:
17
+ #
18
+ # http://rubygems.org/read/chapter/20
19
+ #
20
+ spec = Gem::Specification.new do |s|
21
+
22
+ # Change these as appropriate
23
+ s.name = "ip-ranges"
24
+ s.version = "0.1.0"
25
+ s.summary = "Manage ranges of IP numbers determining equivalence, containment and overlaps"
26
+ s.author = "David Salgado"
27
+ s.email = "david@digitalronin.com"
28
+ s.homepage = "http://roninonrails.blogspot.com"
29
+
30
+ s.has_rdoc = true
31
+ s.description = "Compare multiple IP ranges for overlaps, equivalence and containment"
32
+ # s.extra_rdoc_files = %w(README)
33
+ s.rdoc_options = %w(-x pkg)
34
+
35
+ # Add any extra files to include in the gem (like your README)
36
+ s.files = %w(Gemfile Gemfile.lock Rakefile README.markdown) + Dir.glob("{spec,lib}/**/*")
37
+ s.require_paths = ["lib"]
38
+
39
+ # If you want to depend on other gems, add them here, along with any
40
+ # relevant versions
41
+ s.add_dependency("netaddr", "~> 1.5.0")
42
+
43
+ # If your tests use any gems, include them here
44
+ s.add_development_dependency("rspec")
45
+ s.add_development_dependency("rdoc")
46
+ end
47
+
48
+ # This task actually builds the gem. We also regenerate a static
49
+ # .gemspec file, which is useful if something (i.e. GitHub) will
50
+ # be automatically building a gem for this project. If you're not
51
+ # using GitHub, edit as appropriate.
52
+ #
53
+ # To publish your gem online, install the 'gemcutter' gem; Read more
54
+ # about that here: http://gemcutter.org/pages/gem_docs
55
+ Gem::PackageTask.new(spec) do |pkg|
56
+ pkg.gem_spec = spec
57
+ end
58
+
59
+ desc "Build the gemspec file #{spec.name}.gemspec"
60
+ task :gemspec do
61
+ file = File.dirname(__FILE__) + "/#{spec.name}.gemspec"
62
+ File.open(file, "w") {|f| f << spec.to_ruby }
63
+ end
64
+
65
+ # If you don't want to generate the .gemspec file, just remove this line. Reasons
66
+ # why you might want to generate a gemspec:
67
+ # - using bundler with a git source
68
+ # - building the gem without rake (i.e. gem build blah.gemspec)
69
+ # - maybe others?
70
+ task :package => :gemspec
71
+
72
+ # Generate documentation
73
+ RDoc::Task.new do |rd|
74
+
75
+ rd.rdoc_files.include("lib/**/*.rb")
76
+ rd.rdoc_dir = "rdoc"
77
+ end
78
+
79
+ desc 'Clear out RDoc and generated packages'
80
+ task :clean => [:clobber_rdoc, :clobber_package] do
81
+ rm "#{spec.name}.gemspec"
82
+ end
data/lib/ip-ranges.rb ADDED
@@ -0,0 +1,51 @@
1
+ require 'netaddr'
2
+
3
+ # Manipulate and compare ranges of Ip numbers.
4
+ # In particular, this gem allows you to specify
5
+ # multiple Ip ranges, in a variety of formats,
6
+ # and compare them for overlaps, equivalence and
7
+ # containment (i.e. range1 contains all of range2)
8
+ #
9
+ # :title: IpRanges Gem
10
+ # Author:: David Salgado (mailto:david@digitalronin.com)
11
+ # Copyright:: Copyright (c) 2011 David Salgado
12
+ # License:: Distributes under the same terms as Ruby
13
+
14
+ module IpRanges
15
+
16
+ # Checks multiple ranges of IP numbers for uniqueness (in the sense that a given IP number
17
+ # should only appear once within the set of all supplied ranges).
18
+ #
19
+ # Arguments:
20
+ # * An array of strings that define single IP numbers or ranges (in dotted or CIDR notation)
21
+ #
22
+ # Returns:
23
+ # * An array of strings describing any overlaps, equivalences and containments which occur in the list (see the specs for details)
24
+ #
25
+ def check_for_overlaps(list)
26
+ rtn = []
27
+ while list.any?
28
+ test_range = list.shift
29
+ test = Range.new :range => test_range
30
+ list.each do |range|
31
+ i = Range.new :range => range
32
+ if test.overlaps_range?(i)
33
+ if test == i
34
+ rtn << "#{test_range} equals range #{range}"
35
+ elsif test.contains_range?(i)
36
+ rtn << "#{test_range} contains range #{range}"
37
+ elsif i.contains_range?(test)
38
+ rtn << "#{test_range} is contained by range #{range}"
39
+ else
40
+ rtn << "#{test_range} overlaps with #{range}"
41
+ end
42
+ end
43
+ end
44
+ end
45
+ rtn
46
+ end
47
+ end
48
+
49
+ libdir = File.join(File.dirname(__FILE__), 'ip-ranges')
50
+ require File.join(libdir, 'ip')
51
+ require File.join(libdir, 'range')
@@ -0,0 +1,140 @@
1
+ module IpRanges
2
+
3
+ # Class to represent a single IP number. Exposes methods for comparisons
4
+ # with other IP numbers, as well as a method to increment the IP number.
5
+ class Ip
6
+ # String representation of this Ip number
7
+ #
8
+ # Example:
9
+ #
10
+ # ip = Ip.new :number => '1.2.3.4'
11
+ # ip.number => '1.2.3.4'
12
+ #
13
+ attr_accessor :number
14
+
15
+ # Arguments:
16
+ #
17
+ # * A hash containing a single key whose value is the IP number as a string
18
+ #
19
+ # Example:
20
+ #
21
+ # Ip.new(:number => '1.2.3.4')
22
+ #
23
+ def initialize(params = {})
24
+ @number = params[:number]
25
+ end
26
+
27
+ # Example:
28
+ # Ip.new(:number => '1.2.3.4').to_s => '1.2.3.4'
29
+ def to_s
30
+ number
31
+ end
32
+
33
+ # Arguments:
34
+ #
35
+ # * another Ip object
36
+ #
37
+ # Example:
38
+ #
39
+ # ip1 = Ip.new(:number => '1.2.3.4')
40
+ # ip2 = Ip.new(:number => '1.2.3.5')
41
+ # ip3 = Ip.new(:number => '1.2.3.4')
42
+ #
43
+ # ip1 == ip2 => false
44
+ # ip1 == ip3 => true
45
+ def ==(ip)
46
+ number == ip.number
47
+ end
48
+
49
+ # Arguments:
50
+ #
51
+ # * another Ip object
52
+ #
53
+ # Example:
54
+ #
55
+ # ip1 = Ip.new(:number => '1.2.3.4')
56
+ # ip2 = Ip.new(:number => '1.2.3.5')
57
+ # ip3 = Ip.new(:number => '1.2.3.4')
58
+ #
59
+ # ip1 >= ip2 => false
60
+ # ip2 >= ip3 => true
61
+ # ip1 >= ip3 => true
62
+ def >=(ip)
63
+ self == ip || self > ip
64
+ end
65
+
66
+ # Arguments:
67
+ #
68
+ # * another Ip object
69
+ #
70
+ # Example:
71
+ #
72
+ # ip1 = Ip.new(:number => '1.2.3.4')
73
+ # ip2 = Ip.new(:number => '1.2.3.5')
74
+ #
75
+ # ip1 > ip2 => false
76
+ # ip2 > ip1 => true
77
+ # ip2 > ip2 => false
78
+ def >(ip)
79
+ a1, b1, c1, d1 = tuples number
80
+ a2, b2, c2, d2 = tuples ip.number
81
+ if a1 > a2
82
+ true
83
+ elsif a1 < a2
84
+ false
85
+ elsif b1 > b2
86
+ true
87
+ elsif b1 < b2
88
+ false
89
+ elsif c1 > c2
90
+ true
91
+ elsif c1 < c2
92
+ false
93
+ else
94
+ d1 > d2
95
+ end
96
+ end
97
+
98
+ # Changes the 'number' property of this Ip
99
+ # to the next number after this one.
100
+ #
101
+ # example:
102
+ #
103
+ # ip = Ip.new(:number => '1.2.3.4')
104
+ # ip.number => '1.2.3.4'
105
+ # ip.increment
106
+ # ip.number => '1.2.3.5'
107
+ #
108
+ def increment
109
+ a, b, c, d = tuples number
110
+ if d < 255
111
+ d += 1
112
+ else
113
+ d = 0
114
+ if c < 255
115
+ c += 1
116
+ else
117
+ c = 0
118
+ if b < 255
119
+ b += 1
120
+ else
121
+ b = 0
122
+ if a < 255
123
+ a += 1
124
+ else
125
+ raise "No more IPs"
126
+ end
127
+ end
128
+ end
129
+ end
130
+ @number = [a, b, c, d].join('.')
131
+ end
132
+
133
+ private
134
+
135
+ def tuples(string)
136
+ string.split(/\./).map {|i| i.to_i}
137
+ end
138
+
139
+ end
140
+ end
@@ -0,0 +1,119 @@
1
+ module IpRanges
2
+
3
+ # Class to represent a range of IP numbers.
4
+ # Range objects are instantiated from a string which may be a single
5
+ # Exposes methods to compare this range with another range, and to
6
+ # iterate through all IP numbers in the range.
7
+ class Range
8
+ # First IP number in the range, as an Ip object
9
+ attr_accessor :first
10
+
11
+ # Last IP number in the range, as an Ip object
12
+ attr_accessor :last
13
+
14
+ SINGLE_IP = /^\d+\.\d+\.\d+\.\d+$/
15
+ DOTTED_RANGE = /^(\d+\.\d+\.\d+\.\d+)\.\.(\d+\.\d+\.\d+\.\d+)$/
16
+ CIDR_RANGE = %r[/]
17
+
18
+ BATCHSIZE = 250
19
+ PAUSE = 2
20
+
21
+ # Instatiate a range from a string representation which can be a single
22
+ # Ip number, a dotted range or a CIDR range
23
+ #
24
+ # Arguments:
25
+ # * A +Hash+ with a single key ':range' whose value is a string defining the range.
26
+ #
27
+ # Examples:
28
+ #
29
+ # IpRanges::Range.new :range => '1.2.3.4' => a range containing one Ip
30
+ # IpRanges::Range.new :range => '1.2.3.4 .. 1.2.3.5' => a range containing two Ip numbers (spaces around the '..' are ignored)
31
+ # IpRanges::Range.new :range => '1.2.3.0/24' => a range containing 256 Ip numbers (1.2.3.0 .. 1.2.3.255)
32
+ #
33
+ def initialize(params)
34
+ string = params[:range].gsub(' ', '')
35
+ case string
36
+ when SINGLE_IP
37
+ @first = Ip.new :number => string
38
+ @last = Ip.new :number => string
39
+ when DOTTED_RANGE
40
+ @first = Ip.new :number => $1
41
+ @last = Ip.new :number => $2
42
+ when CIDR_RANGE
43
+ begin
44
+ cidr = NetAddr::CIDR.create(string)
45
+ @first = Ip.new :number => cidr.first
46
+ @last = Ip.new :number => cidr.last
47
+ rescue Exception => e
48
+ puts "Error creating CIDR range from: #{string}"
49
+ raise e
50
+ end
51
+ end
52
+ end
53
+
54
+ # Returns the last Ip object in this range.
55
+ def last
56
+ @last
57
+ end
58
+
59
+ # Returns the first Ip object in this range.
60
+ def first
61
+ @first
62
+ end
63
+
64
+ # Arguments
65
+ # * A +Range+ object
66
+ # True if this range is equivalent to the passed-in range.
67
+ def ==(range)
68
+ first == range.first && last == range.last
69
+ end
70
+
71
+ # Arguments
72
+ # * A +Range+ object
73
+ # True if this range intersects with the passed-in range.
74
+ def overlaps_range?(range)
75
+ contains_ip?(range.first) || contains_ip?(range.last) || range.contains_ip?(first) || range.contains_ip?(last)
76
+ end
77
+
78
+ # Arguments
79
+ # * A +Range+ object
80
+ # True if this range completely contains the passed-in range.
81
+ def contains_range?(range)
82
+ contains_ip?(range.first) && contains_ip?(range.last)
83
+ end
84
+
85
+ # Arguments
86
+ # * A +String+ or an +Ip+ object
87
+ # True if the range contains the IP
88
+ def contains_ip?(ip)
89
+ test = ip.kind_of?(String) ? Ip.new(:number => ip) : ip
90
+ test >= @first && @last >= test
91
+ end
92
+
93
+ # Yields each IP in the range, in turn, as an Ip object.
94
+ def each_ip
95
+ @counter = 0 # for throttling
96
+ ip = first
97
+ while last >= ip
98
+ yield ip.dup
99
+ ip.increment
100
+ throttle
101
+ end
102
+ end
103
+
104
+ private
105
+
106
+ def throttle
107
+ @counter += 1
108
+ if @counter % BATCHSIZE == 0
109
+ take_a_nap
110
+ end
111
+ end
112
+
113
+ def take_a_nap
114
+ log "Counter: #{$counter}, sleeping for #{PAUSE} seconds" if $verbose
115
+ sleep PAUSE
116
+ end
117
+
118
+ end
119
+ end
@@ -0,0 +1,94 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe IpRanges::Ip do
4
+ before do
5
+ @ip = IpRanges::Ip.new
6
+ end
7
+
8
+ it "duplicates with a different object" do
9
+ @ip.number = "1.200.3.4"
10
+ ip2 = @ip.dup
11
+ ip2.object_id.should_not == @ip.object_id
12
+ end
13
+
14
+ it "duplicates" do
15
+ @ip.number = "1.200.3.4"
16
+ ip2 = @ip.dup
17
+ ip2.should == @ip
18
+ end
19
+
20
+ it "is greater than or equal to" do
21
+ @ip.number = "1.200.3.4"
22
+ eq = IpRanges::Ip.new :number => "1.200.3.4"
23
+ lt = IpRanges::Ip.new :number => "1.200.3.3"
24
+ [eq, lt].each {|test| (@ip >= test).should be_true}
25
+ end
26
+
27
+ it "is equivalent" do
28
+ @ip.number = "1.200.3.4"
29
+ ip2 = IpRanges::Ip.new :number => "1.200.3.4"
30
+ @ip.should == ip2
31
+ end
32
+
33
+ it "renders to string" do
34
+ @ip.number = "1.200.3.4"
35
+ @ip.to_s.should == "1.200.3.4"
36
+ end
37
+
38
+ it "knows 1.2.3.255 < 1.2.4.0" do
39
+ @ip.number = '1.2.3.255'
40
+ ip2 = IpRanges::Ip.new :number => "1.2.4.0"
41
+ (@ip > ip2).should be_false
42
+ end
43
+
44
+ it "knows 1.2.4.0 >= 1.2.3.255" do
45
+ @ip.number = "1.2.4.0"
46
+ ip2 = IpRanges::Ip.new :number => '1.2.3.255'
47
+ (@ip >= ip2).should be_true
48
+ end
49
+
50
+ it "knows 1.200.3.4 > 1.199.3.4" do
51
+ @ip.number = "1.200.3.4"
52
+ ip2 = IpRanges::Ip.new :number => '1.199.3.4'
53
+ (@ip > ip2).should be_true
54
+ end
55
+
56
+ it "knows 1.2.3.4 > 1.2.2.4" do
57
+ @ip.number = "1.2.3.4"
58
+ ip2 = IpRanges::Ip.new :number => '1.2.2.4'
59
+ (@ip > ip2).should be_true
60
+ end
61
+
62
+ it "knows 1.2.3.4 > 1.2.3.3" do
63
+ @ip.number = "1.2.3.4"
64
+ ip2 = IpRanges::Ip.new :number => '1.2.3.3'
65
+ (@ip > ip2).should be_true
66
+ end
67
+
68
+ it "knows 1.2.3.3 <= 1.2.3.4" do
69
+ @ip.number = "1.2.3.3"
70
+ ip2 = IpRanges::Ip.new :number => '1.2.3.4'
71
+ (@ip > ip2).should be_false
72
+ end
73
+
74
+ it "increments when 2nd tuple hits 255" do
75
+ @ip.number = "1.255.255.255"
76
+ @ip.increment.should == "2.0.0.0"
77
+ end
78
+
79
+ it "increments when 3rd tuple hits 255" do
80
+ @ip.number = "1.2.255.255"
81
+ @ip.increment.should == "1.3.0.0"
82
+ end
83
+
84
+ it "increments when last tuple hits 255" do
85
+ @ip.number = "1.2.3.255"
86
+ @ip.increment.should == "1.2.4.0"
87
+ end
88
+
89
+ it "increments last tuple" do
90
+ @ip.number = '1.2.3.4'
91
+ @ip.increment.should == "1.2.3.5"
92
+ end
93
+
94
+ end
@@ -0,0 +1,119 @@
1
+ require 'spec/spec_helper'
2
+
3
+ describe IpRanges::Range do
4
+ before do
5
+ end
6
+
7
+ context "checking for overlaps" do
8
+
9
+ it "knows when range a contains range b" do
10
+ ipr1 = IpRanges::Range.new :range => '1.2.3.3..1.2.3.5'
11
+ ipr2 = IpRanges::Range.new :range => '1.2.3.4..1.2.3.5'
12
+ ipr1.contains_range?(ipr2).should be_true
13
+ end
14
+
15
+ it "knows when range a does not contain range b" do
16
+ ipr1 = IpRanges::Range.new :range => '1.2.3.4..1.2.3.5'
17
+ ipr2 = IpRanges::Range.new :range => '1.2.3.5..1.2.3.6'
18
+ ipr1.contains_range?(ipr2).should be_false
19
+ end
20
+
21
+ it "knows there is an overlap" do
22
+ ipr1 = IpRanges::Range.new :range => '1.2.3.4'
23
+ ipr2 = IpRanges::Range.new :range => '1.2.3.4'
24
+ ipr1.overlaps_range?(ipr2).should be_true
25
+ end
26
+
27
+ it "knows there is no overlap" do
28
+ ipr1 = IpRanges::Range.new :range => '1.2.3.4'
29
+ ipr2 = IpRanges::Range.new :range => '1.2.3.5'
30
+ ipr1.overlaps_range?(ipr2).should be_false
31
+ end
32
+
33
+ it "accepts Ip object for contains_ip" do
34
+ ipr = IpRanges::Range.new :range => '1.2.3.4..1.2.3.6'
35
+ ip = IpRanges::Ip.new :number => '1.2.3.5'
36
+ ipr.contains_ip?(ip).should be_true
37
+ end
38
+
39
+ it "knows it doesn't contain ip numbers" do
40
+ ipr = IpRanges::Range.new :range => '1.2.3.4..1.2.3.6'
41
+ %w(1.2.3.3 1.2.3.7).each {|num| ipr.contains_ip?(num).should be_false}
42
+ end
43
+
44
+ it "knows it contains an ip number" do
45
+ ipr = IpRanges::Range.new :range => '1.2.3.4..1.2.3.6'
46
+ %w(1.2.3.4 1.2.3.5 1.2.3.6).each {|num| ipr.contains_ip?(num).should be_true}
47
+ end
48
+
49
+ end
50
+
51
+ it "knows it equals another range" do
52
+ dotted = IpRanges::Range.new :range => '1.2.3.0..1.2.3.255'
53
+ cidr = IpRanges::Range.new :range => '1.2.3.0/24'
54
+ (dotted == cidr).should be_true
55
+ (cidr == dotted).should be_true
56
+ end
57
+
58
+ it "iterates over all ips" do
59
+ ipr = IpRanges::Range.new :range => '1.2.3.4 .. 1.2.3.6'
60
+ expected = [
61
+ IpRanges::Ip.new(:number => '1.2.3.4'),
62
+ IpRanges::Ip.new(:number => '1.2.3.5'),
63
+ IpRanges::Ip.new(:number => '1.2.3.6')
64
+ ]
65
+ arr = []
66
+ ipr.each_ip {|ip| arr << ip}
67
+ arr.should == expected
68
+ end
69
+
70
+ it "returns last ip from cidr range" do
71
+ ipr = IpRanges::Range.new :range => '1.2.3.0/24'
72
+ ipr.last.to_s.should == '1.2.3.255'
73
+ end
74
+
75
+ it "returns first ip from cidr range" do
76
+ ipr = IpRanges::Range.new :range => '1.2.3.0/24'
77
+ ipr.first.to_s.should == '1.2.3.0'
78
+ end
79
+
80
+ it "instantiates from a cidr range" do
81
+ ipr = IpRanges::Range.new :range => '1.2.3.0/24'
82
+ ipr.should be_kind_of(IpRanges::Range)
83
+ end
84
+
85
+ it "returns last ip from dotted range" do
86
+ ipr = IpRanges::Range.new :range => '1.2.3.4 .. 1.2.3.5'
87
+ ipr.last.to_s.should == '1.2.3.5'
88
+ end
89
+
90
+ it "returns first ip from dotted range" do
91
+ ipr = IpRanges::Range.new :range => '1.2.3.4 .. 1.2.3.5'
92
+ ipr.first.to_s.should == '1.2.3.4'
93
+ end
94
+
95
+ it "instantiates from a dotted range" do
96
+ ipr = IpRanges::Range.new :range => '1.2.3.4 .. 1.2.3.5'
97
+ ipr.should be_kind_of(IpRanges::Range)
98
+ end
99
+
100
+ it "returns last ip" do
101
+ ipr = IpRanges::Range.new :range => '1.2.3.4'
102
+ ipr.last.to_s.should == '1.2.3.4'
103
+ end
104
+
105
+ it "returns first ip" do
106
+ ipr = IpRanges::Range.new :range => '1.2.3.4'
107
+ ipr.first.to_s.should == '1.2.3.4'
108
+ end
109
+
110
+ it "instantiates from a single IP" do
111
+ ipr = IpRanges::Range.new :range => '1.2.3.4'
112
+ ipr.should be_kind_of(IpRanges::Range)
113
+ end
114
+
115
+ it "instantiates" do
116
+ ipr = IpRanges::Range.new :range => ''
117
+ ipr.should be_kind_of(IpRanges::Range)
118
+ end
119
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe IpRanges do
4
+ include IpRanges
5
+
6
+ context "checking for overlaps" do
7
+
8
+ it "explains range a equals range b" do
9
+ list = %w(1.2.3.0..1.2.3.255 1.2.3.0/24)
10
+ overlaps = check_for_overlaps(list)
11
+ overlaps[0].should == "1.2.3.0..1.2.3.255 equals range 1.2.3.0/24"
12
+ end
13
+
14
+ it "explains range a is contained by range b" do
15
+ list = %w(1.2.3.4..1.2.3.5 1.2.3.4..1.2.3.8)
16
+ overlaps = check_for_overlaps(list)
17
+ overlaps[0].should == "1.2.3.4..1.2.3.5 is contained by range 1.2.3.4..1.2.3.8"
18
+ end
19
+
20
+ it "explains range a contains range b" do
21
+ list = %w(1.2.3.4..1.2.3.8 1.2.3.4..1.2.3.5)
22
+ overlaps = check_for_overlaps(list)
23
+ overlaps[0].should == "1.2.3.4..1.2.3.8 contains range 1.2.3.4..1.2.3.5"
24
+ end
25
+
26
+ it "explains overlap" do
27
+ list = %w(1.2.3.4..1.2.3.8 1.2.3.5..1.2.3.9)
28
+ overlaps = check_for_overlaps(list)
29
+ overlaps[0].should == "1.2.3.4..1.2.3.8 overlaps with 1.2.3.5..1.2.3.9"
30
+ end
31
+
32
+ it "counts the overlaps" do
33
+ list = %w(1.2.3.4..1.2.3.8 1.2.3.4..1.2.3.5 1.2.3.8)
34
+ check_for_overlaps(list).size.should == 2
35
+ end
36
+
37
+ it "takes a list" do
38
+ check_for_overlaps []
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1 @@
1
+ require 'lib/ip-ranges'
metadata ADDED
@@ -0,0 +1,119 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ip-ranges
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - David Salgado
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-09-10 00:00:00 Z
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: netaddr
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ none: false
25
+ requirements:
26
+ - - ~>
27
+ - !ruby/object:Gem::Version
28
+ hash: 3
29
+ segments:
30
+ - 1
31
+ - 5
32
+ - 0
33
+ version: 1.5.0
34
+ type: :runtime
35
+ version_requirements: *id001
36
+ - !ruby/object:Gem::Dependency
37
+ name: rspec
38
+ prerelease: false
39
+ requirement: &id002 !ruby/object:Gem::Requirement
40
+ none: false
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ hash: 3
45
+ segments:
46
+ - 0
47
+ version: "0"
48
+ type: :development
49
+ version_requirements: *id002
50
+ - !ruby/object:Gem::Dependency
51
+ name: rdoc
52
+ prerelease: false
53
+ requirement: &id003 !ruby/object:Gem::Requirement
54
+ none: false
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ hash: 3
59
+ segments:
60
+ - 0
61
+ version: "0"
62
+ type: :development
63
+ version_requirements: *id003
64
+ description: Compare multiple IP ranges for overlaps, equivalence and containment
65
+ email: david@digitalronin.com
66
+ executables: []
67
+
68
+ extensions: []
69
+
70
+ extra_rdoc_files: []
71
+
72
+ files:
73
+ - Gemfile
74
+ - Gemfile.lock
75
+ - Rakefile
76
+ - README.markdown
77
+ - spec/ip-ranges/ip_spec.rb
78
+ - spec/ip-ranges/range_spec.rb
79
+ - spec/ip_ranges_spec.rb
80
+ - spec/spec_helper.rb
81
+ - lib/ip-ranges/ip.rb
82
+ - lib/ip-ranges/range.rb
83
+ - lib/ip-ranges.rb
84
+ homepage: http://roninonrails.blogspot.com
85
+ licenses: []
86
+
87
+ post_install_message:
88
+ rdoc_options:
89
+ - -x
90
+ - pkg
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ">="
97
+ - !ruby/object:Gem::Version
98
+ hash: 3
99
+ segments:
100
+ - 0
101
+ version: "0"
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ none: false
104
+ requirements:
105
+ - - ">="
106
+ - !ruby/object:Gem::Version
107
+ hash: 3
108
+ segments:
109
+ - 0
110
+ version: "0"
111
+ requirements: []
112
+
113
+ rubyforge_project:
114
+ rubygems_version: 1.8.10
115
+ signing_key:
116
+ specification_version: 3
117
+ summary: Manage ranges of IP numbers determining equivalence, containment and overlaps
118
+ test_files: []
119
+