geoip 0.8.9 → 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
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