maxmind-geoip2 0.4.0

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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +28 -0
  3. data/Gemfile +12 -0
  4. data/Gemfile.lock +72 -0
  5. data/LICENSE-APACHE +202 -0
  6. data/LICENSE-MIT +17 -0
  7. data/README.dev.md +4 -0
  8. data/README.md +326 -0
  9. data/Rakefile +14 -0
  10. data/lib/maxmind/geoip2.rb +4 -0
  11. data/lib/maxmind/geoip2/client.rb +328 -0
  12. data/lib/maxmind/geoip2/errors.rb +41 -0
  13. data/lib/maxmind/geoip2/model/abstract.rb +31 -0
  14. data/lib/maxmind/geoip2/model/anonymous_ip.rb +67 -0
  15. data/lib/maxmind/geoip2/model/asn.rb +43 -0
  16. data/lib/maxmind/geoip2/model/city.rb +79 -0
  17. data/lib/maxmind/geoip2/model/connection_type.rb +36 -0
  18. data/lib/maxmind/geoip2/model/country.rb +74 -0
  19. data/lib/maxmind/geoip2/model/domain.rb +36 -0
  20. data/lib/maxmind/geoip2/model/enterprise.rb +19 -0
  21. data/lib/maxmind/geoip2/model/insights.rb +18 -0
  22. data/lib/maxmind/geoip2/model/isp.rb +57 -0
  23. data/lib/maxmind/geoip2/reader.rb +279 -0
  24. data/lib/maxmind/geoip2/record/abstract.rb +26 -0
  25. data/lib/maxmind/geoip2/record/city.rb +42 -0
  26. data/lib/maxmind/geoip2/record/continent.rb +41 -0
  27. data/lib/maxmind/geoip2/record/country.rb +58 -0
  28. data/lib/maxmind/geoip2/record/location.rb +77 -0
  29. data/lib/maxmind/geoip2/record/maxmind.rb +21 -0
  30. data/lib/maxmind/geoip2/record/place.rb +32 -0
  31. data/lib/maxmind/geoip2/record/postal.rb +34 -0
  32. data/lib/maxmind/geoip2/record/represented_country.rb +27 -0
  33. data/lib/maxmind/geoip2/record/subdivision.rb +52 -0
  34. data/lib/maxmind/geoip2/record/traits.rb +204 -0
  35. data/maxmind-geoip2.gemspec +26 -0
  36. data/test/test_client.rb +424 -0
  37. data/test/test_model_country.rb +80 -0
  38. data/test/test_model_names.rb +47 -0
  39. data/test/test_reader.rb +459 -0
  40. metadata +130 -0
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'maxmind/geoip2/model/abstract'
4
+
5
+ module MaxMind
6
+ module GeoIP2
7
+ module Model
8
+ # Model class for the GeoLite2 ASN database.
9
+ class ASN < Abstract
10
+ # The autonomous system number associated with the IP address.
11
+ #
12
+ # @return [Integer, nil]
13
+ def autonomous_system_number
14
+ get('autonomous_system_number')
15
+ end
16
+
17
+ # The organization associated with the registered autonomous system number
18
+ # for the IP address.
19
+ #
20
+ # @return [String, nil]
21
+ def autonomous_system_organization
22
+ get('autonomous_system_organization')
23
+ end
24
+
25
+ # The IP address that the data in the model is for.
26
+ #
27
+ # @return [String]
28
+ def ip_address
29
+ get('ip_address')
30
+ end
31
+
32
+ # The network in CIDR notation associated with the record. In particular,
33
+ # this is the largest network where all of the fields besides ip_address
34
+ # have the same value.
35
+ #
36
+ # @return [String]
37
+ def network
38
+ get('network')
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'maxmind/geoip2/model/country'
4
+ require 'maxmind/geoip2/record/city'
5
+ require 'maxmind/geoip2/record/location'
6
+ require 'maxmind/geoip2/record/postal'
7
+ require 'maxmind/geoip2/record/subdivision'
8
+
9
+ module MaxMind
10
+ module GeoIP2
11
+ module Model
12
+ # Model class for the data returned by the GeoIP2 City web service and
13
+ # database. It is also used for GeoLite2 City lookups.
14
+ #
15
+ # The only difference between the City and Insights model classes is which
16
+ # fields in each record may be populated. See
17
+ # https://dev.maxmind.com/geoip/geoip2/web-services for more details.
18
+ #
19
+ # See {MaxMind::GeoIP2::Model::Country} for inherited methods.
20
+ class City < Country
21
+ # City data for the IP address.
22
+ #
23
+ # @return [MaxMind::GeoIP2::Record::City]
24
+ attr_reader :city
25
+
26
+ # Location data for the IP address.
27
+ #
28
+ # @return [MaxMind::GeoIP2::Record::Location]
29
+ attr_reader :location
30
+
31
+ # Postal data for the IP address.
32
+ #
33
+ # @return [MaxMind::GeoIP2::Record::Postal]
34
+ attr_reader :postal
35
+
36
+ # The country subdivisions for the IP address.
37
+ #
38
+ # The number and type of subdivisions varies by country, but a subdivision
39
+ # is typically a state, province, country, etc. Subdivisions are ordered
40
+ # from most general (largest) to most specific (smallest).
41
+ #
42
+ # If the response did not contain any subdivisions, this attribute will be
43
+ # an empty array.
44
+ #
45
+ # @return [Array<MaxMind::GeoIP2::Record::Subdivision>]
46
+ attr_reader :subdivisions
47
+
48
+ # @!visibility private
49
+ def initialize(record, locales)
50
+ super(record, locales)
51
+ @city = MaxMind::GeoIP2::Record::City.new(record['city'], locales)
52
+ @location = MaxMind::GeoIP2::Record::Location.new(record['location'])
53
+ @postal = MaxMind::GeoIP2::Record::Postal.new(record['postal'])
54
+ @subdivisions = create_subdivisions(record['subdivisions'], locales)
55
+ end
56
+
57
+ # The most specific subdivision returned.
58
+ #
59
+ # If the response did not contain any subdivisions, this method returns
60
+ # nil.
61
+ #
62
+ # @return [MaxMind::GeoIP2::Record::Subdivision, nil]
63
+ def most_specific_subdivision
64
+ @subdivisions.last
65
+ end
66
+
67
+ private
68
+
69
+ def create_subdivisions(subdivisions, locales)
70
+ return [] if subdivisions.nil?
71
+
72
+ subdivisions.map do |s|
73
+ MaxMind::GeoIP2::Record::Subdivision.new(s, locales)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'maxmind/geoip2/model/abstract'
4
+
5
+ module MaxMind
6
+ module GeoIP2
7
+ module Model
8
+ # Model class for the GeoIP2 Connection Type database.
9
+ class ConnectionType < Abstract
10
+ # The connection type may take the following values: "Dialup", "Cable/DSL",
11
+ # "Corporate", "Cellular". Additional values may be added in the future.
12
+ #
13
+ # @return [String, nil]
14
+ def connection_type
15
+ get('connection_type')
16
+ end
17
+
18
+ # The IP address that the data in the model is for.
19
+ #
20
+ # @return [String]
21
+ def ip_address
22
+ get('ip_address')
23
+ end
24
+
25
+ # The network in CIDR notation associated with the record. In particular,
26
+ # this is the largest network where all of the fields besides ip_address
27
+ # have the same value.
28
+ #
29
+ # @return [String]
30
+ def network
31
+ get('network')
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'maxmind/geoip2/record/continent'
4
+ require 'maxmind/geoip2/record/country'
5
+ require 'maxmind/geoip2/record/maxmind'
6
+ require 'maxmind/geoip2/record/represented_country'
7
+ require 'maxmind/geoip2/record/traits'
8
+
9
+ module MaxMind
10
+ module GeoIP2
11
+ module Model
12
+ # Model class for the data returned by the GeoIP2 Country web service and
13
+ # database. It is also used for GeoLite2 Country lookups.
14
+ class Country
15
+ # Continent data for the IP address.
16
+ #
17
+ # @return [MaxMind::GeoIP2::Record::Continent]
18
+ attr_reader :continent
19
+
20
+ # Country data for the IP address. This object represents the country where
21
+ # MaxMind believes the end user is located.
22
+ #
23
+ # @return [MaxMind::GeoIP2::Record::Country]
24
+ attr_reader :country
25
+
26
+ # Data related to your MaxMind account.
27
+ #
28
+ # @return [MaxMind::GeoIP2::Record::MaxMind]
29
+ attr_reader :maxmind
30
+
31
+ # Registered country data for the IP address. This record represents the
32
+ # country where the ISP has registered a given IP block and may differ from
33
+ # the user's country.
34
+ #
35
+ # @return [MaxMind::GeoIP2::Record::Country]
36
+ attr_reader :registered_country
37
+
38
+ # Represented country data for the IP address. The represented country is
39
+ # used for things like military bases. It is only present when the
40
+ # represented country differs from the country.
41
+ #
42
+ # @return [MaxMind::GeoIP2::Record::RepresentedCountry]
43
+ attr_reader :represented_country
44
+
45
+ # Data for the traits of the IP address.
46
+ #
47
+ # @return [MaxMind::GeoIP2::Record::Traits]
48
+ attr_reader :traits
49
+
50
+ # @!visibility private
51
+ def initialize(record, locales)
52
+ @continent = MaxMind::GeoIP2::Record::Continent.new(
53
+ record['continent'],
54
+ locales,
55
+ )
56
+ @country = MaxMind::GeoIP2::Record::Country.new(
57
+ record['country'],
58
+ locales,
59
+ )
60
+ @maxmind = MaxMind::GeoIP2::Record::MaxMind.new(record['maxmind'])
61
+ @registered_country = MaxMind::GeoIP2::Record::Country.new(
62
+ record['registered_country'],
63
+ locales,
64
+ )
65
+ @represented_country = MaxMind::GeoIP2::Record::RepresentedCountry.new(
66
+ record['represented_country'],
67
+ locales,
68
+ )
69
+ @traits = MaxMind::GeoIP2::Record::Traits.new(record['traits'])
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'maxmind/geoip2/model/abstract'
4
+
5
+ module MaxMind
6
+ module GeoIP2
7
+ module Model
8
+ # Model class for the GeoIP2 Domain database.
9
+ class Domain < Abstract
10
+ # The second level domain associated with the IP address. This will be
11
+ # something like "example.com" or "example.co.uk", not "foo.example.com".
12
+ #
13
+ # @return [String, nil]
14
+ def domain
15
+ get('domain')
16
+ end
17
+
18
+ # The IP address that the data in the model is for.
19
+ #
20
+ # @return [String]
21
+ def ip_address
22
+ get('ip_address')
23
+ end
24
+
25
+ # The network in CIDR notation associated with the record. In particular,
26
+ # this is the largest network where all of the fields besides ip_address
27
+ # have the same value.
28
+ #
29
+ # @return [String]
30
+ def network
31
+ get('network')
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'maxmind/geoip2/model/city'
4
+
5
+ module MaxMind
6
+ module GeoIP2
7
+ module Model
8
+ # Model class for the data returned by GeoIP2 Enterprise database lookups.
9
+ #
10
+ # The only difference between the City and Insights model classes is which
11
+ # fields in each record may be populated. See
12
+ # https://dev.maxmind.com/geoip/geoip2/web-services for more details.
13
+ #
14
+ # See {MaxMind::GeoIP2::Model::City} for inherited methods.
15
+ class Enterprise < City
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'maxmind/geoip2/model/city'
4
+
5
+ module MaxMind
6
+ module GeoIP2
7
+ module Model
8
+ # Model class for the data returned by the GeoIP2 Precision Insights web
9
+ # service.
10
+ #
11
+ # The only difference between the City and Insights model classes is which
12
+ # fields in each record may be populated. See
13
+ # https://dev.maxmind.com/geoip/geoip2/web-services for more details.
14
+ class Insights < City
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'maxmind/geoip2/model/abstract'
4
+
5
+ module MaxMind
6
+ module GeoIP2
7
+ module Model
8
+ # Model class for the GeoIP2 ISP database.
9
+ class ISP < Abstract
10
+ # The autonomous system number associated with the IP address.
11
+ #
12
+ # @return [Integer, nil]
13
+ def autonomous_system_number
14
+ get('autonomous_system_number')
15
+ end
16
+
17
+ # The organization associated with the registered autonomous system number
18
+ # for the IP address.
19
+ #
20
+ # @return [String, nil]
21
+ def autonomous_system_organization
22
+ get('autonomous_system_organization')
23
+ end
24
+
25
+ # The IP address that the data in the model is for.
26
+ #
27
+ # @return [String]
28
+ def ip_address
29
+ get('ip_address')
30
+ end
31
+
32
+ # The name of the ISP associated with the IP address.
33
+ #
34
+ # @return [String, nil]
35
+ def isp
36
+ get('isp')
37
+ end
38
+
39
+ # The network in CIDR notation associated with the record. In particular,
40
+ # this is the largest network where all of the fields besides ip_address
41
+ # have the same value.
42
+ #
43
+ # @return [String]
44
+ def network
45
+ get('network')
46
+ end
47
+
48
+ # The name of the organization associated with the IP address.
49
+ #
50
+ # @return [String, nil]
51
+ def organization
52
+ get('organization')
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,279 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'maxmind/db'
4
+ require 'maxmind/geoip2/errors'
5
+ require 'maxmind/geoip2/model/anonymous_ip'
6
+ require 'maxmind/geoip2/model/asn'
7
+ require 'maxmind/geoip2/model/city'
8
+ require 'maxmind/geoip2/model/connection_type'
9
+ require 'maxmind/geoip2/model/country'
10
+ require 'maxmind/geoip2/model/domain'
11
+ require 'maxmind/geoip2/model/enterprise'
12
+ require 'maxmind/geoip2/model/isp'
13
+
14
+ module MaxMind
15
+ module GeoIP2
16
+ # Reader is a reader for the GeoIP2/GeoLite2 database format. IP addresses
17
+ # can be looked up using the database specific methods.
18
+ #
19
+ # == Example
20
+ #
21
+ # require 'maxmind/geoip2'
22
+ #
23
+ # reader = MaxMind::GeoIP2::Reader.new('GeoIP2-Country.mmdb')
24
+ #
25
+ # record = reader.country('1.2.3.4')
26
+ # puts record.country.iso_code
27
+ #
28
+ # reader.close
29
+ class Reader
30
+ # Create a Reader for looking up IP addresses in a GeoIP2/GeoLite2 database
31
+ # file.
32
+ #
33
+ # If you're performing multiple lookups, it's most efficient to create one
34
+ # Reader and reuse it.
35
+ #
36
+ # Once created, the Reader is safe to use for lookups from multiple
37
+ # threads. It is safe to use after forking only if you use
38
+ # MaxMind::DB::MODE_MEMORY or if your version of Ruby supports IO#pread.
39
+ #
40
+ # @param database [String] a path to a GeoIP2/GeoLite2 database file.
41
+ #
42
+ # @param locales [Array<String>] a list of locale codes to use in the name
43
+ # property from most preferred to least preferred.
44
+ #
45
+ # @param options [Hash<Symbol, Symbol>] options controlling the behavior of
46
+ # the Reader.
47
+ #
48
+ # @option options [Symbol] :mode Defines how to open the database. It may
49
+ # be one of MaxMind::DB::MODE_AUTO, MaxMind::DB::MODE_FILE, or
50
+ # MaxMind::DB::MODE_MEMORY. If you don't provide one, the Reader uses
51
+ # MaxMind::DB::MODE_AUTO. Refer to the definition of those constants in
52
+ # MaxMind::DB for an explanation of their meaning.
53
+ #
54
+ # @raise [MaxMind::DB::InvalidDatabaseError] if the database is corrupt or
55
+ # invalid.
56
+ #
57
+ # @raise [ArgumentError] if the mode is invalid.
58
+ def initialize(database, locales = ['en'], options = {})
59
+ @reader = MaxMind::DB.new(database, options)
60
+ @type = @reader.metadata.database_type
61
+ locales = ['en'] if locales.empty?
62
+ @locales = locales
63
+ end
64
+
65
+ # Look up the IP address in the database.
66
+ #
67
+ # @param ip_address [String] a string in the standard notation. It may be
68
+ # IPv4 or IPv6.
69
+ #
70
+ # @return [MaxMind::GeoIP2::Model::AnonymousIP]
71
+ #
72
+ # @raise [ArgumentError] if used against a non-Anonymous IP database or if
73
+ # you attempt to look up an IPv6 address in an IPv4 only database.
74
+ #
75
+ # @raise [AddressNotFoundError] if the IP address is not found in the
76
+ # database.
77
+ #
78
+ # @raise [MaxMind::DB::InvalidDatabaseError] if the database appears
79
+ # corrupt.
80
+ def anonymous_ip(ip_address)
81
+ flat_model_for(
82
+ Model::AnonymousIP,
83
+ 'anonymous_ip',
84
+ 'GeoIP2-Anonymous-IP',
85
+ ip_address,
86
+ )
87
+ end
88
+
89
+ # Look up the IP address in an ASN database.
90
+ #
91
+ # @param ip_address [String] a string in the standard notation. It may be
92
+ # IPv4 or IPv6.
93
+ #
94
+ # @return [MaxMind::GeoIP2::Model::ASN]
95
+ #
96
+ # @raise [ArgumentError] if used against a non-ASN database or if you
97
+ # attempt to look up an IPv6 address in an IPv4 only database.
98
+ #
99
+ # @raise [AddressNotFoundError] if the IP address is not found in the
100
+ # database.
101
+ #
102
+ # @raise [MaxMind::DB::InvalidDatabaseError] if the database appears
103
+ # corrupt.
104
+ def asn(ip_address)
105
+ flat_model_for(Model::ASN, 'asn', 'GeoLite2-ASN', ip_address)
106
+ end
107
+
108
+ # Look up the IP address in a City database.
109
+ #
110
+ # @param ip_address [String] a string in the standard notation. It may be
111
+ # IPv4 or IPv6.
112
+ #
113
+ # @return [MaxMind::GeoIP2::Model::City]
114
+ #
115
+ # @raise [ArgumentError] if used against a non-City database or if you
116
+ # attempt to look up an IPv6 address in an IPv4 only database.
117
+ #
118
+ # @raise [AddressNotFoundError] if the IP address is not found in the
119
+ # database.
120
+ #
121
+ # @raise [MaxMind::DB::InvalidDatabaseError] if the database appears
122
+ # corrupt.
123
+ def city(ip_address)
124
+ model_for(Model::City, 'city', 'City', ip_address)
125
+ end
126
+
127
+ # Look up the IP address in a Connection Type database.
128
+ #
129
+ # @param ip_address [String] a string in the standard notation. It may be
130
+ # IPv4 or IPv6.
131
+ #
132
+ # @return [MaxMind::GeoIP2::Model::ConnectionType]
133
+ #
134
+ # @raise [ArgumentError] if used against a non-Connection Type database or if
135
+ # you attempt to look up an IPv6 address in an IPv4 only database.
136
+ #
137
+ # @raise [AddressNotFoundError] if the IP address is not found in the
138
+ # database.
139
+ #
140
+ # @raise [MaxMind::DB::InvalidDatabaseError] if the database appears
141
+ # corrupt.
142
+ def connection_type(ip_address)
143
+ flat_model_for(
144
+ Model::ConnectionType,
145
+ 'connection_type',
146
+ 'GeoIP2-Connection-Type',
147
+ ip_address,
148
+ )
149
+ end
150
+
151
+ # Look up the IP address in a Country database.
152
+ #
153
+ # @param ip_address [String] a string in the standard notation. It may be
154
+ # IPv4 or IPv6.
155
+ #
156
+ # @return [MaxMind::GeoIP2::Model::Country]
157
+ #
158
+ # @raise [ArgumentError] if used against a non-Country database or if you
159
+ # attempt to look up an IPv6 address in an IPv4 only database.
160
+ #
161
+ # @raise [AddressNotFoundError] if the IP address is not found in the
162
+ # database.
163
+ #
164
+ # @raise [MaxMind::DB::InvalidDatabaseError] if the database appears
165
+ # corrupt.
166
+ def country(ip_address)
167
+ model_for(Model::Country, 'country', 'Country', ip_address)
168
+ end
169
+
170
+ # Look up the IP address in a Domain database.
171
+ #
172
+ # @param ip_address [String] a string in the standard notation. It may be
173
+ # IPv4 or IPv6.
174
+ #
175
+ # @return [MaxMind::GeoIP2::Model::Domain]
176
+ #
177
+ # @raise [ArgumentError] if used against a non-Domain database or if you
178
+ # attempt to look up an IPv6 address in an IPv4 only database.
179
+ #
180
+ # @raise [AddressNotFoundError] if the IP address is not found in the
181
+ # database.
182
+ #
183
+ # @raise [MaxMind::DB::InvalidDatabaseError] if the database appears
184
+ # corrupt.
185
+ def domain(ip_address)
186
+ flat_model_for(Model::Domain, 'domain', 'GeoIP2-Domain', ip_address)
187
+ end
188
+
189
+ # Look up the IP address in an Enterprise database.
190
+ #
191
+ # @param ip_address [String] a string in the standard notation. It may be
192
+ # IPv4 or IPv6.
193
+ #
194
+ # @return [MaxMind::GeoIP2::Model::Enterprise]
195
+ #
196
+ # @raise [ArgumentError] if used against a non-Enterprise database or if
197
+ # you attempt to look up an IPv6 address in an IPv4 only database.
198
+ #
199
+ # @raise [AddressNotFoundError] if the IP address is not found in the
200
+ # database.
201
+ #
202
+ # @raise [MaxMind::DB::InvalidDatabaseError] if the database appears
203
+ # corrupt.
204
+ def enterprise(ip_address)
205
+ model_for(Model::Enterprise, 'enterprise', 'Enterprise', ip_address)
206
+ end
207
+
208
+ # Look up the IP address in an ISP database.
209
+ #
210
+ # @param ip_address [String] a string in the standard notation. It may be
211
+ # IPv4 or IPv6.
212
+ #
213
+ # @return [MaxMind::GeoIP2::Model::ISP]
214
+ #
215
+ # @raise [ArgumentError] if used against a non-ISP database or if you
216
+ # attempt to look up an IPv6 address in an IPv4 only database.
217
+ #
218
+ # @raise [AddressNotFoundError] if the IP address is not found in the
219
+ # database.
220
+ #
221
+ # @raise [MaxMind::DB::InvalidDatabaseError] if the database appears
222
+ # corrupt.
223
+ def isp(ip_address)
224
+ flat_model_for(Model::ISP, 'isp', 'GeoIP2-ISP', ip_address)
225
+ end
226
+
227
+ # Return the metadata associated with the database.
228
+ #
229
+ # @return [MaxMind::DB::Metadata]
230
+ def metadata
231
+ @reader.metadata
232
+ end
233
+
234
+ # Close the Reader and return resources to the system.
235
+ #
236
+ # @return [void]
237
+ def close
238
+ @reader.close
239
+ end
240
+
241
+ private
242
+
243
+ def model_for(model_class, method, type, ip_address)
244
+ record, prefix_length = get_record(method, type, ip_address)
245
+
246
+ record['traits'] = {} if !record.key?('traits')
247
+ record['traits']['ip_address'] = ip_address
248
+ record['traits']['prefix_length'] = prefix_length
249
+
250
+ model_class.new(record, @locales)
251
+ end
252
+
253
+ def get_record(method, type, ip_address)
254
+ if !@type.include?(type)
255
+ raise ArgumentError,
256
+ "The #{method} method cannot be used with the #{@type} database."
257
+ end
258
+
259
+ record, prefix_length = @reader.get_with_prefix_length(ip_address)
260
+
261
+ if record.nil?
262
+ raise AddressNotFoundError,
263
+ "The address #{ip_address} is not in the database."
264
+ end
265
+
266
+ [record, prefix_length]
267
+ end
268
+
269
+ def flat_model_for(model_class, method, type, ip_address)
270
+ record, prefix_length = get_record(method, type, ip_address)
271
+
272
+ record['ip_address'] = ip_address
273
+ record['prefix_length'] = prefix_length
274
+
275
+ model_class.new(record)
276
+ end
277
+ end
278
+ end
279
+ end