ipaddr
ipaddr copied to clipboard
Add IPAddr#unmasked
Prior to this commit there was no way to retrieve the unmasked address
for an IPAddr
. For example, given the IPAddr
below there is no
method or instance variable that would return "1.2.3.4"
.
IPAddr.new("1.2.3.4/8")
This poses a problem for Rails in supporting the PostgreSQL inet type.
Rails uses IPAddr
for both the cidr and inet types, but at the moment
cannot fully support the inet type, which "accepts values with nonzero
bits to the right of the netmask". This came up originally in
rails/rails#14857, and more recently in
rails/rails#40138. The change in this commit
would allow us to support the inet type without moving to another
library.
This commit holds onto the address before the mask is applied, in a new
instance variable @unmasked_addr
. It then exposes this via
unmasked
.
This was originally implemented back in ruby/ruby#599, but it got stale and was eventually closed. There are also a few differences with that PR:
- That PR set up the instance variable inside of
mask!
, which meant that the new method was broken in the case of anIPAddr
with no mask. - That PR introduced a method that returned a
to_s
-like value, whereas this one returns a new, unmasked IPAddr object. - As a result of the above, this version doesn't need to change the
signature of
to_s
- This PR also sets @unmasked_addr within
set
, which covers a few additional edge cases with non-string arguments and with methods like&
orsucc
that clone the IpAdrr and callset
Nice catch @CvX. While reviewing your changes I also spotted some other cases where we call .clone.set
. I kept your test case and added you as a co-author, but moved the line to set @unmasked_addr
into the set
method.
spotted some other cases where we call
.clone.set
Good one! LGTM 🙂 :shipit:
Do you think to_unmasked_string
is the right name for this?
I agree with the functionality.
Just want to make sure this is the right name.
Is this a to_
conversion or a property of the address? e.g. address.unmasked.to_s
might make more sense?
How does this work with IPv6?
@ioquatix thanks for the review.
address.unmasked.to_s might make more sense?
Yeah, I think your suggestion of unmasked
instead of to_unmasked_string
is better. I updated the PR to take that suggestion.
How does this work with IPv6?
I have a couple IPv6 test cases. Are there additional cases that I am missing?
LGTM.
The maintainer of ipaddr is @knu
Hello,
I have a question about this.
It actually took me quite a while to figure out what's wrong, as also Postgres' inet data type is mapped to IPAddr.
> IPAddr.new('1.2.3.4/24').to_s
=> "1.2.3.0"
Athough it's a behavior change, normally I would expect IPAddr to keep the host bits. So wouldn't it make sense to adjust the code, that the example above returns '1.2.3.4'? and to introduce a function that returns the network address (discards the host bits)?
> IPAddr.new('1.2.3.4/24').to_s
=> "1.2.3.4"
> IPAddr.new('1.2.3.4/24').network_address.to_s
=> "1.2.3.0"
Also, #succ would be more useful if you could initialize a new object with non-zero host bits.. On the proposed function to_unmasked_string
you can't call it.
If the goal is to not break the (strange) behavior, maybe an unmasked
function that again returns an IPAddr
object (and not a string) would make sense:
> IPAddr.new('1.2.3.4/24').unmasked
=> <IPAddr: IPv4:1.2.3.4/255.255.255.0>
> IPAddr.new('1.2.3.4/24').unmasked.to_s
=> "1.2.3.4"
Both would work fine, as IPAddr already now is able to cope with non-zero host bits:
> IPAddr.new('1.2.3.4/24').succ.succ.to_s
=> "1.2.3.2"
Seb
@composerinteralia do you have some time to check the above feedback? It would be awesome to merge this in time for Ruby 3.1
cc @knu
I’m on vacation for the next two weeks away from a computer, but I can take a look when I get back. Anyone is also welcome to take over in the meantime.
Athough it's a behavior change, normally I would expect IPAddr to keep the host bits. So wouldn't it make sense to adjust the code, that the example above returns '1.2.3.4'
I can revise this PR if that's the direction folks want to go, but it's a potentially disruptive breaking change for anyone relying on the existing behavior (I believe we're relying on it for the cidr type in Rails, for example). I assume that would need to be a major release, and ideally there would be some sort of deprecation cycle.
If the goal is to not break the (strange) behavior, maybe an unmasked function that again returns an IPAddr object (and not a string) would make sense:
I believe that's what this PR does. (Although it's been a while, so please correct me if I am mistaken.)