iparty 0.1.2 → 0.1.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5f040fd93232947aa2be967cac0ad59617cc24e4751c14908466e221d4830fc6
4
- data.tar.gz: 4b34b52ee226d40b306da7d8dcf7f576ceb37c98d2695c1e2a78db43703ba40a
3
+ metadata.gz: 61224732c6f0206d4525d1a238e76106646a14efdd76cbb3dc4e989a6a41b1a5
4
+ data.tar.gz: da8851962434b85c846b378e9332380101f22fc3bc10037a3d2b787b52bda003
5
5
  SHA512:
6
- metadata.gz: 1f319c5dbfcad4f51192b750a973f1de60dbee54db84f176aa67ed921f883770eaf61fb82f1b32a837e93072eb0a10f562bed4e2743cf5df3a4936e957bcf1b1
7
- data.tar.gz: 67f50c5df51c1c1e06266c780d5b27163b9ee5a7bb45c22f8c846c4de8baa6c8f083db174d09690211780b8edcbc13bb0196acc7355e6e8f24ee18f75d0b7e4a
6
+ metadata.gz: a387ddb5bc240ec0c81f358da1f1696f398bfb7371e81e8d84f78c7dcc05ea6a440f42965df619df5f642321a62a6d5e7453ff33f5c1162bb4bac626b5c2704e
7
+ data.tar.gz: 3e7e52dac937e4610957b2145f639ca6bc82c6a4503a495db03f9f17cb89df456431b005e19a416079754fe57ef0aa36c054b3b8d6693b58f820dcc444a77328
data/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.1.3] - 2026-05-29
4
+
5
+ * Fix: Correctly re-normalize insignificant ipv6 IParty::Address
6
+ * Fix: `significant: true` option on insignificant ipv6 IParty::Address#to_s
7
+ * Add `IParty.normalize_family` to allow all of Socket constant, 4/6 or :ipv4/:ipv6
8
+ * Add `IParty.insignificant(long)` and `IParty::Address.from_insignificant(long)` to get address from insignificant ipv6-long
9
+ * `IParty.normalize`, when given an integer as address, will accept as family: 4 6 :ipv4 :ipv6 (also Socket::AF_INET Socket::AF_INET6 as before)
10
+ * `IParty.config.singletons = true` will no longer cause immediate init of database objects. Use `IParty.config.init_singletons!` after you've set all options if you want "true behaviour".
11
+ If the value is set to true `init_singletons!` will be called the first time a database object is initialized.
12
+ * Compact input to expand_hostnames
13
+ * Add dev script to test performance with rbspy
14
+ * [doc] Add CurrentAttribute example for AS<8
15
+ * [cli] Add cookbook helper
16
+ * [cli] Show dispatchable actions in verbose appinfo
17
+
18
+
19
+
3
20
  ## [0.1.2] - 2026-05-27
4
21
 
5
22
  * Include documentation in gem package
data/README.md CHANGED
@@ -123,7 +123,7 @@ ip.country.name(:es, fallback_locale: :fr) == "Germany"
123
123
  ip.country.dig(:names, :en) == "Germany"
124
124
  ```
125
125
 
126
- You should definitely take a quick look at the documentation, specifically about the [MaxMine::Result](docs/maxmind_result.md) object.
126
+ You should definitely take a quick look at the documentation, specifically about the [MaxMind::Result](docs/maxmind_result.md) object.
127
127
  It should be intuitive magic but you may scratch your head if you "don't get it".
128
128
 
129
129
 
data/docs/benchmark.md CHANGED
@@ -5,31 +5,31 @@
5
5
  This is the result of `script/benchmark.rb` on my M1.
6
6
 
7
7
  ```
8
- RSS-before[uncached]: 30.31 MB
8
+ RSS-before[uncached]: 28.98 MB
9
9
  ruby 3.3.6 (2024-11-05 revision 75015d4c1f) [arm64-darwin21]
10
10
  Warming up --------------------------------------
11
11
  uncached 145.000 i/100ms
12
12
  Calculating -------------------------------------
13
- uncached 1.448k (± 1.9%) i/s (690.59 μs/i) - 14.500k in 10.017464s
14
- RSS-after[uncached]: 48.75 MB
13
+ uncached 1.454k2.1%) i/s (687.88 μs/i) - 87.290k in 60.075809s
14
+ RSS-after[uncached]: 49.45 MB
15
15
 
16
16
 
