geoip 0.8.9 → 0.9.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 (3) hide show
  1. data/README.rdoc +18 -6
  2. data/lib/geoip.rb +203 -146
  3. metadata +4 -4
@@ -16,19 +16,31 @@ This release adds support for timezone names, thanks to Tonni Aagesen.
16
16
  == SYNOPSIS:
17
17
 
18
18
  require 'geoip'
19
- GeoIP.new('GeoLiteCity.dat').country('www.atlantis.sk')
20
- => ["www.atlantis.sk", "217.67.18.26", "SK", "SVK", "Slovakia", "EU", "02", "Bratislava", "", 48.15, 17.1167, nil, nil, "Europe/Bratislava"]
19
+
20
+ # Use the country database:
21
+ GeoIP.new('GeoIP.dat').country('www.atlantis.sk')
22
+ => ["www.atlantis.sk", "217.67.18.26", 196, "SK", "SVK", "Slovakia", "EU"]
21
23
 
22
24
  Returned values are the requested hostname, the IP address as a dotted quad,
23
- Maxmind's country code, the ISO3166-1 country code, the ISO3166-2 country code,
24
- the ISO3166 country name, and the continent code.
25
+ Maxmind's country code, the ISO3166-1 alpha-2 country code, the ISO3166-2 alpha-3
26
+ country code, the ISO3166 country name, and the continent code.
27
+
28
+ # Use the city database:
29
+ GeoIP.new('GeoLiteCity.dat').country('www.atlantis.sk')
30
+ => ["www.atlantis.sk", "217.67.18.26", "SK", "SVK", "Slovakia", "EU", "02", "Bratislava", "", 48.15, 17.1167, nil, nil, "Europe/Bratislava"]
25
31
 
26
32
  GeoIP.new('GeoCity.dat').city('github.com')
27
33
  => ["github.com", "207.97.227.239", "US", "USA", "United States", "NA", "CA", "San Francisco", "94110", 37.7484, -122.4156, 807, 415, "America/Los_Angeles"]
28
34
 
29
- Returned values are the country values followed by region or state name,
35
+ Returned values are the requested hostname, the IP address as a dotted quad,
36
+ the ISO3166-1 alpha-2 country code, the ISO3166-2 alpha-3 country code, the
37
+ ISO3166 country name, the continent code, the region (state or territory) name,
30
38
  city name, postal_code/zipcode, latitude, longitude, USA DMA code, USA area code,
31
- timezone name. Sorry it's not a Hash... historical.
39
+ timezone name.
40
+
41
+ Result arrays from both city and country also contain accessor methods as appropriate:
42
+ request, ip, country_code, country_code2, country_code3, country_name, continent_code,
43
+ region_name, city_name, postal_code, latitude, longitude, dma_code, area_code, timezone
32
44
 
33
45
  GeoIP.new('GeoIPASNum.dat').asn("www.fsb.ru")
34
46
  => ["AS8342", "RTComm.RU Autonomous System"]
@@ -50,8 +50,11 @@ rescue LoadError
50
50
  end
51
51
 
52
52
  class GeoIP
53
- VERSION = "0.8.9"
53
+ # The GeoIP GEM version number
54
+ VERSION = "0.9.0"
55
+
54
56
  private
57
+ # Ordered list of the ISO3166 2-character country codes, ordered by GeoIP ID
55
58
  CountryCode = [
56
59
  "--","AP","EU","AD","AE","AF","AG","AI","AL","AM","AN",
57
60
  "AO","AQ","AR","AS","AT","AU","AW","AZ","BA","BB",
@@ -81,6 +84,7 @@ class GeoIP
81
84
  "BL","MF"
82
85
  ]
83
86
 
