google_maps_geocoder 0.4.0.pre1 → 0.5.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.
- checksums.yaml +4 -4
- data/README.md +17 -16
- data/google_maps_geocoder.gemspec +2 -1
- data/lib/google_maps_geocoder/google_maps_geocoder.rb +77 -27
- data/lib/google_maps_geocoder/version.rb +1 -1
- data/spec/lib/google_maps_geocoder_spec.rb +15 -1
- data/spec/spec_helper.rb +6 -0
- metadata +19 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 060caf858407bc718955028bc0edc35a518b2f63
|
4
|
+
data.tar.gz: 616a78c20c6ff0926380a413b51094fe8b824bf1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6aebec357d9d33fbba4586cc7c09cceebcd3d159ecc7cd648b1548e6f80810838208f486fba1c526b81e73c40d30eb7e9cffed58912a8d1c1462aa6977d961a2
|
7
|
+
data.tar.gz: a624fd613c2c84ed5641186c1549135760d545976c4f83e12fce3c05322bdc24e7be9929e3f96020bc19f42608992af2de6d5035e23e070f6e9102bba9705017
|
data/README.md
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
# GoogleMapsGeocoder
|
2
2
|
|
3
|
-
[](http://travis-ci.org/ivanoblomov/google_maps_geocoder)
|
4
4
|
[](https://codeclimate.com/github/ivanoblomov/google_maps_geocoder)
|
5
5
|
[](https://coveralls.io/github/ivanoblomov/google_maps_geocoder?branch=master)
|
6
|
-
[](http://travis-ci.org/ivanoblomov/google_maps_geocoder)
|
7
6
|
[](https://gemnasium.com/ivanoblomov/google_maps_geocoder)
|
7
|
+
[](https://inch-ci.org/github/Ivanoblomov/google_maps_geocoder)
|
8
|
+
[](http://badge.fury.io/rb/google_maps_geocoder)
|
8
9
|
|
9
|
-
A simple
|
10
|
+
A simple Plain Old Ruby Object wrapper for geocoding with Google Maps.
|
10
11
|
|
11
12
|
## Installation
|
12
13
|
|
@@ -62,19 +63,19 @@ chez_barack.state_short_name
|
|
62
63
|
|
63
64
|
The complete, hopefully self-explanatory, API is:
|
64
65
|
|
65
|
-
* city
|
66
|
-
* country_long_name
|
67
|
-
* country_short_name
|
68
|
-
* county
|
69
|
-
* exact_match
|
70
|
-
* formatted_address
|
71
|
-
* formatted_street_address
|
72
|
-
* lat
|
73
|
-
* lng
|
74
|
-
* partial_match
|
75
|
-
* postal_code
|
76
|
-
* state_long_name
|
77
|
-
* state_short_name
|
66
|
+
* `GoogleMapsGeocoder#city`
|
67
|
+
* `GoogleMapsGeocoder#country_long_name`
|
68
|
+
* `GoogleMapsGeocoder#country_short_name`
|
69
|
+
* `GoogleMapsGeocoder#county`
|
70
|
+
* `GoogleMapsGeocoder#exact_match?`
|
71
|
+
* `GoogleMapsGeocoder#formatted_address`
|
72
|
+
* `GoogleMapsGeocoder#formatted_street_address`
|
73
|
+
* `GoogleMapsGeocoder#lat`
|
74
|
+
* `GoogleMapsGeocoder#lng`
|
75
|
+
* `GoogleMapsGeocoder#partial_match?`
|
76
|
+
* `GoogleMapsGeocoder#postal_code`
|
77
|
+
* `GoogleMapsGeocoder#state_long_name`
|
78
|
+
* `GoogleMapsGeocoder#state_short_name`
|
78
79
|
|
79
80
|
## Google Maps API Key (Optional)
|
80
81
|
|
@@ -9,6 +9,7 @@ Gem::Specification.new do |s|
|
|
9
9
|
s.homepage = 'http://github.com/ivanoblomov/google_maps_geocoder'
|
10
10
|
s.authors = ['Roderick Monje']
|
11
11
|
|
12
|
+
s.add_development_dependency 'codeclimate-test-reporter', '>= 0'
|
12
13
|
s.add_development_dependency 'coveralls', '>= 0'
|
13
14
|
s.add_development_dependency 'rake', '>= 0'
|
14
15
|
s.add_development_dependency 'rspec', '>= 0'
|
@@ -20,6 +21,6 @@ Gem::Specification.new do |s|
|
|
20
21
|
s.files = `git ls-files`.split "\n"
|
21
22
|
s.test_files = `git ls-files -- {test,spec,features}/*`.split "\n"
|
22
23
|
s.executables = `git ls-files -- bin/*`.split("\n")
|
23
|
-
|
24
|
+
.map { |f| File.basename f }
|
24
25
|
s.require_paths = ['lib']
|
25
26
|
end
|
@@ -1,10 +1,43 @@
|
|
1
1
|
require 'active_support'
|
2
|
+
require 'active_support/core_ext/string/inflections'
|
2
3
|
require 'logger'
|
3
4
|
require 'net/http'
|
4
5
|
require 'rack'
|
5
6
|
|
6
7
|
# A simple PORO wrapper for geocoding with Google Maps.
|
8
|
+
#
|
9
|
+
# @example
|
10
|
+
# chez_barack = GoogleMapsGeocoder.new '1600 Pennsylvania Ave'
|
11
|
+
# chez_barack.formatted_address
|
12
|
+
# => "1600 Pennsylvania Avenue Northwest, President's Park,
|
13
|
+
# Washington, DC 20500, USA"
|
14
|
+
# rubocop:disable Metrics/ClassLength
|
7
15
|
class GoogleMapsGeocoder
|
16
|
+
# Error handling for google statuses
|
17
|
+
class GeocodingError < StandardError
|
18
|
+
def initialize(response_json = '')
|
19
|
+
@json = response_json
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
def message
|
24
|
+
"Google returned:\n#{@json.inspect}"
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class ZeroResultsError < GeocodingError; end
|
29
|
+
class QueryLimitError < GeocodingError; end
|
30
|
+
class RequestDeniedError < GeocodingError; end
|
31
|
+
class InvalidRequestError < GeocodingError; end
|
32
|
+
class UnknownError < GeocodingError; end
|
33
|
+
|
34
|
+
ERROR_STATUSES = { zero_results: 'ZERO_RESULTS',
|
35
|
+
query_limit: 'OVER_QUERY_LIMIT',
|
36
|
+
request_denied: 'REQUEST_DENIED',
|
37
|
+
invalid_request: 'INVALID_REQUEST',
|
38
|
+
unknown: 'UNKNOWN_ERROR'
|
39
|
+
}.freeze
|
40
|
+
|
8
41
|
GOOGLE_ADDRESS_SEGMENTS = %i(
|
9
42
|
city country_long_name country_short_name county lat lng postal_code
|
10
43
|
state_long_name state_short_name
|
@@ -18,60 +51,65 @@ class GoogleMapsGeocoder
|
|
18
51
|
).freeze
|
19
52
|
|
20
53
|
# Returns the complete formatted address with standardized abbreviations.
|
54
|
+
#
|
55
|
+
# @return [String] the complete formatted address
|
56
|
+
# @example
|
57
|
+
# chez_barack.formatted_address
|
58
|
+
# => "1600 Pennsylvania Avenue Northwest, President's Park,
|
59
|
+
# Washington, DC 20500, USA"
|
21
60
|
attr_reader :formatted_address
|
61
|
+
|
22
62
|
# Returns the formatted street address with standardized abbreviations.
|
63
|
+
#
|
64
|
+
# @return [String] the formatted street address
|
65
|
+
# @example
|
66
|
+
# chez_barack.formatted_street_address
|
67
|
+
# => "1600 Pennsylvania Avenue"
|
23
68
|
attr_reader :formatted_street_address
|
24
69
|
# Self-explanatory
|
25
70
|
attr_reader(*GOOGLE_ADDRESS_SEGMENTS)
|
26
71
|
|
27
|
-
#
|
28
|
-
|
29
|
-
# Geocodes the specified address and wraps the results in a geocoder object.
|
30
|
-
#
|
31
|
-
# ==== Attributes
|
32
|
-
#
|
33
|
-
# * +data+ - a geocodable address
|
72
|
+
# Geocodes the specified address and wraps the results in a GoogleMapsGeocoder
|
73
|
+
# object.
|
34
74
|
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
#
|
38
|
-
#
|
39
|
-
#
|
40
|
-
# Washington, DC 20500, USA"
|
75
|
+
# @param data [String] a geocodable address
|
76
|
+
# @return [GoogleMapsGeocoder] the Google Maps result for the specified
|
77
|
+
# address
|
78
|
+
# @example
|
79
|
+
# chez_barack = GoogleMapsGeocoder.new '1600 Pennsylvania Ave'
|
41
80
|
def initialize(data)
|
42
81
|
@json = data.is_a?(String) ? json_from_url(data) : data
|
43
|
-
|
44
|
-
"#{@json.inspect}" if @json.blank? || @json['status'] != 'OK'
|
82
|
+
handle_error if @json.blank? || @json['status'] != 'OK'
|
45
83
|
set_attributes_from_json
|
46
84
|
logger.info('GoogleMapsGeocoder') do
|
47
85
|
"Geocoded \"#{data}\" => \"#{formatted_address}\""
|
48
86
|
end
|
49
87
|
end
|
50
88
|
|
51
|
-
# Instance Methods ===========================================================
|
52
|
-
|
53
89
|
# Returns true if the address Google returns is an exact match.
|
54
90
|
#
|
55
|
-
#
|
56
|
-
#
|
57
|
-
#
|
58
|
-
# white_house.exact_match?
|
91
|
+
# @return [boolean] whether the Google Maps result is an exact match
|
92
|
+
# @example
|
93
|
+
# chez_barack.exact_match?
|
59
94
|
# => true
|
60
95
|
def exact_match?
|
61
|
-
!
|
96
|
+
!partial_match?
|
62
97
|
end
|
63
98
|
|
64
99
|
# Returns true if the address Google returns isn't an exact match.
|
65
100
|
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
# => false
|
101
|
+
# @return [boolean] whether the Google Maps result is a partial match
|
102
|
+
# @example
|
103
|
+
# GoogleMapsGeocoder.new('1600 Pennsylvania Washington').partial_match?
|
104
|
+
# => true
|
71
105
|
def partial_match?
|
72
106
|
@json['results'][0]['partial_match'] == true
|
73
107
|
end
|
74
108
|
|
109
|
+
def self.error_class_name(key)
|
110
|
+
"google_maps_geocoder/#{key}_error".classify.constantize
|
111
|
+
end
|
112
|
+
|
75
113
|
private
|
76
114
|
|
77
115
|
def api_key
|
@@ -88,11 +126,23 @@ class GoogleMapsGeocoder
|
|
88
126
|
|
89
127
|
def json_from_url(url)
|
90
128
|
uri = URI.parse query_url(url)
|
129
|
+
|
91
130
|
logger.debug('GoogleMapsGeocoder') { uri }
|
131
|
+
|
92
132
|
response = http(uri).request(Net::HTTP::Get.new(uri.request_uri))
|
93
133
|
ActiveSupport::JSON.decode response.body
|
94
134
|
end
|
95
135
|
|
136
|
+
def handle_error
|
137
|
+
status = @json['status']
|
138
|
+
message = GeocodingError.new(@json).message
|
139
|
+
|
140
|
+
# for status codes see https://developers.google.com/maps/documentation/geocoding/intro#StatusCodes
|
141
|
+
ERROR_STATUSES.each do |key, value|
|
142
|
+
raise GoogleMapsGeocoder.error_class_name(key), message if status == value
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
96
146
|
def logger
|
97
147
|
@logger ||= Logger.new STDERR
|
98
148
|
end
|
@@ -75,10 +75,24 @@ describe GoogleMapsGeocoder do
|
|
75
75
|
subject { @exact_match }
|
76
76
|
|
77
77
|
it do
|
78
|
-
expect(subject.send
|
78
|
+
expect(subject.send(:query_url, nil)).to eq(
|
79
79
|
'https://maps.googleapis.com/maps/api/geocode/json?address='\
|
80
80
|
'&sensor=false&key=INVALID_KEY'
|
81
81
|
)
|
82
82
|
end
|
83
83
|
end
|
84
|
+
|
85
|
+
context 'with google returns empty results' do
|
86
|
+
let(:results_hash) { { 'results' => [] } }
|
87
|
+
|
88
|
+
GoogleMapsGeocoder::ERROR_STATUSES.each do |key, value|
|
89
|
+
it "raises #{key} error" do
|
90
|
+
allow_any_instance_of(GoogleMapsGeocoder).to receive(:json_from_url)\
|
91
|
+
.and_return results_hash.merge('status' => value)
|
92
|
+
|
93
|
+
expect { GoogleMapsGeocoder.new('anything') }.to \
|
94
|
+
raise_error(GoogleMapsGeocoder.error_class_name(key))
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
84
98
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,5 +1,11 @@
|
|
1
|
+
require 'codeclimate-test-reporter'
|
2
|
+
CodeClimate::TestReporter.start
|
1
3
|
require 'coveralls'
|
2
4
|
Coveralls.wear!
|
5
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
6
|
+
CodeClimate::TestReporter::Formatter,
|
7
|
+
Coveralls::SimpleCov::Formatter
|
8
|
+
]
|
3
9
|
require 'rubygems'
|
4
10
|
require 'bundler'
|
5
11
|
begin
|
metadata
CHANGED
@@ -1,15 +1,29 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: google_maps_geocoder
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roderick Monje
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2016-04-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: codeclimate-test-reporter
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ">="
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
13
27
|
- !ruby/object:Gem::Dependency
|
14
28
|
name: coveralls
|
15
29
|
requirement: !ruby/object:Gem::Requirement
|
@@ -128,12 +142,12 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
128
142
|
version: '0'
|
129
143
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
130
144
|
requirements:
|
131
|
-
- - "
|
145
|
+
- - ">="
|
132
146
|
- !ruby/object:Gem::Version
|
133
|
-
version:
|
147
|
+
version: '0'
|
134
148
|
requirements: []
|
135
149
|
rubyforge_project:
|
136
|
-
rubygems_version: 2.4.
|
150
|
+
rubygems_version: 2.4.5
|
137
151
|
signing_key:
|
138
152
|
specification_version: 4
|
139
153
|
summary: A simple PORO wrapper for geocoding with Google Maps.
|