17
- RSS-before[singletons]: 29.77 MB
17
+ RSS-before[singletons]: 30.17 MB
18
18
  ruby 3.3.6 (2024-11-05 revision 75015d4c1f) [arm64-darwin21]
19
19
  Warming up --------------------------------------
20
- singletons 162.000 i/100ms
20
+ singletons 163.000 i/100ms
21
21
  Calculating -------------------------------------
22
- singletons 1.565k8.0%) i/s (638.87 μs/i) - 15.552k in 10.006033s
23
- RSS-after[singletons]: 31.27 MB
22
+ singletons 1.633k1.3%) i/s (612.35 μs/i) - 98.126k in 60.097790s
23
+ RSS-after[singletons]: 31.28 MB
24
24
 
25
25
 
26
- RSS-before[eager_load]: 30.20 MB
26
+ RSS-before[eager_load]: 30.62 MB
27
27
  ruby 3.3.6 (2024-11-05 revision 75015d4c1f) [arm64-darwin21]
28
28
  Warming up --------------------------------------
29
- eager_load 500.000 i/100ms
29
+ eager_load 449.000 i/100ms
30
30
  Calculating -------------------------------------
31
- eager_load 4.940k4.1%) i/s (202.43 μs/i) - 49.500k in 10.038651s
32
- RSS-after[eager_load]: 112.73 MB
31
+ eager_load 4.566k1.3%) i/s (219.02 μs/i) - 274.339k in 60.095499s
32
+ RSS-after[eager_load]: 114.52 MB
33
33
  ```
34
34
 
35
35
  ### Personal conclusion
data/docs/cli/README.md CHANGED
@@ -51,19 +51,28 @@ Also look at the action cookbook in `docs/cli/action_cookbook`.
51
51
  # For IParty options refer to the IParty documentation.
52
52
  # https://github.com/2called-chaos/iparty/blob/master/README.md
53
53
 
54
- IParty.config.annotate "1.1.1.1", "1.0.0.1", tags: %i[cloudflare_dns]
55
- IParty.config.annotate_tag %i[loopback], "127.0.0.1/8", "::1"
54
+ IParty.config.account_id = "..."
55
+ IParty.config.license_key = "..."
56
+
57
+ # tip: tidy up with offloading to `require_relative "annotations"`
58
+ IParty.configure do |config|
59
+ config.annotate "1.1.1.1", "1.0.0.1", tags: %i[cloudflare_dns]
60
+ config.annotate_tag %i[loopback], "127.0.0.1/8", "::1"
61
+ end
56
62
 
57
63
 
58
- # Change defaults (arguments will still override)
64
+ # Change CLI defaults (arguments will still override)
59
65
  # For CLI options refer to application/options.rb#default_options
60
66
  # https://github.com/2called-chaos/iparty/blob/master/lib/iparty/cli/application/options.rb
61
67
 
62
- # @opts[:summarize] = false
63
- # @opts[:except] += [
64
- # "registered_country",
65
- # "subdivisions",
66
- # ]
68
+ @opts[:summarize] = false
69
+ @opts[:except] += [
70
+ "registered_country",
71
+ "subdivisions",
72
+ ]
73
+
74
+ # This will eval (usable) examples from docs/cli/action_cookbook
75
+ cookbook("show_my_remote_ips")
67
76
 
68
77
 
69
78
 
@@ -4,6 +4,7 @@
4
4
 
5
5
  # iparty -d me
6
6
  # iparty --dispatch me
7
+ # iparty --dispatch me short
7
8
  def dispatch_me
8
9
  require "net/http"
9
10
 
@@ -11,20 +12,22 @@ def dispatch_me
11
12
  Net::HTTP.get(URI(url))
12
13
  rescue Socket::ResolutionError
13
14
  formatter.colorize? ? c("SOCKET_ERROR", :red) : :SOCKET_ERROR
14
- rescue StandardError
15
- formatter.colorize? ? c("ERROR", :red) : :ERROR
15
+ rescue StandardError => ex
16
+ formatter.colorize? ? c("ERROR-#{ex.class}", :red) : :"ERROR-#{ex.class}"
16
17
  end
17
18
 
18
- if the_more_clever_way = true
19
- @argv << get_ip["https://api.ipify.org"]
20
- @argv << get_ip["https://api6.ipify.org"]
21
- dispatch_info
22
- else
19
+ if @argv.delete("short")
23
20
  out << formatter.format("my external ips") do
24
21
  onlyexcept_data!(
25
22
  ipv4: get_ip["https://api.ipify.org"],
26
23
  ipv6: get_ip["https://api6.ipify.org"],
27
24
  )
28
25
  end
26
+ else
27
+ @argv.concat([
28
+ get_ip["https://api.ipify.org"],
29
+ get_ip["https://api6.ipify.org"],
30
+ ].reject{ _1.include?("ERROR") })
31
+ dispatch_info
29
32
  end
30
33
  end
@@ -30,7 +30,7 @@ IParty(request.remote_ip).best_tenant # => :us
30
30
 
31
31
  ### Full config
32
32
 
33
- This config, minus the CurrentAttributes cache is default config. For basic usage you only need account_id/license_key.
33
+ Apart from the examples this config represents default behaviour. For basic usage you only need account_id/license_key.
34
34
  Please also take a look at docs/maxmind_result.md for extending the address and geo results.
35
35
 
36
36
  ```ruby