87
+ # Ordered list of the ISO3166 3-character country codes, ordered by GeoIP ID
84
88
  CountryCode3 = [
85
89
  "--","AP","EU","AND","ARE","AFG","ATG","AIA","ALB","ARM","ANT",
86
90
  "AGO","AQ","ARG","ASM","AUT","AUS","ABW","AZE","BIH","BRB",
@@ -110,6 +114,7 @@ class GeoIP
110
114
  "BLM","MAF"
111
115
  ]
112
116
 
117
+ # Ordered list of the English names of the countries, ordered by GeoIP ID
113
118
  CountryName = [
114
119
  "N/A",
115
120
  "Asia/Pacific Region",
@@ -366,6 +371,7 @@ class GeoIP
366
371
  "Saint Martin"
367
372
  ]
368
373
 
374
+ # Ordered list of the ISO3166 2-character continent code of the countries, ordered by GeoIP ID
369
375
  CountryContinent = [
370
376
  "--","AS","EU","EU","AS","AS","SA","SA","EU","AS","SA",
371
377
  "AF","AN","SA","OC","EU","OC","SA","AS","EU","SA",
@@ -395,6 +401,7 @@ class GeoIP
395
401
  "SA","SA"
396
402
  ]
397
403
 
404
+ # Hash of the timezone codes mapped to timezone name, per zoneinfo
398
405
  TimeZone = {
399
406
  "USAL" => "America/Chicago", "USAK" => "America/Anchorage", "USAZ" => "America/Phoenix",
400
407
  "USAR" => "America/Chicago", "USCA" => "America/Los_Angeles", "USCO" => "America/Denver",
@@ -624,37 +631,36 @@ class GeoIP
624
631
  }
625
632
 
626
633
  public
627
- # Edition enumeration:
628
- (GEOIP_COUNTRY_EDITION,
629
- GEOIP_CITY_EDITION_REV1,
630
- GEOIP_REGION_EDITION_REV1,
631
- GEOIP_ISP_EDITION,
632
- GEOIP_ORG_EDITION,
633
- GEOIP_CITY_EDITION_REV0,
634
- GEOIP_REGION_EDITION_REV0,
635
- GEOIP_PROXY_EDITION,
636
- GEOIP_ASNUM_EDITION,
637
- GEOIP_NETSPEED_EDITION,
638
- ) = *1..10
634
+ GEOIP_COUNTRY_EDITION = 1
635
+ GEOIP_CITY_EDITION_REV1 = 2
636
+ GEOIP_REGION_EDITION_REV1 = 3
637
+ GEOIP_ISP_EDITION = 4
638
+ GEOIP_ORG_EDITION = 5
639
+ GEOIP_CITY_EDITION_REV0 = 6
640
+ GEOIP_REGION_EDITION_REV0 = 7
641
+ GEOIP_PROXY_EDITION = 8
642
+ GEOIP_ASNUM_EDITION = 9
643
+ GEOIP_NETSPEED_EDITION = 10
639
644
 
640
645
  private
641
- COUNTRY_BEGIN = 16776960
642
- STATE_BEGIN_REV0 = 16700000
643
- STATE_BEGIN_REV1 = 16000000
644
- STRUCTURE_INFO_MAX_SIZE = 20
645
- DATABASE_INFO_MAX_SIZE = 100
646
- MAX_ORG_RECORD_LENGTH = 300
647
- MAX_ASN_RECORD_LENGTH = 300 # unverified
648
- US_OFFSET = 1
649
- CANADA_OFFSET = 677
650
- WORLD_OFFSET = 1353
651
- FIPS_RANGE = 360
652
- FULL_RECORD_LENGTH = 50
646
+ COUNTRY_BEGIN = 16776960 #:nodoc:
647
+ STATE_BEGIN_REV0 = 16700000 #:nodoc:
648
+ STATE_BEGIN_REV1 = 16000000 #:nodoc:
649
+ STRUCTURE_INFO_MAX_SIZE = 20 #:nodoc:
650
+ DATABASE_INFO_MAX_SIZE = 100 #:nodoc:
651
+ MAX_ORG_RECORD_LENGTH = 300 #:nodoc:
652
+ MAX_ASN_RECORD_LENGTH = 300 #:nodoc: unverified
653
+ US_OFFSET = 1 #:nodoc:
654
+ CANADA_OFFSET = 677 #:nodoc:
655
+ WORLD_OFFSET = 1353 #:nodoc:
656
+ FIPS_RANGE = 360 #:nodoc:
657
+ FULL_RECORD_LENGTH = 50 #:nodoc:
653
658
 
