geonames_api 0.1.1 → 0.1.2

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.
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