@@ -42,33 +42,6 @@ defined?(IParty) && IParty.configure do |config|
42
42
  # * significant: keyword (affected methods only but including new/initialize)
43
43
  config.ipv6_significant = config.env_value("IPARTY_IPV6_SIGNIFICANT", true)
44
44
 
45
- # Whether to use the low memory file reader or load mmdb into memory as a whole (see docs/benchmark.md)
46
- config.eager_load = config.env_value("IPARTY_EAGER_LOAD", false)
47
-
48
- # Use singleton instances of MaxMind::Database readers.
49
- # These are lazily initialized so in a threaded environment or with eager load enabled you should pre-init them.
50
- # Side Effect: You will have to reboot your app for mmdb changes to take effect.
51
- config.singletons = config.env_value("IPARTY_SINGLETONS", false)
52
- #config.singletons = {} # lazy load singleton instances, not thread-safe
53
- #config.singletons = true # eagerly init all editions (eager_load into memory if enabled, thread-safe after init)
54
-
55
- # alternatively you can do something like this:
56
- # * singleton DB instances (lazily memoized) per-request (eager load should be disabled)
57
- # * IParty::Address cache (including their geo lookups)
58
- if defined?(ActiveSupport::CurrentAttributes)
59
- class IPartyCache < ActiveSupport::CurrentAttributes
60
- attribute(:databases, default: {})
61
-
62
- # cached IParty addresses, i.e. IPartyCache.ips["1.2.3.4"]
63
- # Be wary of mutating (i.e. mask!), use clones
64
- attribute(:ips, default: -> { Hash.new{|h, ip| h[ip.to_s] = IParty(ip.to_s) } })
65
- end
66
- config.singletons = -> { IPartyCache.databases }
67
- end
68
-
69
- # An IP that is used instead of local IPs
70
- config.local_ip_alias = config.env_value("IPARTY_LOCAL_IP_ALIAS", nil)
71
-
72
45
  # MaxMind account_id and license_key aka mirror basic-auth
73
46
  config.account_id = config.env_value("MAXMIND_ACCOUNT_ID", nil)
74
47
  config.license_key = config.env_value("MAXMIND_LICENSE_KEY", nil)
@@ -99,8 +72,52 @@ defined?(IParty) && IParty.configure do |config|
99
72
  system("#{curl} | #{tar}")
100
73
  end
101
74
 
75
+ # An IP that is used instead of local IPs
76
+ config.local_ip_alias = config.env_value("IPARTY_LOCAL_IP_ALIAS", nil)
77
+
78
+ # Whether to use the low memory file reader or load mmdb into memory as a whole (see docs/benchmark.md)
79
+ config.eager_load = config.env_value("IPARTY_EAGER_LOAD", false)
80
+
81
+ # Use singleton instances of MaxMind::Database readers.
82
+ # These are lazily initialized so in a threaded environment or with eager load enabled you should pre-init them.
83
+ # Side Effect: You will have to reboot your app for mmdb changes to take effect.
84
+ # See per-request singletons+cache in examples below.
85
+ config.singletons = config.env_value("IPARTY_SINGLETONS", false)
86
+
87
+ # Lazy load singleton instances, not thread-safe
88
+ #config.singletons = {}
89
+
90
+ # Eager load instances (eager_load into memory if enabled)
91
+ # WARNING: do that after all other options!
92
+ #config.init_singletons!
93
+
94
+
102
95
  # --- following is example code and not default behaviour ---
103
96
 