654
- STANDARD_RECORD_LENGTH = 3
655
- SEGMENT_RECORD_LENGTH = 3
659
+ STANDARD_RECORD_LENGTH = 3 #:nodoc:
660
+ SEGMENT_RECORD_LENGTH = 3 #:nodoc:
656
661
 
657
662
  public
663
+ # The Edition number that identifies which kind of database you've opened
658
664
  attr_reader :databaseType
659
665
 
660
666
  # Open the GeoIP database and determine the file format version
@@ -710,15 +716,20 @@ class GeoIP
710
716
  # Search the GeoIP database for the specified host, returning country info
711
717
  #
712
718
  # +hostname+ is a String holding the host's DNS name or numeric IP address.
713
- # Return an array of seven elements:
719
+ # If the database is a City database (normal), return the result that +city+ would return.
720
+ # Otherwise, return an array of seven elements:
714
721
  # * The host or IP address string as requested
715
722
  # * The IP address string after looking up the host
716
- # * The GeoIP country-ID as an integer
717
- # * The ISO3166-1 two-character country code
718
- # * The ISO3166-2 three-character country code
719
- # * The ISO3166 English-language name of the country
723
+ # * The GeoIP country-ID as an integer (N.B. this is excluded from the city results!)
724
+ # * The two-character country code (ISO 3166-1 alpha-2)
725
+ # * The three-character country code (ISO 3166-2 alpha-3)
726
+ # * The ISO 3166 English-language name of the country
720
727
  # * The two-character continent code
721
728
  #
729
+ # The array has been extended with methods listed in GeoIP::CountryAccessors.ACCESSORS:
730
+ # request, ip, country_code, country_code2, country_code3, country_name, continent_code.
731
+ # In addition, +to_hash+ provides a symbol-keyed hash for the above values.
732
+ #
722
733
  def country(hostname)
723
734
  if (@databaseType == GEOIP_CITY_EDITION_REV0 ||
724
735
  @databaseType == GEOIP_CITY_EDITION_REV1)
@@ -734,7 +745,7 @@ class GeoIP
734
745
 
735
746
  # Convert numeric IP address to an integer
736
747
  ipnum = iptonum(ip)
