google_distance_matrix 0.4.0 → 0.5.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.
- 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
@@ -1,9 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require_relative 'url_builder/polyline_encoder_buffer'
|
2
4
|
|
3
5
|
module GoogleDistanceMatrix
|
6
|
+
# Takes care of building the url for given matrix
|
4
7
|
class UrlBuilder
|
5
|
-
BASE_URL =
|
6
|
-
DELIMITER = CGI.escape(
|
8
|
+
BASE_URL = 'maps.googleapis.com/maps/api/distancematrix/json'
|
9
|
+
DELIMITER = CGI.escape('|')
|
7
10
|
MAX_URL_SIZE = 2048
|
8
11
|
|
9
12
|
attr_reader :matrix
|
@@ -12,41 +15,62 @@ module GoogleDistanceMatrix
|
|
12
15
|
def initialize(matrix)
|
13
16
|
@matrix = matrix
|
14
17
|
|
15
|
-
|
18
|
+
raise InvalidMatrix, matrix if matrix.invalid?
|
16
19
|
end
|
17
20
|
|
18
|
-
|
19
|
-
|
21
|
+
# Returns the URL we'll call Google API with
|
22
|
+
#
|
23
|
+
# This URL contains key and signature and is therefor
|
24
|
+
# sensitive.
|
25
|
+
#
|
26
|
+
# @return String
|
27
|
+
# @see filtered_url
|
28
|
+
def sensitive_url
|
29
|
+
@sensitive_url ||= build_url
|
20
30
|
end
|
21
31
|
|
32
|
+
# Returns the URL filtered as the configuration of the matrix dictates
|
33
|
+
#
|
34
|
+
# @return String
|
35
|
+
def filtered_url
|
36
|
+
filter_url sensitive_url
|
37
|
+
end
|
22
38
|
|
23
39
|
private
|
24
40
|
|
25
41
|
def build_url
|
26
|
-
url = [protocol, BASE_URL,
|
42
|
+
url = [protocol, BASE_URL, '?', query_params_string].join
|
27
43
|
|
28
44
|
if sign_url?
|
29
|
-
url = GoogleBusinessApiUrlSigner.add_signature(
|
45
|
+
url = GoogleBusinessApiUrlSigner.add_signature(
|
46
|
+
url, configuration.google_business_api_private_key
|
47
|
+
)
|
30
48
|
end
|
31
49
|
|
32
|
-
if url.length > MAX_URL_SIZE
|
33
|
-
|
50
|
+
raise MatrixUrlTooLong.new url, MAX_URL_SIZE if url.length > MAX_URL_SIZE
|
51
|
+
|
52
|
+
url
|
53
|
+
end
|
54
|
+
|
55
|
+
def filter_url(url)
|
56
|
+
configuration.filter_parameters_in_logged_url.each do |param|
|
57
|
+
url = url.gsub(/(#{param})=.*?(&|$)/, '\1=[FILTERED]\2')
|
34
58
|
end
|
35
59
|
|
36
60
|
url
|
37
61
|
end
|
38
62
|
|
39
63
|
def sign_url?
|
40
|
-
configuration.google_business_api_client_id.present?
|
41
|
-
|
64
|
+
configuration.google_business_api_client_id.present? &&
|
65
|
+
configuration.google_business_api_private_key.present?
|
42
66
|
end
|
43
67
|
|
44
68
|
def include_api_key?
|
45
69
|
configuration.google_api_key.present?
|
46
70
|
end
|
47
71
|
|
48
|
-
def
|
49
|
-
params.to_a.map { |key_value| key_value.join(
|
72
|
+
def query_params_string
|
73
|
+
params.to_a.map { |key_value| key_value.join('=') }.join('&')
|
50
74
|
end
|
51
75
|
|
52
76
|
def params
|
@@ -56,8 +80,10 @@ module GoogleDistanceMatrix
|
|
56
80
|
)
|
57
81
|
end
|
58
82
|
|
83
|
+
# rubocop:disable Metrics/MethodLength
|
84
|
+
# rubocop:disable Metrics/AbcSize
|
59
85
|
def places_to_param(places)
|
60
|
-
places_to_param_config = {lat_lng_scale: configuration.lat_lng_scale}
|
86
|
+
places_to_param_config = { lat_lng_scale: configuration.lat_lng_scale }
|
61
87
|
|
62
88
|
out = []
|
63
89
|
polyline_encode_buffer = PolylineEncoderBuffer.new
|
@@ -67,7 +93,7 @@ module GoogleDistanceMatrix
|
|
67
93
|
polyline_encode_buffer << place.lat_lng
|
68
94
|
else
|
69
95
|
polyline_encode_buffer.flush to: out
|
70
|
-
out << escape(place.to_param
|
96
|
+
out << escape(place.to_param(places_to_param_config))
|
71
97
|
end
|
72
98
|
end
|
73
99
|
|
@@ -75,9 +101,11 @@ module GoogleDistanceMatrix
|
|
75
101
|
|
76
102
|
out.join(DELIMITER)
|
77
103
|
end
|
104
|
+
# rubocop:enable Metrics/MethodLength
|
105
|
+
# rubocop:enable Metrics/AbcSize
|
78
106
|
|
79
107
|
def protocol
|
80
|
-
configuration.protocol +
|
108
|
+
configuration.protocol + '://'
|
81
109
|
end
|
82
110
|
|
83
111
|
def escape(string)
|
@@ -1,29 +1,31 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
4
|
-
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
8
|
-
|
9
|
-
require
|
10
|
-
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
16
|
-
require
|
17
|
-
require
|
18
|
-
require
|
19
|
-
require
|
20
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'google_distance_matrix/version'
|
4
|
+
|
5
|
+
require 'cgi'
|
6
|
+
require 'json'
|
7
|
+
require 'active_model'
|
8
|
+
require 'active_support/core_ext/hash'
|
9
|
+
require 'google_business_api_url_signer'
|
10
|
+
|
11
|
+
require 'google_distance_matrix/logger'
|
12
|
+
require 'google_distance_matrix/errors'
|
13
|
+
require 'google_distance_matrix/configuration'
|
14
|
+
require 'google_distance_matrix/url_builder'
|
15
|
+
require 'google_distance_matrix/client'
|
16
|
+
require 'google_distance_matrix/client_cache'
|
17
|
+
require 'google_distance_matrix/routes_finder'
|
18
|
+
require 'google_distance_matrix/matrix'
|
19
|
+
require 'google_distance_matrix/places'
|
20
|
+
require 'google_distance_matrix/place'
|
21
|
+
require 'google_distance_matrix/route'
|
22
|
+
require 'google_distance_matrix/polyline_encoder'
|
21
23
|
|
22
24
|
require 'google_distance_matrix/railtie' if defined? Rails
|
23
25
|
|
24
|
-
|
26
|
+
# Main module for the GoogleDistanceMatrix
|
25
27
|
module GoogleDistanceMatrix
|
26
|
-
|
28
|
+
module_function
|
27
29
|
|
28
30
|
def default_configuration
|
29
31
|
@default_configuration ||= Configuration.new
|
@@ -38,4 +40,4 @@ module GoogleDistanceMatrix
|
|
38
40
|
end
|
39
41
|
end
|
40
42
|
|
41
|
-
require
|
43
|
+
require 'google_distance_matrix/log_subscriber'
|
@@ -1,25 +1,40 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
2
4
|
|
3
5
|
describe GoogleDistanceMatrix::ClientCache do
|
4
|
-
let(:
|
5
|
-
let(:
|
6
|
+
let(:config) { GoogleDistanceMatrix::Configuration.new }
|
7
|
+
let(:url) { 'http://www.example.com' }
|
8
|
+
let(:options) { { hello: :options, configuration: config } }
|
6
9
|
|
7
|
-
let(:client) { double get:
|
10
|
+
let(:client) { double get: 'data' }
|
8
11
|
let(:cache) { double }
|
9
12
|
|
10
13
|
subject { described_class.new client, cache }
|
11
14
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
15
|
+
# rubocop:disable Metrics/LineLength
|
16
|
+
describe '::key' do
|
17
|
+
it 'returns a digest of given URL' do
|
18
|
+
key = described_class.key 'some url with secret parts', config
|
19
|
+
expect(key).to eq 'e90595434d4e321da6b01d2b99d77419ddaa8861d83c5971c4a119ee76bb80a7003915cc16e6966615f205b4a1d5411bb5d4a0d907f611b3fe4cc8d9049f4f9c'
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe '#get' do
|
24
|
+
it 'returns from cache if it hits' do
|
25
|
+
expect(cache)
|
26
|
+
.to receive(:fetch)
|
27
|
+
.with('2f7d4c4d8a51afd0f9efb9edfda07591591cccc3704130328ad323d3cb5bf7ff19df5e895b402c99217d27d5f4547618094d47069c9ba58370ed8e26cc1de114')
|
28
|
+
.and_return 'cached-data'
|
29
|
+
|
30
|
+
expect(subject.get(url, options)).to eq 'cached-data'
|
16
31
|
end
|
17
32
|
|
18
|
-
it
|
19
|
-
expect(client).to receive(:get).with(url, options).and_return
|
33
|
+
it 'asks client when cache miss' do
|
34
|
+
expect(client).to receive(:get).with(url, options).and_return 'api-data'
|
20
35
|
expect(cache).to receive(:fetch) { |&block| block.call }
|
21
36
|
|
22
|
-
expect(subject.get(url, options)).to eq
|
37
|
+
expect(subject.get(url, options)).to eq 'api-data'
|
23
38
|
end
|
24
39
|
end
|
25
40
|
end
|
@@ -1,66 +1,76 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
2
4
|
|
3
5
|
describe GoogleDistanceMatrix::Client, :request_recordings do
|
4
|
-
let(:origin_1) { GoogleDistanceMatrix::Place.new address:
|
5
|
-
let(:destination_1) { GoogleDistanceMatrix::Place.new address:
|
6
|
-
let(:matrix)
|
6
|
+
let(:origin_1) { GoogleDistanceMatrix::Place.new address: 'Karl Johans gate, Oslo' }
|
7
|
+
let(:destination_1) { GoogleDistanceMatrix::Place.new address: 'Drammensveien 1, Oslo' }
|
8
|
+
let(:matrix) do
|
9
|
+
GoogleDistanceMatrix::Matrix.new(origins: [origin_1], destinations: [destination_1])
|
10
|
+
end
|
7
11
|
|
8
12
|
let(:url_builder) { GoogleDistanceMatrix::UrlBuilder.new matrix }
|
9
|
-
let(:url) { url_builder.
|
13
|
+
let(:url) { url_builder.sensitive_url }
|
10
14
|
|
11
15
|
subject { GoogleDistanceMatrix::Client.new }
|
12
16
|
|
13
|
-
describe
|
17
|
+
describe 'success' do
|
14
18
|
before { stub_request(:get, url).to_return body: recorded_request_for(:success) }
|
15
19
|
|
16
|
-
it
|
17
|
-
expect(subject.get(url_builder.
|
20
|
+
it 'makes the request' do
|
21
|
+
expect(subject.get(url_builder.sensitive_url).body).to eq recorded_request_for(:success).read
|
18
22
|
end
|
19
23
|
end
|
20
24
|
|
21
|
-
describe
|
22
|
-
describe
|
23
|
-
it
|
24
|
-
stub_request(:get, url).to_return status: [400,
|
25
|
-
expect { subject.get(url_builder.
|
25
|
+
describe 'client errors' do
|
26
|
+
describe 'server issues 4xx client error' do
|
27
|
+
it 'wraps the error http response' do
|
28
|
+
stub_request(:get, url).to_return status: [400, 'Client error']
|
29
|
+
expect { subject.get(url_builder.sensitive_url) }
|
30
|
+
.to raise_error GoogleDistanceMatrix::ClientError
|
26
31
|
end
|
27
32
|
|
28
|
-
it
|
29
|
-
stub_request(:get, url).to_return status: [414,
|
30
|
-
expect { subject.get(url_builder.
|
33
|
+
it 'wraps uri too long error' do
|
34
|
+
stub_request(:get, url).to_return status: [414, 'Client error']
|
35
|
+
expect { subject.get(url_builder.sensitive_url) }
|
36
|
+
.to raise_error GoogleDistanceMatrix::MatrixUrlTooLong
|
31
37
|
end
|
32
38
|
end
|
33
39
|
|
34
40
|
described_class::CLIENT_ERRORS.each do |error|
|
35
41
|
it "wraps '#{error}' client error" do
|
36
|
-
stub_request(:get, url).to_return body: JSON.generate(
|
37
|
-
expect { subject.get(url_builder.
|
42
|
+
stub_request(:get, url).to_return body: JSON.generate(status: error)
|
43
|
+
expect { subject.get(url_builder.sensitive_url) }
|
44
|
+
.to raise_error GoogleDistanceMatrix::ClientError
|
38
45
|
end
|
39
46
|
end
|
40
47
|
end
|
41
48
|
|
42
|
-
describe
|
43
|
-
describe
|
44
|
-
before { stub_request(:get, url).to_return status: [500,
|
49
|
+
describe 'request errors' do
|
50
|
+
describe 'server error' do
|
51
|
+
before { stub_request(:get, url).to_return status: [500, 'Internal Server Error'] }
|
45
52
|
|
46
|
-
it
|
47
|
-
expect { subject.get(url_builder.
|
53
|
+
it 'wraps the error http response' do
|
54
|
+
expect { subject.get(url_builder.sensitive_url) }
|
55
|
+
.to raise_error GoogleDistanceMatrix::ServerError
|
48
56
|
end
|
49
57
|
end
|
50
58
|
|
51
|
-
describe
|
59
|
+
describe 'timeout' do
|
52
60
|
before { stub_request(:get, url).to_timeout }
|
53
61
|
|
54
|
-
it
|
55
|
-
expect { subject.get(url_builder.
|
62
|
+
it 'wraps the error from Net::HTTP' do
|
63
|
+
expect { subject.get(url_builder.sensitive_url).body }
|
64
|
+
.to raise_error GoogleDistanceMatrix::ServerError
|
56
65
|
end
|
57
66
|
end
|
58
67
|
|
59
|
-
describe
|
60
|
-
before { stub_request(:get, url).to_return status: [999,
|
68
|
+
describe 'server error' do
|
69
|
+
before { stub_request(:get, url).to_return status: [999, 'Unknown'] }
|
61
70
|
|
62
|
-
it
|
63
|
-
expect { subject.get(url_builder.
|
71
|
+
it 'wraps the error http response' do
|
72
|
+
expect { subject.get(url_builder.sensitive_url) }
|
73
|
+
.to raise_error GoogleDistanceMatrix::ServerError
|
64
74
|
end
|
65
75
|
end
|
66
76
|
end
|
@@ -1,11 +1,13 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'spec_helper'
|
2
4
|
|
3
5
|
describe GoogleDistanceMatrix::Configuration do
|
4
6
|
include Shoulda::Matchers::ActiveModel
|
5
7
|
|
6
8
|
subject { described_class.new }
|
7
9
|
|
8
|
-
describe
|
10
|
+
describe 'Validations' do
|
9
11
|
describe 'departure_time' do
|
10
12
|
it 'is valid with a timestamp' do
|
11
13
|
subject.departure_time = Time.now.to_i
|
@@ -46,28 +48,32 @@ describe GoogleDistanceMatrix::Configuration do
|
|
46
48
|
end
|
47
49
|
end
|
48
50
|
|
49
|
-
it { should validate_inclusion_of(:mode).in_array([
|
51
|
+
it { should validate_inclusion_of(:mode).in_array(%w[driving walking bicycling transit]) }
|
50
52
|
it { should allow_value(nil).for(:mode) }
|
51
53
|
|
52
|
-
it { should validate_inclusion_of(:avoid).in_array([
|
54
|
+
it { should validate_inclusion_of(:avoid).in_array(%w[tolls highways ferries indoor]) }
|
53
55
|
it { should allow_value(nil).for(:avoid) }
|
54
56
|
|
55
|
-
it { should validate_inclusion_of(:units).in_array([
|
57
|
+
it { should validate_inclusion_of(:units).in_array(%w[metric imperial]) }
|
56
58
|
it { should allow_value(nil).for(:units) }
|
57
59
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
it {
|
62
|
-
|
63
|
-
|
60
|
+
it { should validate_inclusion_of(:protocol).in_array(%w[http https]) }
|
61
|
+
|
62
|
+
it { should validate_inclusion_of(:transit_mode).in_array(%w[bus subway train tram rail]) }
|
63
|
+
it {
|
64
|
+
should validate_inclusion_of(
|
65
|
+
:transit_routing_preference
|
66
|
+
).in_array(%w[less_walking fewer_transfers])
|
67
|
+
}
|
68
|
+
it {
|
69
|
+
should validate_inclusion_of(:traffic_model).in_array(%w[best_guess pessimistic optimistic])
|
70
|
+
}
|
64
71
|
end
|
65
72
|
|
66
|
-
|
67
|
-
|
68
|
-
it { expect(subject.mode).to eq "driving" }
|
73
|
+
describe 'defaults' do
|
74
|
+
it { expect(subject.mode).to eq 'driving' }
|
69
75
|
it { expect(subject.avoid).to be_nil }
|
70
|
-
it { expect(subject.units).to eq
|
76
|
+
it { expect(subject.units).to eq 'metric' }
|
71
77
|
it { expect(subject.lat_lng_scale).to eq 5 }
|
72
78
|
it { expect(subject.use_encoded_polylines).to eq false }
|
73
79
|
it { expect(subject.protocol).to eq 'https' }
|
@@ -84,13 +90,19 @@ describe GoogleDistanceMatrix::Configuration do
|
|
84
90
|
|
85
91
|
it { expect(subject.logger).to be_nil }
|
86
92
|
it { expect(subject.cache).to be_nil }
|
87
|
-
end
|
88
93
|
|
94
|
+
# rubocop:disable Metrics/LineLength
|
95
|
+
it 'has a default expected cache_key_transform' do
|
96
|
+
key = subject.cache_key_transform.call('foo')
|
97
|
+
expect(key).to eq 'f7fbba6e0636f890e56fbbf3283e524c6fa3204ae298382d624741d0dc6638326e282c41be5e4254d8820772c5518a2c5a8c0c7f7eda19594a7eb539453e1ed7'
|
98
|
+
end
|
99
|
+
# rubocop:enable Metrics/LineLength
|
100
|
+
end
|
89
101
|
|
90
|
-
describe
|
102
|
+
describe '#to_param' do
|
91
103
|
described_class::ATTRIBUTES.each do |attr|
|
92
104
|
it "includes #{attr}" do
|
93
|
-
subject[attr] =
|
105
|
+
subject[attr] = 'foo'
|
94
106
|
expect(subject.to_param[attr]).to eq subject.public_send(attr)
|
95
107
|
end
|
96
108
|
|
@@ -108,14 +120,14 @@ describe GoogleDistanceMatrix::Configuration do
|
|
108
120
|
end
|
109
121
|
end
|
110
122
|
|
111
|
-
it
|
112
|
-
subject.google_business_api_client_id =
|
113
|
-
expect(subject.to_param['client']).to eq
|
123
|
+
it 'includes client if google_business_api_client_id has been set' do
|
124
|
+
subject.google_business_api_client_id = '123'
|
125
|
+
expect(subject.to_param['client']).to eq '123'
|
114
126
|
end
|
115
127
|
|
116
|
-
it
|
117
|
-
subject.google_api_key =
|
118
|
-
expect(subject.to_param['key']).to eq(
|
128
|
+
it 'includes key if google_api_key has been set' do
|
129
|
+
subject.google_api_key = '12345'
|
130
|
+
expect(subject.to_param['key']).to eq('12345')
|
119
131
|
end
|
120
132
|
end
|
121
133
|
end
|