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