geonames_api 0.1.1 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -15,5 +15,6 @@ rdoc
15
15
  spec/reports
16
16
  test/tmp
17
17
  test/version_tmp
18
- tmp
19
18
  test.log
19
+ tmp/*.*
20
+ .DS_Store
data/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # GeoNames API
2
2
 
3
- [![Build Status](https://secure.travis-ci.org/mceachen/geonames_api.png?branch=master)](http://travis-ci.org/mceachen/geonames_api)
4
- [![Code Climate](https://codeclimate.com/github/mceachen/geonames_api.png)](https://codeclimate.com/github/mceachen/geonames_api)
3
+ [![Build Status](https://secure.travis-ci.org/buytruckload/geonames_api.png?branch=master)](http://travis-ci.org/buytruckload/geonames_api)
4
+ [![Code Climate](https://codeclimate.com/repos/52a4b4c513d63712180028d4/badges/33cf2af8b28c4cb66098/gpa.png)](https://codeclimate.com/repos/52a4b4c513d63712180028d4/feed)
5
5
 
6
6
  This is a lightweight client for the [GeoNames](http://www.geonames.org) API. Huge thanks to them for such a great service!
7
7
 
@@ -11,16 +11,6 @@ This is a simplified ruby implementation that does not implement the entire API.
11
11
 
12
12
  ## Getting Started
13
13
 
14
- Add this line to your application's Gemfile:
15
-
16
- gem 'geonames_api'
17
-
18
- And then execute:
19
-
20
- $ bundle
21
-
22
- Or install it yourself as:
23
-
24
14
  $ gem install geonames_api
25
15
 
26
16
  ## Usage
@@ -190,6 +180,8 @@ A number of nice features were added by [@mrm](http://twitter.com/mrm) including
190
180
  * Timezone, AlternateName, and GeoName entries properly encoded in results
191
181
  * better test coverage, Travis CI, and CodeClimate integration
192
182
 
183
+ To contribute:
184
+
193
185
  1. Fork it
194
186
  2. Create your feature branch (`git checkout -b my-new-feature`)
195
187
  3. Commit your changes (`git commit -am 'Add some feature'`)
@@ -14,7 +14,7 @@ Gem::Specification.new do |gem|
14
14
 
15
15
  gem.add_runtime_dependency "activesupport"
16
16
  gem.add_runtime_dependency "tzinfo"
17
- gem.add_runtime_dependency "zipruby"
17
+ gem.add_runtime_dependency "rubyzip"
18
18
  gem.add_development_dependency 'rake'
19
19
  gem.add_development_dependency 'rspec'
20
20
 
@@ -2,7 +2,7 @@ require 'open-uri'
2
2
  require 'json'
3
3
  require 'csv'
4
4
  require 'active_support/all'
5
- require 'zipruby'
5
+ require 'zip'
6
6
 
7
7
  module GeoNamesAPI
8
8
 
@@ -25,10 +25,10 @@ module GeoNamesAPI
25
25
  self.logger = nil
26
26
 
27
27
  mattr_accessor :retries
28
- self.retries = 3
28
+ self.retries = 5
29
29
 
30
30
  mattr_accessor :max_sleep_time_between_retries
31
- self.max_sleep_time_between_retries = 5
31
+ self.max_sleep_time_between_retries = 10
32
32
 
33
33
  def self.params
34
34
  {
@@ -28,7 +28,6 @@ module GeoNamesAPI
28
28
  rescue Timeout => e
29
29
  if retries_remaining > 0
30
30
  retries_remaining -= 1
31
- puts "retrying!"
32
31
  sleep rand * GeoNamesAPI.max_sleep_time_between_retries
33
32
  retry
34
33
  else
@@ -1,3 +1,5 @@
1
+ require 'fileutils'
2
+
1
3
  module GeoNamesAPI
2
4
  class Country < ListEndpoint
3
5
 
@@ -8,21 +10,61 @@ module GeoNamesAPI
8
10
  EXPORT_HEADERS = %W(country_code postal_code place_name admin_name1 admin_code1 admin_name2 admin_code2 admin_name3 admin_code3 latitude longitude accuracy)
9
11
 
10
12
  def postal_code_export
11
- zip_data = open(postal_code_export_url) { |f| f.binmode; f.read }
12
- stream = lambda { return zip_data.slice!(0, 256) }
13
- csv = EXPORT_HEADERS.join("\t") + "\n"
14
- Zip::Archive.open_buffer(stream) do |archive|
15
- archive.each do |f|
16
- csv << f.read if f.name =~ /\A#{country_code}/
17
- end
18
- end
19
- csv
13
+ download_archive
14
+ extract_file
15
+ create_csv
20
16
  end
21
17
 
22
18
  def postal_code_csv
23
19
  CSV.parse(postal_code_export, headers: true, col_sep: "\t", header_converters: :symbol, encoding: "ISO8859-1")
24
20
  end
25
21
 
22
+ private
23
+
24
+ def download_archive
25
+ File.open(export_directory.to_s + "/export.zip", "wb") do |f|
26
+ open(postal_code_export_url, "rb") do |export|
27
+ f.write export.read
28
+ end
29
+ end
30
+ end
31
+
32
+ def extract_file
33
+ Zip::File.foreach(export_directory.to_s + "/export.zip") do |f|
34
+ File.delete(extract_file_name) if File.exist?(extract_file_name)
35
+ f.extract(extract_file_name) if f.name =~ /\A#{country_code}/
36
+ end
37
+ end
38
+
39
+ def create_csv
40
+ File.open(csv_file_name, "wb") do |f|
41
+ f.write EXPORT_HEADERS.join("\t") + "\n"
42
+ f.write File.open(extract_file_name, "r").read
43
+ end
44
+ File.open(csv_file_name, "r").read
45
+ end
46
+
47
+ def extract_file_name
48
+ export_directory + "/tmp.txt"
49
+ end
50
+
51
+ def csv_file_name
52
+ export_directory + "/#{country_code}.txt"
53
+ end
54
+
55
+ # if rails is defined use the rails tmp directory to ensure
56
+ # compatability with heroku
57
+ def export_directory
58
+ directory = if defined?(Rails)
59
+ Rails.root.join("tmp","geonames_api").to_s
60
+ else
61
+ File.expand_path("../../../tmp", __FILE__)
62
+ end
63
+ FileUtils.mkdir(directory) unless File.directory?(directory)
64
+ FileUtils.chmod_R(0777, directory)
65
+ directory
66
+ end
67
+
26
68
  def postal_code_export_url
27
69
  EXPORT_BASE_URL + country_code + ".zip"
28
70
  end
@@ -24,8 +24,8 @@ module GeoNamesAPI
24
24
  aliases = []
25
25
  value = @response[key]
26
26
  parsed_value = case (key)
27
- when 'geonames', 'streetSegment'
28
- aliases = [:geonames, :results]
27
+ when 'geonames', 'streetSegment', 'postalcodes'
28
+ aliases = [:geonames, :results, :postalcodes]
29
29
  value.map { |ea| self.class.new(ea) }
30
30
  when 'alternateNames'
31
31
  AlternateNames.new(value)
@@ -2,27 +2,51 @@ module GeoNamesAPI
2
2
  class Error < StandardError
3
3
  # See http://www.geonames.org/export/webservice-exception.html
4
4
  def self.from_status(status)
5
- val = status['value'].to_i
6
- error_type = case val
7
- when 12, 13, 22
8
- Timeout
9
- when 14
10
- InvalidParameter
11
- when 21
12
- InvalidInput
13
- else
14
- Error
5
+ error_code = status['value'].to_i
6
+ error_class = for_error_code(error_code) || self
7
+ raise error_class, "#{status['message']} (#{error_code})"
8
+ end
9
+
10
+ def self.for_error_code(error_code)
11
+ @lookup ||= subclasses.reduce({}) do |h, subclass|
12
+ subclass::ERROR_CODES.each { |ea| h[ea] = subclass }
13
+ h
15
14
  end
16
- raise error_type, "#{status['message']} (#{val})"
15
+ @lookup[error_code]
17
16
  end
18
17
  end
19
18
 
19
+ class AuthorizationException < Error
20
+ ERROR_CODES = [10]
21
+ end
22
+ class RecordDoesNotExist < Error
23
+ ERROR_CODES = [11]
24
+ end
20
25
  class Timeout < Error
26
+ ERROR_CODES = [13, 22]
21
27
  end
22
-
23
28
  class InvalidParameter < Error
29
+ ERROR_CODES = [14]
24
30
  end
25
-
26
- class InvalidInput < Error
31
+ class NoResultFound < Error
32
+ ERROR_CODES = [15]
33
+ end
34
+ class DuplicateException < Error
35
+ ERROR_CODES = [16]
36
+ end
37
+ class PostalCodeNotFound < Error
38
+ ERROR_CODES = [17]
39
+ end
40
+ class DailyLimitExceeded < Error
41
+ ERROR_CODES = [18]
42
+ end
43
+ class HourlyLimitExceeded < Error
44
+ ERROR_CODES = [19]
45
+ end
46
+ class WeeklyLimitExceeded < Error
47
+ ERROR_CODES = [20]
48
+ end
49
+ class ServiceNotImplemented < Error
50
+ ERROR_CODES = [23]
27
51
  end
28
52
  end
@@ -0,0 +1,38 @@
1
+ module GeoNamesAPI
2
+ class Neighbourhood < SingletonEndpoint
3
+ METHOD = 'neighbourhoodJSON'
4
+ FIND_PARAMS = %w(lat lng)
5
+
6
+ def hierarchy
7
+ %w{countryName adminName1 adminName2 city name}.map do |ea|
8
+ @neighbourhood[ea]
9
+ end
10
+ end
11
+ end
12
+ end
13
+
14
+ =begin
15
+
16
+ {
17
+ "neighbourhood": {
18
+ "countryName": "United States",
19
+ "adminName1": "New York",
20
+ "adminName2": "New York County",
21
+ "city": "New York City-Manhattan",
22
+ "name": "Central Park",
23
+ "adminCode2": "061",
24
+ "adminCode1": "NY",
25
+ "countryCode": "US"
26
+ }
27
+ }
28
+
29
+ or
30
+
31
+ {
32
+ "status": {
33
+ "message": "we are afraid we could not find a neighbourhood for latitude and longitude :XXX, YYYY",
34
+ "value": 15
35
+ }
36
+ }
37
+
38
+ =end
@@ -1,3 +1,3 @@
1
1
  module GeoNamesAPI
2
- VERSION = Gem::Version.new('0.1.1')
2
+ VERSION = Gem::Version.new('0.1.2')
3
3
  end
@@ -0,0 +1,15 @@
1
+ require "spec_helper"
2
+
3
+ module GeoNamesAPI
4
+ describe Country do
5
+
6
+ subject do
7
+ described_class.find "CA"
8
+ end
9
+
10
+ it "can download and unpack the csv" do
11
+ expect(subject.postal_code_csv.size).to be > 0
12
+ end
13
+
14
+ end
15
+ end
@@ -0,0 +1,14 @@
1
+ require 'spec_helper'
2
+
3
+ describe GeoNamesAPI::Neighbourhood do
4
+ describe "::find" do
5
+ it "should find NYC" do
6
+ result = GeoNamesAPI::Neighbourhood.find(lat: 40.78343, lng: -73.96625)
7
+ result.hierarchy.should == ["United States", "New York", "New York County", "New York City-Manhattan", "Central Park"]
8
+ end
9
+
10
+ it "should not find streets outside of the US" do
11
+ proc { GeoNamesAPI::Neighbourhood.find(0, 0) }.should raise_error GeoNamesAPI::Error
12
+ end
13
+ end
14
+ end
@@ -28,8 +28,9 @@ describe GeoNamesAPI::PlaceSearch do
28
28
 
29
29
  describe "#next_page" do
30
30
  it "should grab the next page of results from the same search" do
31
- big_search = GeoNamesAPI::PlaceSearch.where(name: 'columbus', maxRows: 9)
32
- search_pg1 = GeoNamesAPI::PlaceSearch.where(name: 'columbus', maxRows: 3)
31
+ # the paging with 'columbus' sometimes doesn't match across the 3 pages.
32
+ big_search = GeoNamesAPI::PlaceSearch.where(name: 'goleta', maxRows: 9)
33
+ search_pg1 = GeoNamesAPI::PlaceSearch.where(name: 'goleta', maxRows: 3)
33
34
  search_pg2 = search_pg1.next_page
34
35
  search_pg3 = search_pg2.next_page
35
36
  search_pg1.size.should == 3
@@ -1,27 +1,25 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe GeoNamesAPI::Base do
4
- TIMEOUT = JSON.load <<-JSON
5
- {
6
- "status": {
7
- "message": "ERROR: canceling statement due to statement timeout",
8
- "value": 13
9
- }
10
- }
11
- JSON
12
-
13
- # This is not a correct, complete response, but that's not relevant to the spec:
14
- EXAMPLE_RESPONSE = JSON.load <<-JSON
15
- {"geonames": [{ "name": "Earth" }]}
16
- JSON
17
-
18
- describe "::find" do
4
+ describe "retries" do
19
5
  before :each do
20
6
  GeoNamesAPI.max_sleep_time_between_retries = 0
7
+ @timeout = JSON.load <<-JSON
8
+ {
9
+ "status": {
10
+ "message": "ERROR: canceling statement due to statement timeout",
11
+ "value": 13
12
+ }
13
+ }
14
+ JSON
15
+ # This is not a correct, complete response, but that's not relevant to the spec:
16
+ @response = JSON.load <<-JSON
17
+ {"geonames": [{ "name": "Earth" }]}
18
+ JSON
21
19
  end
22
20
 
23
21
  it "retries when geonames returns timeout errors" do
24
- GeoNamesAPI::Hierarchy.stub(:make_request).and_return(TIMEOUT, EXAMPLE_RESPONSE)
22
+ GeoNamesAPI::Hierarchy.stub(:make_request).and_return(@timeout, @response)
25
23
  hierarchy = GeoNamesAPI::Hierarchy.find(6295630)
26
24
  earth = hierarchy.first
27
25
  earth.should be_present
@@ -29,7 +27,7 @@ describe GeoNamesAPI::Base do
29
27
  end
30
28
 
31
29
  it "fails when geonames returns timeout errors too many times" do
32
- GeoNamesAPI::Hierarchy.stub(:make_request).and_return(TIMEOUT, TIMEOUT, EXAMPLE_RESPONSE)
30
+ GeoNamesAPI::Hierarchy.stub(:make_request).and_return(@timeout, @timeout, @response)
33
31
  GeoNamesAPI.retries = 1
34
32
  proc { GeoNamesAPI::Hierarchy.find(6295630) }.should raise_error GeoNamesAPI::Timeout
35
33
  end
@@ -12,4 +12,6 @@ RSpec.configure do |config|
12
12
  GeoNamesAPI.username = name if name.present?
13
13
  end
14
14
  GeoNamesAPI.logger = Logger.new("test.log")
15
+ GeoNamesAPI.retries = 10
16
+ GeoNamesAPI.max_sleep_time_between_retries = 60
15
17
  end
metadata CHANGED
@@ -1,7 +1,8 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geonames_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.1.2
5
+ prerelease:
5
6
  platform: ruby
6
7
  authors:
7
8
  - Sean Devine
@@ -9,76 +10,86 @@ authors:
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2013-12-08 00:00:00.000000000 Z
13
+ date: 2014-01-03 00:00:00.000000000 Z
13
14
  dependencies:
14
15
  - !ruby/object:Gem::Dependency
15
16
  name: activesupport
16
17
  requirement: !ruby/object:Gem::Requirement
18
+ none: false
17
19
  requirements:
18
- - - '>='
20
+ - - ! '>='
19
21
  - !ruby/object:Gem::Version
20
22
  version: '0'
21
23
  type: :runtime
22
24
  prerelease: false
23
25
  version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
24
27
  requirements:
25
- - - '>='
28
+ - - ! '>='
26
29
  - !ruby/object:Gem::Version
27
30
  version: '0'
28
31
  - !ruby/object:Gem::Dependency
29
32
  name: tzinfo
30
33
  requirement: !ruby/object:Gem::Requirement
34
+ none: false
31
35
  requirements:
32
- - - '>='
36
+ - - ! '>='
33
37
  - !ruby/object:Gem::Version
34
38
  version: '0'
35
39
  type: :runtime
36
40
  prerelease: false
37
41
  version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
38
43
  requirements:
39
- - - '>='
44
+ - - ! '>='
40
45
  - !ruby/object:Gem::Version
41
46
  version: '0'
42
47
  - !ruby/object:Gem::Dependency
43
- name: zipruby
48
+ name: rubyzip
44
49
  requirement: !ruby/object:Gem::Requirement
50
+ none: false
45
51
  requirements:
46
- - - '>='
52
+ - - ! '>='
47
53
  - !ruby/object:Gem::Version
48
54
  version: '0'
49
55
  type: :runtime
50
56
  prerelease: false
51
57
  version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
52
59
  requirements:
53
- - - '>='
60
+ - - ! '>='
54
61
  - !ruby/object:Gem::Version
55
62
  version: '0'
56
63
  - !ruby/object:Gem::Dependency
57
64
  name: rake
58
65
  requirement: !ruby/object:Gem::Requirement
66
+ none: false
59
67
  requirements:
60
- - - '>='
68
+ - - ! '>='
61
69
  - !ruby/object:Gem::Version
62
70
  version: '0'
63
71
  type: :development
64
72
  prerelease: false
65
73
  version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
66
75
  requirements:
67
- - - '>='
76
+ - - ! '>='
68
77
  - !ruby/object:Gem::Version
69
78
  version: '0'
70
79
  - !ruby/object:Gem::Dependency
71
80
  name: rspec
72
81
  requirement: !ruby/object:Gem::Requirement
82
+ none: false
73
83
  requirements:
74
- - - '>='
84
+ - - ! '>='
75
85
  - !ruby/object:Gem::Version
76
86
  version: '0'
77
87
  type: :development
78
88
  prerelease: false
79
89
  version_requirements: !ruby/object:Gem::Requirement
90
+ none: false
80
91
  requirements:
81
- - - '>='
92
+ - - ! '>='
82
93
  - !ruby/object:Gem::Version
83
94
  version: '0'
84
95
  description: Simple ruby client for the GeoNames API to get free and easy geographic
@@ -114,6 +125,7 @@ files:
114
125
  - lib/geonames_api/hierarchy.rb
115
126
  - lib/geonames_api/list_endpoint.rb
116
127
  - lib/geonames_api/nearby_postal_code.rb
128
+ - lib/geonames_api/neighbourhood.rb
117
129
  - lib/geonames_api/place.rb
118
130
  - lib/geonames_api/place_name.rb
119
131
  - lib/geonames_api/place_search.rb
@@ -125,8 +137,10 @@ files:
125
137
  - lib/geonames_api/weather.rb
126
138
  - lib/geonames_api/weather_i_c_a_o.rb
127
139
  - lib/geonames_api/wikipedia.rb
140
+ - spec/geonames_api/country_spec.rb
128
141
  - spec/geonames_api/country_subdivision_spec.rb
129
142
  - spec/geonames_api/hierarchy_spec.rb
143
+ - spec/geonames_api/neighbourhood_spec.rb
130
144
  - spec/geonames_api/place_name_spec.rb
131
145
  - spec/geonames_api/place_search_spec.rb
132
146
  - spec/geonames_api/place_spec.rb
@@ -136,34 +150,37 @@ files:
136
150
  - spec/spec_helper.rb
137
151
  homepage: https://github.com/buytruckload/geonames_api
138
152
  licenses: []
139
- metadata: {}
140
153
  post_install_message:
141
154
  rdoc_options: []
142
155
  require_paths:
143
156
  - lib
144
157
  required_ruby_version: !ruby/object:Gem::Requirement
158
+ none: false
145
159
  requirements:
146
- - - '>='
160
+ - - ! '>='
147
161
  - !ruby/object:Gem::Version
148
162
  version: '0'
149
163
  required_rubygems_version: !ruby/object:Gem::Requirement
164
+ none: false
150
165
  requirements:
151
- - - '>='
166
+ - - ! '>='
152
167
  - !ruby/object:Gem::Version
153
168
  version: '0'
154
169
  requirements: []
155
170
  rubyforge_project:
156
- rubygems_version: 2.0.3
171
+ rubygems_version: 1.8.23
157
172
  signing_key:
158
- specification_version: 4
173
+ specification_version: 3
159
174
  summary: This is a lightweight client for the GeoNames API. Huge thanks to them for
160
175
  such a great service! There are many GeoNames API clients. BUT, most are rewritten
161
176
  versions of a Java API whose interface is a little funny =| This is a simplified
162
177
  ruby implementation that does not implement the entire API. But, it's lightweight
163
178
  and has a nice interface and will be easy to extend :)
164
179
  test_files:
180
+ - spec/geonames_api/country_spec.rb
165
181
  - spec/geonames_api/country_subdivision_spec.rb
166
182
  - spec/geonames_api/hierarchy_spec.rb
183
+ - spec/geonames_api/neighbourhood_spec.rb
167
184
  - spec/geonames_api/place_name_spec.rb
168
185
  - spec/geonames_api/place_search_spec.rb
169
186
  - spec/geonames_api/place_spec.rb
@@ -171,3 +188,4 @@ test_files:
171
188
  - spec/geonames_api/street_spec.rb
172
189
  - spec/geonames_api/weather_icao_spec.rb
173
190
  - spec/spec_helper.rb
191
+ has_rdoc:
checksums.yaml DELETED
@@ -1,7 +0,0 @@
1
- ---
2
- SHA1:
3
- metadata.gz: feb56410dfb4499eaac158048e1950c760ea4215
4
- data.tar.gz: b10b25b67f681f847c7fe42ad263009ee366b25c
5
- SHA512:
6
- metadata.gz: f92de847f4285eb1ae61a61c03e9429b39ab3ea423291ab669e30c16a59656a1d9ae0ae1570ffc274ed5db930efb00dbd28bc5268adf55d9fea562c70665540d
7
- data.tar.gz: 40439223d27f035a99fbe37f02489f47c4c449a4e373d51086bcbb5575522de39bfc0c50ca772906208b6e1e3c6ae57efd8d148cf9a41dd746a3752931816516