97
+
98
+ # You may want to rely on rake task and/or ensure on app boot?
99
+ # fetch_when can be
100
+ # * :always
101
+ # * :missing
102
+ # * (Numeric) maxAge (i.e. (int)seconds or (AS::Duration)14.days)
103
+ IParty.fetch_db_files!(:missing, verbose: true)
104
+
105
+
106
+ # Per-request singletons and IP cache (ActiveSupport 8+):
107
+ # * singleton DB instances (lazily memoized) per-request (eager_load should be disabled)
108
+ # * IParty::Address cache (including their geo lookups)
109
+ if defined?(ActiveSupport::CurrentAttributes)
110
+ class IPartyCache < ActiveSupport::CurrentAttributes
111
+ attribute(:databases, default: {})
112
+
113
+ # cached IParty addresses, i.e. IPartyCache.ip["1.2.3.4"]
114
+ # Be wary of mutating (i.e. mask!), use clones
115
+ attribute(:ip, default: -> { Hash.new{|h, ip| h[ip.to_s] = IParty(ip.to_s) } })
116
+ end
117
+ config.singletons = -> { IPartyCache.databases }
118
+ end
119
+
120
+
104
121
  # Proc to transform geo result data, by default does nothing.
105
122
  # yields
106
123
  # data the result hash
@@ -113,17 +130,11 @@ defined?(IParty) && IParty.configure do |config|
113
130
  end
114
131
  end
115
132
 
133
+
116
134
  # For more info on extending look at docs/maxmind_result.md
117
135
  IParty::Address.define_method(:detailed) do |*args|
118
136
  "#{to_s} -- #{geo.detailed} -- #{asn.detailed}"
119
137
  end
120
-
121
- # You may want to rely on rake task and/or ensure on app boot?
122
- # fetch_when can be
123
- # * :always
124
- # * :missing
125
- # * (Numeric) maxAge (i.e. (int)seconds or (AS::Duration)14.days)
126
- IParty.fetch_db_files!(:missing, verbose: true)
127
138
  end
128
139
  ```
129
140
 
@@ -168,3 +179,24 @@ rake iparty:config[json]
168
179
  # check mmdb file status
169
180
  rake iparty:status
170
181
  ```
