google-maps 2.2.0 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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/) ? "nul" : "/dev/null"
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
- require File.expand_path('../api', __FILE__)
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 :to_s :text
9
- alias :to_html :html
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
- API.query(:places_service, args).predictions.map{|prediction| Place.new(prediction, keyword) }
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 :to_s :address
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=:en)
67
- args = {:language => language, :placeid => place_id}
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(method, *args, &block)
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 == method.to_s
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,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'hashie/mash'
4
+
5
+ module Google
6
+ module Maps
7
+ class Result < Hashie::Mash
8
+ disable_warnings
9
+ end
10
+ end
11
+ 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,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Google
4
+ module Maps
5
+ VERSION = '3.0.0'.freeze
6
+ end
7
+ 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