Skip to content

Commit

Permalink
Merge branch 'release/v.0.2.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
edwardsamuel committed Sep 17, 2015
2 parents 06a1881 + 063785a commit b547b44
Show file tree
Hide file tree
Showing 21 changed files with 452 additions and 173 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

[![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)

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

## Description

Expand Down
1 change: 1 addition & 0 deletions google_maps_service.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Gem::Specification.new do |spec|

spec.add_runtime_dependency 'multi_json', '~> 1.11'
spec.add_runtime_dependency 'hurley', '~> 0.1'
spec.add_runtime_dependency 'retriable', '~> 2.0.2'
spec.add_runtime_dependency 'ruby-hmac', '~> 0.4.0'
spec.add_development_dependency 'bundler', '~> 1.7'
spec.add_development_dependency 'rake', '~> 10.0'
Expand Down
2 changes: 1 addition & 1 deletion lib/google_maps_service.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module GoogleMapsService
class << self
attr_accessor :key, :client_id, :client_secret, :ssl, :connection_middleware
attr_accessor :key, :client_id, :client_secret, :connect_timeout, :read_timeout, :retry_timeout

def configure
yield self
Expand Down
33 changes: 28 additions & 5 deletions lib/google_maps_service/client.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
require 'uri'
require 'hurley'
require 'multi_json'
require 'retriable'

module GoogleMapsService
class Client
USER_AGENT = "GoogleGeoApiClientRuby/#{GoogleMapsService::VERSION}"
DEFAULT_BASE_URL = "https://maps.googleapis.com"
RETRIABLE_STATUSES = [500, 503, 504]
RETRIABLE_ERRORS = [GoogleMapsService::Error::ServerError, GoogleMapsService::Error::RateLimitError]

include GoogleMapsService::Directions
include GoogleMapsService::DistanceMatrix
Expand All @@ -28,10 +29,27 @@ class Client
# @return [String]
attr_reader :client_secret

# Connection timeout for HTTP requests, in seconds.
# You should specify read_timeout in addition to this option.
# @return [Integer]
attr_reader :connect_timeout

# Read timeout for HTTP requests, in seconds.
# You should specify connect_timeout in addition to this
# @return [Integer]
attr_reader :read_timeout

# Timeout across multiple retriable requests, in seconds.
# @return [Integer]
attr_reader :retry_timeout

def initialize(options={})
@key = options[:key] || GoogleMapsService.key
@client_id = options[:client_id] || GoogleMapsService.client_id
@client_secret = options[:client_secret] || GoogleMapsService.client_secret
@connect_timeout = options[:connect_timeout] || GoogleMapsService.connect_timeout
@read_timeout = options[:read_timeout] || GoogleMapsService.read_timeout
@retry_timeout = options[:retry_timeout] || GoogleMapsService.retry_timeout || 60
end

# Get the current HTTP client
Expand All @@ -47,18 +65,23 @@ def client
def new_client
client = Hurley::Client.new
client.request_options.query_class = Hurley::Query::Flat
client.request_options.timeout = @read_timeout if @read_timeout
client.request_options.open_timeout = @connect_timeout if @connect_timeout
client.header[:user_agent] = USER_AGENT
client
end

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

if custom_response_decoder
return custom_response_decoder.call(response)
Retriable.retriable timeout: @retry_timeout,
on: RETRIABLE_ERRORS do |try|
response = client.get url
if custom_response_decoder
return custom_response_decoder.call(response)
end
return decode_response_body(response)
end
return decode_response_body(response)
end

# Extract and parse body response as hash. Throw an error if there is something wrong with the response.
Expand Down
43 changes: 26 additions & 17 deletions lib/google_maps_service/convert.rb
Original file line number Diff line number Diff line change
Expand Up @@ -31,18 +31,9 @@ def latlng(arg)
# @return [Array] Pair of lat and lng array.
def normalize_latlng(arg)
if arg.kind_of?(Hash)
if arg.has_key?(:lat) and arg.has_key?(:lng)
return arg[:lat], arg[:lng]
end
if arg.has_key?(:latitude) and arg.has_key?(:longitude)
return arg[:latitude], arg[:longitude]
end
if arg.has_key?("lat") and arg.has_key?("lng")
return arg["lat"], arg["lng"]
end
if arg.has_key?("latitude") and arg.has_key?("longitude")
return arg["latitude"], arg["longitude"]
end
lat = arg[:lat] || arg[:latitude] || arg["lat"] || arg["latitude"]
lng = arg[:lng] || arg[:longitude] || arg["lng"] || arg["longitude"]
return lat, lng
elsif arg.kind_of?(Array)
return arg[0], arg[1]
end
Expand Down Expand Up @@ -137,16 +128,34 @@ def components(arg)
# @return [String]
def bounds(arg)
if arg.kind_of?(Hash)
if arg.has_key?("southwest") && arg.has_key?("northeast")
return "#{latlng(arg["southwest"])}|#{latlng(arg["northeast"])}"
elsif arg.has_key?(:southwest) && arg.has_key?(:northeast)
return "#{latlng(arg[:southwest])}|#{latlng(arg[:northeast])}"
end
southwest = arg[:southwest] || arg["southwest"]
northeast = arg[:northeast] || arg["northeast"]
return "#{latlng(southwest)}|#{latlng(northeast)}"
end

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

# Converts an array of waypoints (path) to the format expected by the Google Maps
# server.
#
# Accept two representation of waypoint:
#
# 1. String: Name of place or comma-separated lat/lon pair.
# 2. Hash/Array: Lat/lon pair.
#
# @param [Array, String, Hash] waypoints Path.
#
# @return [String]
def waypoints(waypoints)
if waypoints.kind_of?(Array) and waypoints.length == 2 and waypoints[0].kind_of?(Numeric) and waypoints[1].kind_of?(Numeric)
waypoints = [waypoints]
end

waypoints = as_list(waypoints)
return join_list('|', waypoints.map { |k| k.kind_of?(String) ? k : latlng(k) })
end

# Decodes a Polyline string into a list of lat/lng hash.
#
# See the developer docs for a detailed description of this encoding:
Expand Down
19 changes: 7 additions & 12 deletions lib/google_maps_service/directions.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require_relative './validator'

module GoogleMapsService

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

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

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

params[:alternatives] = "true" if alternatives
params[:avoid] = GoogleMapsService::Convert.join_list("|", avoid) if avoid
params[:alternatives] = 'true' if alternatives
params[:avoid] = GoogleMapsService::Convert.join_list('|', avoid) if avoid
params[:language] = language if language
params[:units] = units if units
params[:region] = region if region
params[:departure_time] = GoogleMapsService::Convert.time(departure_time) if departure_time
params[:arrival_time] = GoogleMapsService::Convert.time(arrival_time) if arrival_time

if departure_time and arrival_time
raise ArgumentError, "Should not specify both departure_time and arrival_time."
raise ArgumentError, 'Should not specify both departure_time and arrival_time.'
end

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

return get("/maps/api/directions/json", params)[:routes]
return get('/maps/api/directions/json', params)[:routes]
end

private
Expand Down
41 changes: 11 additions & 30 deletions lib/google_maps_service/distance_matrix.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
require_relative './validator'

module GoogleMapsService

# Performs requests to the Google Maps Distance Matrix API.
Expand Down Expand Up @@ -39,47 +41,26 @@ def distance_matrix(origins: nil, destinations: nil,
departure_time: nil, arrival_time: nil, transit_mode: nil,
transit_routing_preference: nil)
params = {
origins: _convert_path(origins),
destinations: _convert_path(destinations)
origins: GoogleMapsService::Convert.waypoints(origins),
destinations: GoogleMapsService::Convert.waypoints(destinations)
}

if mode
# NOTE(broady): the mode parameter is not validated by the Maps API
# server. Check here to prevent silent failures.
unless ["driving", "walking", "bicycling", "transit"].include?(mode)
raise ArgumentError, "Invalid travel mode."
end
params[:mode] = mode
end

params[:language] = language if language

if avoid
unless ["tolls", "highways", "ferries"].include?(avoid)
raise ArgumentError, "Invalid route restriction."
end
params[:avoid] = avoid
end

params[:mode] = GoogleMapsService::Validator.travel_mode(mode) if mode
params[:avoid] = GoogleMapsService::Validator.avoid(avoid) if avoid

params[:units] = units if units
params[:departure_time] = convert.time(departure_time) if departure_time
params[:arrival_time] = convert.time(arrival_time) if arrival_time
params[:departure_time] = GoogleMapsService::Convert.time(departure_time) if departure_time
params[:arrival_time] = GoogleMapsService::Convert.time(arrival_time) if arrival_time

if departure_time and arrival_time
raise ArgumentError, "Should not specify both departure_time and arrival_time."
raise ArgumentError, 'Should not specify both departure_time and arrival_time.'
end

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

return get("/maps/api/distancematrix/json", params)
return get('/maps/api/distancematrix/json', params)
end

private
def _convert_path(waypoints)
waypoints = GoogleMapsService::Convert.as_list(waypoints)
return GoogleMapsService::Convert.join_list("|", waypoints.map { |k| k.kind_of?(String) ? k : GoogleMapsService::Convert.latlng(k) })
end
end
end
20 changes: 3 additions & 17 deletions lib/google_maps_service/roads.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,7 @@ module Roads
#
# :rtype: A list of snapped points.
def snap_to_roads(path: nil, interpolate: false)
if path.kind_of?(Array) and path.length == 2 and not path[0].kind_of?(Array)
path = [path]
end

path = _convert_path(path)
path = GoogleMapsService::Convert.waypoints(path)

params = {
path: path
Expand Down Expand Up @@ -71,12 +67,7 @@ def speed_limits(place_ids: nil)
# @return [Hash] a dict with both a list of speed limits and a list of the snapped
# points.
def snapped_speed_limits(path: nil)

if path.kind_of?(Array) and path.length == 2 and not path[0].kind_of?(Array)
path = [path]
end

path = _convert_path(path)
path = GoogleMapsService::Convert.waypoints(path)

params = {
path: path
Expand All @@ -89,11 +80,6 @@ def snapped_speed_limits(path: nil)
end

private
def _convert_path(paths)
paths = GoogleMapsService::Convert.as_list(paths)
return GoogleMapsService::Convert.join_list("|", paths.map { |k| k.kind_of?(String) ? k : GoogleMapsService::Convert.latlng(k) })
end

# Extracts a result from a Roads API HTTP response.
def extract_roads_body(response)
begin
Expand Down Expand Up @@ -132,7 +118,7 @@ def extract_roads_body(response)
end

unless response.status_code == 200
raise GoogleMapsService::Error::HTTPError.new(response)
raise GoogleMapsService::Error::ApiError.new(response)
end

return body
Expand Down
23 changes: 23 additions & 0 deletions lib/google_maps_service/validator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
require_relative './convert'

module GoogleMapsService
module Validator
module_function

def travel_mode(mode)
# NOTE(broady): the mode parameter is not validated by the Maps API
# server. Check here to prevent silent failures.
unless [:driving, :walking, :bicycling, :transit].include?(mode.to_sym)
raise ArgumentError, 'Invalid travel mode.'
end
mode
end

def avoid(avoid)
unless [:tolls, :highways, :ferries].include?(avoid.to_sym)
raise ArgumentError, 'Invalid route restriction.'
end
avoid
end
end
end
2 changes: 1 addition & 1 deletion lib/google_maps_service/version.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
module GoogleMapsService
VERSION = "0.1.0"
VERSION = '0.2.0'
end
Loading

0 comments on commit b547b44

Please sign in to comment.