Skip to content

Commit b547b44

Browse files
committed
Merge branch 'release/v.0.2.0'
2 parents 06a1881 + 063785a commit b547b44

21 files changed

Lines changed: 452 additions & 173 deletions

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
[![Gem Version](https://badge.fury.io/rb/google_maps_service.svg)](http://badge.fury.io/rb/google_maps_service) [![Build Status](https://travis-ci.org/edwardsamuel/google-maps-services-ruby.svg?branch=master)](https://travis-ci.org/edwardsamuel/google-maps-services-ruby) [![Dependency Status](https://gemnasium.com/edwardsamuel/google-maps-services-ruby.svg)](https://gemnasium.com/edwardsamuel/google-maps-services-ruby) [![Code Climate](https://codeclimate.com/github/edwardsamuel/google-maps-services-ruby/badges/gpa.svg)](https://codeclimate.com/github/edwardsamuel/google-maps-services-ruby) [![Coverage Status](https://coveralls.io/repos/edwardsamuel/google-maps-services-ruby/badge.svg?branch=master&service=github)](https://coveralls.io/github/edwardsamuel/google-maps-services-ruby?branch=master)
44

5-
*This is porting of [Python Client for Google Maps Services](https://github.com/googlemaps/google-maps-services-python). All Google Maps Service APIs are supported, but some features (e.g: auto retry) are not supported right now.*
5+
*This is porting of [Python Client for Google Maps Services](https://github.com/googlemaps/google-maps-services-python). All Google Maps Service APIs are supported, but some features (e.g: rate limiting) are not supported right now.*
66

77
## Description
88

google_maps_service.gemspec

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ Gem::Specification.new do |spec|
1919

2020
spec.add_runtime_dependency 'multi_json', '~> 1.11'
2121
spec.add_runtime_dependency 'hurley', '~> 0.1'
22+
spec.add_runtime_dependency 'retriable', '~> 2.0.2'
2223
spec.add_runtime_dependency 'ruby-hmac', '~> 0.4.0'
2324
spec.add_development_dependency 'bundler', '~> 1.7'
2425
spec.add_development_dependency 'rake', '~> 10.0'

lib/google_maps_service.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
module GoogleMapsService
22
class << self
3-
attr_accessor :key, :client_id, :client_secret, :ssl, :connection_middleware
3+
attr_accessor :key, :client_id, :client_secret, :connect_timeout, :read_timeout, :retry_timeout
44

55
def configure
66
yield self

lib/google_maps_service/client.rb

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
require 'uri'
22
require 'hurley'
33
require 'multi_json'
4+
require 'retriable'
45

56
module GoogleMapsService
67
class Client
78
USER_AGENT = "GoogleGeoApiClientRuby/#{GoogleMapsService::VERSION}"
89
DEFAULT_BASE_URL = "https://maps.googleapis.com"
9-
RETRIABLE_STATUSES = [500, 503, 504]
10+
RETRIABLE_ERRORS = [GoogleMapsService::Error::ServerError, GoogleMapsService::Error::RateLimitError]
1011

1112
include GoogleMapsService::Directions
1213
include GoogleMapsService::DistanceMatrix
@@ -28,10 +29,27 @@ class Client
2829
# @return [String]
2930
attr_reader :client_secret
3031

32+
# Connection timeout for HTTP requests, in seconds.
33+
# You should specify read_timeout in addition to this option.
34+
# @return [Integer]
35+
attr_reader :connect_timeout
36+
37+
# Read timeout for HTTP requests, in seconds.
38+
# You should specify connect_timeout in addition to this
39+
# @return [Integer]
40+
attr_reader :read_timeout
41+
42+
# Timeout across multiple retriable requests, in seconds.
43+
# @return [Integer]
44+
attr_reader :retry_timeout
45+
3146
def initialize(options={})
3247
@key = options[:key] || GoogleMapsService.key
3348
@client_id = options[:client_id] || GoogleMapsService.client_id
3449
@client_secret = options[:client_secret] || GoogleMapsService.client_secret
50+
@connect_timeout = options[:connect_timeout] || GoogleMapsService.connect_timeout
51+
@read_timeout = options[:read_timeout] || GoogleMapsService.read_timeout
52+
@retry_timeout = options[:retry_timeout] || GoogleMapsService.retry_timeout || 60
3553
end
3654

3755
# Get the current HTTP client
@@ -47,18 +65,23 @@ def client
4765
def new_client
4866
client = Hurley::Client.new
4967
client.request_options.query_class = Hurley::Query::Flat
68+
client.request_options.timeout = @read_timeout if @read_timeout
69+
client.request_options.open_timeout = @connect_timeout if @connect_timeout
5070
client.header[:user_agent] = USER_AGENT
5171
client
5272
end
5373

5474
def get(path, params, base_url: DEFAULT_BASE_URL, accepts_client_id: true, custom_response_decoder: nil)
5575
url = base_url + generate_auth_url(path, params, accepts_client_id)
56-
response = client.get url
5776

58-
if custom_response_decoder
59-
return custom_response_decoder.call(response)
77+
Retriable.retriable timeout: @retry_timeout,
78+
on: RETRIABLE_ERRORS do |try|
79+
response = client.get url
80+
if custom_response_decoder
81+
return custom_response_decoder.call(response)
82+
end
83+
return decode_response_body(response)
6084
end
61-
return decode_response_body(response)
6285
end
6386

6487
# Extract and parse body response as hash. Throw an error if there is something wrong with the response.

lib/google_maps_service/convert.rb

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -31,18 +31,9 @@ def latlng(arg)
3131
# @return [Array] Pair of lat and lng array.
3232
def normalize_latlng(arg)
3333
if arg.kind_of?(Hash)
34-
if arg.has_key?(:lat) and arg.has_key?(:lng)
35-
return arg[:lat], arg[:lng]
36-
end
37-
if arg.has_key?(:latitude) and arg.has_key?(:longitude)
38-
return arg[:latitude], arg[:longitude]
39-
end
40-
if arg.has_key?("lat") and arg.has_key?("lng")
41-
return arg["lat"], arg["lng"]
42-
end
43-
if arg.has_key?("latitude") and arg.has_key?("longitude")
44-
return arg["latitude"], arg["longitude"]
45-
end
34+
lat = arg[:lat] || arg[:latitude] || arg["lat"] || arg["latitude"]
35+
lng = arg[:lng] || arg[:longitude] || arg["lng"] || arg["longitude"]
36+
return lat, lng
4637
elsif arg.kind_of?(Array)
4738
return arg[0], arg[1]
4839
end
@@ -137,16 +128,34 @@ def components(arg)
137128
# @return [String]
138129
def bounds(arg)
139130
if arg.kind_of?(Hash)
140-
if arg.has_key?("southwest") && arg.has_key?("northeast")
141-
return "#{latlng(arg["southwest"])}|#{latlng(arg["northeast"])}"
142-
elsif arg.has_key?(:southwest) && arg.has_key?(:northeast)
143-
return "#{latlng(arg[:southwest])}|#{latlng(arg[:northeast])}"
144-
end
131+
southwest = arg[:southwest] || arg["southwest"]
132+
northeast = arg[:northeast] || arg["northeast"]
133+
return "#{latlng(southwest)}|#{latlng(northeast)}"
145134
end
146135

147136
raise ArgumentError, "Expected a bounds (southwest/northeast) Hash, but got #{arg.class}"
148137
end
149138

139+
# Converts an array of waypoints (path) to the format expected by the Google Maps
140+
# server.
141+
#
142+
# Accept two representation of waypoint:
143+
#
144+
# 1. String: Name of place or comma-separated lat/lon pair.
145+
# 2. Hash/Array: Lat/lon pair.
146+
#
147+
# @param [Array, String, Hash] waypoints Path.
148+
#
149+
# @return [String]
150+
def waypoints(waypoints)
151+
if waypoints.kind_of?(Array) and waypoints.length == 2 and waypoints[0].kind_of?(Numeric) and waypoints[1].kind_of?(Numeric)
152+
waypoints = [waypoints]
153+
end
154+
155+
waypoints = as_list(waypoints)
156+
return join_list('|', waypoints.map { |k| k.kind_of?(String) ? k : latlng(k) })
157+
end
158+
150159
# Decodes a Polyline string into a list of lat/lng hash.
151160
#
152161
# See the developer docs for a detailed description of this encoding:

lib/google_maps_service/directions.rb

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
require_relative './validator'
2+
13
module GoogleMapsService
24

35
# Performs requests to the Google Maps Directions API.
@@ -47,14 +49,7 @@ def directions(origin: nil, destination: nil,
4749
destination: _convert_waypoint(destination)
4850
}
4951

50-
if mode
51-
# NOTE(broady): the mode parameter is not validated by the Maps API
52-
# server. Check here to prevent silent failures.
53-
unless ["driving", "walking", "bicycling", "transit"].include?(mode)
54-
raise ArgumentError, "Invalid travel mode."
55-
end
56-
params[:mode] = mode
57-
end
52+
params[:mode] = GoogleMapsService::Validator.travel_mode(mode) if mode
5853

5954
if waypoints
6055
waypoints = GoogleMapsService::Convert.as_list(waypoints)
@@ -64,22 +59,22 @@ def directions(origin: nil, destination: nil,
6459
params[:waypoints] = GoogleMapsService::Convert.join_list("|", waypoints)
6560
end
6661

67-
params[:alternatives] = "true" if alternatives
68-
params[:avoid] = GoogleMapsService::Convert.join_list("|", avoid) if avoid
62+
params[:alternatives] = 'true' if alternatives
63+
params[:avoid] = GoogleMapsService::Convert.join_list('|', avoid) if avoid
6964
params[:language] = language if language
7065
params[:units] = units if units
7166
params[:region] = region if region
7267
params[:departure_time] = GoogleMapsService::Convert.time(departure_time) if departure_time
7368
params[:arrival_time] = GoogleMapsService::Convert.time(arrival_time) if arrival_time
7469

7570
if departure_time and arrival_time
76-
raise ArgumentError, "Should not specify both departure_time and arrival_time."
71+
raise ArgumentError, 'Should not specify both departure_time and arrival_time.'
7772
end
7873

7974
params[:transit_mode] = GoogleMapsService::Convert.join_list("|", transit_mode) if transit_mode
8075
params[:transit_routing_preference] = transit_routing_preference if transit_routing_preference
8176

82-
return get("/maps/api/directions/json", params)[:routes]
77+
return get('/maps/api/directions/json', params)[:routes]
8378
end
8479

8580
private

lib/google_maps_service/distance_matrix.rb

Lines changed: 11 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
require_relative './validator'
2+
13
module GoogleMapsService
24

35
# Performs requests to the Google Maps Distance Matrix API.
@@ -39,47 +41,26 @@ def distance_matrix(origins: nil, destinations: nil,
3941
departure_time: nil, arrival_time: nil, transit_mode: nil,
4042
transit_routing_preference: nil)
4143
params = {
42-
origins: _convert_path(origins),
43-
destinations: _convert_path(destinations)
44+
origins: GoogleMapsService::Convert.waypoints(origins),
45+
destinations: GoogleMapsService::Convert.waypoints(destinations)
4446
}
4547

46-
if mode
47-
# NOTE(broady): the mode parameter is not validated by the Maps API
48-
# server. Check here to prevent silent failures.
49-
unless ["driving", "walking", "bicycling", "transit"].include?(mode)
50-
raise ArgumentError, "Invalid travel mode."
51-
end
52-
params[:mode] = mode
53-
end
54-
5548
params[:language] = language if language
56-
57-
if avoid
58-
unless ["tolls", "highways", "ferries"].include?(avoid)
59-
raise ArgumentError, "Invalid route restriction."
60-
end
61-
params[:avoid] = avoid
62-
end
63-
49+
params[:mode] = GoogleMapsService::Validator.travel_mode(mode) if mode
50+
params[:avoid] = GoogleMapsService::Validator.avoid(avoid) if avoid
6451

6552
params[:units] = units if units
66-
params[:departure_time] = convert.time(departure_time) if departure_time
67-
params[:arrival_time] = convert.time(arrival_time) if arrival_time
53+
params[:departure_time] = GoogleMapsService::Convert.time(departure_time) if departure_time
54+
params[:arrival_time] = GoogleMapsService::Convert.time(arrival_time) if arrival_time
6855

6956
if departure_time and arrival_time
70-
raise ArgumentError, "Should not specify both departure_time and arrival_time."
57+
raise ArgumentError, 'Should not specify both departure_time and arrival_time.'
7158
end
7259

73-
params[:transit_mode] = convert.join_list("|", transit_mode) if transit_mode
60+
params[:transit_mode] = GoogleMapsService::Convert.join_list("|", transit_mode) if transit_mode
7461
params[:transit_routing_preference] = transit_routing_preference if transit_routing_preference
7562

76-
return get("/maps/api/distancematrix/json", params)
63+
return get('/maps/api/distancematrix/json', params)
7764
end
78-
79-
private
80-
def _convert_path(waypoints)
81-
waypoints = GoogleMapsService::Convert.as_list(waypoints)
82-
return GoogleMapsService::Convert.join_list("|", waypoints.map { |k| k.kind_of?(String) ? k : GoogleMapsService::Convert.latlng(k) })
83-
end
8465
end
8566
end

lib/google_maps_service/roads.rb

Lines changed: 3 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,7 @@ module Roads
2727
#
2828
# :rtype: A list of snapped points.
2929
def snap_to_roads(path: nil, interpolate: false)
30-
if path.kind_of?(Array) and path.length == 2 and not path[0].kind_of?(Array)
31-
path = [path]
32-
end
33-
34-
path = _convert_path(path)
30+
path = GoogleMapsService::Convert.waypoints(path)
3531

3632
params = {
3733
path: path
@@ -71,12 +67,7 @@ def speed_limits(place_ids: nil)
7167
# @return [Hash] a dict with both a list of speed limits and a list of the snapped
7268
# points.
7369
def snapped_speed_limits(path: nil)
74-
75-
if path.kind_of?(Array) and path.length == 2 and not path[0].kind_of?(Array)
76-
path = [path]
77-
end
78-
79-
path = _convert_path(path)
70+
path = GoogleMapsService::Convert.waypoints(path)
8071

8172
params = {
8273
path: path
@@ -89,11 +80,6 @@ def snapped_speed_limits(path: nil)
8980
end
9081

9182
private
92-
def _convert_path(paths)
93-
paths = GoogleMapsService::Convert.as_list(paths)
94-
return GoogleMapsService::Convert.join_list("|", paths.map { |k| k.kind_of?(String) ? k : GoogleMapsService::Convert.latlng(k) })
95-
end
96-
9783
# Extracts a result from a Roads API HTTP response.
9884
def extract_roads_body(response)
9985
begin
@@ -132,7 +118,7 @@ def extract_roads_body(response)
132118
end
133119

134120
unless response.status_code == 200
135-
raise GoogleMapsService::Error::HTTPError.new(response)
121+
raise GoogleMapsService::Error::ApiError.new(response)
136122
end
137123

138124
return body
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
require_relative './convert'
2+
3+
module GoogleMapsService
4+
module Validator
5+
module_function
6+
7+
def travel_mode(mode)
8+
# NOTE(broady): the mode parameter is not validated by the Maps API
9+
# server. Check here to prevent silent failures.
10+
unless [:driving, :walking, :bicycling, :transit].include?(mode.to_sym)
11+
raise ArgumentError, 'Invalid travel mode.'
12+
end
13+
mode
14+
end
15+
16+
def avoid(avoid)
17+
unless [:tolls, :highways, :ferries].include?(avoid.to_sym)
18+
raise ArgumentError, 'Invalid route restriction.'
19+
end
20+
avoid
21+
end
22+
end
23+
end

lib/google_maps_service/version.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
module GoogleMapsService
2-
VERSION = "0.1.0"
2+
VERSION = '0.2.0'
33
end

0 commit comments

Comments
 (0)