summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.gitignore21
-rw-r--r--.rock.yml5
-rw-r--r--.travis.yml17
-rw-r--r--CHANGELOG.rdoc14
-rw-r--r--CONTRIBUTING.md39
-rw-r--r--Gemfile12
-rw-r--r--LICENSE.txt (renamed from LICENSE)2
-rw-r--r--README.rdoc180
-rw-r--r--Rakefile21
-rw-r--r--VERSION2
-rw-r--r--ipaddress.gemspec62
-rw-r--r--lib/ipaddress.rb28
-rw-r--r--lib/ipaddress/ipv4.rb126
-rw-r--r--lib/ipaddress/ipv6.rb18
-rw-r--r--lib/ipaddress/mongoid.rb75
-rw-r--r--lib/ipaddress/prefix.rb12
-rw-r--r--lib/ipaddress/version.rb3
-rw-r--r--tasks/jeweler.rake15
-rw-r--r--test/ipaddress/ipv4_test.rb115
-rw-r--r--test/ipaddress/ipv6_test.rb53
-rw-r--r--test/ipaddress/mongoid_test.rb70
-rw-r--r--test/ipaddress/prefix_test.rb14
-rw-r--r--test/ipaddress_test.rb39
-rw-r--r--test/test_helper.rb15
24 files changed, 758 insertions, 200 deletions
diff --git a/.gitignore b/.gitignore
index e582264..1c2e4bf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,7 +1,20 @@
-*.sw?
+*.a
+/.bundle/
+*.bundle
+/coverage/
+/doc/
.DS_Store
-coverage
+/Gemfile.lock
+ipaddr.html
+mkmf.log
+*.o
+/pkg/
rdoc
-pkg
server.rb
-ipaddr.html
+*.so
+/spec/reports/
+*.sw?
+/tmp/
+/_yardoc/
+.idea/
+**/*.gem
diff --git a/.rock.yml b/.rock.yml
new file mode 100644
index 0000000..551b6e7
--- /dev/null
+++ b/.rock.yml
@@ -0,0 +1,5 @@
+runtime: ruby21
+doc: exec rake rdoc
+build: |
+ rock clean
+ {{parent}} \ No newline at end of file
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..bb08cdb
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,17 @@
+language: ruby
+rvm:
+- 2.3
+- 2.4
+install:
+- gem install bundler
+- gem uninstall rake -x
+- gem install rake
+- bundle install
+script:
+- uname -s
+- rake test
+- codeclimate-test-reporter
+addons:
+ code_climate:
+ repo_token:
+ secure: Na2Ghl3W0IpUWAWx66V9skLie5MqiJWfn7muCSRkAYIVL/j9fR6jhhawOKlX0R6bg4byouOyLieDu9HWsv2EY5L7JweVspodRuuaJndxDQ1E5rOp0mU6vDl7kIqBeboX3AsivXDgcc8C8qvE+WD++vr3oEMg22c2/RIbt6ecJGs=
diff --git a/CHANGELOG.rdoc b/CHANGELOG.rdoc
index 59a0a0b..c386602 100644
--- a/CHANGELOG.rdoc
+++ b/CHANGELOG.rdoc
@@ -1,10 +1,19 @@
-== ipaddress 0.9.0
+== ipaddress 0.8.2
-CHANGED:: ipaddress now uses this[https://gist.github.com/cpetschnig/294476] regexp to validate IPv6 addresses. Thanks to Christoph Petschnig for his regexp and to Bronislav Robenek for fixing this.
+CHANGED:: merged bundler branch to cleanup gemspec and Rakefiles
+FIXED:: IPAddress::IPv4.split handling (Issue #40)
+NEW:: Added #[]= method to IPv4/6 classes to add octet writing support. (Issue #24)
+NEW:: IPV4#multicast?
+NEW:: IPV4#loopback?
+NEW:: IPV4#to()
+
+== ipaddress 0.8.1
+CHANGED:: ipaddress now uses minitest for testing, all tests passing
== ipaddress 0.8.0
+CHANGED:: ipaddress now uses this[https://gist.github.com/cpetschnig/294476] regexp to validate IPv6 addresses. Thanks to Christoph Petschnig for his regexp and to Bronislav Robenek for fixing this.
CHANGED:: Removed extension methods and extension directory to facilitate integration with the stdlib
CHANGED:: Reworked IPv4#<=>, now intuitively sorts objects based on the prefix
CHANGED:: IPv4#supernet now returns "0.0.0.0/0" if supernetting with a prefix less than 1
@@ -15,6 +24,7 @@ NEW:: IPv6#broadcast_u128
NEW:: IPv6#each
NEW:: IPv6#<=>
NEW:: IPv4#split
+NEW:: IP#ntoa
== ipaddress 0.7.5
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..94f60b5
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,39 @@
+# Contributing to the IPAddress Gem
+
+[![Build Status](https://travis-ci.org/ipaddress-gem/ipaddress.svg?branch=master)](https://travis-ci.org/ipaddress-gem/ipaddress) [![Code Climate](https://codeclimate.com/github/ipaddress-gem/ipaddress/badges/gpa.svg)](https://codeclimate.com/github/ipaddress-gem/ipaddress) [![Dependency Status](https://www.versioneye.com/user/projects/57001305fcd19a0051853bde/badge.svg?style=flat)](https://www.versioneye.com/user/projects/57001305fcd19a0051853bde)
+
+This gem is run by people who have jobs. So please understand if we can't always prioritize PRs and issues.
+
+You can help by making your code submissions. We can't promise a specific turnaround time, or that your code will be incorporated but all submissions are appreciated.
+
+## Steps to Submit a Pull Request
+
+* [Fork](https://help.github.com/articles/fork-a-repo) the project on GitHub.
+* Make your feature addition or bug fix [in a feature branch](https://github.com/Kunena/Kunena-Forum/wiki/Create-a-new-branch-with-git-and-manage-branches). (Include a description of your changes in the PR)
+* Push your feature branch to GitHub.
+* Send a [Pull Request](https://help.github.com/articles/using-pull-requests)
+
+## Style Guide
+
+We will require that you adhere to the [ruby-style-guide](https://github.com/bbatsov/ruby-style-guide) for your code submissions.
+
+## Test Coverage
+
+All submissions of code must include test coverage which describes intent and expected behavior. The test suite used by this gem is [Minitest](https://github.com/seattlerb/minitest)
+
+Unit tests are expected to execute quickly. We will ask you to revise any long-running tests.
+
+We intend to add [Travis CI](https://travis-ci.org/) for automatic execution of branch tests.
+
+## Versioning: Jeweler and Semantic Versioning
+
+This repo uses [semantic versioning](http://semver.org/) implemented by the [Jeweler Gem](https://github.com/technicalpickles/jeweler). Please do not update the version by editting files, instead, you may increment or modify the version using the Rake tasks added by Jeweler.
+
+```
+$ rake -T | grep version
+rake version # Displays the current version
+rake version:bump:major # Bump the major version by 1
+rake version:bump:minor # Bump the a minor version by 1
+rake version:bump:patch # Bump the patch version by 1
+rake version:write # Writes out an explicit version
+``` \ No newline at end of file
diff --git a/Gemfile b/Gemfile
new file mode 100644
index 0000000..e479aab
--- /dev/null
+++ b/Gemfile
@@ -0,0 +1,12 @@
+source "https://rubygems.org"
+
+group :development do
+ gem 'bundler', '>= 1.0'
+ gem 'rake', '10.5.0'
+ gem 'minitest', '~> 5.8', '>= 5.8.4'
+ gem 'pry', '>= 0.10.1'
+ gem 'travis', '>= 1.8.2'
+ gem 'jeweler', '>=2.0.1'
+ gem 'codeclimate-test-reporter'
+ gem 'simplecov'
+end
diff --git a/LICENSE b/LICENSE.txt
index 25def2b..943eee1 100644
--- a/LICENSE
+++ b/LICENSE.txt
@@ -1,4 +1,4 @@
-Copyright (c) 2009-2011 Marco Ceresa
+Copyright (c) 2009-2015 Marco Ceresa
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
diff --git a/README.rdoc b/README.rdoc
index 2146384..7d9600a 100644
--- a/README.rdoc
+++ b/README.rdoc
@@ -14,10 +14,13 @@ examples of typical usage.
== Requirements
-* Ruby >= 1.8.7 (not tested with previous versions)
-* Ruby 1.9.2 or later is strongly recommended
+* Ruby 1.9.3 or later
-IPAddress 0.8.0 has been tested on:
+Please refer to {Travis CI}[https://travis-ci.org/ipaddress-gem/ipaddress] for Build Tests on specific versions of Ruby.
+
+{<img src="https://travis-ci.org/ipaddress-gem/ipaddress.svg?branch=master" alt="Build Status" />}[https://travis-ci.org/ipaddress-gem/ipaddress] {<img src="https://codeclimate.com/github/ipaddress-gem/ipaddress/badges/gpa.svg" />}[https://codeclimate.com/github/ipaddress-gem/ipaddress] {<img src="https://www.versioneye.com/user/projects/57001305fcd19a0051853bde/badge.svg?style=flat" alt="Dependency Status" />}[https://www.versioneye.com/user/projects/57001305fcd19a0051853bde]
+
+IPAddress 0.8.2 was manually tested on:
* ruby-1.8.7-p334 [ i386 ]
* ree-1.8.7-2011.03 [ i386 ]
@@ -25,10 +28,10 @@ IPAddress 0.8.0 has been tested on:
* jruby-1.6.1 [ linux-i386-java ]
* ruby-1.9.1-p431 [ i386 ]
* ruby-1.9.2-p180 [ i386 ]
+* ruby-2.0.0-p353 [ x86_64-darwin14.0.0 ]
+* ruby-2.1.3-p242 [ x86_64-darwin14.0.0 ]
-If you want to collaborate feel
-free to send a small report to my email address, or
-{join the discussion}[http://groups.google.com/group/ruby-ipaddress].
+If you want to contribute, please refer to {Contributing.md}[https://github.com/ipaddress-gem/ipaddress/blob/master/CONTRIBUTING.md].
== Installation
@@ -63,14 +66,14 @@ The latest documentation can be found online at
== IPv4
-Class IPAddress::IPv4 is used to handle IPv4 type addresses. IPAddress
+Class `IPAddress::IPv4` is used to handle IPv4 type addresses. IPAddress
is similar to other IP Addresses libraries, like Ruby's own
IPAddr. However it works slightly different, as we will see.
=== Create a new IPv4 address
The usual way to express an IP Address is using its dotted decimal
-form, such as 172.16.10.1, and a prefix, such as 24, separated by a
+form, such as `172.16.10.1`, and a prefix, such as `24`, separated by a
slash.
172.16.10.1/24
@@ -83,11 +86,11 @@ or, in a easier way, using the IPAddress parse method
ip = IPAddress.parse "172.16.10.1/24"
-which accepts and parses any kind of IP (IPv4, IPV6 and
+which accepts and parses any kind of IP (uint32, IPv4, IPV6 and
IPv4 IPv6 Mapped addresses).
If you like syntactic sugar, you can use the wrapper method
-IPAddress(), which is built around IPAddress::parse:
+`IPAddress()`, which is built around `IPAddress::parse`:
ip = IPAddress "172.16.10.1/24"
@@ -96,13 +99,13 @@ You can specify an IPv4 address in any of two ways:
IPAddress "172.16.10.1/24"
IPAddress "172.16.10.1/255.255.255.0"
-In this example, prefix /24 and netmask 255.255.255.0 are the same and
+In this example, prefix `/24` and netmask `255.255.255.0` are the same and
you have the flexibility to use either one of them.
If you don't explicitly specify the prefix (or the subnet mask),
IPAddress thinks you're dealing with host addresses and not with
-networks. Therefore, the default prefix will be /32, or
-255.255.255.255. For example:
+networks. Therefore, the default prefix will be `/32`, or
+`255.255.255.255`. For example:
# let's declare an host address
host = IPAddress::IPv4.new "10.1.1.1"
@@ -110,11 +113,18 @@ networks. Therefore, the default prefix will be /32, or
puts host.to_string
#=> "10.1.1.1/32"
-The new created object has prefix /32, which is the same
+The new created object has prefix `/32`, which is the same
as we created the following:
host = IPAddress::IPv4.new "10.1.1.1/32"
-
+
+You can also pass a `uint32` to obtain an `IPAddress::IPv4` object:
+
+ # Create host object
+ ip = IPAddress 167837953
+ puts ip.to_string
+ #=> "10.1.1.1/32"
+
=== Handling the IPv4 address
Once created, you can obtain the attributes for an IPv4 object:
@@ -127,25 +137,25 @@ Once created, you can obtain the attributes for an IPv4 object:
#=> 24
In case you need to retrieve the netmask in IPv4 format, you can use
-the IPv4#netmask method:
+the `IPv4#netmask` method:
ip.netmask
#=> "255.255.255.0"
-A special attribute, IPv4#octets, is available to get the four
+A special attribute, `IPv4#octets`, is available to get the four
decimal octets from the IP address:
ip.octets
#=> [172,16,10,1]
-Shortcut method IPv4#[], provides access to a given octet whithin the
+Shortcut method `IPv4#[]`, provides access to a given octet whithin the
range:
ip[1]
#=> 16
If you need to print out the IPv4 address in a canonical form, you can
-use IPv4#to_string
+use `IPv4#to_string`:
ip.to_string
#=> "172.16.10.l/24"
@@ -161,7 +171,7 @@ object. For example:
#=> "172.16.10.l/25"
If you need to use a netmask in IPv4 format, you can achive so by
-using the IPv4#netmask= method
+using the `IPv4#netmask=` method:
ip.netmask = "255.255.255.252"
@@ -174,7 +184,7 @@ Some very important topics in dealing with IP addresses are the
concepts of +network+ and +broadcast+, as well as the addresses
included in a range.
-When you specify an IPv4 address such as "172.16.10.1/24", you are
+When you specify an IPv4 address such as `172.16.10.1/24`, you are
actually handling two different information:
* The IP address itself, "172.16.10.1"
@@ -183,11 +193,11 @@ actually handling two different information:
The network number is the IP which has all zeroes in the host
portion. In our example, because the prefix is 24, we identify our
network number to have the last 8 (32-24) bits all zeroes. Thus, IP
-address "172.16.10.1/24" belongs to network "172.16.10.0/24".
+address `172.16.10.1/24` belongs to network `172.16.10.0/24`.
-This is very important because, for instance, IP "172.16.10.1/16" is
+This is very important because, for instance, IP `172.16.10.1/16` is
very different to the previous one, belonging to the very different
-network "172.16.0.0/16".
+network `172.16.0.0/16`.
==== Networks
@@ -208,7 +218,7 @@ number, calculated after the original object. We want to outline here
that the network address is a perfect legitimate IPv4 address, which
just happen to have all zeroes in the host portion.
-You can use method IPv4#network? to check whether an IP address is a
+You can use method `IPv4#network?` to check whether an IP address is a
network or not:
ip1 = IPAddress "172.16.10.1/24"
@@ -223,11 +233,11 @@ network or not:
The broadcast address is the contrary than the network number: where
the network number has all zeroes in the host portion, the broadcast
-address has all one's. For example, ip "172.16.10.1/24" has broadcast
-"172.16.10.255/24", where ip "172.16.10.1/16" has broadcast
-"172.16.255.255/16".
+address has all one's. For example, ip `172.16.10.1/24` has broadcast
+`172.16.10.255/24`, where ip `172.16.10.1/16` has broadcast
+`172.16.255.255/16`.
-Method IPv4#broadcast has the same behavior as is #network
+Method `IPv4#broadcast` has the same behavior as is `#network`
counterpart: it creates a new IPv4 object to handle the broadcast
address:
@@ -245,7 +255,7 @@ address:
So we see that the netmask essentially specifies a range for IP
addresses that are included in a network: all the addresses between
the network number and the broadcast. IPAddress has many methods to
-iterate between those addresses. Let's start with IPv4#each, which
+iterate between those addresses. Let's start with `IPv4#each`, which
iterates over all addresses in a range
ip = IPAddress "172.16.10.1/24"
@@ -258,7 +268,7 @@ It is important to note that it doesn't matter if the original IP is a
host IP or a network number (or a broadcast address): the #each method
only considers the range that the original IP specifies.
-If you only want to iterate over hosts IP, use the IPv4#each_host
+If you only want to iterate over hosts IP, use the `IPv4#each_host`
method:
ip = IPAddress "172.16.10.1/24"
@@ -267,7 +277,7 @@ method:
puts host
end
-Methods IPv4#first and IPv4#last return a new object containing
+Methods `IPv4#first` and `IPv4#last` return a new object containing
respectively the first and the last host address in the range
ip = IPAddress "172.16.10.100/24"
@@ -278,6 +288,28 @@ respectively the first and the last host address in the range
ip.last.to_string
#=> "172.16.10.254/24"
+Checking if an address is loopback is easy with the `IPv4#loopback?`
+method:
+
+ ip = IPAddress "127.0.0.1"
+
+ ip.loopback?
+ #=> true
+
+Checking if an address is in the multicast range can be done using the `IPv4#multicast?`
+method:
+
+ ip = IPAddress "224.0.0.1/32"
+
+ ip.multicast?
+ #=> true
+
+The ability to generate a range also exists by using the `IPv4#to()` method. This allows you to create a subnet agnostic range based off a fixed amount.
+
+ ip = IPAddress "172.16.10.100/24"
+ ip.to('172.16.10.110')
+ #=> ["172.16.10.100", ..., "172.16.10.110"]
+
=== IP special formats
The IPAddress library provides a complete set of methods to access an
@@ -295,38 +327,43 @@ The first thing to highlight here is that all these conversion methods
only take into consideration the address portion of an IPv4 object and
not the prefix (netmask).
-So, to express the address in binary format, use the IPv4#bits method:
+So, to express the address in binary format, use the `IPv4#bits` method:
ip.bits
#=> "10101100000100000000101000000001"
To calculate the 32 bits unsigned int format of the ip address, use
-the IPv4#to_u32 method
+the `IPv4#to_u32` method
ip.to_u32
#=> 2886732289
-This method is the equivalent of the Unix call pton(), expressing an
+This method is the equivalent of the Unix call `pton()`, expressing an
IP address in the so called +network byte order+ notation. However, if
you want to transmit your IP over a network socket, you might need to
-transform it in data format using the IPv4#data method:
+transform it in data format using the `IPv4#data` method:
ip.data
#=> "\254\020\n\001"
-Finally, you can transform an IPv4 address into a format which is
+Also, you can transform an IPv4 address into a format which is
suitable to use in IPv4-IPv6 mapped addresses:
ip.to_ipv6
#=> "ac10:0a01"
+Finally, much like `IPv4#to_ipv6` you can use to `IPv4#to_h` method to return a non-semicolon delineated string (useful with pcap/byte level usage):
+
+ ip.to_h
+ #=> "ac100a01"
+
=== Classful networks
IPAddress allows you to create and manipulate objects using the old
and deprecated (but apparently still popular) classful networks concept.
Classful networks and addresses don't have a prefix: their subnet mask
-is univocally identified by their address, and therefore diveded in classes.
+is univocally identified by their address, and therefore divided in classes.
As per RFC 791, these classes are:
* Class A, from 0.0.0.0 to 127.255.255.255
@@ -388,7 +425,7 @@ Subnetting is easy with IPAddress. You actually have two options:
* IPv4#subnet: specify a new prefix
* IPv4#split: tell IPAddress how many subnets you want to create.
-Let's examine IPv4#subnet first. Say you have network "172.16.10.0/24"
+Let's examine `IPv4#subnet` first. Say you have network "172.16.10.0/24"
and you want to subnet it into /26 networks. With IPAddress it's very
easy:
@@ -408,7 +445,7 @@ representing the new subnets.
Another way to create subnets is to tell IPAddress how many subnets you'd
like to have, and letting the library calculate the new prefix for you.
-Let's see how it works, using IPv4#split method. Say you want 4 new subnets:
+Let's see how it works, using `IPv4#split` method. Say you want 4 new subnets:
network = IPAddress("172.16.10.0/24")
@@ -421,15 +458,15 @@ Let's see how it works, using IPv4#split method. Say you want 4 new subnets:
"172.16.10.192/26"]
Hey, that's the same result as before! This actually makes sense, as the
-two operations are complementary. When you use IPv4#subnet with the new
+two operations are complementary. When you use `IPv4#subnet` with the new
prefix, IPAddress will always create a number of subnets that is a power
of two. This is equivalent to use IPv4#split with a power of 2.
-Where IPv4#split really shines is with the so called "uneven subnetting".
+Where `IPv4#split` really shines is with the so called "uneven subnetting".
You are not limited to split a network into a power-of-two numbers of
subnets: IPAddress lets you create any number of subnets, and it will
try to organize the new created network in the best possible way, making
-an efficent allocation of the space.
+an efficient allocation of the space.
An example here is worth a thousand words. Let's use the same network
as the previous examples:
@@ -447,8 +484,8 @@ How do we split this network into 3 subnets? Very easy:
As you can see, IPAddress tried to perform a good allocation by filling up
all the address space from the original network. There is no point in splitting
-a network into 3 subnets like "172.16.10.0/26", "172.16.10.64/26" and
-"172.16.10.128/26", as you would end up having "172.16.10.192/26" wasted (plus,
+a network into 3 subnets like `172.16.10.0/26`, `172.16.10.64/26` and
+`172.16.10.128/26`, as you would end up having `172.16.10.192/26` wasted (plus,
I suppose I wouldn't need a Ruby library to perform un-efficient IP
allocation, as I do that myself very well ;) ).
@@ -460,8 +497,8 @@ We can go even further and split into 11 subnets:
"172.16.10.96/28", "172.16.10.112/28", "172.16.10.128/27",
"172.16.10.160/27", "172.16.10.192/26"]
-As you can see, most of the networks are /28, with a few /27 and one
-/26 to fill up the remaining space.
+As you can see, most of the networks are `/28`, with a few `/27` and one
+`/26` to fill up the remaining space.
==== Summarization
@@ -492,7 +529,7 @@ network if we change the prefix. Let Ruby do the work:
IPAddress::IPv4::summarize(ip1,ip2).map(&:to_string)
#=> "172.16.10.0/23"
-We note how the network "172.16.10.0/23" includes all the
+We note how the network `172.16.10.0/23` includes all the
addresses specified in the above networks, and (more important) includes
ONLY those addresses.
@@ -509,7 +546,7 @@ is not an aggregate network for +ip1+ and +ip2+.
If it's not possible to compute a single aggregated network for
all the original networks, the method returns an array with all the
aggregate networks found. For example, the following four networks can be
-aggregated in a single /22:
+aggregated in a single `/22`:
ip1 = IPAddress("10.0.0.1/24")
ip2 = IPAddress("10.0.1.1/24")
@@ -531,7 +568,7 @@ network:
#=> ["10.0.1.0/24","10.0.2.0/23","10.0.4.0/24"]
In this case, the two summarizables networks have been aggregated into
-a single /23, while the other two networks have been left untouched.
+a single `/23`, while the other two networks have been left untouched.
==== Supernetting
@@ -550,13 +587,13 @@ you can supernet it with a new /23 prefix
ip.supernet(23).to_string
#=> "172.16.10.0/23"
-However if you supernet it with a /22 prefix, the network address will
+However if you supernet it with a `/22` prefix, the network address will
change:
ip.supernet(22).to_string
#=> "172.16.8.0/22"
-This is because "172.16.10.0/22" is not a network anymore, but an host
+This is because `172.16.10.0/22` is not a network anymore, but an host
address.
== IPv6
@@ -638,7 +675,7 @@ methods:
#=> 64
A compressed version of the IPv6 address can be obtained with the
-IPv6#compressed method:
+`IPv6#compressed` method:
ip6 = IPAddress "2001:0db8:0000:0000:0008:200c:417a:00ab/64"
@@ -648,7 +685,7 @@ IPv6#compressed method:
=== Handling the IPv6 address
Accessing the groups that form an IPv6 address is very easy with the
-IPv6#groups method:
+`IPv6#groups` method:
ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
@@ -656,7 +693,7 @@ IPv6#groups method:
#=> [8193, 3512, 0, 0, 8, 2048, 8204, 16762]
As with IPv4 addresses, each individual group can be accessed using
-the IPv6#[] shortcut method:
+the `IPv6#[]` shortcut method:
ip6[0]
#=> 8193
@@ -668,14 +705,14 @@ the IPv6#[] shortcut method:
#=> 0
Note that each 16 bits group is expressed in its decimal form. You can
-also obtain the groups into hexadecimal format using the IPv6#hexs
+also obtain the groups into hexadecimal format using the `IPv6#hexs`
method:
ip6.hexs
#=> => ["2001", "0db8", "0000", "0000", "0008", "0800", "200c", "417a"]
A few other methods are available to transform an IPv6 address into
-decimal representation, with IPv6.to_i
+decimal representation, with `IPv6.to_i`
ip6.to_i
#=> 42540766411282592856906245548098208122
@@ -685,8 +722,8 @@ or to hexadecimal representation
ip6.to_hex
#=> "20010db80000000000080800200c417a"
-To print out an IPv6 address in human readable form, use the IPv6#to_s, IPv6#to_string
-and IPv6#to_string_uncompressed methods
+To print out an IPv6 address in human readable form, use the `IPv6#to_s`, `IPv6#to_string`
+and `IPv6#to_string_uncompressed` methods
ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
@@ -696,8 +733,8 @@ and IPv6#to_string_uncompressed methods
ip6.to_string_uncompressed
#=> "2001:0db8:0000:0000:0008:0800:200c:417a/96"
-As you can see, IPv6.to_string prints out the compressed form, while
-IPv6.to_string_uncompressed uses the expanded version.
+As you can see, `IPv6.to_string` prints out the compressed form, while
+`IPv6.to_string_uncompressed` uses the expanded version.
==== Compressing and uncompressing
@@ -731,7 +768,7 @@ actually created internally).
You can create a new IPv6 address from different formats than just a
string representing the colon-hex groups.
-For instance, if you have a data stream, you can use IPv6::parse_data,
+For instance, if you have a data stream, you can use `IPv6::parse_data`,
like in the following example:
data = " \001\r\270\000\000\000\000\000\b\b\000 \fAz"
@@ -772,7 +809,7 @@ support for unspecified, loopback and mapped IPv6 addresses.
==== Unspecified address
The address with all zero bits is called the +unspecified+ address
-(corresponding to 0.0.0.0 in IPv4). It should be something like this:
+(corresponding to `0.0.0.0` in IPv4). It should be something like this:
0000:0000:0000:0000:0000:0000:0000:0000
@@ -814,7 +851,7 @@ packets with the unspecified address.
==== Loopback address
-The loopback address is a unicast localhost address. If an
+The loopback address is a unicast localhost address. If an
application in a host sends packets to this address, the IPv6 stack
will loop these packets back on the same virtual interface.
@@ -841,13 +878,13 @@ or by using the wrapper:
ip.to_string
#=> "::1/128"
-Checking if an address is loopback is easy with the IPv6#loopback?
+Checking if an address is loopback is easy with the `IPv6#loopback?`
method:
ip.loopback?
#=> true
-The IPv6 loopback address corresponds to 127.0.0.1 in IPv4.
+The IPv6 loopback address corresponds to `127.0.0.1` in IPv4.
==== Mapped address
@@ -857,7 +894,7 @@ structure of the address is
::ffff:w.y.x.z
-where w.x.y.z is a normal IPv4 address. For example, the following is
+where `w.x.y.z` is a normal IPv4 address. For example, the following is
a mapped IPv6 address:
::ffff:192.168.100.1
@@ -881,7 +918,7 @@ Let's check it's really a mapped address:
ip6.to_string
#=> "::ffff:172.16.10.1/128"
-Now with the +ipv4+ attribute, we can easily access the IPv4 portion
+Now with the `#ipv4` attribute, we can easily access the IPv4 portion
of the mapped IPv6 address:
ip6.ipv4.address
@@ -902,7 +939,7 @@ following format:
ip6 = IPAddress "::172.16.10.1"
-That is, two colons and the IPv4 address. However, as by RFC, the ffff
+That is, two colons and the IPv4 address. However, as by RFC, the `ffff`
group will be automatically added at the beginning
ip6.to_string
@@ -955,7 +992,4 @@ feedback and bug reports.
== Copyright
-Copyright (c) 2009-2011 Marco Ceresa. See LICENSE for details.
-
-
-
+Copyright (c) 2009-2015 Marco Ceresa and Mike Mackintosh. See LICENSE for details.
diff --git a/Rakefile b/Rakefile
index b7dbf64..0948203 100644
--- a/Rakefile
+++ b/Rakefile
@@ -1,25 +1,8 @@
require 'rubygems'
require 'rake'
require 'rake/clean'
+require "bundler/gem_tasks"
-begin
- require 'jeweler'
- Jeweler::Tasks.new do |gem|
- gem.name = "ipaddress"
- gem.summary = %Q{IPv4/IPv6 addresses manipulation library}
- gem.email = "ceresa@gmail.com"
- gem.homepage = "http://github.com/bluemonk/ipaddress"
- gem.authors = ["Marco Ceresa"]
- gem.description = <<-EOD
- IPAddress is a Ruby library designed to make manipulation
- of IPv4 and IPv6 addresses both powerful and simple. It mantains
- a layer of compatibility with Ruby's own IPAddr, while
- addressing many of its issues.
- EOD
- end
-rescue LoadError
- puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
-end
require 'rake/testtask'
Rake::TestTask.new(:test) do |test|
@@ -81,3 +64,5 @@ task :todo do
end
egrep /(FIXME|TODO|TBD)/
end
+
+Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |raketask| load raketask }
diff --git a/VERSION b/VERSION
index 8adc70f..ee94dd8 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-0.8.0 \ No newline at end of file
+0.8.3
diff --git a/ipaddress.gemspec b/ipaddress.gemspec
index 41af7bf..6b61e92 100644
--- a/ipaddress.gemspec
+++ b/ipaddress.gemspec
@@ -2,28 +2,31 @@
# DO NOT EDIT THIS FILE DIRECTLY
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
# -*- encoding: utf-8 -*-
+# stub: ipaddress 0.8.3 ruby lib
Gem::Specification.new do |s|
- s.name = %q{ipaddress}
- s.version = "0.8.0"
+ s.name = "ipaddress"
+ s.version = "0.8.3"
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
- s.authors = ["Marco Ceresa"]
- s.date = %q{2011-05-17}
- s.description = %q{ IPAddress is a Ruby library designed to make manipulation
- of IPv4 and IPv6 addresses both powerful and simple. It mantains
- a layer of compatibility with Ruby's own IPAddr, while
- addressing many of its issues.
-}
- s.email = %q{ceresa@gmail.com}
+ s.require_paths = ["lib"]
+ s.authors = ["bluemonk", "mikemackintosh"]
+ s.date = "2016-03-23"
+ s.description = "IPAddress is a Ruby library designed to make manipulation\n of IPv4 and IPv6 addresses both powerful and simple. It mantains\n a layer of compatibility with Ruby's own IPAddr, while\n addressing many of its issues."
+ s.email = "ceresa@gmail.com"
s.extra_rdoc_files = [
- "LICENSE",
+ "CHANGELOG.rdoc",
+ "LICENSE.txt",
"README.rdoc"
]
s.files = [
".document",
+ ".rock.yml",
+ ".travis.yml",
"CHANGELOG.rdoc",
- "LICENSE",
+ "CONTRIBUTING.md",
+ "Gemfile",
+ "LICENSE.txt",
"README.rdoc",
"Rakefile",
"VERSION",
@@ -31,25 +34,50 @@ Gem::Specification.new do |s|
"lib/ipaddress.rb",
"lib/ipaddress/ipv4.rb",
"lib/ipaddress/ipv6.rb",
+ "lib/ipaddress/mongoid.rb",
"lib/ipaddress/prefix.rb",
+ "lib/ipaddress/version.rb",
+ "tasks/jeweler.rake",
"test/ipaddress/ipv4_test.rb",
"test/ipaddress/ipv6_test.rb",
+ "test/ipaddress/mongoid_test.rb",
"test/ipaddress/prefix_test.rb",
"test/ipaddress_test.rb",
"test/test_helper.rb"
]
- s.homepage = %q{http://github.com/bluemonk/ipaddress}
- s.require_paths = ["lib"]
- s.rubygems_version = %q{1.6.2}
- s.summary = %q{IPv4/IPv6 addresses manipulation library}
+ s.homepage = "https://github.com/bluemonk/ipaddress"
+ s.licenses = ["MIT"]
+ s.rubygems_version = "2.4.6"
+ s.summary = "IPv4/IPv6 address manipulation library"
if s.respond_to? :specification_version then
- s.specification_version = 3
+ s.specification_version = 4
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
+ s.add_development_dependency(%q<bundler>, [">= 1.0"])
+ s.add_development_dependency(%q<rake>, [">= 0"])
+ s.add_development_dependency(%q<minitest>, [">= 5.8.4", "~> 5.8"])
+ s.add_development_dependency(%q<pry>, [">= 0.10.1"])
+ s.add_development_dependency(%q<travis>, [">= 1.8.2"])
+ s.add_development_dependency(%q<jeweler>, [">= 2.0.1"])
+ s.add_development_dependency(%q<codeclimate-test-reporter>, [">= 0"])
else
+ s.add_dependency(%q<bundler>, [">= 1.0"])
+ s.add_dependency(%q<rake>, [">= 0"])
+ s.add_dependency(%q<minitest>, [">= 5.8.4", "~> 5.8"])
+ s.add_dependency(%q<pry>, [">= 0.10.1"])
+ s.add_dependency(%q<travis>, [">= 1.8.2"])
+ s.add_dependency(%q<jeweler>, [">= 2.0.1"])
+ s.add_dependency(%q<codeclimate-test-reporter>, [">= 0"])
end
else
+ s.add_dependency(%q<bundler>, [">= 1.0"])
+ s.add_dependency(%q<rake>, [">= 0"])
+ s.add_dependency(%q<minitest>, [">= 5.8.4", "~> 5.8"])
+ s.add_dependency(%q<pry>, [">= 0.10.1"])
+ s.add_dependency(%q<travis>, [">= 1.8.2"])
+ s.add_dependency(%q<jeweler>, [">= 2.0.1"])
+ s.add_dependency(%q<codeclimate-test-reporter>, [">= 0"])
end
end
diff --git a/lib/ipaddress.rb b/lib/ipaddress.rb
index 7631952..19b526f 100644
--- a/lib/ipaddress.rb
+++ b/lib/ipaddress.rb
@@ -14,6 +14,7 @@
require 'ipaddress/ipv4'
require 'ipaddress/ipv6'
+require 'ipaddress/mongoid' if defined?(Mongoid)
module IPAddress
@@ -25,6 +26,7 @@ module IPAddress
# Parse the argument string to create a new
# IPv4, IPv6 or Mapped IP object
#
+ # ip = IPAddress.parse 167837953 # 10.1.1.1
# ip = IPAddress.parse "172.16.10.1/24"
# ip6 = IPAddress.parse "2001:db8::8:800:200c:417a/64"
# ip_mapped = IPAddress.parse "::ffff:172.16.10.1/128"
@@ -40,6 +42,12 @@ module IPAddress
# #=> IPAddress::IPv6::Mapped
#
def IPAddress::parse(str)
+
+ # Check if an int was passed
+ if str.kind_of? Integer
+ return IPAddress::IPv4.new(ntoa(str))
+ end
+
case str
when /:.+\./
IPAddress::IPv6::Mapped.new(str)
@@ -53,6 +61,24 @@ module IPAddress
end
#
+ # Converts a unit32 to IPv4
+ #
+ # IPAddress::ntoa(167837953)
+ # #-> "10.1.1.1"
+ #
+ def self.ntoa(uint)
+ unless(uint.is_a? Numeric and uint <= 0xffffffff and uint >= 0)
+ raise(::ArgumentError, "not a long integer: #{uint.inspect}")
+ end
+ ret = []
+ 4.times do
+ ret.unshift(uint & 0xff)
+ uint >>= 8
+ end
+ ret.join('.')
+ end
+
+ #
# True if the object is an IPv4 address
#
# ip = IPAddress("192.168.10.100/24")
@@ -188,7 +214,7 @@ end
#
# Compatibility with Ruby 1.8
#
-if RUBY_VERSION =~ /1\.8/
+if RUBY_VERSION =~ /^1\.8/
class Hash # :nodoc:
alias :key :index
end
diff --git a/lib/ipaddress/ipv4.rb b/lib/ipaddress/ipv4.rb
index e99aea2..19ab14c 100644
--- a/lib/ipaddress/ipv4.rb
+++ b/lib/ipaddress/ipv4.rb
@@ -61,6 +61,7 @@ module IPAddress;
# IPAddress::IPv4.new "10.0.0.1/255.0.0.0"
#
def initialize(str)
+ raise ArgumentError, "Nil IP" unless str
ip, netmask = str.split("/")
# Check the ip and remove white space
@@ -231,6 +232,21 @@ module IPAddress;
alias_method :to_u32, :u32
#
+ # Returns the address portion in
+ # hex
+ #
+ # ip = IPAddress("10.0.0.0")
+ #
+ # ip.to_h
+ # #=> 0a000000
+ #
+ def hex(space=true)
+ "%.4x%.4x" % [to_u32].pack("N").unpack("nn")
+ end
+ alias_method :to_h, :hex
+ alias_method :to_hex, :hex
+
+ #
# Returns the address portion of an IPv4 object
# in a network byte order format.
#
@@ -271,6 +287,21 @@ module IPAddress;
@octets[index]
end
alias_method :octet, :[]
+
+ #
+ # Updated the octet specified at index
+ #
+ # ip = IPAddress("172.16.100.50/24")
+ # ip[2] = 200
+ #
+ # #=> #<IPAddress::IPv4:0x00000000000000 @address="172.16.200.1",
+ # #=> @prefix=32, @octets=[172, 16, 200, 1], @u32=2886780929>
+ #
+ def []=(index, value)
+ @octets[index] = value.to_i
+ initialize("#{@octets.join('.')}/#{prefix}")
+ end
+ alias_method :octet=, :[]=
#
# Returns the address portion of an IP in binary format,
@@ -294,7 +325,14 @@ module IPAddress;
# #=> "172.16.10.255"
#
def broadcast
- self.class.parse_u32(broadcast_u32, @prefix)
+ case
+ when prefix <= 30
+ self.class.parse_u32(broadcast_u32, @prefix)
+ when prefix == 31
+ self.class.parse_u32(-1, @prefix)
+ when prefix == 32
+ return self
+ end
end
#
@@ -311,7 +349,7 @@ module IPAddress;
# #=> true
#
def network?
- @u32 | @prefix.to_u32 == @prefix.to_u32
+ (@prefix < 32) && (@u32 | @prefix.to_u32 == @prefix.to_u32)
end
#
@@ -348,7 +386,14 @@ module IPAddress;
# #=> "192.168.100.1"
#
def first
- self.class.parse_u32(network_u32+1, @prefix)
+ case
+ when prefix <= 30
+ self.class.parse_u32(network_u32+1, @prefix)
+ when prefix == 31
+ self.class.parse_u32(network_u32, @prefix)
+ when prefix == 32
+ return self
+ end
end
#
@@ -373,7 +418,14 @@ module IPAddress;
# #=> "192.168.100.254"
#
def last
- self.class.parse_u32(broadcast_u32-1, @prefix)
+ case
+ when prefix <= 30
+ self.class.parse_u32(broadcast_u32-1, @prefix)
+ when prefix == 31
+ self.class.parse_u32(broadcast_u32, @prefix)
+ when prefix == 32
+ return self
+ end
end
#
@@ -458,6 +510,7 @@ module IPAddress;
# #=> ["10.100.100.1/8","10.100.100.1/16","172.16.0.1/16"]
#
def <=>(oth)
+ return nil unless oth.is_a?(self.class)
return prefix <=> oth.prefix if to_u32 == oth.to_u32
to_u32 <=> oth.to_u32
end
@@ -570,6 +623,34 @@ module IPAddress;
end
#
+ # Checks if an IPv4 address objects belongs
+ # to a multicast network RFC3171
+ #
+ # Example:
+ #
+ # ip = IPAddress "224.0.0.0/4"
+ # ip.multicast?
+ # #=> true
+ #
+ def multicast?
+ [self.class.new("224.0.0.0/4")].any? {|i| i.include? self}
+ end
+
+ #
+ # Checks if an IPv4 address objects belongs
+ # to a loopback network RFC1122
+ #
+ # Example:
+ #
+ # ip = IPAddress "127.0.0.1"
+ # ip.loopback?
+ # #=> true
+ #
+ def loopback?
+ [self.class.new("127.0.0.0/8")].any? {|i| i.include? self}
+ end
+
+ #
# Returns the IP address in in-addr.arpa format
# for DNS lookups
#
@@ -584,6 +665,26 @@ module IPAddress;
alias_method :arpa, :reverse
#
+ # Return a list of IP's between @address
+ # and the supplied IP
+ #
+ # ip = IPAddress("172.16.100.51/32")
+ #
+ # ip.to("172.16.100.100")
+ # #=> ["172.16.100.51",
+ # #=> "172.16.100.52",
+ # #=> ...
+ # #=> "172.16.100.99",
+ # #=> "172.16.100.100"]
+ #
+ def to(e)
+ unless e.is_a? IPAddress::IPv4
+ e = IPv4.new(e)
+ end
+
+ Range.new(@u32, e.to_u32).map{|i| IPAddress.ntoa(i) }
+ end
+ #
# Splits a network into different subnets
#
# If the IP Address is a network, it can be divided into
@@ -598,9 +699,9 @@ module IPAddress;
#
# network / 4 # implies map{|i| i.to_string}
# #=> ["172.16.10.0/26",
- # "172.16.10.64/26",
- # "172.16.10.128/26",
- # "172.16.10.192/26"]
+ # #=> "172.16.10.64/26",
+ # #=> "172.16.10.128/26",
+ # #=> "172.16.10.192/26"]
#
# If +num+ is any other number, the supernet will be
# divided into some networks with a even number of hosts and
@@ -610,8 +711,8 @@ module IPAddress;
#
# network / 3 # implies map{|i| i.to_string}
# #=> ["172.16.10.0/26",
- # "172.16.10.64/26",
- # "172.16.10.128/25"]
+ # #=> "172.16.10.64/26",
+ # #=> "172.16.10.128/25"]
#
# Returns an array of IPv4 objects
#
@@ -969,12 +1070,9 @@ module IPAddress;
#
private
+ # Tweaked to remove the #upto(32)
def newprefix(num)
- num.upto(32) do |i|
- if (a = Math::log2(i).to_i) == Math::log2(i)
- return @prefix + a
- end
- end
+ return @prefix + (Math::log2(num).ceil )
end
def sum_first_found(arr)
diff --git a/lib/ipaddress/ipv6.rb b/lib/ipaddress/ipv6.rb
index 33d4d19..3e506ac 100644
--- a/lib/ipaddress/ipv6.rb
+++ b/lib/ipaddress/ipv6.rb
@@ -87,6 +87,7 @@ module IPAddress;
# ip6 = IPAddress "2001:db8::8:800:200c:417a/64"
#
def initialize(str)
+ raise ArgumentError, "Nil IP" unless str
ip, netmask = str.split("/")
if str =~ /:.+\./
@@ -252,6 +253,15 @@ module IPAddress;
end
alias_method :group, :[]
+ #
+ # Updated the octet specified at index
+ #
+ def []=(index, value)
+ @groups[index] = value
+ initialize("#{IN6FORMAT % @groups}/#{prefix}")
+ end
+ alias_method :group=, :[]=
+
#
# Returns a Base16 number representing the IPv6
# address
@@ -388,6 +398,13 @@ module IPAddress;
@compressed
end
+ #
+ # Returns true if the address is a link local address
+ #
+ def link_local?
+ @groups[0] == 0xfe80
+ end
+
#
# Returns true if the address is an unspecified address
#
@@ -478,6 +495,7 @@ module IPAddress;
# #=> ["2001:db8:1::1/64","2001:db8:1::1/65","2001:db8:2::1/64"]
#
def <=>(oth)
+ return nil unless oth.is_a?(self.class)
return prefix <=> oth.prefix if to_u128 == oth.to_u128
to_u128 <=> oth.to_u128
end
diff --git a/lib/ipaddress/mongoid.rb b/lib/ipaddress/mongoid.rb
new file mode 100644
index 0000000..3af880a
--- /dev/null
+++ b/lib/ipaddress/mongoid.rb
@@ -0,0 +1,75 @@
+module IPAddress
+
+ #
+ # Mongoid field serialization
+ #
+ # IPAddress objects are converted to String
+ #
+ # IPAddress.mongoize IPAddress.parse("172.16.10.1")
+ # #=> "172.16.10.1"
+ #
+ # Prefix will be removed from host adresses
+ #
+ # IPAddress.mongoize "172.16.10.1/32"
+ # #=> "172.16.10.1"
+ #
+ # Prefix will be kept for network addresses
+ #
+ # IPAddress.mongoize "172.16.10.1/24"
+ # #=> "172.16.10.1/24"
+ #
+ # IPv6 addresses will be stored uncompressed to ease DB search and sorting
+ #
+ # IPAddress.mongoize "2001:db8::8:800:200c:417a"
+ # #=> "2001:0db8:0000:0000:0008:0800:200c:417a"
+ # IPAddress.mongoize "2001:db8::8:800:200c:417a/64"
+ # #=> "2001:0db8:0000:0000:0008:0800:200c:417a/64"
+ #
+ # Invalid addresses will be serialized as nil
+ #
+ # IPAddress.mongoize "invalid"
+ # #=> nil
+ # IPAddress.mongoize ""
+ # #=> nil
+ # IPAddress.mongoize 1
+ # #=> nil
+ # IPAddress.mongoize nil
+ # #=> nil
+ #
+ def self.mongoize(ipaddress)
+ ipaddress = self.parse(ipaddress) unless ipaddress.is_a?(IPAddress)
+ if ipaddress.bits.length == ipaddress.prefix
+ ipaddress.address
+ elsif ipaddress.is_a?(IPAddress::IPv6)
+ ipaddress.to_string_uncompressed
+ else
+ ipaddress.to_string
+ end
+ rescue ArgumentError
+ nil
+ end
+
+ #
+ # Mongoid field deserialization
+ #
+ def self.demongoize(string)
+ parse(string)
+ rescue ArgumentError
+ nil
+ end
+
+ #
+ # Delegates to IPAddress.mongoize
+ #
+ def self.evolve(ipaddress)
+ mongoize(ipaddress)
+ end
+
+ #
+ # Sends self object to IPAddress#mongoize
+ #
+ def mongoize
+ IPAddress.mongoize(self)
+ end
+
+end \ No newline at end of file
diff --git a/lib/ipaddress/prefix.rb b/lib/ipaddress/prefix.rb
index f3d585d..136f2d3 100644
--- a/lib/ipaddress/prefix.rb
+++ b/lib/ipaddress/prefix.rb
@@ -55,10 +55,10 @@ module IPAddress
#
# Sums two prefixes or a prefix to a
- # number, returns a Fixnum
+ # number, returns a Integer
#
def +(oth)
- if oth.is_a? Fixnum
+ if oth.is_a? Integer
self.prefix + oth
else
self.prefix + oth.prefix
@@ -68,10 +68,10 @@ module IPAddress
#
# Returns the difference between two
# prefixes, or a prefix and a number,
- # as a Fixnum
+ # as a Integer
#
def -(oth)
- if oth.is_a? Fixnum
+ if oth.is_a? Integer
self.prefix - oth
else
(self.prefix - oth.prefix).abs
@@ -214,8 +214,8 @@ module IPAddress
# #=> 64
#
def initialize(num=128)
- unless (1..128).include? num.to_i
- raise ArgumentError, "Prefix must be in range 1..128, got: #{num}"
+ unless (0..128).include? num.to_i
+ raise ArgumentError, "Prefix must be in range 0..128, got: #{num}"
end
super(num.to_i)
end
diff --git a/lib/ipaddress/version.rb b/lib/ipaddress/version.rb
new file mode 100644
index 0000000..f637ef8
--- /dev/null
+++ b/lib/ipaddress/version.rb
@@ -0,0 +1,3 @@
+module Ipaddress
+ VERSION = "0.8.3"
+end
diff --git a/tasks/jeweler.rake b/tasks/jeweler.rake
new file mode 100644
index 0000000..34d27ed
--- /dev/null
+++ b/tasks/jeweler.rake
@@ -0,0 +1,15 @@
+require 'jeweler'
+Jeweler::Tasks.new do |gem|
+ # gem is a Gem::Specification... see http://guides.rubygems.org/specification-reference/ for more options
+ gem.name = "ipaddress"
+ gem.summary = %q{IPv4/IPv6 address manipulation library}
+ gem.description = %q{IPAddress is a Ruby library designed to make manipulation
+ of IPv4 and IPv6 addresses both powerful and simple. It mantains
+ a layer of compatibility with Ruby's own IPAddr, while
+ addressing many of its issues.}
+ gem.email = "ceresa@gmail.com"
+ gem.homepage = "https://github.com/bluemonk/ipaddress"
+ gem.authors = ["bluemonk", "mikemackintosh"]
+ gem.license = "MIT"
+end
+Jeweler::RubygemsDotOrgTasks.new
diff --git a/test/ipaddress/ipv4_test.rb b/test/ipaddress/ipv4_test.rb
index f144634..621f33c 100644
--- a/test/ipaddress/ipv4_test.rb
+++ b/test/ipaddress/ipv4_test.rb
@@ -1,6 +1,6 @@
require 'test_helper'
-class IPv4Test < Test::Unit::TestCase
+class IPv4Test < Minitest::Test
def setup
@klass = IPAddress::IPv4
@@ -26,7 +26,8 @@ class IPv4Test < Test::Unit::TestCase
"10.0.0.0/8" => "255.0.0.0",
"172.16.0.0/16" => "255.255.0.0",
"192.168.0.0/24" => "255.255.255.0",
- "192.168.100.4/30" => "255.255.255.252"}
+ "192.168.100.4/30" => "255.255.255.252",
+ "192.168.12.4/32" => "255.255.255.255"}
@decimal_values ={
"0.0.0.0/0" => 0,
@@ -34,7 +35,13 @@ class IPv4Test < Test::Unit::TestCase
"172.16.0.0/16" => 2886729728,
"192.168.0.0/24" => 3232235520,
"192.168.100.4/30" => 3232261124}
-
+
+ @hex_values = {
+ "10.0.0.0" => "0a000000",
+ "172.16.5.4" => "ac100504",
+ "192.168.100.4" => "c0a86404",
+ }
+
@ip = @klass.new("172.16.10.1/24")
@network = @klass.new("172.16.10.0/24")
@@ -42,13 +49,17 @@ class IPv4Test < Test::Unit::TestCase
"10.0.0.0/8" => "10.255.255.255/8",
"172.16.0.0/16" => "172.16.255.255/16",
"192.168.0.0/24" => "192.168.0.255/24",
- "192.168.100.4/30" => "192.168.100.7/30"}
+ "192.168.100.4/30" => "192.168.100.7/30",
+ "192.168.12.3/31" => "255.255.255.255/31",
+ "10.0.0.1/32" => "10.0.0.1/32"}
@networks = {
"10.5.4.3/8" => "10.0.0.0/8",
"172.16.5.4/16" => "172.16.0.0/16",
"192.168.4.3/24" => "192.168.4.0/24",
- "192.168.100.5/30" => "192.168.100.4/30"}
+ "192.168.100.5/30" => "192.168.100.4/30",
+ "192.168.1.3/31" => "192.168.1.2/31",
+ "192.168.2.5/32" => "192.168.2.5/32"}
@class_a = @klass.new("10.0.0.1/8")
@class_b = @klass.new("172.16.0.1/16")
@@ -58,6 +69,11 @@ class IPv4Test < Test::Unit::TestCase
"10.1.1.1" => 8,
"150.1.1.1" => 16,
"200.1.1.1" => 24 }
+
+ @in_range = {
+ "10.32.0.1" => ["10.32.0.253", 253],
+ "192.0.0.0" => ["192.1.255.255", 131072]
+ }
end
@@ -67,25 +83,20 @@ class IPv4Test < Test::Unit::TestCase
assert_instance_of @klass, ip
end
assert_instance_of IPAddress::Prefix32, @ip.prefix
- assert_raise (ArgumentError) do
+ assert_raises (ArgumentError) do
@klass.new
end
- assert_nothing_raised do
- @klass.new "10.0.0.0/8"
- end
end
def test_initialize_format_error
@invalid_ipv4.each do |i|
- assert_raise(ArgumentError) {@klass.new(i)}
+ assert_raises(ArgumentError) {@klass.new(i)}
end
- assert_raise (ArgumentError) {@klass.new("10.0.0.0/asd")}
+ assert_raises (ArgumentError) {@klass.new(nil)}
+ assert_raises (ArgumentError) {@klass.new("10.0.0.0/asd")}
end
def test_initialize_without_prefix
- assert_nothing_raised do
- @klass.new("10.10.0.0")
- end
ip = @klass.new("10.10.0.0")
assert_instance_of IPAddress::Prefix32, ip.prefix
assert_equal 32, ip.prefix.to_i
@@ -105,7 +116,7 @@ class IPv4Test < Test::Unit::TestCase
end
def test_initialize_should_require_ip
- assert_raise(ArgumentError) { @klass.new }
+ assert_raises(ArgumentError) { @klass.new }
end
def test_method_data
@@ -144,10 +155,22 @@ class IPv4Test < Test::Unit::TestCase
end
end
+ def test_method_to_hex
+ @hex_values.each do |addr,hex|
+ ip = @klass.new(addr)
+ assert_equal hex, ip.to_hex
+ end
+ end
+
def test_method_network?
assert_equal true, @network.network?
assert_equal false, @ip.network?
end
+
+ def test_one_address_network
+ network = @klass.new("172.16.10.1/32")
+ assert_equal false, network.network?
+ end
def test_method_broadcast
@broadcast.each do |addr,bcast|
@@ -177,6 +200,12 @@ class IPv4Test < Test::Unit::TestCase
ip = @klass.new("192.168.100.50/24")
assert_instance_of @klass, ip.first
assert_equal "192.168.100.1", ip.first.to_s
+ ip = @klass.new("192.168.100.50/32")
+ assert_instance_of @klass, ip.first
+ assert_equal "192.168.100.50", ip.first.to_s
+ ip = @klass.new("192.168.100.50/31")
+ assert_instance_of @klass, ip.first
+ assert_equal "192.168.100.50", ip.first.to_s
end
def test_method_last
@@ -186,6 +215,12 @@ class IPv4Test < Test::Unit::TestCase
ip = @klass.new("192.168.100.50/24")
assert_instance_of @klass, ip.last
assert_equal "192.168.100.254", ip.last.to_s
+ ip = @klass.new("192.168.100.50/32")
+ assert_instance_of @klass, ip.last
+ assert_equal "192.168.100.50", ip.last.to_s
+ ip = @klass.new("192.168.100.50/31")
+ assert_instance_of @klass, ip.last
+ assert_equal "192.168.100.51", ip.last.to_s
end
def test_method_each_host
@@ -338,6 +373,12 @@ class IPv4Test < Test::Unit::TestCase
ip3 = @klass.new("10.0.0.0/8")
arr = ["10.0.0.0/8","10.0.0.0/16","10.0.0.0/24"]
assert_equal arr, [ip1,ip2,ip3].sort.map{|s| s.to_string}
+ # compare with alien thing
+ ip1 = @klass.new('127.0.0.1')
+ ip2 = IPAddress::IPv6.new('::1')
+ not_ip = String
+ assert_equal nil, ip1 <=> ip2
+ assert_equal nil, ip1 <=> not_ip
end
def test_method_minus
@@ -382,8 +423,8 @@ class IPv4Test < Test::Unit::TestCase
end
def test_method_split
- assert_raise(ArgumentError) {@ip.split(0)}
- assert_raise(ArgumentError) {@ip.split(257)}
+ assert_raises(ArgumentError) {@ip.split(0)}
+ assert_raises(ArgumentError) {@ip.split(257)}
assert_equal @ip.network, @ip.split(1).first
@@ -413,9 +454,8 @@ class IPv4Test < Test::Unit::TestCase
end
def test_method_subnet
- assert_raise(ArgumentError) {@network.subnet(23)}
- assert_raise(ArgumentError) {@network.subnet(33)}
- assert_nothing_raised {@ip.subnet(30)}
+ assert_raises(ArgumentError) {@network.subnet(23)}
+ assert_raises(ArgumentError) {@network.subnet(33)}
arr = ["172.16.10.0/26", "172.16.10.64/26", "172.16.10.128/26",
"172.16.10.192/26"]
assert_equal arr, @network.subnet(26).map {|s| s.to_string}
@@ -426,7 +466,7 @@ class IPv4Test < Test::Unit::TestCase
end
def test_method_supernet
- assert_raise(ArgumentError) {@ip.supernet(24)}
+ assert_raises(ArgumentError) {@ip.supernet(24)}
assert_equal "0.0.0.0/0", @ip.supernet(0).to_string
assert_equal "0.0.0.0/0", @ip.supernet(-2).to_string
assert_equal "172.16.10.0/23", @ip.supernet(23).to_string
@@ -528,9 +568,40 @@ class IPv4Test < Test::Unit::TestCase
assert_equal prefix, res.prefix
assert_equal "#{ip}/#{prefix}", res.to_string
end
- assert_raise(ArgumentError){ @klass.parse_classful("192.168.256.257") }
+ assert_raises(ArgumentError){ @klass.parse_classful("192.168.256.257") }
end
+ def test_network_split
+ @classful.each do |ip,net|
+ x = @klass.new("#{ip}/#{net}")
+ assert_equal x.split(1).length, 1
+ assert_equal x.split(2).length, 2
+ assert_equal x.split(32).length, 32
+ assert_equal x.split(256).length, 256
+ end
+ end
+
+ def test_in_range
+ @in_range.each do |s,d|
+ ip = @klass.new(s)
+ assert_equal ip.to(d[0]).length, d[1]
+ end
+ end
+
+ def test_octect_updates
+ ip = @klass.new("10.0.1.15/32")
+ ip[1] = 15
+ assert_equal "10.15.1.15/32", ip.to_string
+
+ ip = @klass.new("172.16.100.1")
+ ip[3] = 200
+ assert_equal "172.16.100.200/32", ip.to_string
+
+ ip = @klass.new("192.168.199.0/24")
+ ip[2] = 200
+ assert_equal "192.168.200.0/24", ip.to_string
+ end
+
end # class IPv4Test
diff --git a/test/ipaddress/ipv6_test.rb b/test/ipaddress/ipv6_test.rb
index 8e6213c..c7b8142 100644
--- a/test/ipaddress/ipv6_test.rb
+++ b/test/ipaddress/ipv6_test.rb
@@ -1,6 +1,6 @@
require 'test_helper'
-class IPv6Test < Test::Unit::TestCase
+class IPv6Test < Minitest::Test
def setup
@klass = IPAddress::IPv6
@@ -53,15 +53,13 @@ class IPv6Test < Test::Unit::TestCase
def test_initialize
assert_instance_of @klass, @ip
- @valid_ipv6.keys.each do |ip|
- assert_nothing_raised {@klass.new ip}
- end
@invalid_ipv6.each do |ip|
- assert_raise(ArgumentError) {@klass.new ip}
+ assert_raises(ArgumentError) {@klass.new ip}
end
assert_equal 64, @ip.prefix
- assert_raise(ArgumentError) {
+ assert_raises(ArgumentError) {@klass.new nil }
+ assert_raises(ArgumentError) {
@klass.new "::10.1.1.1"
}
end
@@ -203,6 +201,12 @@ class IPv6Test < Test::Unit::TestCase
assert_equal "1::1", @klass.new("1:0:0:0:0:0:0:1").compressed
end
+ def test_method_link_local?
+ assert_equal true, @klass.new("fe80::1").link_local?
+ assert_equal true, @klass.new("fe80:ffff::1").link_local?
+ assert_equal false, @klass.new("fe81::1").link_local?
+ end
+
def test_method_unspecified?
assert_equal true, @klass.new("::").unspecified?
assert_equal false, @ip.unspecified?
@@ -257,24 +261,30 @@ class IPv6Test < Test::Unit::TestCase
arr = ["2001:db8:1::1/64","2001:db8:1::1/65",
"2001:db8:1::2/64","2001:db8:2::1/64"]
assert_equal arr, [ip1,ip2,ip3,ip4].sort.map{|s| s.to_string}
+ # compare with alien thing
+ ip1 = @klass.new('::1')
+ ip2 = IPAddress::IPv4.new('127.0.0.1')
+ not_ip = String
+ assert_equal nil, ip1 <=> ip2
+ assert_equal nil, ip1 <=> not_ip
end
def test_classmethod_expand
compressed = "2001:db8:0:cd30::"
expanded = "2001:0db8:0000:cd30:0000:0000:0000:0000"
assert_equal expanded, @klass.expand(compressed)
- assert_not_equal expanded, @klass.expand("2001:0db8:0::cd3")
- assert_not_equal expanded, @klass.expand("2001:0db8::cd30")
- assert_not_equal expanded, @klass.expand("2001:0db8::cd3")
+ refute_equal expanded, @klass.expand("2001:0db8:0::cd3")
+ refute_equal expanded, @klass.expand("2001:0db8::cd30")
+ refute_equal expanded, @klass.expand("2001:0db8::cd3")
end
def test_classmethod_compress
compressed = "2001:db8:0:cd30::"
expanded = "2001:0db8:0000:cd30:0000:0000:0000:0000"
assert_equal compressed, @klass.compress(expanded)
- assert_not_equal compressed, @klass.compress("2001:0db8:0::cd3")
- assert_not_equal compressed, @klass.compress("2001:0db8::cd30")
- assert_not_equal compressed, @klass.compress("2001:0db8::cd3")
+ refute_equal compressed, @klass.compress("2001:0db8:0::cd3")
+ refute_equal compressed, @klass.compress("2001:0db8::cd30")
+ refute_equal compressed, @klass.compress("2001:0db8::cd3")
end
def test_classmethod_parse_data
@@ -295,9 +305,15 @@ class IPv6Test < Test::Unit::TestCase
assert_equal @ip.to_s, @klass.parse_hex(@hex,64).to_s
end
+ def test_group_updates
+ ip = @klass.new("2001:db8::8:800:200c:417a/64")
+ ip[2] = '1234'
+ assert_equal "2001:db8:4d2:0:8:800:200c:417a/64", ip.to_string
+ end
+
end # class IPv6Test
-class IPv6UnspecifiedTest < Test::Unit::TestCase
+class IPv6UnspecifiedTest < Minitest::Test
def setup
@klass = IPAddress::IPv6::Unspecified
@@ -310,7 +326,6 @@ class IPv6UnspecifiedTest < Test::Unit::TestCase
end
def test_initialize
- assert_nothing_raised {@klass.new}
assert_instance_of @klass, @ip
end
@@ -331,7 +346,7 @@ class IPv6UnspecifiedTest < Test::Unit::TestCase
end # class IPv6UnspecifiedTest
-class IPv6LoopbackTest < Test::Unit::TestCase
+class IPv6LoopbackTest < Minitest::Test
def setup
@klass = IPAddress::IPv6::Loopback
@@ -344,7 +359,6 @@ class IPv6LoopbackTest < Test::Unit::TestCase
end
def test_initialize
- assert_nothing_raised {@klass.new}
assert_instance_of @klass, @ip
end
@@ -364,7 +378,7 @@ class IPv6LoopbackTest < Test::Unit::TestCase
end # class IPv6LoopbackTest
-class IPv6MappedTest < Test::Unit::TestCase
+class IPv6MappedTest < Minitest::Test
def setup
@klass = IPAddress::IPv6::Mapped
@@ -390,14 +404,11 @@ class IPv6MappedTest < Test::Unit::TestCase
end
def test_initialize
- assert_nothing_raised {@klass.new("::172.16.10.1")}
assert_instance_of @klass, @ip
@valid_mapped.each do |ip, u128|
- assert_nothing_raised {@klass.new ip}
assert_equal u128, @klass.new(ip).to_u128
end
@valid_mapped_ipv6.each do |ip, u128|
- assert_nothing_raised {@klass.new ip}
assert_equal u128, @klass.new(ip).to_u128
end
end
@@ -424,5 +435,5 @@ class IPv6MappedTest < Test::Unit::TestCase
def test_mapped?
assert_equal true, @ip.mapped?
end
-
+
end # class IPv6MappedTest
diff --git a/test/ipaddress/mongoid_test.rb b/test/ipaddress/mongoid_test.rb
new file mode 100644
index 0000000..b463e84
--- /dev/null
+++ b/test/ipaddress/mongoid_test.rb
@@ -0,0 +1,70 @@
+require 'test_helper'
+require 'ipaddress/mongoid'
+
+class MongoidTest < Minitest::Test
+
+ def setup
+ @valid_host4 = "172.16.10.1"
+ @valid_host6 = "2001:0db8:0000:0000:0008:0800:200c:417a"
+ @valid_host6_compressed = IPAddress::IPv6.compress(@valid_host6)
+ @valid_network4 = "#{@valid_host4}/24"
+ @valid_network6 = "#{@valid_host6}/96"
+ @valid_network6_compressed = "#{@valid_host6_compressed}/96"
+ @host4 = IPAddress.parse(@valid_host4)
+ @host6 = IPAddress.parse(@valid_host6)
+ @network4 = IPAddress.parse(@valid_network4)
+ @network6 = IPAddress.parse(@valid_network6)
+ @invalid_values = [nil, "", "invalid"]
+ end
+
+ def test_mongoize
+ # Instance method should be delegated to class method
+ assert_equal @host4.mongoize, IPAddress.mongoize(@host4)
+ assert_equal @network4.mongoize, IPAddress.mongoize(@network4)
+
+ # Hosts addresses should be stored without prefix
+ assert_equal @valid_host4, IPAddress.mongoize(@host4)
+ assert_equal @valid_host6, IPAddress.mongoize(@host6)
+ assert_equal @valid_host4, IPAddress.mongoize("#{@host4}/32")
+ assert_equal @valid_host6, IPAddress.mongoize("#{@host6}/128")
+
+ # Network addresses should be stored with their prefix
+ assert_equal @valid_network4, IPAddress.mongoize(@network4)
+ assert_equal @valid_network6, IPAddress.mongoize(@network6)
+
+ # IPv6 addresses should always be stored uncompressed
+ assert_equal @valid_host6, IPAddress.mongoize(@valid_host6_compressed)
+ assert_equal @valid_network6, IPAddress.mongoize(@valid_network6_compressed)
+
+ @invalid_values.each do |invalid_value|
+ # Invalid addresses should serialize to nil
+ assert_equal nil, IPAddress.mongoize(invalid_value)
+ end
+ end
+
+ def test_demongoize
+ # Valid stored values should be loaded with expected IPAddress type
+ assert_instance_of IPAddress::IPv4, IPAddress.demongoize(@valid_host4)
+ assert_instance_of IPAddress::IPv6, IPAddress.demongoize(@valid_host6)
+ assert_instance_of IPAddress::IPv4, IPAddress.demongoize(@valid_network4)
+ assert_instance_of IPAddress::IPv6, IPAddress.demongoize(@valid_network6)
+
+ # Valid stored values should be loaded as the original IPAddress object
+ assert_equal @host4, IPAddress.demongoize(@valid_host4)
+ assert_equal @host6, IPAddress.demongoize(@valid_host6)
+ assert_equal @network4, IPAddress.demongoize(@valid_network4)
+ assert_equal @network6, IPAddress.demongoize(@valid_network6)
+
+ @invalid_values.each do |invalid_value|
+ # Invalid stored value should be loaded as nil
+ assert_equal nil, IPAddress.demongoize(invalid_value)
+ end
+ end
+
+ def test_evolve
+ # evolve should delegate to mongoize
+ assert_equal IPAddress.mongoize(@valid_host4), IPAddress.evolve(@valid_host4)
+ assert_equal IPAddress.mongoize(@valid_network4), IPAddress.evolve(@valid_network4)
+ end
+
+end \ No newline at end of file
diff --git a/test/ipaddress/prefix_test.rb b/test/ipaddress/prefix_test.rb
index bff812e..1a0e277 100644
--- a/test/ipaddress/prefix_test.rb
+++ b/test/ipaddress/prefix_test.rb
@@ -1,6 +1,6 @@
require 'test_helper'
-class Prefix32Test < Test::Unit::TestCase
+class Prefix32Test < Minitest::Test
def setup
@netmask0 = "0.0.0.0"
@@ -89,12 +89,9 @@ class Prefix32Test < Test::Unit::TestCase
end
def test_initialize
- assert_raise (ArgumentError) do
+ assert_raises (ArgumentError) do
@klass.new 33
end
- assert_nothing_raised do
- @klass.new 8
- end
assert_instance_of @klass, @klass.new(8)
end
@@ -122,7 +119,7 @@ class Prefix32Test < Test::Unit::TestCase
end # class Prefix32Test
-class Prefix128Test < Test::Unit::TestCase
+class Prefix128Test < Minitest::Test
def setup
@u128_hash = {
@@ -135,12 +132,9 @@ class Prefix128Test < Test::Unit::TestCase
end
def test_initialize
- assert_raise (ArgumentError) do
+ assert_raises (ArgumentError) do
@klass.new 129
end
- assert_nothing_raised do
- @klass.new 64
- end
assert_instance_of @klass, @klass.new(64)
end
diff --git a/test/ipaddress_test.rb b/test/ipaddress_test.rb
index 8e2bb66..862c889 100644
--- a/test/ipaddress_test.rb
+++ b/test/ipaddress_test.rb
@@ -1,6 +1,6 @@
require 'test_helper'
-class IPAddressTest < Test::Unit::TestCase
+class IPAddressTest < Minitest::Test
def setup
@valid_ipv4 = "172.16.10.1/24"
@@ -11,25 +11,50 @@ class IPAddressTest < Test::Unit::TestCase
@invalid_ipv6 = ":1:2:3:4:5:6:7"
@invalid_mapped = "::1:2.3.4"
+ @valid_ipv4_uint32 = [4294967295, # 255.255.255.255
+ 167772160, # 10.0.0.0
+ 3232235520, # 192.168.0.0
+ 0]
+
+ @invalid_ipv4_uint32 = [4294967296, # 256.0.0.0
+ "A294967295", # Invalid uINT
+ -1] # Invalid
+
+
@ipv4class = IPAddress::IPv4
@ipv6class = IPAddress::IPv6
@mappedclass = IPAddress::IPv6::Mapped
+
+ @invalid_ipv4 = ["10.0.0.256",
+ "10.0.0.0.0",
+ "10.0.0",
+ "10.0"]
+
+ @valid_ipv4_range = ["10.0.0.1-254",
+ "10.0.1-254.0",
+ "10.1-254.0.0"]
@method = Module.method("IPAddress")
end
def test_method_IPAddress
- assert_nothing_raised {@method.call(@valid_ipv4)}
- assert_nothing_raised {@method.call(@valid_ipv6)}
- assert_nothing_raised {@method.call(@valid_mapped)}
assert_instance_of @ipv4class, @method.call(@valid_ipv4)
assert_instance_of @ipv6class, @method.call(@valid_ipv6)
assert_instance_of @mappedclass, @method.call(@valid_mapped)
- assert_raise(ArgumentError) {@method.call(@invalid_ipv4)}
- assert_raise(ArgumentError) {@method.call(@invalid_ipv6)}
- assert_raise(ArgumentError) {@method.call(@invalid_mapped)}
+ assert_raises(ArgumentError) {@method.call(@invalid_ipv4)}
+ assert_raises(ArgumentError) {@method.call(@invalid_ipv6)}
+ assert_raises(ArgumentError) {@method.call(@invalid_mapped)}
+
+ assert_instance_of @ipv4class, @method.call(@valid_ipv4_uint32[0])
+ assert_instance_of @ipv4class, @method.call(@valid_ipv4_uint32[1])
+ assert_instance_of @ipv4class, @method.call(@valid_ipv4_uint32[2])
+ assert_instance_of @ipv4class, @method.call(@valid_ipv4_uint32[3])
+
+ assert_raises(ArgumentError) {@method.call(@invalid_ipv4_uint32[0])}
+ assert_raises(ArgumentError) {@method.call(@invalid_ipv4_uint32[1])}
+ assert_raises(ArgumentError) {@method.call(@invalid_ipv4_uint32[2])}
end
diff --git a/test/test_helper.rb b/test/test_helper.rb
index 61e381e..4a80d3e 100644
--- a/test/test_helper.rb
+++ b/test/test_helper.rb
@@ -1,13 +1,22 @@
require 'rubygems'
-require 'test/unit'
+require 'minitest/autorun'
+require 'simplecov'
+SimpleCov.start
$LOAD_PATH.unshift(File.dirname(__FILE__))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
require 'ipaddress'
-module Test::Unit
+if Minitest.const_defined?('Test')
+ # We're on Minitest 5+. Nothing to do here.
+else
+ # Minitest 4 doesn't have Minitest::Test yet.
+ Minitest::Test = MiniTest::Unit::TestCase
+end
+
+module Minitest
- class TestCase
+ class Test
def self.must(name, &block)
test_name = "test_#{name.gsub(/\s+/,'_')}".to_sym