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 +4 -4
- data/CHANGELOG.md +17 -0
- data/README.md +1 -1
- data/docs/benchmark.md +11 -11
- data/docs/cli/README.md +17 -8
- data/docs/cli/action_cookbook/show_my_remote_ips.rb +10 -7
- data/docs/configuration.md +67 -35
- data/docs/maxmind_result.md +1 -1
- data/docs/mmdb_download.md +3 -3
- data/lib/iparty/address.rb +16 -7
- data/lib/iparty/cli/application/actions.rb +1 -1
- data/lib/iparty/cli/application/appinfo.rb +14 -1
- data/lib/iparty/cli/application.rb +7 -0
- data/lib/iparty/config.rb +0 -1
- data/lib/iparty/max_mind.rb +6 -1
- data/lib/iparty/version.rb +1 -1
- data/lib/iparty.rb +27 -6
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 61224732c6f0206d4525d1a238e76106646a14efdd76cbb3dc4e989a6a41b1a5
|
|
4
|
+
data.tar.gz: da8851962434b85c846b378e9332380101f22fc3bc10037a3d2b787b52bda003
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
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 [
|
|
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]:
|
|
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.
|
|
14
|
-
RSS-after[uncached]:
|
|
13
|
+
uncached 1.454k (± 2.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]:
|
|
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
|
|
20
|
+
singletons 163.000 i/100ms
|
|
21
21
|
Calculating -------------------------------------
|
|
22
|
-
singletons 1.
|
|
23
|
-
RSS-after[singletons]: 31.
|
|
22
|
+
singletons 1.633k (± 1.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.
|
|
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
|
|
29
|
+
eager_load 449.000 i/100ms
|
|
30
30
|
Calculating -------------------------------------
|
|
31
|
-
eager_load 4.
|
|
32
|
-
RSS-after[eager_load]:
|
|
31
|
+
eager_load 4.566k (± 1.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.
|
|
55
|
-
IParty.config.
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
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
|
|
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
|
data/docs/configuration.md
CHANGED
|
@@ -30,7 +30,7 @@ IParty(request.remote_ip).best_tenant # => :us
|
|
|
30
30
|
|
|
31
31
|
### Full config
|
|
32
32
|
|
|
33
|
-
|
|
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
|
+
```
|
data/docs/maxmind_result.md
CHANGED
|
@@ -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])
|
data/docs/mmdb_download.md
CHANGED
|
@@ -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
|
|
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 #{
|
|
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
|
```
|
data/lib/iparty/address.rb
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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
|
|
102
|
+
return original_to_s unless @family == Socket::AF_INET6
|
|
98
103
|
|
|
99
|
-
|
|
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
|
|
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
data/lib/iparty/max_mind.rb
CHANGED
|
@@ -13,7 +13,12 @@ module IParty
|
|
|
13
13
|
return unless file.exist?
|
|
14
14
|
|
|
15
15
|
if ctn = IParty.config.singletons
|
|
16
|
-
|
|
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
|
|
data/lib/iparty/version.rb
CHANGED
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.
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
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)
|