google_distance_matrix 0.4.0 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.editorconfig +16 -0
- data/.rubocop.yml +6 -0
- data/.ruby-version +1 -1
- data/.travis.yml +1 -0
- data/CHANGELOG.md +20 -0
- data/Gemfile +2 -0
- data/Rakefile +9 -4
- data/google_distance_matrix.gemspec +20 -18
- data/lib/google_distance_matrix/client.rb +32 -18
- data/lib/google_distance_matrix/client_cache.rb +9 -3
- data/lib/google_distance_matrix/configuration.rb +37 -19
- data/lib/google_distance_matrix/errors.rb +6 -3
- data/lib/google_distance_matrix/log_subscriber.rb +14 -14
- data/lib/google_distance_matrix/logger.rb +6 -4
- data/lib/google_distance_matrix/matrix.rb +45 -22
- data/lib/google_distance_matrix/place.rb +32 -25
- data/lib/google_distance_matrix/places.rb +5 -4
- data/lib/google_distance_matrix/polyline_encoder/delta.rb +4 -2
- data/lib/google_distance_matrix/polyline_encoder/value_encoder.rb +11 -4
- data/lib/google_distance_matrix/polyline_encoder.rb +2 -2
- data/lib/google_distance_matrix/railtie.rb +4 -1
- data/lib/google_distance_matrix/route.rb +22 -15
- data/lib/google_distance_matrix/routes_finder.rb +25 -29
- data/lib/google_distance_matrix/url_builder/polyline_encoder_buffer.rb +3 -0
- data/lib/google_distance_matrix/url_builder.rb +44 -16
- data/lib/google_distance_matrix/version.rb +3 -1
- data/lib/google_distance_matrix.rb +25 -23
- data/spec/lib/google_distance_matrix/client_cache_spec.rb +26 -11
- data/spec/lib/google_distance_matrix/client_spec.rb +40 -30
- data/spec/lib/google_distance_matrix/configuration_spec.rb +36 -24
- data/spec/lib/google_distance_matrix/log_subscriber_spec.rb +13 -44
- data/spec/lib/google_distance_matrix/logger_spec.rb +16 -13
- data/spec/lib/google_distance_matrix/matrix_spec.rb +90 -57
- data/spec/lib/google_distance_matrix/place_spec.rb +30 -25
- data/spec/lib/google_distance_matrix/places_spec.rb +29 -28
- data/spec/lib/google_distance_matrix/polyline_encoder/delta_spec.rb +5 -3
- data/spec/lib/google_distance_matrix/polyline_encoder_spec.rb +7 -2
- data/spec/lib/google_distance_matrix/route_spec.rb +11 -9
- data/spec/lib/google_distance_matrix/routes_finder_spec.rb +95 -81
- data/spec/lib/google_distance_matrix/url_builder_spec.rb +97 -48
- data/spec/spec_helper.rb +3 -1
- metadata +35 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e2a2232b164549acbd29af07935315c8859dc19c
|
4
|
+
data.tar.gz: 3f29dc7ef98d87e913c1372fabd660766150dc8b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bead0dcdb680422cac5db6463af54580036dc6fae0f98373eec513e18a3a197ca7981d57db4ca64a27cc7461e675bd181b81a284b0cd98129e98fa916735be28
|
7
|
+
data.tar.gz: 0d6d9b8a96282568c647293adab6c58e9eed438e36d1e42f1f4b473a6da59c7cb5014eebc5cc56b4326211e8faa68e292651940a5f1e1d2c30b9b82ba5435710
|
data/.editorconfig
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
# EditorConfig helps developers define and maintain consistent
|
2
|
+
# coding styles between different editors and IDEs
|
3
|
+
# editorconfig.org
|
4
|
+
|
5
|
+
root = true
|
6
|
+
|
7
|
+
[*]
|
8
|
+
end_of_line = lf
|
9
|
+
charset = utf-8
|
10
|
+
trim_trailing_whitespace = true
|
11
|
+
insert_final_newline = true
|
12
|
+
indent_style = space
|
13
|
+
indent_size = 2
|
14
|
+
|
15
|
+
[*.{diff,md}]
|
16
|
+
trim_trailing_whitespace = false
|
data/.rubocop.yml
ADDED
data/.ruby-version
CHANGED
@@ -1 +1 @@
|
|
1
|
-
2.3.
|
1
|
+
2.3.4
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
## v.0.5.0
|
2
|
+
|
3
|
+
This release contains breaking change where `url` has been renamed to
|
4
|
+
`sensitive_url`. A `filtered_url` method is added to make it clear that
|
5
|
+
the URL returned is filtered according to configuration while the other one
|
6
|
+
will contain sensitive information like key and signature.
|
7
|
+
|
8
|
+
The cache key is changed so it no longer uses the URL, but a digest of the URL
|
9
|
+
as key. You may set `config.cache_key_transform` to a block passing given url
|
10
|
+
through if you don't want this.
|
11
|
+
|
12
|
+
* Fixed an issue where read/write to cache used url with sensitive data and
|
13
|
+
and filtered url resulting in cache miss.
|
14
|
+
* Instrumentation payload `url` renamed `sensitive_url`.
|
15
|
+
* Instrumentation payload added `filtered_url`.
|
16
|
+
* Cache key is a digest of the `sensitive_url` so we don't store in cache the
|
17
|
+
sensitive parts of the URL.
|
18
|
+
* Digesting cache key is configurable with `cache_key_transform`. It's a callable
|
19
|
+
object expected to take the url and transform it to the key you want.
|
20
|
+
|
1
21
|
## v.0.4.0
|
2
22
|
* When mode is `driving` and `departure_time` is set all `route` objects will contain
|
3
23
|
`duration_in_traffic_in_seconds` and `duration_in_traffic_text`.
|
data/Gemfile
CHANGED
data/Rakefile
CHANGED
@@ -1,9 +1,14 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require 'bundler/gem_tasks'
|
4
|
+
|
5
|
+
require 'rubocop/rake_task'
|
6
|
+
RuboCop::RakeTask.new
|
7
|
+
|
8
|
+
require 'rspec/core/rake_task'
|
4
9
|
RSpec::Core::RakeTask.new(:spec) do |t|
|
5
|
-
t.rspec_opts = [
|
10
|
+
t.rspec_opts = ['-c', '-f progress', '-r ./spec/spec_helper.rb']
|
6
11
|
t.pattern = 'spec/**/*_spec.rb'
|
7
12
|
end
|
8
13
|
|
9
|
-
task default:
|
14
|
+
task default: %i[rubocop spec]
|
@@ -1,30 +1,32 @@
|
|
1
|
-
#
|
1
|
+
# frozen_String_literal: true
|
2
|
+
|
2
3
|
lib = File.expand_path('../lib', __FILE__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
5
|
require 'google_distance_matrix/version'
|
5
6
|
|
6
7
|
Gem::Specification.new do |spec|
|
7
|
-
spec.name =
|
8
|
+
spec.name = 'google_distance_matrix'
|
8
9
|
spec.version = GoogleDistanceMatrix::VERSION
|
9
|
-
spec.authors = [
|
10
|
-
spec.email = [
|
11
|
-
spec.description = %
|
12
|
-
spec.summary = %
|
13
|
-
spec.homepage =
|
14
|
-
spec.license =
|
10
|
+
spec.authors = ['Thorbjørn Hermansen']
|
11
|
+
spec.email = ['thhermansen@gmail.com']
|
12
|
+
spec.description = %(Ruby client for The Google Distance Matrix API)
|
13
|
+
spec.summary = %(Ruby client for The Google Distance Matrix API)
|
14
|
+
spec.homepage = ''
|
15
|
+
spec.license = 'MIT'
|
15
16
|
|
16
|
-
spec.files = `git ls-files`.split(
|
17
|
+
spec.files = `git ls-files`.split($RS)
|
17
18
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
19
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = [
|
20
|
+
spec.require_paths = ['lib']
|
20
21
|
|
21
|
-
spec.add_dependency
|
22
|
-
spec.add_dependency
|
23
|
-
spec.add_dependency
|
22
|
+
spec.add_dependency 'activesupport', '>= 3.2.13', '<= 5'
|
23
|
+
spec.add_dependency 'activemodel', '>= 3.2.13', '<= 5'
|
24
|
+
spec.add_dependency 'google_business_api_url_signer', '~> 0.1.3'
|
24
25
|
|
25
|
-
spec.add_development_dependency
|
26
|
-
spec.add_development_dependency
|
27
|
-
spec.add_development_dependency
|
28
|
-
spec.add_development_dependency
|
29
|
-
spec.add_development_dependency
|
26
|
+
spec.add_development_dependency 'bundler'
|
27
|
+
spec.add_development_dependency 'rspec', '~> 3.5.0'
|
28
|
+
spec.add_development_dependency 'rubocop', '~> 0.48'
|
29
|
+
spec.add_development_dependency 'shoulda-matchers', '~> 3.1.1'
|
30
|
+
spec.add_development_dependency 'webmock', '~> 3.0.1'
|
31
|
+
spec.add_development_dependency 'rake'
|
30
32
|
end
|
@@ -1,4 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module GoogleDistanceMatrix
|
4
|
+
# HTTP client making request to Google's API
|
2
5
|
class Client
|
3
6
|
CLIENT_ERRORS = %w[
|
4
7
|
INVALID_REQUEST
|
@@ -6,41 +9,52 @@ module GoogleDistanceMatrix
|
|
6
9
|
OVER_QUERY_LIMIT
|
7
10
|
REQUEST_DENIED
|
8
11
|
UNKNOWN_ERROR
|
9
|
-
]
|
10
|
-
|
11
|
-
|
12
|
+
].freeze
|
13
|
+
|
14
|
+
# Make a GET request to given URL
|
15
|
+
#
|
16
|
+
# @param url The URL to Google's API we'll make a request to
|
17
|
+
# @param instrumentation A hash with instrumentation payload
|
18
|
+
# @param options Other options we don't care about, for example we'll capture
|
19
|
+
# `configuration` option which we are not using, but the ClientCache
|
20
|
+
# is using.
|
21
|
+
#
|
22
|
+
# @return Hash with data from parsed response body
|
23
|
+
def get(url, instrumentation: {}, **_options)
|
12
24
|
uri = URI.parse url
|
13
|
-
instrumentation = {url: url}.merge(options[:instrumentation] || {})
|
14
25
|
|
15
|
-
response = ActiveSupport::Notifications.instrument
|
26
|
+
response = ActiveSupport::Notifications.instrument(
|
27
|
+
'client_request_matrix_data.google_distance_matrix', instrumentation
|
28
|
+
) do
|
16
29
|
Net::HTTP.get_response uri
|
17
30
|
end
|
18
31
|
|
32
|
+
handle response, url
|
33
|
+
rescue Timeout::Error => error
|
34
|
+
raise ServerError, error
|
35
|
+
end
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def handle(response, url) # rubocop:disable Metrics/MethodLength
|
19
40
|
case response
|
20
41
|
when Net::HTTPSuccess
|
21
42
|
inspect_for_client_errors! response
|
22
43
|
when Net::HTTPRequestURITooLong
|
23
|
-
|
44
|
+
raise MatrixUrlTooLong.new url, UrlBuilder::MAX_URL_SIZE, response
|
24
45
|
when Net::HTTPClientError
|
25
|
-
|
46
|
+
raise ClientError, response
|
26
47
|
when Net::HTTPServerError
|
27
|
-
|
48
|
+
raise ServerError, response
|
28
49
|
else # Handle this as a request error for now. Maybe fine tune this more later.
|
29
|
-
|
50
|
+
raise ServerError, response
|
30
51
|
end
|
31
|
-
rescue Timeout::Error => error
|
32
|
-
fail ServerError.new error
|
33
52
|
end
|
34
53
|
|
35
|
-
|
36
|
-
private
|
37
|
-
|
38
54
|
def inspect_for_client_errors!(response)
|
39
|
-
status = JSON.parse(response.body).fetch
|
55
|
+
status = JSON.parse(response.body).fetch 'status'
|
40
56
|
|
41
|
-
if CLIENT_ERRORS.include? status
|
42
|
-
fail ClientError.new response, status
|
43
|
-
end
|
57
|
+
raise ClientError.new response, status if CLIENT_ERRORS.include? status
|
44
58
|
|
45
59
|
response
|
46
60
|
end
|
@@ -1,9 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module GoogleDistanceMatrix
|
4
|
+
# Cached client, which takes care of caching data from Google API
|
2
5
|
class ClientCache
|
3
6
|
attr_reader :client, :cache
|
4
7
|
|
5
|
-
|
6
|
-
|
8
|
+
# Returns a cache key for given URL
|
9
|
+
#
|
10
|
+
# @return String
|
11
|
+
def self.key(url, config)
|
12
|
+
config.cache_key_transform.call url
|
7
13
|
end
|
8
14
|
|
9
15
|
def initialize(client, cache)
|
@@ -12,7 +18,7 @@ module GoogleDistanceMatrix
|
|
12
18
|
end
|
13
19
|
|
14
20
|
def get(url, options = {})
|
15
|
-
cache.fetch self.class.key(url) do
|
21
|
+
cache.fetch self.class.key(url, options.fetch(:configuration)) do
|
16
22
|
client.get url, options
|
17
23
|
end
|
18
24
|
end
|
@@ -1,3 +1,7 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'digest'
|
4
|
+
|
1
5
|
module GoogleDistanceMatrix
|
2
6
|
# Public: Configuration of matrix and it's request.
|
3
7
|
#
|
@@ -15,19 +19,20 @@ module GoogleDistanceMatrix
|
|
15
19
|
departure_time arrival_time
|
16
20
|
transit_mode transit_routing_preference
|
17
21
|
traffic_model
|
18
|
-
]
|
22
|
+
].freeze
|
19
23
|
|
20
24
|
API_DEFAULTS = {
|
21
|
-
mode:
|
22
|
-
units:
|
23
|
-
traffic_model:
|
25
|
+
mode: 'driving',
|
26
|
+
units: 'metric',
|
27
|
+
traffic_model: 'best_guess',
|
24
28
|
use_encoded_polylines: false,
|
25
29
|
protocol: 'https',
|
26
30
|
lat_lng_scale: 5,
|
27
|
-
filter_parameters_in_logged_url: [
|
31
|
+
filter_parameters_in_logged_url: %w[key signature].freeze,
|
32
|
+
cache_key_transform: ->(url) { Digest::SHA512.new.update(url).to_s }
|
28
33
|
}.with_indifferent_access
|
29
34
|
|
30
|
-
attr_accessor
|
35
|
+
attr_accessor(*ATTRIBUTES)
|
31
36
|
|
32
37
|
# The protocol to use, either http or https
|
33
38
|
attr_accessor :protocol
|
@@ -50,22 +55,38 @@ module GoogleDistanceMatrix
|
|
50
55
|
# When logging we filter sensitive parameters
|
51
56
|
attr_accessor :filter_parameters_in_logged_url
|
52
57
|
|
53
|
-
|
54
|
-
|
55
|
-
|
58
|
+
# Callable object which transform given url to key used in cache
|
59
|
+
# @see ClientCache
|
60
|
+
attr_accessor :cache_key_transform
|
61
|
+
|
62
|
+
validates :mode, inclusion: { in: %w[driving walking bicycling transit] }, allow_blank: true
|
63
|
+
validates :avoid, inclusion: { in: %w[tolls highways ferries indoor] }, allow_blank: true
|
64
|
+
validates :units, inclusion: { in: %w[metric imperial] }, allow_blank: true
|
56
65
|
|
57
66
|
validates :departure_time, format: /\A(\d+|now)\Z/, allow_blank: true
|
58
67
|
validates :arrival_time, numericality: true, allow_blank: true
|
59
68
|
|
60
|
-
validates :transit_mode,
|
61
|
-
|
62
|
-
|
69
|
+
validates :transit_mode,
|
70
|
+
inclusion: { in: %w[bus subway train tram rail] },
|
71
|
+
allow_blank: true
|
72
|
+
|
73
|
+
validates :transit_routing_preference,
|
74
|
+
inclusion: { in: %w[less_walking fewer_transfers] },
|
75
|
+
allow_blank: true
|
63
76
|
|
64
|
-
validates :
|
77
|
+
validates :traffic_model,
|
78
|
+
inclusion: { in: %w[best_guess pessimistic optimistic] },
|
79
|
+
allow_blank: true
|
80
|
+
|
81
|
+
validates :protocol, inclusion: { in: %w[http https] }, allow_blank: true
|
65
82
|
|
66
83
|
def initialize
|
67
84
|
API_DEFAULTS.each_pair do |attr_name, value|
|
68
|
-
self[attr_name] =
|
85
|
+
self[attr_name] = begin
|
86
|
+
value.dup
|
87
|
+
rescue
|
88
|
+
value
|
89
|
+
end
|
69
90
|
end
|
70
91
|
end
|
71
92
|
|
@@ -77,7 +98,6 @@ module GoogleDistanceMatrix
|
|
77
98
|
public_send "#{attr_name}=", value
|
78
99
|
end
|
79
100
|
|
80
|
-
|
81
101
|
private
|
82
102
|
|
83
103
|
def array_param
|
@@ -89,15 +109,13 @@ module GoogleDistanceMatrix
|
|
89
109
|
out << ['client', google_business_api_client_id]
|
90
110
|
end
|
91
111
|
|
92
|
-
if google_api_key.present?
|
93
|
-
out << ['key', google_api_key]
|
94
|
-
end
|
112
|
+
out << ['key', google_api_key] if google_api_key.present?
|
95
113
|
|
96
114
|
out
|
97
115
|
end
|
98
116
|
|
99
117
|
def param_same_as_api_default?(param)
|
100
|
-
|
118
|
+
API_DEFAULTS[param[0]] == param[1]
|
101
119
|
end
|
102
120
|
end
|
103
121
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module GoogleDistanceMatrix
|
2
4
|
# Public: Error class for lib.
|
3
5
|
class Error < StandardError
|
@@ -67,7 +69,8 @@ module GoogleDistanceMatrix
|
|
67
69
|
end
|
68
70
|
|
69
71
|
def to_s
|
70
|
-
"GoogleDistanceMatrix::ClientError -
|
72
|
+
"GoogleDistanceMatrix::ClientError - \
|
73
|
+
#{[response, status_read_from_api_response].compact.join('. ')}."
|
71
74
|
end
|
72
75
|
end
|
73
76
|
|
@@ -75,7 +78,8 @@ module GoogleDistanceMatrix
|
|
75
78
|
#
|
76
79
|
# See https://developers.google.com/maps/documentation/distancematrix/#Limits, which states:
|
77
80
|
# "Distance Matrix API URLs are restricted to 2048 characters, before URL encoding."
|
78
|
-
# "As some Distance Matrix API service URLs may involve many locations,
|
81
|
+
# "As some Distance Matrix API service URLs may involve many locations,
|
82
|
+
# be aware of this limit when constructing your URLs."
|
79
83
|
#
|
80
84
|
class MatrixUrlTooLong < ClientError
|
81
85
|
attr_reader :url, :max_url_size
|
@@ -91,5 +95,4 @@ module GoogleDistanceMatrix
|
|
91
95
|
"Matrix API URL max size is: #{max_url_size}. Built URL was: #{url.length}. URL: '#{url}'."
|
92
96
|
end
|
93
97
|
end
|
94
|
-
|
95
98
|
end
|
@@ -1,8 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module GoogleDistanceMatrix
|
4
|
+
# LogSubscriber logs to GoogleDistanceMatrix.logger by subscribing to gem's intstrumentation.
|
5
|
+
#
|
6
|
+
# NOTE: This log subscruber uses the default_configuration as it's configuration.
|
7
|
+
# This is relevant for example for the filter_parameters_in_logged_url configuration
|
2
8
|
class LogSubscriber < ActiveSupport::LogSubscriber
|
3
9
|
attr_reader :logger, :config
|
4
10
|
|
5
|
-
def initialize(
|
11
|
+
def initialize(
|
12
|
+
logger: GoogleDistanceMatrix.logger,
|
13
|
+
config: GoogleDistanceMatrix.default_configuration
|
14
|
+
)
|
6
15
|
super()
|
7
16
|
|
8
17
|
@logger = logger
|
@@ -10,20 +19,11 @@ module GoogleDistanceMatrix
|
|
10
19
|
end
|
11
20
|
|
12
21
|
def client_request_matrix_data(event)
|
13
|
-
url =
|
14
|
-
logger.info "(#{event.duration}ms) (elements: #{event.payload[:elements]}) GET #{url}",
|
15
|
-
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def filter_url!(url)
|
20
|
-
config.filter_parameters_in_logged_url.each do |param|
|
21
|
-
url.gsub! %r{(#{param})=.*?(&|$)}, '\1=[FILTERED]\2'
|
22
|
-
end
|
23
|
-
|
24
|
-
url
|
22
|
+
url = event.payload[:filtered_url]
|
23
|
+
logger.info "(#{event.duration}ms) (elements: #{event.payload[:elements]}) GET #{url}",
|
24
|
+
tag: :client
|
25
25
|
end
|
26
26
|
end
|
27
27
|
end
|
28
28
|
|
29
|
-
GoogleDistanceMatrix::LogSubscriber.attach_to
|
29
|
+
GoogleDistanceMatrix::LogSubscriber.attach_to 'google_distance_matrix'
|
@@ -1,7 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module GoogleDistanceMatrix
|
4
|
+
# Logger class for Google Distance Matrix
|
2
5
|
class Logger
|
3
|
-
PREFIXES = %w[google_distance_matrix]
|
4
|
-
LEVELS = %w[fatal error warn info debug]
|
6
|
+
PREFIXES = %w[google_distance_matrix].freeze
|
7
|
+
LEVELS = %w[fatal error warn info debug].freeze
|
5
8
|
|
6
9
|
attr_reader :backend
|
7
10
|
|
@@ -20,13 +23,12 @@ module GoogleDistanceMatrix
|
|
20
23
|
end
|
21
24
|
end
|
22
25
|
|
23
|
-
|
24
26
|
private
|
25
27
|
|
26
28
|
def tag_msg(msg, tags)
|
27
29
|
msg_buffer = tags.map { |tag| "[#{tag}]" }
|
28
30
|
msg_buffer << msg
|
29
|
-
msg_buffer.join
|
31
|
+
msg_buffer.join ' '
|
30
32
|
end
|
31
33
|
end
|
32
34
|
end
|