Class: IpAddress

Inherits:
Object
  • Object
show all
Includes:
Comparable
Defined in:
lib/ip_address.rb

Constant Summary collapse

IP_BITS =
{4 => 32, 6 => 128}
IP_MAX =
{4 => (1 << 32) - 1, 6 => (1 << 128) - 1}

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(value, version = nil) ⇒ IpAddress

Takes an Integer or a String representation of an IP address and creates a new IpAddress object with it. If the version (4 / 6) is not specified then it will be guessed with guess_version.



62
63
64
65
66
# File 'lib/ip_address.rb', line 62

def initialize(value, version = nil)
  @version = version || IpAddress.guess_version(value)
  @integer = IpAddress.coerce(value, :integer, @version)
  @string = IpAddress.coerce(value, :string, @version)
end

Instance Attribute Details

#integerObject (readonly)

Returns the value of attribute integer.



58
59
60
# File 'lib/ip_address.rb', line 58

def integer
  @integer
end

#stringObject (readonly)

Returns the value of attribute string.



58
59
60
# File 'lib/ip_address.rb', line 58

def string
  @string
end

#versionObject (readonly)

Returns the value of attribute version.



58
59
60
# File 'lib/ip_address.rb', line 58

def version
  @version
end

Class Method Details

.coerce(value, to_type, version = nil) ⇒ Object

Takes an Integer or a String representation of an IP address and coerces it to a representation of the requested type (:integer / :string). If the version (4 / 6) is not specified then it will be guessed with guess_version.



9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# File 'lib/ip_address.rb', line 9

def self.coerce(value, to_type, version = nil)
  raise "unknown type #{to_type.inspect} requested" unless [:integer, :string].include?(to_type)
  version ||= guess_version(value)
  
  case value
    when Integer
      if to_type == :integer then value
      elsif version == 4 then [24, 16, 8, 0].map { |shift| (value >> shift) & 255 }.join(".")
      else sprintf("%.32x", value).scan(/.{4}/).join(":")
      end
    when String
      if to_type == :string then value
      elsif version == 4 then value.split(".").inject(0) { |total, octet| (total << 8) + octet.to_i }
      else value.delete(":").to_i(16)
      end
  end
end

.guess_version(value) ⇒ Object

Takes an Integer or a String and guesses whether it represents an IPv4 or IPv6 address. For an Integer, IPv4 is assumed unless the value is greater than IP_MAX. For a String, IPv6 is assumed if it contains at least one colon (:).



30
31
32
33
34
35
# File 'lib/ip_address.rb', line 30

def self.guess_version(value)
  case value
    when Integer then value > IP_MAX[4] ? 6 : 4
    when String then value =~ /:/ ? 6 : 4
  end
end

.mask_from_slash_bits(bits, version = nil) ⇒ Object

Takes an Integer bitcount (bits) and returns an appropriate masking Integer. For example, a /24 network (in IPv4) corresponds to a mask of 255.255.255.0 or the number 4294967040. If the version (4 / 6) is not specified then it will be assumed to be 4 unless bits > 32.



40
41
42
43
44
45
46
47
# File 'lib/ip_address.rb', line 40

def self.mask_from_slash_bits(bits, version = nil)
  raise "bits > 128" if bits > 128
  version ||= bits > 32 ? 6 : 4
  
  max = IP_MAX[version]
  left_shift = IP_BITS[version] - bits
  (max << left_shift) & max
end

.mask_size(value, version = nil) ⇒ Object

Takes an Integer or a String representation of a network mask and returns the number of addresses it encodes. If the version (4 / 6) is not specified then it will be guessed with guess_version.



51
52
53
54
# File 'lib/ip_address.rb', line 51

def self.mask_size(value, version = nil)
  version ||= guess_version(value)
  (coerce(value, :integer, version) ^ IP_MAX[version]) + 1
end

Instance Method Details

#+(value) ⇒ Object

Adds the specified Integer value to that of the IpAddress and returns a new IpAddress based on the sum.



69
70
71
# File 'lib/ip_address.rb', line 69

def +(value)
  IpAddress.new(@integer + value, @version)
end

#-(value) ⇒ Object

Subtracts the specified Integer value from that of the IpAddress and returns a new IpAddress based on the difference.



74
75
76
# File 'lib/ip_address.rb', line 74

def -(value)
  IpAddress.new(@integer - value, @version)
end

#/(bits) ⇒ Object

Returns the range of IpAddresses in the specified /bits network. Basically a convenience wrapper around mask.



79
80
81
# File 'lib/ip_address.rb', line 79

def /(bits)
  mask(IpAddress.mask_from_slash_bits(bits, @version))
end

#<=>(other) ⇒ Object

Compares one IpAddress with another based on the Integer representation of their values.



84
85
86
# File 'lib/ip_address.rb', line 84

def <=>(other)
  @integer <=> other.integer
end

#mask(value) ⇒ Object

Takes an Integer or a String representation of a network mask and returns the range of IpAddresses in that network.



89
90
91
92
# File 'lib/ip_address.rb', line 89

def mask(value)
  base_int = @integer & IpAddress.coerce(value, :integer, @version)
  IpAddressRange.new(IpAddress.new(base_int, @version), IpAddress.new(base_int + IpAddress.mask_size(value, @version) - 1))
end

#succObject

Returns the next IpAddress after this one.



95
96
97
# File 'lib/ip_address.rb', line 95

def succ
  self + 1
end

#to_iObject



99
100
101
# File 'lib/ip_address.rb', line 99

def to_i
  @integer
end

#to_sObject



103
104
105
# File 'lib/ip_address.rb', line 103

def to_s
  @string
end