google-maps 2.2.0 → 3.0.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/.rubocop.yml +20 -0
- data/.travis.yml +6 -1
- data/Gemfile +3 -1
- data/{LICENSE.mkd → LICENSE.md} +0 -0
- data/README.md +216 -0
- data/Rakefile +5 -3
- data/google-maps.gemspec +24 -21
- data/lib/google_maps.rb +48 -0
- data/lib/google_maps/api.rb +104 -0
- data/lib/google_maps/configuration.rb +90 -0
- data/lib/google_maps/location.rb +34 -0
- data/lib/{google-maps → google_maps}/logger.rb +5 -4
- data/lib/{google-maps → google_maps}/place.rb +21 -18
- data/lib/google_maps/result.rb +11 -0
- data/lib/google_maps/route.rb +45 -0
- data/lib/google_maps/version.rb +7 -0
- data/spec/google_maps/api_spec.rb +82 -0
- data/spec/google_maps/logger_spec.rb +17 -0
- data/spec/google_maps/place_details_spec.rb +40 -0
- data/spec/google_maps/place_spec.rb +47 -0
- data/spec/google_maps/route_spec.rb +53 -0
- data/spec/google_maps_spec.rb +174 -0
- data/spec/spec_helper.rb +5 -6
- metadata +97 -57
- data/README.mkd +0 -39
- data/lib/google-maps.rb +0 -44
- data/lib/google-maps/api.rb +0 -89
- data/lib/google-maps/configuration.rb +0 -70
- data/lib/google-maps/location.rb +0 -25
- data/lib/google-maps/route.rb +0 -38
- data/lib/google-maps/version.rb +0 -5
- data/spec/google-maps/api_spec.rb +0 -103
- data/spec/google-maps/logger_spec.rb +0 -15
- data/spec/google-maps/place_details_spec.rb +0 -32
- data/spec/google-maps/place_spec.rb +0 -45
- data/spec/google-maps/route_spec.rb +0 -52
- data/spec/google-maps_spec.rb +0 -103
@@ -0,0 +1,90 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Google
|
4
|
+
module Maps
|
5
|
+
class InvalidConfigurationError < StandardError; end
|
6
|
+
# Defines constants and methods related to configuration
|
7
|
+
module Configuration
|
8
|
+
# An array of valid keys in the options hash when configuring an {Google::Maps::API}
|
9
|
+
VALID_OPTIONS_KEYS = %i[
|
10
|
+
end_point authentication_mode client_id client_secret format
|
11
|
+
directions_service places_service geocode_service
|
12
|
+
api_key default_language place_details_service default_params
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
API_KEY = 'api_key'.freeze
|
16
|
+
DIGITAL_SIGNATURE = 'digital_signature'.freeze
|
17
|
+
|
18
|
+
# By default, set "https://maps.googleapis.com/maps/api/" as the server
|
19
|
+
DEFAULT_END_POINT = 'https://maps.googleapis.com/maps/api/'.freeze
|
20
|
+
|
21
|
+
DEFAULT_DIRECTIONS_SERVICE = 'directions'.freeze
|
22
|
+
DEFAULT_PLACES_SERVICE = 'place/autocomplete'.freeze
|
23
|
+
DEFAULT_PLACE_DETAILS_SERVICE = 'place/details'.freeze
|
24
|
+
DEFAULT_GEOCODE_SERVICE = 'geocode'.freeze
|
25
|
+
|
26
|
+
DEFAULT_FORMAT = 'json'.freeze
|
27
|
+
|
28
|
+
# default language
|
29
|
+
DEFAULT_LANGUAGE = :en
|
30
|
+
|
31
|
+
# params to send which each request configured per service
|
32
|
+
# ie.: {places_service: {location: "52.0910,5.1220", radius: 300000}}
|
33
|
+
DEFAULT_PARAMS = {}.freeze
|
34
|
+
|
35
|
+
# @private
|
36
|
+
attr_accessor(*VALID_OPTIONS_KEYS)
|
37
|
+
|
38
|
+
# When this module is extended, set all configuration options to their default values
|
39
|
+
def self.extended(base)
|
40
|
+
base.reset
|
41
|
+
end
|
42
|
+
|
43
|
+
# Convenience method to allow configuration options to be set in a block
|
44
|
+
def configure
|
45
|
+
yield self
|
46
|
+
validate_config
|
47
|
+
end
|
48
|
+
|
49
|
+
def validate_config
|
50
|
+
return validate_api_key if authentication_mode == API_KEY
|
51
|
+
return validate_digital_signature if authentication_mode == DIGITAL_SIGNATURE
|
52
|
+
|
53
|
+
raise Google::Maps::InvalidConfigurationError, 'No valid authentication mode provided'
|
54
|
+
end
|
55
|
+
|
56
|
+
def validate_api_key
|
57
|
+
raise Google::Maps::InvalidConfigurationError, 'No API key provided' unless api_key.present?
|
58
|
+
end
|
59
|
+
|
60
|
+
def validate_digital_signature
|
61
|
+
raise Google::Maps::InvalidConfigurationError, 'No client id provided' unless client_id.present?
|
62
|
+
raise Google::Maps::InvalidConfigurationError, 'No client secret provided' unless client_secret.present?
|
63
|
+
end
|
64
|
+
|
65
|
+
# Create a hash of options and their values
|
66
|
+
def options
|
67
|
+
VALID_OPTIONS_KEYS.inject({}) do |option, key|
|
68
|
+
option.merge!(key => send(key))
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Reset all configuration options to defaults
|
73
|
+
def reset
|
74
|
+
self.end_point = DEFAULT_END_POINT
|
75
|
+
self.format = DEFAULT_FORMAT
|
76
|
+
self.directions_service = DEFAULT_DIRECTIONS_SERVICE
|
77
|
+
self.places_service = DEFAULT_PLACES_SERVICE
|
78
|
+
self.place_details_service = DEFAULT_PLACE_DETAILS_SERVICE
|
79
|
+
self.geocode_service = DEFAULT_GEOCODE_SERVICE
|
80
|
+
self.default_language = DEFAULT_LANGUAGE
|
81
|
+
self.default_params = DEFAULT_PARAMS
|
82
|
+
self.authentication_mode = nil
|
83
|
+
self.api_key = nil
|
84
|
+
self.client_id = nil
|
85
|
+
self.client_secret = nil
|
86
|
+
self
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('api', __dir__)
|
4
|
+
|
5
|
+
module Google
|
6
|
+
module Maps
|
7
|
+
class Location
|
8
|
+
attr_reader :address, :latitude, :longitude
|
9
|
+
alias to_s address
|
10
|
+
|
11
|
+
def initialize(address, latitude, longitude)
|
12
|
+
@address = address
|
13
|
+
@latitude = latitude
|
14
|
+
@longitude = longitude
|
15
|
+
end
|
16
|
+
|
17
|
+
def lat_lng
|
18
|
+
[latitude, longitude]
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.find(address, language = :en)
|
22
|
+
args = { language: language, address: address }
|
23
|
+
|
24
|
+
API.query(:geocode_service, args).results.map do |result|
|
25
|
+
Location.new(
|
26
|
+
result.formatted_address,
|
27
|
+
result.geometry.location.lat,
|
28
|
+
result.geometry.location.lng
|
29
|
+
)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,17 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'logger'
|
2
4
|
|
3
5
|
module Google
|
4
6
|
module Maps
|
5
7
|
module Logger
|
6
|
-
|
7
8
|
attr_accessor :logger
|
8
|
-
|
9
|
+
|
9
10
|
def log_file=(file)
|
10
11
|
self.logger = ::Logger.new(file)
|
11
12
|
end
|
12
|
-
|
13
|
+
|
13
14
|
def self.extended(base)
|
14
|
-
base.log_file = RUBY_PLATFORM.index(/mswin(?!ce)|mingw|cygwin|bccwin/) ?
|
15
|
+
base.log_file = RUBY_PLATFORM.index(/mswin(?!ce)|mingw|cygwin|bccwin/) ? 'nul' : '/dev/null'
|
15
16
|
end
|
16
17
|
end
|
17
18
|
end
|
@@ -1,24 +1,23 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('api', __dir__)
|
2
4
|
|
3
5
|
module Google
|
4
6
|
module Maps
|
5
|
-
|
6
7
|
class Place
|
7
8
|
attr_reader :text, :html, :keyword, :place_id
|
8
|
-
alias
|
9
|
-
alias
|
10
|
-
|
9
|
+
alias to_s text
|
10
|
+
alias to_html html
|
11
|
+
|
11
12
|
def initialize(data, keyword)
|
12
13
|
@text = data.description
|
13
14
|
@place_id = data.place_id
|
14
15
|
@html = highligh_keywords(data, keyword)
|
15
16
|
end
|
16
|
-
|
17
|
-
def self.find(keyword, language=:en)
|
18
|
-
args = {:language => language, :input => keyword }
|
19
|
-
args.merge!(key: Google::Maps.api_key) unless Google::Maps.api_key.nil?
|
20
17
|
|
21
|
-
|
18
|
+
def self.find(keyword, language = :en)
|
19
|
+
args = { language: language, input: keyword }
|
20
|
+
API.query(:places_service, args).predictions.map { |prediction| Place.new(prediction, keyword) }
|
22
21
|
end
|
23
22
|
|
24
23
|
private
|
@@ -57,16 +56,14 @@ module Google
|
|
57
56
|
def address
|
58
57
|
@data.formatted_address
|
59
58
|
end
|
60
|
-
alias
|
59
|
+
alias to_s address
|
61
60
|
|
62
61
|
def address_components
|
63
62
|
AddressComponentsProxy.new(@data.address_components)
|
64
63
|
end
|
65
64
|
|
66
|
-
def self.find(place_id, language
|
67
|
-
args = {:
|
68
|
-
args.merge!(key: Google::Maps.api_key) unless Google::Maps.api_key.nil?
|
69
|
-
|
65
|
+
def self.find(place_id, language = :en)
|
66
|
+
args = { language: language, placeid: place_id }
|
70
67
|
PlaceDetails.new(API.query(:place_details_service, args).result)
|
71
68
|
end
|
72
69
|
|
@@ -75,12 +72,18 @@ module Google
|
|
75
72
|
@address_components = address_components
|
76
73
|
end
|
77
74
|
|
78
|
-
def method_missing(
|
75
|
+
def method_missing(method_name, *args)
|
79
76
|
raise ArgumentError unless args.empty?
|
80
77
|
|
81
78
|
@address_components.find do |component|
|
82
|
-
component.types.first ==
|
83
|
-
end
|
79
|
+
component.types.first == method_name.to_s
|
80
|
+
end || super
|
81
|
+
end
|
82
|
+
|
83
|
+
def respond_to_missing?(method_name, include_private = false)
|
84
|
+
@address_components.any? do |component|
|
85
|
+
component.types.first == method_name.to_s
|
86
|
+
end || super
|
84
87
|
end
|
85
88
|
end
|
86
89
|
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('api', __dir__)
|
4
|
+
|
5
|
+
module Google
|
6
|
+
module Maps
|
7
|
+
class Route
|
8
|
+
attr_accessor :from, :to, :options
|
9
|
+
|
10
|
+
def initialize(from, to, options = {})
|
11
|
+
options = { language: options } unless options.is_a? Hash
|
12
|
+
@from = from
|
13
|
+
@to = to
|
14
|
+
@options = { language: :en }.merge(options)
|
15
|
+
end
|
16
|
+
|
17
|
+
def method_missing(method_name, *args, &block)
|
18
|
+
if route.legs.first.key?(method_name)
|
19
|
+
route.legs.first.send(method_name)
|
20
|
+
else
|
21
|
+
super
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def respond_to_missing?(method_name, include_private = false)
|
26
|
+
route.legs.first.key?(method_name) || super
|
27
|
+
end
|
28
|
+
|
29
|
+
def origin_latlong
|
30
|
+
"#{start_location.lat},#{start_location.lng}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def destination_latlong
|
34
|
+
"#{end_location.lat},#{end_location.lng}"
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def route
|
40
|
+
# default to the first returned route (the most efficient one)
|
41
|
+
@route ||= API.query(:directions_service, @options.merge(origin: from, destination: to)).routes.first
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('../spec_helper', __dir__)
|
4
|
+
|
5
|
+
describe Google::Maps::API do
|
6
|
+
it 'should raise a custom exception when the query fails by net' do
|
7
|
+
HTTPClient.any_instance.unstub(:get_content)
|
8
|
+
|
9
|
+
Google::Maps.end_point = 'http://unknown.tld/'
|
10
|
+
expect { Google::Maps.distance('Amsterdam', 'Deventer') }.to raise_error(Google::Maps::InvalidResponseException)
|
11
|
+
Google::Maps.end_point = 'http://unknown-domain-asdasdasdas123123zxcasd.com/'
|
12
|
+
expect { Google::Maps.distance('Amsterdam', 'Deventer') }.to raise_error(Google::Maps::InvalidResponseException)
|
13
|
+
Google::Maps.end_point = 'http://www.google.com/404'
|
14
|
+
expect { Google::Maps.distance('Amsterdam', 'Deventer') }.to raise_error(Google::Maps::InvalidResponseException)
|
15
|
+
end
|
16
|
+
|
17
|
+
it 'should raise a custom exception when the query fails by Google' do
|
18
|
+
stub_response('over_query_limit.json')
|
19
|
+
expect { Google::Maps.distance('Amsterdam', 'Deventer') }.to raise_error(Google::Maps::InvalidResponseException)
|
20
|
+
end
|
21
|
+
|
22
|
+
it 'should raise a custom exception when there are no results' do
|
23
|
+
stub_response('zero-results.json')
|
24
|
+
expect { Google::Maps.distance('Blah blah', 'Jalala') }.to raise_error(Google::Maps::ZeroResultsException)
|
25
|
+
end
|
26
|
+
|
27
|
+
it 'should raise a custom exception that is rescue-able' do
|
28
|
+
stub_response('zero-results.json')
|
29
|
+
begin
|
30
|
+
Google::Maps.distance('Blah blah', 'Jalala')
|
31
|
+
rescue StandardError => error
|
32
|
+
@error = error
|
33
|
+
ensure
|
34
|
+
expect(@error).not_to be_nil
|
35
|
+
expect(@error).to be_a_kind_of StandardError
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe 'authentication' do
|
40
|
+
context 'with digital signature' do
|
41
|
+
before do
|
42
|
+
Google::Maps.configure do |config|
|
43
|
+
config.authentication_mode = Google::Maps::Configuration::DIGITAL_SIGNATURE
|
44
|
+
config.client_id = 'clientID'
|
45
|
+
config.client_secret = 'vNIXE0xscrmjlyV-12Nj_BvUPaw='
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should sign the url parameters when a client id and premier key is set' do
|
50
|
+
stub_response(
|
51
|
+
'place_details.json',
|
52
|
+
'https://maps.googleapis.com/maps/api/geocode/json?address=New+York&client=clientID&signature=chaRF2hTJKOScPr-RQCEhZbSzIE='
|
53
|
+
)
|
54
|
+
# http://code.google.com/apis/maps/documentation/webservices/index.html#URLSigning
|
55
|
+
|
56
|
+
# Example:
|
57
|
+
# Private Key: vNIXE0xscrmjlyV-12Nj_BvUPaw=
|
58
|
+
# Signature: chaRF2hTJKOScPr-RQCEhZbSzIE=
|
59
|
+
# Client ID: clientID
|
60
|
+
# URL: http://maps.googleapis.com/maps/api/geocode/json?address=New+York&client=clientID
|
61
|
+
Google::Maps::API.query(:geocode_service, address: 'New York')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
context 'with api key' do
|
66
|
+
before do
|
67
|
+
Google::Maps.configure do |config|
|
68
|
+
config.authentication_mode = Google::Maps::Configuration::API_KEY
|
69
|
+
config.api_key = 'api_key123'
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
it 'should sign the url parameters when a client id and premier key is set' do
|
74
|
+
stub_response(
|
75
|
+
'place_details.json',
|
76
|
+
'https://maps.googleapis.com/maps/api/geocode/json?address=New+York&api_key=api_key123'
|
77
|
+
)
|
78
|
+
Google::Maps::API.query(:geocode_service, address: 'New York')
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('../spec_helper', __dir__)
|
4
|
+
require 'logger'
|
5
|
+
|
6
|
+
describe Google::Maps::Logger do
|
7
|
+
it 'should be able to log messages when a log output is set' do
|
8
|
+
# fake an exception
|
9
|
+
HTTPClient.any_instance.expects(:get_content).raises('test exception')
|
10
|
+
|
11
|
+
# expect the logger to be called once
|
12
|
+
Logger.any_instance.expects(:error).at_least_once
|
13
|
+
|
14
|
+
# trigger the exception
|
15
|
+
expect { Google::Maps.distance('Amsterdam', 'Deventer') }.to raise_error(Google::Maps::InvalidResponseException)
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('../spec_helper', __dir__)
|
4
|
+
|
5
|
+
describe Google::Maps::PlaceDetails do
|
6
|
+
let(:place_id) do
|
7
|
+
'CpQBiAAAAGs4XDizjQoVk9NjuY3ll3aLBLafpDxaFPSJSO7icOj07' \
|
8
|
+
'IRHO4KjjcRIbKEmeSVTcG75kIvwqE7VzA8D7BFvWp8OPwgAiKMveQ' \
|
9
|
+
'QUsTGfJrRG5EVd7J34hY8e5JDbaXEPOMUPIWLfiugwUfQqAImvWQC' \
|
10
|
+
'GrMG1iyOpZfaW22NNhornssEg90uxrLbwLJ7QZhwGIRIQSBc_BlD7' \
|
11
|
+
'mILqQaixzTqE1BoUbNrhbmsZYkIurvK4l9exKBryfKk'
|
12
|
+
end
|
13
|
+
|
14
|
+
context 'given a canned response' do
|
15
|
+
before(:each) do
|
16
|
+
stub_response('place_details.json')
|
17
|
+
@details = Google::Maps::PlaceDetails.find(place_id, :nl)
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'should have a place_id' do
|
21
|
+
expect(@details.place_id).to eq(place_id)
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'should have a latlong' do
|
25
|
+
expect(@details.latitude).to eq('-33.866975')
|
26
|
+
expect(@details.longitude).to eq('151.195677')
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'has data containing at least address components' do
|
30
|
+
expect(@details.data.address_components).not_to be_empty
|
31
|
+
end
|
32
|
+
|
33
|
+
context '#address_components' do
|
34
|
+
it 'allows easy access by type' do
|
35
|
+
expect(@details.address_components.postal_code.long_name).to eq '2009'
|
36
|
+
expect(@details.address_components.locality.long_name).to eq 'Pyrmont'
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require File.expand_path('../spec_helper', __dir__)
|
4
|
+
|
5
|
+
describe Google::Maps::Place do
|
6
|
+
describe '.find' do
|
7
|
+
subject { Google::Maps::Place.find(keyword, country).first }
|
8
|
+
|
9
|
+
context ':nl' do
|
10
|
+
before { stub_response('deventer-nl.json') }
|
11
|
+
|
12
|
+
let(:keyword) { 'Deventer' }
|
13
|
+
let(:country) { :nl }
|
14
|
+
|
15
|
+
its(:text) { should eq 'Deventer, Nederland' }
|
16
|
+
its(:html) { should eq '<strong>Deventer</strong>, Nederland' }
|
17
|
+
|
18
|
+
context 'keyword with escapeable characters' do
|
19
|
+
let(:keyword) { 'Deventer \\' }
|
20
|
+
let(:country) { :nl }
|
21
|
+
|
22
|
+
its(:text) { should eq 'Deventer, Nederland' }
|
23
|
+
its(:html) { should eq '<strong>Deventer</strong>, Nederland' }
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context ':en' do
|
28
|
+
before { stub_response('deventer-en.json') }
|
29
|
+
|
30
|
+
let(:keyword) { 'Deventer' }
|
31
|
+
let(:country) { :en }
|
32
|
+
|
33
|
+
its(:text) { should eq 'Deventer, The Netherlands' }
|
34
|
+
its(:html) { should eq '<strong>Deventer</strong>, The Netherlands' }
|
35
|
+
end
|
36
|
+
|
37
|
+
context 'only highlights words' do
|
38
|
+
before { stub_response 'den-haag-nl.json' }
|
39
|
+
|
40
|
+
let(:keyword) { 'Den . * { } Haag \\' }
|
41
|
+
let(:country) { :nl }
|
42
|
+
|
43
|
+
its(:text) { should eq 'Den Haag, Nederland' }
|
44
|
+
its(:html) { should eq '<strong>Den</strong> <strong>Haag</strong>, Nederland' }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|