737
- if (@databaseType != GEOIP_COUNTRY_EDITION &&
748
+ if (@databaseType != GEOIP_COUNTRY_EDITION &&
738
749
  @databaseType != GEOIP_PROXY_EDITION &&
739
750
  @databaseType != GEOIP_NETSPEED_EDITION)
740
751
  throw "Invalid GeoIP database type, can't look up Country by IP"
@@ -743,118 +754,36 @@ class GeoIP
743
754
  [ hostname, # Requested hostname
744
755
  ip, # Ip address as dotted quad
745
756
  code, # GeoIP's country code
746
- CountryCode[code], # ISO3166-1 code
747
- CountryCode3[code], # ISO3166-2 code
748
- CountryName[code], # Country name, per IS03166
749
- CountryContinent[code] ] # Continent code.
750
- end
751
-
752
- # Search the GeoIP database for the specified host, returning city info
753
- #
754
- # +hostname+ is a String holding the host's DNS name or numeric IP address
755
- # Return an array of twelve or fourteen elements:
756
- # * All elements from the country query
757
- # * The region (state or territory) name
758
- # * The city name
759
- # * The postal code (zipcode)
760
- # * The latitude
761
- # * The longitude
762
- # * The dma_code and area_code, if available (REV1 City database)
763
- # * The timezone name, if known
764
- private
765
-
766
- def read_city(pos, hostname = '', ip = '')
767
- off = pos + (2*@record_length-1) * @databaseSegments[0]
768
- record = atomic_read(FULL_RECORD_LENGTH, off)
769
- return nil unless record && record.size == FULL_RECORD_LENGTH
770
-
771
- # The country code is the first byte:
772
- code = record[0]
773
- code = code.ord if code.respond_to?(:ord)
774
- record = record[1..-1]
775
- @iter_pos += 1 unless @iter_pos.nil?
776
-
777
- spl = record.split("\x00", 4)
778
- # Get the region:
779
- region = spl[0]
780
- @iter_pos += (region.size + 1) unless @iter_pos.nil?
781
-
782
- # Get the city:
783
- city = spl[1]
784
- @iter_pos += (city.size + 1) unless @iter_pos.nil?
785
- # set the correct encoding in ruby 1.9 compatible environments:
786
- city.force_encoding('iso-8859-1') if city.respond_to?(:force_encoding)
787
-
788
- # Get the postal code:
789
- postal_code = spl[2]
790
- @iter_pos += (postal_code.size + 1) unless @iter_pos.nil?
791
-
792
- record = spl[3]
793
- # Get the latitude/longitude:
794
- if(record && record[0,3]) then
795
- latitude = le_to_ui(record[0,3].unpack('C*')) / 10000.0 - 180
796
- record = record[3..-1]
797
- @iter_pos += 3 unless @iter_pos.nil?
798
- else
799
- latitude = ''
800
- end
801
- if(record && record[0,3]) then
802
- longitude = le_to_ui(record[0,3].unpack('C*')) / 10000.0 - 180
803
- record = record[3..-1]
804
- @iter_pos += 3 unless @iter_pos.nil?
805
- else
806
- longitude = ''
807
- end
808
-
809
- us_area_codes = []
810
- if (record &&
811
- record[0,3] &&
812
- @databaseType == GEOIP_CITY_EDITION_REV1 &&
813
- CountryCode[code] == "US") # UNTESTED
814
- dmaarea_combo = le_to_ui(record[0,3].unpack('C*'))
815
- dma_code = dmaarea_combo / 1000;
816
- area_code = dmaarea_combo % 1000;
817
- us_area_codes = [ dma_code, area_code ]
818
- @iter_pos += 3 unless @iter_pos.nil?
819
- else
820
- us_area_codes = [ nil, nil ] # Ensure that TimeZone is always at the same offset
821
- end
822
-
823
- [ hostname, # Requested hostname
824
- ip, # Ip address as dotted quad
825
- CountryCode[code], # ISO3166-1 code
826
- CountryCode3[code], # ISO3166-2 code
827
- CountryName[code], # Country name, per IS03166
828
- CountryContinent[code], # Continent code.
829
- region, # Region name
830
- city, # City name
831
- postal_code, # Postal code
832
- latitude,
833
- longitude,
834
- ] +
835
- us_area_codes +
836
- [ TimeZone["#{CountryCode[code]}#{region}"] || TimeZone["#{CountryCode[code]}"] ]
757
+ CountryCode[code], # ISO3166-1 alpha-2 code
758
+ CountryCode3[code], # ISO3166-2 alpha-3 code
759
+ CountryName[code], # Country name, per ISO 3166
760
+ CountryContinent[code] # Continent code.
761
+ ].extend(CountryAccessors)
837
762
  end
838
763
 
839
- public
840
-
841
764
  # Search the GeoIP database for the specified host, returning city info.
842
765
  #
843
766
  # +hostname+ is a String holding the host's DNS name or numeric IP address.
844
- # Return an array of twelve or fourteen elements:
767
+ # Return an array of fourteen elements:
845
768
  # * The host or IP address string as requested