182
+
183
+
184
+
185
+ ### CurrentAttribute cache (ActiveSupport <8)
186
+
187
+ This works around the lack of `default` keyword support in older ActiveSupport.
188
+
189
+ ```ruby
190
+ class IPartyCache < ActiveSupport::CurrentAttributes
191
+ attribute(:databases)
192
+ attribute(:ip)
193
+
194
+ def databases
195
+ super || attributes[:databases] = {}
196
+ end
197
+
198
+ def ip
199
+ super || attributes[:ip] = Hash.new{|h, ip| h[ip.to_s] = IParty(ip.to_s) }
200
+ end
201
+ end
202
+ ```
@@ -61,7 +61,7 @@ IParty::MaxMind::Result::Geo.define_attr(:best_tenant, export: true) { continent
61
61
  IParty::MaxMind::Result::Geo.define_attr(:short, export: true) { [continent.code, country.name].compact.join(" / ") }
62
62
  IParty::MaxMind::Result::Asn.define_attr(:short, export: :asn_short) { "AS#{number} #{organization}" }
63
63
  IParty::Address.define_method(:detailed) { "#{to_s} -- #{geo.detailed} -- #{asn_short}" }
64
- # IParty(ip).short/asn_short/detailed
64
+ # IParty(ip).best_tenant/short/asn_short/detailed
65
65
 
66
66
  # Delegate geo/asn methods so you can do `ip.METHOD` instead of `ip.geo.METHOD`
67
67
  IParty::Address.def_delegators(:geo, *%i[registered_country])
@@ -15,10 +15,10 @@ By default IParty will use this proc (and you may change it) to turn a URL (to a
15
15
  Note: This requires a unix-oid environment with curl, tar and gzip available.
16
16
 
17
17
  ```ruby
18
- IParty::MaxMind.download_proc = proc do |url, temp_dir|
19
- auth = %{-u "#{account_id}:#{license_key}"} if account_id && license_key
18
+ IParty.config.url_to_mmdb = proc do |url, dir, config|
19
+ auth = %{-u "#{config.account_id}:#{config.license_key}"} if config.account_id && config.license_key
20
20
  curl = %{curl -L -s #{"#{auth} " if auth}"#{url}"}
21
- tar = %{tar xz --strip-components 1 --exclude "*.txt" --no-same-owner -C #{temp_dir.to_s.shellescape}}
21
+ tar = %{tar xz --strip-components 1 --exclude "*.txt" --no-same-owner -C #{dir.to_s.shellescape}}
22
22
  system("#{curl} | #{tar}")
23
23
  end
24
24
  ```
@@ -6,6 +6,10 @@ module IParty
6
6
  class Address < IPAddr
7
7
  extend Forwardable
8
8
 
9
+ def self.from_insignificant long, **kw
10
+ new((long << 64) & ((1 >> 64) - 1), Socket::AF_INET6, **kw)
11
+ end
12
+
9
13
  attr_accessor :ipv6_significant
10
14
 
11
15
  def initialize *args, **kw
@@ -17,7 +21,7 @@ module IParty
17
21
  end
18
22
 
19
23
  def force_significant?
20
- @family == Socket::AF_INET6 && @addr == 1
24
+ @family == Socket::AF_INET6 && (@addr == 1 || ipv4_mapped? || ipv4_compat?)
21
25
  end
22
26
 
23
27
  def type
@@ -32,7 +36,7 @@ module IParty
32
36
  if ipv4?
33
37
  2**(32 - prefix)
34
38
  elsif ipv6?
35
- if force_significant? || significant || ipv4_mapped? || ipv4_compat?
39
+ if significant || force_significant?
36
40
  2**(128 - prefix)
37
41
  else
38
42
  2**[0, 128 - prefix - 64].max
@@ -81,26 +85,31 @@ module IParty
81
85
  end
82
86
 
83
87
  def prefix significant: ipv6_significant
84
- return super() if force_significant? || significant || !ipv6? || ipv4_mapped? || ipv4_compat? || super() <= 64
88
+ return super() if significant || force_significant? || super() <= 64
85
89
 
86
90
  64
87
91
  end
88
92
 
89
93
  def to_i significant: ipv6_significant
90
- return super() if force_significant? || significant || !ipv6? || ipv4_mapped? || ipv4_compat? || prefix(significant: true) <= 64
94
+ return super() if significant || force_significant? || prefix(significant: true) <= 64
91
95
 
92
96
  # drop upper 64 bits / host-identifier of ipv6
93
97
  (super() >> 64) & ((1 << 64) - 1)
94
98
  end
95
99
 
100
+ alias_method :original_to_s, :to_s
96
101
  def to_s significant: ipv6_significant
97
- return super() if force_significant? || significant || !ipv6? || ipv4_mapped? || ipv4_compat? || prefix(significant: true) <= 64
102
+ return original_to_s unless @family == Socket::AF_INET6
98
103
 
99
- mask(64).to_s(significant: true)
104
+ if significant || force_significant? || prefix(significant: true) <= 64
105
+ to_significant.original_to_s
106
+ else
107
+ mask(64).original_to_s
108
+ end
100
109
  end
101
110
 
102
111
  def to_string significant: ipv6_significant
103
- return super() if force_significant? || significant || !ipv6? || ipv4_mapped? || ipv4_compat? || prefix(significant: true) <= 64
112
+ return super() if significant || force_significant? || prefix(significant: true) <= 64
104
113
 
105
114
  mask(64).to_string(significant: true)
106
115
  end
@@ -20,7 +20,7 @@ module IParty
20
20
  # via -v/--version
21
21
  def dispatch_appinfo pad: 20
22
22
  parts = if @opts[:debug]
23
- %i[runtime cli_opts cli_config formatters iparty_config mmdb_status]
23
+ %i[runtime cli_opts cli_config actions formatters iparty_config mmdb_status]
24
24
  else
25
25
  %i[runtime cli_config mmdb_status]
26
26
  end
@@ -73,10 +73,23 @@ module IParty
73
73
  end.all?
74
74
  end
75
75
 
76
+ def appinfo_actions pad: 20
77
+ pad = -2 if pad.zero?
78
+
79
+ puts c("#{"".rjust(pad + 2)}# Available actions (-d)", :magenta)
80
+ methods.grep(/^dispatch_/).reverse_each do |name|
81
+ puts [
82
+ c("#{"".rjust(pad)}* "),
83
+ c(name.to_s.delete_prefix("dispatch_"), :blue),
84
+ c(" in #{method(name).source_location.join(":")}", :black),
85
+ ].join
86
+ end
87
+ end
88
+
76
89
  def appinfo_formatters pad: 20
77
90
  pad = -2 if pad.zero?
78
91
 
79
- puts c("#{"".rjust(pad + 2)}# Available formatters", :magenta)
92
+ puts c("#{"".rjust(pad + 2)}# Available formatters (-f)", :magenta)
80
93
  CLI::Formatter.descendants.each do |fmt|
81
94
  name = fmt.to_s
82
95
  source_location = begin
@@ -47,6 +47,13 @@ module IParty
47
47
  yield(self) if block_given?
48
48
  end
49
49
 
50
+ def cookbook *recipes
51
+ recipes.flatten.each do |recipe|
52
+ file = IParty::GEM_ROOT.join("docs", "cli", "action_cookbook", "#{recipe}.rb")
53
+ instance_eval(file.read, "#{caller(3..3).first}, eval #{file}")
54
+ end
55
+ end
56
+
50
57
  def ensure_mmdb_files! fetch_when = @opts[:mmdb_fetch_when]
51
58
  IParty::MaxMind.fetch_db_files!(fetch_when, verbose: true)
52
59
  end
data/lib/iparty/config.rb CHANGED
@@ -18,7 +18,6 @@ module IParty
18
18
  ) do
19
19
  def singletons=(val)
20
20
  self[:singletons] = val
21
- init_singletons! if val == true
22
21
  end
23
22
 
24
23
  def init_singletons!
@@ -13,7 +13,12 @@ module IParty
13
13
  return unless file.exist?
14
14
 
15
15
  if ctn = IParty.config.singletons
16
- ctn = ctn.call if ctn.is_a?(Proc)
16
+ if ctn.is_a?(Proc)
17
+ ctn = ctn.call
18
+ elsif ctn == true
19
+ ctn = IParty.config.init_singletons!
20
+ end
21
+
17
22
  return ctn.fetch(edition) if ctn.key?(edition)
18
23
  end
19
24
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module IParty
4
- VERSION = "0.1.2"
4
+ VERSION = "0.1.3"
5
5
  end
data/lib/iparty.rb CHANGED
@@ -28,6 +28,18 @@ module IParty
28
28
  IParty::MaxMind.fetch_db_files!(*args, **kw)
29
29
  end
30
30
 
31
+ def self.insignificant long, **kw
32
+ return unless long
33
+
34
+ Address.from_insignificant(long, **kw)
35
+ end
36
+
37
+ def self.classify input
38
+ normalize(input).type
39
+ rescue IPAddr::Error
40
+ :invalid
41
+ end
42
+
31
43
  def self.normalize input, family = nil, native: false, **kw
32
44
  return unless input
33
45
  return if input.is_a?(String) && input.match?(/\A[[:space:]]*\z/)
@@ -36,11 +48,13 @@ module IParty
36
48
  addr = case input
37
49
  when String
38
50
  Address.new(input.strip, **kw)
51
+ when IParty::Address
52
+ Address.new(input.to_i(significant: true), input.family)
39
53
  when IPAddr
40
54
  Address.new(input.to_i, input.family)
41
55
  when Integer
42
56
  family ||= input > (2**32) - 1 ? Socket::AF_INET6 : Socket::AF_INET
43
- Address.new(input, family, **kw)
57
+ Address.new(input, normalize_family(family), **kw)
44
58
  else
45
59
  raise IPAddr::InvalidAddressError, "invalid address: #{input}"
46
60
  end
@@ -48,14 +62,21 @@ module IParty
48
62
  native ? addr.native : addr
49
63
  end
50
64
 
51
- def self.classify input
52
- normalize(input).type
53
- rescue IPAddr::Error
54
- :invalid
65
+ def self.normalize_family family
66
+ case family
67
+ when :ipv6, 6, 30 # Socket::AF_INET6 = 30
68
+ Socket::AF_INET6
69
+ when :ipv4, 4, 2 # Socket::AF_INET = 2
70
+ Socket::AF_INET
71
+ when nil
72
+ raise IPAddr::AddressFamilyError, "address family must be specified"
73
+ else
74
+ raise IPAddr::AddressFamilyError, "unsupported address family: #{family}"
75
+ end
55
76
  end
56
77
 
57
78
  def self.expand_hostnames *ips_or_hosts
58
- ips_or_hosts.flatten.flat_map do |name|
79
+ ips_or_hosts.flatten.compact.flat_map do |name|
59
80
  next name unless name.is_a?(String) && !name.include?(":") && name.match?(/[a-z]/i)
60
81
 
61
82
  if defined?(Resolv)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: iparty
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sven Pachnit