846
769
  # * The IP address string after looking up the host
847
- # * The GeoIP country-ID as an integer
848
- # * The ISO3166-1 two-character country code
849
- # * The ISO3166-2 three-character country code
850
- # * The ISO3166 English-language name of the country
770
+ # * The two-character country code (ISO 3166-1 alpha-2)
771
+ # * The three-character country code (ISO 3166-2 alpha-3)
772
+ # * The ISO 3166 English-language name of the country
851
773
  # * The two-character continent code
852
- # * The region name
774
+ # * The region name (state or territory)
853
775
  # * The city name
854
- # * The postal code
776
+ # * The postal code (zipcode)
855
777
  # * The latitude
856
778
  # * The longitude
857
- # * The USA dma_code and area_code, if available (REV1 City database)
779
+ # * The USA dma_code if known (only REV1 City database)
780
+ # * The USA area_code if known (only REV1 City database)
781
+ # * The timezone name, if known
782
+ #
783
+ # The array has been extended with methods listed in GeoIP::CityAccessors.ACCESSORS:
784
+ # request, ip, country_code2, country_code3, country_name, continent_code,
785
+ # region_name, city_name, postal_code, latitude, longitude, dma_code, area_code, timezone.
786
+ # In addition, +to_hash+ provides a symbol-keyed hash for the above values.
858
787
  #
859
788
  def city(hostname)
860
789
  ip = hostname
@@ -879,10 +808,11 @@ class GeoIP
879
808
  # and I'll send you the test program so you can test whatever IP range you think is causing
880
809
  # problems, as I don't care to undertake an exhaustive search of the 32-bit space.
881
810
  return nil if pos == @databaseSegments[0]
882
- read_city(pos, hostname, ip)
811
+ read_city(pos, hostname, ip).extend(CityAccessors)
883
812
  end
884
813
 
885
814
  # Search a ISP GeoIP database for the specified host, returning the ISP
815
+ # Not all GeoIP databases contain ISP information. Check http://maxmind.com
886
816
  #
887
817
  # +hostname+ is a String holding the host's DNS name or numeric IP address.
888
818
  # Return the ISP name
@@ -906,7 +836,7 @@ class GeoIP
906
836
  record = record.sub(/\000.*/n, '')
907
837
  record
908
838
  end
909
-
839
+
910
840
  # Search a ASN GeoIP database for the specified host, returning the AS number + description
911
841
  #
912
842
  # +hostname+ is a String holding the host's DNS name or numeric IP address.
@@ -914,7 +844,7 @@ class GeoIP
914
844
  #
915
845
  # Source:
916
846
  # http://geolite.maxmind.com/download/geoip/database/asnum/GeoIPASNum.dat.gz
917
- #
847
+ #
918
848
  def asn(hostname)
919
849
  ip = hostname
920
850
  if ip.kind_of?(String) && ip !~ /^[0-9.]*$/
@@ -932,10 +862,10 @@ class GeoIP
932
862
  off = pos + (2*@record_length-1) * @databaseSegments[0]
933
863
  record = atomic_read(MAX_ASN_RECORD_LENGTH, off)
934
864
  record = record.sub(/\000.*/n, '')
935
-
865
+
936
866
  if record =~ /^(AS\d+)\s(.*)$/
937
- # AS####, Description
938
- return [$1, $2]
867
+ # AS####, Description
868
+ return [$1, $2].extend(ASNAccessors)
939
869
  end
940
870
  end
941
871
 
@@ -964,8 +894,92 @@ class GeoIP
964
894
  end
965
895
 
966
896
  private
967
-
968
- def iptonum(ip) # Convert numeric IP address to integer
897
+
898
+ # Search the GeoIP database for the specified host, returning city info
899
+ #
900
+ # +hostname+ is a String holding the host's DNS name or numeric IP address
901
+ # Return an array of fourteen elements:
902
+ # * All elements from the country query (except GeoIP's country code, bah!)
903
+ # * The region (state or territory) name
904
+ # * The city name
905
+ # * The postal code (zipcode)
906
+ # * The latitude
907
+ # * The longitude
908
+ # * The dma_code and area_code, if available (REV1 City database)
909
+ # * The timezone name, if known
910
+ def read_city(pos, hostname = '', ip = '') #:nodoc:
911
+ off = pos + (2*@record_length-1) * @databaseSegments[0]
912
+ record = atomic_read(FULL_RECORD_LENGTH, off)
913
+ return nil unless record && record.size == FULL_RECORD_LENGTH
914
+
915
+ # The country code is the first byte:
916
+ code = record[0]
917
+ code = code.ord if code.respond_to?(:ord)
918
+ record = record[1..-1]
919
+ @iter_pos += 1 unless @iter_pos.nil?
920
+
921
+ spl = record.split("\x00", 4)
922
+ # Get the region:
923
+ region = spl[0]
924
+ @iter_pos += (region.size + 1) unless @iter_pos.nil?
925
+
926
+ # Get the city:
927
+ city = spl[1]
928
+ @iter_pos += (city.size + 1) unless @iter_pos.nil?
929
+ # set the correct encoding in ruby 1.9 compatible environments:
930
+ city.force_encoding('iso-8859-1') if city.respond_to?(:force_encoding)
931
+
932
+ # Get the postal code:
933
+ postal_code = spl[2]
934
+ @iter_pos += (postal_code.size + 1) unless @iter_pos.nil?
935
+
936
+ record = spl[3]
937
+ # Get the latitude/longitude:
938
+ if(record && record[0,3]) then
939
+ latitude = le_to_ui(record[0,3].unpack('C*')) / 10000.0 - 180
940
+ record = record[3..-1]
941
+ @iter_pos += 3 unless @iter_pos.nil?
942
+ else
943
+ latitude = ''
944
+ end
945
+ if(record && record[0,3]) then
946
+ longitude = le_to_ui(record[0,3].unpack('C*')) / 10000.0 - 180
947
+ record = record[3..-1]
948
+ @iter_pos += 3 unless @iter_pos.nil?
949
+ else
950
+ longitude = ''
951
+ end
952
+
953
+ if (record &&
954
+ record[0,3] &&
955
+ @databaseType == GEOIP_CITY_EDITION_REV1 &&
956
+ CountryCode[code] == "US") # UNTESTED
957
+ dmaarea_combo = le_to_ui(record[0,3].unpack('C*'))
958
+ dma_code = dmaarea_combo / 1000;
959
+ area_code = dmaarea_combo % 1000;
960
+ @iter_pos += 3 unless @iter_pos.nil?
961
+ else
962
+ dma_code, area_code = nil, nil
963
+ end
964
+
965
+ [ hostname, # Requested hostname
966
+ ip, # Ip address as dotted quad
967
+ CountryCode[code], # ISO3166-1 code
968
+ CountryCode3[code], # ISO3166-2 code
969
+ CountryName[code], # Country name, per IS03166
970
+ CountryContinent[code], # Continent code.
971
+ region, # Region name
972
+ city, # City name
973
+ postal_code, # Postal code
974
+ latitude,
975
+ longitude,
976
+ dma_code,
977
+ area_code
978
+ ] +
979
+ [ TimeZone["#{CountryCode[code]}#{region}"] || TimeZone["#{CountryCode[code]}"] ]
980
+ end
981
+
982
+ def iptonum(ip) #:nodoc: Convert numeric IP address to integer
969
983
  if ip.kind_of?(String) &&
970
984
  ip =~ /^([0-9]+)\.([0-9]+)\.([0-9]+)\.([0-9]+)$/
971
985
  ip = be_to_ui(Regexp.last_match().to_a.slice(1..4))
@@ -973,7 +987,7 @@ class GeoIP
973
987
  ip
974
988
  end
975
989
 
976
- def seek_record(ipnum)
990
+ def seek_record(ipnum) #:nodoc:
977
991
  # Binary search in the file.
978
992
  # Records are pairs of little-endian integers, each of @record_length.
979
993
  offset = 0
@@ -989,14 +1003,14 @@ class GeoIP
989
1003
  end
990
1004
 
991
1005
  # Convert a big-endian array of numeric bytes to unsigned int
992
- def be_to_ui(s)
1006
+ def be_to_ui(s) #:nodoc:
993
1007
  s.inject(0) { |m, o|
994
1008
  (m << 8) + o.to_i
995
1009
  }
996
1010
  end
997
1011
 
998
1012
  # Same for little-endian
999
- def le_to_ui(s)
1013
+ def le_to_ui(s) #:nodoc:
1000
1014
  be_to_ui(s.reverse)
1001
1015
  end
1002
1016
 
@@ -1005,7 +1019,7 @@ class GeoIP
1005
1019
  # and multiprocess-safe).  Otherwise we'll use a mutex to synchronize
1006
1020
  # access (only providing protection against multiple threads, but not
1007
1021
  # file descriptors shared across multiple processes).
1008
- def atomic_read(length, offset)
1022
+ def atomic_read(length, offset) #:nodoc:
1009
1023
  if @mutex
1010
1024
  @mutex.synchronize {
1011
1025
  @file.seek(offset)
@@ -1015,6 +1029,49 @@ class GeoIP
1015
1029
  IO.pread(@file.fileno, length, offset)
1016
1030
  end
1017
1031
  end
1032
+
1033
+ module CountryAccessors #:nodoc:
1034
+ ACCESSORS = [
1035
+ :request, :ip, :country_code, :country_code2, :country_code3, :country_name, :continent_code
1036
+ ]
1037
+ ACCESSORS.each_with_index do |method, i|
1038
+ define_method(method) { self[i] }
1039
+ end
1040
+
1041
+ def to_hash
1042
+ ACCESSORS.inject({}) do |hash, key|
1043
+ hash[key] = self.send(key)
1044
+ hash
1045
+ end
1046
+ end
1047
+ end
1048
+
1049
+ module CityAccessors #:nodoc:
1050
+ ACCESSORS = [
1051
+ :request, :ip, :country_code2, :country_code3, :country_name, :continent_code,
1052
+ :region_name, :city_name, :postal_code, :latitude, :longitude, :dma_code, :area_code, :timezone
1053
+ ]
1054
+ ACCESSORS.each_with_index do |method, i|
1055
+ define_method(method) { self[i] }
1056
+ end
1057
+
1058
+ def to_hash
1059
+ ACCESSORS.inject({}) do |hash, key|
1060
+ hash[key] = self.send(key)
1061
+ hash
1062
+ end
1063
+ end
1064
+ end
1065
+
1066
+ module ASNAccessors #:nodoc:
1067
+ def ip
1068
+ self[0]
1069
+ end
1070
+
1071
+ def asn
1072
+ self[1]
1073
+ end
1074
+ end
1018
1075
  end
1019
1076
 
1020
1077
  if $0 == __FILE__
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geoip
3
3
  version: !ruby/object:Gem::Version
4
- hash: 45
4
+ hash: 59
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 8
9
8
  - 9
10
- version: 0.8.9
9
+ - 0
10
+ version: 0.9.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Clifford Heath
@@ -16,7 +16,7 @@ autorequire:
16
16
  bindir: bin
17
17
  cert_chain: []
18
18
 
19
- date: 2011-01-13 00:00:00 +11:00
19
+ date: 2011-02-07 00:00:00 +11:00
20
20
  default_executable:
21
21
  dependencies:
22
22
  - !ruby/object:Gem::Dependency