google_distance_matrix 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +17 -0
- data/.rbenv-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +106 -0
- data/Rakefile +1 -0
- data/google_distance_matrix.gemspec +30 -0
- data/lib/google_distance_matrix.rb +38 -0
- data/lib/google_distance_matrix/client.rb +47 -0
- data/lib/google_distance_matrix/configuration.rb +68 -0
- data/lib/google_distance_matrix/errors.rb +88 -0
- data/lib/google_distance_matrix/log_subscriber.rb +14 -0
- data/lib/google_distance_matrix/logger.rb +32 -0
- data/lib/google_distance_matrix/matrix.rb +122 -0
- data/lib/google_distance_matrix/place.rb +101 -0
- data/lib/google_distance_matrix/places.rb +43 -0
- data/lib/google_distance_matrix/railtie.rb +9 -0
- data/lib/google_distance_matrix/route.rb +49 -0
- data/lib/google_distance_matrix/routes_finder.rb +149 -0
- data/lib/google_distance_matrix/url_builder.rb +63 -0
- data/lib/google_distance_matrix/version.rb +3 -0
- data/spec/lib/google_distance_matrix/client_spec.rb +67 -0
- data/spec/lib/google_distance_matrix/configuration_spec.rb +63 -0
- data/spec/lib/google_distance_matrix/logger_spec.rb +38 -0
- data/spec/lib/google_distance_matrix/matrix_spec.rb +169 -0
- data/spec/lib/google_distance_matrix/place_spec.rb +93 -0
- data/spec/lib/google_distance_matrix/places_spec.rb +77 -0
- data/spec/lib/google_distance_matrix/route_spec.rb +28 -0
- data/spec/lib/google_distance_matrix/routes_finder_spec.rb +190 -0
- data/spec/lib/google_distance_matrix/url_builder_spec.rb +105 -0
- data/spec/request_recordings/success +62 -0
- data/spec/request_recordings/zero_results +57 -0
- data/spec/spec_helper.rb +24 -0
- metadata +225 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GoogleDistanceMatrix::Places do
|
4
|
+
let(:values) { [{address: "one"}, {address: "two"}, {address: "three"}] }
|
5
|
+
let(:places) { values.map { |v| GoogleDistanceMatrix::Place.new v } }
|
6
|
+
|
7
|
+
let(:place_4) { GoogleDistanceMatrix::Place.new address: "four" }
|
8
|
+
let(:place_5) { GoogleDistanceMatrix::Place.new address: "five" }
|
9
|
+
let(:place_6) { GoogleDistanceMatrix::Place.new address: "six" }
|
10
|
+
|
11
|
+
subject { described_class.new places }
|
12
|
+
|
13
|
+
it { should include *places }
|
14
|
+
it { should_not include 5 }
|
15
|
+
|
16
|
+
|
17
|
+
%w[<< push unshift].each do |attr|
|
18
|
+
describe "#{attr}" do
|
19
|
+
it "adds value" do
|
20
|
+
expect {
|
21
|
+
subject.public_send attr, place_4
|
22
|
+
}.to change { subject.include? place_4 }.to true
|
23
|
+
end
|
24
|
+
|
25
|
+
it "keeps uniq values" do
|
26
|
+
subject.public_send attr, place_4
|
27
|
+
|
28
|
+
expect {
|
29
|
+
subject.public_send attr, place_4
|
30
|
+
}.to_not change subject, :length
|
31
|
+
end
|
32
|
+
|
33
|
+
it "is chanable" do
|
34
|
+
subject.public_send(attr, place_5).public_send(attr, place_6)
|
35
|
+
|
36
|
+
expect(subject).to include place_5, place_6
|
37
|
+
end
|
38
|
+
|
39
|
+
it "wraps values in a Place" do
|
40
|
+
subject.public_send attr, {address: "four"}
|
41
|
+
|
42
|
+
expect(subject.all? { |place| place.is_a? GoogleDistanceMatrix::Place }).to be_true
|
43
|
+
expect(subject.any? { |place| place.address == "four" }).to be_true
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
%w[push unshift].each do |attr|
|
49
|
+
describe "#{attr}" do
|
50
|
+
it "adds multiple values at once" do
|
51
|
+
subject.public_send attr, place_4, place_5
|
52
|
+
expect(subject).to include place_4, place_5
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe "#concat" do
|
58
|
+
let(:places_2) { [place_4, place_5, place_6] }
|
59
|
+
|
60
|
+
it "adds the given array" do
|
61
|
+
subject.concat places_2
|
62
|
+
expect(subject).to include *places_2
|
63
|
+
end
|
64
|
+
|
65
|
+
it "keeps values uniq" do
|
66
|
+
subject.concat places_2
|
67
|
+
|
68
|
+
expect {
|
69
|
+
subject.concat places_2
|
70
|
+
}.to_not change subject, :length
|
71
|
+
end
|
72
|
+
|
73
|
+
it "returns self" do
|
74
|
+
expect(subject.concat places_2).to eq subject
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GoogleDistanceMatrix::Route do
|
4
|
+
let(:attributes) do
|
5
|
+
{
|
6
|
+
"distance" => {"text" => "2.0 km", "value" => 2032},
|
7
|
+
"duration" => {"text" =>"6 mins", "value" => 367},
|
8
|
+
"status" =>"OK"
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
subject { described_class.new attributes }
|
13
|
+
|
14
|
+
its(:status) { should eq "ok" }
|
15
|
+
its(:distance_in_meters) { should eq 2032 }
|
16
|
+
its(:distance_text) { should eq "2.0 km" }
|
17
|
+
its(:duration_in_seconds) { should eq 367 }
|
18
|
+
its(:duration_text) { should eq "6 mins" }
|
19
|
+
|
20
|
+
describe "deprecations" do
|
21
|
+
around { |example| ActiveSupport::Deprecation.silence { example.run } }
|
22
|
+
|
23
|
+
its(:duration_value) { should eq 367 }
|
24
|
+
its(:distance_value) { should eq 2032 }
|
25
|
+
end
|
26
|
+
|
27
|
+
it { should be_ok }
|
28
|
+
end
|
@@ -0,0 +1,190 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GoogleDistanceMatrix::RoutesFinder, :request_recordings do
|
4
|
+
let(:origin_1) { GoogleDistanceMatrix::Place.new address: "Karl Johans gate, Oslo" }
|
5
|
+
let(:origin_2) { GoogleDistanceMatrix::Place.new address: "Askerveien 1, Asker" }
|
6
|
+
|
7
|
+
let(:destination_1) { GoogleDistanceMatrix::Place.new address: "Drammensveien 1, Oslo" }
|
8
|
+
|
9
|
+
let(:destination_2_built_from) { mock address: "Skjellestadhagen, Heggedal" }
|
10
|
+
let(:destination_2) { GoogleDistanceMatrix::Place.new destination_2_built_from }
|
11
|
+
|
12
|
+
let(:url_builder) { GoogleDistanceMatrix::UrlBuilder.new matrix }
|
13
|
+
let(:url) { url_builder.url }
|
14
|
+
|
15
|
+
let(:matrix) do
|
16
|
+
GoogleDistanceMatrix::Matrix.new(
|
17
|
+
origins: [origin_1, origin_2],
|
18
|
+
destinations: [destination_1, destination_2]
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
subject { described_class.new matrix }
|
23
|
+
|
24
|
+
context "success" do
|
25
|
+
let!(:api_request_stub) { stub_request(:get, url).to_return body: recorded_request_for(:success) }
|
26
|
+
|
27
|
+
describe "#routes_for" do
|
28
|
+
it "fails if given place does not exist" do
|
29
|
+
expect { subject.routes_for "foo" }.to raise_error ArgumentError
|
30
|
+
end
|
31
|
+
|
32
|
+
it "returns routes for given origin" do
|
33
|
+
routes = subject.routes_for origin_1
|
34
|
+
|
35
|
+
expect(routes.length).to eq 2
|
36
|
+
expect(routes.map(&:origin).all? { |o| o == origin_1 }).to be true
|
37
|
+
end
|
38
|
+
|
39
|
+
it "returns routes for given destination" do
|
40
|
+
routes = subject.routes_for destination_2
|
41
|
+
|
42
|
+
expect(routes.length).to eq 2
|
43
|
+
expect(routes.map(&:destination).all? { |d| d == destination_2 }).to be true
|
44
|
+
end
|
45
|
+
|
46
|
+
it "returns routes for given object a place was built from" do
|
47
|
+
routes = subject.routes_for destination_2_built_from
|
48
|
+
|
49
|
+
expect(routes.length).to eq 2
|
50
|
+
expect(routes.map(&:destination).all? { |d| d == destination_2 }).to be true
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
describe "#routes_for!" do
|
55
|
+
it "returns the same as routes_for" do
|
56
|
+
expect(subject.routes_for! origin_1).to eq subject.routes_for(origin_1)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
describe "#route_for" do
|
61
|
+
it "returns route" do
|
62
|
+
route = subject.route_for(origin: origin_1, destination: destination_1)
|
63
|
+
expect(route.origin).to eq origin_1
|
64
|
+
expect(route.destination).to eq destination_1
|
65
|
+
end
|
66
|
+
|
67
|
+
it "returns route when you give it the object a place was built from" do
|
68
|
+
route = subject.route_for(origin: origin_1, destination: destination_2_built_from)
|
69
|
+
expect(route.origin).to eq origin_1
|
70
|
+
expect(route.destination).to eq destination_2
|
71
|
+
end
|
72
|
+
|
73
|
+
it "fails with argument error if origin is missing" do
|
74
|
+
expect { subject.route_for destination: destination_2 }.to raise_error ArgumentError
|
75
|
+
end
|
76
|
+
|
77
|
+
it "fails with argument error if destination is missing" do
|
78
|
+
expect { subject.route_for origin: origin_1 }.to raise_error ArgumentError
|
79
|
+
end
|
80
|
+
|
81
|
+
it "fails with argument error if sent in object is neither place nor something it was built from" do
|
82
|
+
expect { subject.route_for origin: origin_1, destination: mock }.to raise_error ArgumentError
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe "#route_for!" do
|
87
|
+
it "returns the same as route_for" do
|
88
|
+
route = subject.route_for(origin: origin_1, destination: destination_1)
|
89
|
+
route_bang = subject.route_for!(origin: origin_1, destination: destination_1)
|
90
|
+
|
91
|
+
expect(route).to eq route_bang
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
describe "#shortest_route_by_distance_to" do
|
96
|
+
it "returns route representing shortest distance to given origin" do
|
97
|
+
expect(subject.shortest_route_by_distance_to(origin_1)).to eq matrix.data[0][0]
|
98
|
+
end
|
99
|
+
|
100
|
+
it "returns route representing shortest distance to given destination" do
|
101
|
+
expect(subject.shortest_route_by_distance_to(destination_2)).to eq matrix.data[1][1]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "#shortest_route_by_distance_to!" do
|
106
|
+
it "returns the same as shortest_route_by_distance_to" do
|
107
|
+
expect(subject.shortest_route_by_distance_to!(origin_1)).to eq subject.shortest_route_by_distance_to(origin_1)
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
describe "#shortest_route_by_duration_to" do
|
112
|
+
it "returns route representing shortest duration to given origin" do
|
113
|
+
expect(subject.shortest_route_by_duration_to(origin_1)).to eq matrix.data[0][0]
|
114
|
+
end
|
115
|
+
|
116
|
+
it "returns route representing shortest duration to given destination" do
|
117
|
+
expect(subject.shortest_route_by_duration_to(destination_2)).to eq matrix.data[1][1]
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "#shortest_route_by_duration_to!" do
|
122
|
+
it "returns the same as shortest_route_by_duration_to" do
|
123
|
+
expect(subject.shortest_route_by_duration_to!(origin_1)).to eq subject.shortest_route_by_duration_to(origin_1)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
context "routes mssing data" do
|
129
|
+
let!(:api_request_stub) { stub_request(:get, url).to_return body: recorded_request_for(:zero_results) }
|
130
|
+
|
131
|
+
describe "#routes_for" do
|
132
|
+
it "returns routes for given origin" do
|
133
|
+
routes = subject.routes_for origin_1
|
134
|
+
|
135
|
+
expect(routes.length).to eq 2
|
136
|
+
expect(routes.map(&:origin).all? { |o| o == origin_1 }).to be true
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "#routes_for!" do
|
141
|
+
it "fails upon any non-ok route" do
|
142
|
+
expect {
|
143
|
+
subject.routes_for! origin_1
|
144
|
+
}.to raise_error GoogleDistanceMatrix::InvalidRoute
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
|
149
|
+
describe "#route_for" do
|
150
|
+
it "returns route" do
|
151
|
+
route = subject.route_for origin: origin_1, destination: destination_2
|
152
|
+
expect(route.origin).to eq origin_1
|
153
|
+
expect(route.destination).to eq destination_2
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "#route_for!" do
|
158
|
+
it "fails upon non-ok route" do
|
159
|
+
expect {
|
160
|
+
subject.route_for! origin: origin_1, destination: destination_2
|
161
|
+
}.to raise_error GoogleDistanceMatrix::InvalidRoute
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
|
166
|
+
describe "#shortest_route_by_distance_to" do
|
167
|
+
it "returns route representing shortest distance to given origin" do
|
168
|
+
expect(subject.shortest_route_by_distance_to(origin_1)).to eq matrix.data[0][0]
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
describe "#shortest_route_by_distance_to!" do
|
173
|
+
it "fails upon non-ok route" do
|
174
|
+
expect { subject.shortest_route_by_distance_to!(origin_1) }.to raise_error GoogleDistanceMatrix::InvalidRoute
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "#shortest_route_by_duration_to" do
|
179
|
+
it "returns route representing shortest distance to given origin" do
|
180
|
+
expect(subject.shortest_route_by_duration_to(origin_1)).to eq matrix.data[0][0]
|
181
|
+
end
|
182
|
+
end
|
183
|
+
|
184
|
+
describe "#shortest_route_by_duration_to!" do
|
185
|
+
it "fails upon non-ok route" do
|
186
|
+
expect { subject.shortest_route_by_duration_to!(origin_1) }.to raise_error GoogleDistanceMatrix::InvalidRoute
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GoogleDistanceMatrix::UrlBuilder do
|
4
|
+
let(:delimiter) { described_class::DELIMITER }
|
5
|
+
let(:comma) { CGI.escape "," }
|
6
|
+
|
7
|
+
let(:origin_1) { GoogleDistanceMatrix::Place.new address: "address_origin_1" }
|
8
|
+
let(:origin_2) { GoogleDistanceMatrix::Place.new address: "address_origin_2" }
|
9
|
+
|
10
|
+
let(:destination_1) { GoogleDistanceMatrix::Place.new lat: 1, lng: 11 }
|
11
|
+
let(:destination_2) { GoogleDistanceMatrix::Place.new lat: 2, lng: 22 }
|
12
|
+
|
13
|
+
let(:origins) { [origin_1, origin_2] }
|
14
|
+
let(:destinations) { [destination_1, destination_2] }
|
15
|
+
|
16
|
+
let(:matrix) do
|
17
|
+
GoogleDistanceMatrix::Matrix.new(
|
18
|
+
origins: origins,
|
19
|
+
destinations: destinations
|
20
|
+
)
|
21
|
+
end
|
22
|
+
|
23
|
+
subject { described_class.new matrix }
|
24
|
+
|
25
|
+
describe "#initialize" do
|
26
|
+
it "has a matrix" do
|
27
|
+
expect(described_class.new(matrix).matrix).to eq matrix
|
28
|
+
end
|
29
|
+
|
30
|
+
it "fails if matrix is invalid" do
|
31
|
+
expect {
|
32
|
+
described_class.new GoogleDistanceMatrix::Matrix.new
|
33
|
+
}.to raise_error GoogleDistanceMatrix::InvalidMatrix
|
34
|
+
end
|
35
|
+
|
36
|
+
it "fails if matrix's configuration is invalid" do
|
37
|
+
expect {
|
38
|
+
matrix.configure { |c| c.sensor = nil }
|
39
|
+
described_class.new matrix
|
40
|
+
}.to raise_error GoogleDistanceMatrix::InvalidMatrix
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
|
45
|
+
describe "#url" do
|
46
|
+
it "fails if the url is more than 2048 characters" do
|
47
|
+
long_string = ""
|
48
|
+
2049.times { long_string << "a" }
|
49
|
+
|
50
|
+
subject.stub(:get_params_string).and_return long_string
|
51
|
+
|
52
|
+
expect { subject.url }.to raise_error GoogleDistanceMatrix::MatrixUrlTooLong
|
53
|
+
end
|
54
|
+
|
55
|
+
it "starts with the base URL" do
|
56
|
+
expect(subject.url).to start_with "http://" + described_class::BASE_URL
|
57
|
+
end
|
58
|
+
|
59
|
+
it "has a configurable protocol" do
|
60
|
+
matrix.configure { |c| c.protocol = "https" }
|
61
|
+
expect(subject.url).to start_with "https://"
|
62
|
+
end
|
63
|
+
|
64
|
+
it "includes origins" do
|
65
|
+
expect(subject.url).to include "origins=address_origin_1#{delimiter}address_origin_2"
|
66
|
+
end
|
67
|
+
|
68
|
+
it "includes destinations" do
|
69
|
+
expect(subject.url).to include "destinations=1#{comma}11#{delimiter}2#{comma}22"
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "lat lng scale" do
|
73
|
+
let(:destination_1) { GoogleDistanceMatrix::Place.new lat: 10.123456789, lng: "10.987654321" }
|
74
|
+
|
75
|
+
it "rounds lat and lng" do
|
76
|
+
subject.matrix.configure { |c| c.lat_lng_scale = 5 }
|
77
|
+
|
78
|
+
expect(subject.url).to include "destinations=10.12346#{comma}10.98765"
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "configuration" do
|
83
|
+
it "includes sensor" do
|
84
|
+
expect(subject.url).to include "sensor=#{matrix.configuration.sensor}"
|
85
|
+
end
|
86
|
+
|
87
|
+
context "with google business client id and private key set" do
|
88
|
+
before do
|
89
|
+
matrix.configure do |config|
|
90
|
+
config.google_business_api_client_id = "123"
|
91
|
+
config.google_business_api_private_key = "secret"
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
it "includes client" do
|
96
|
+
expect(subject.url).to include "client=#{matrix.configuration.google_business_api_client_id}"
|
97
|
+
end
|
98
|
+
|
99
|
+
it "has signature" do
|
100
|
+
expect(subject.url).to include "signature=gskXXJxOmPDXE1jAwafNGM2CVTI"
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
{
|
2
|
+
"destination_addresses" : [
|
3
|
+
"Drammensveien 1, 0271 Oslo, Norway",
|
4
|
+
"Skjellestadhagen, 1389 Heggedal, Norway"
|
5
|
+
],
|
6
|
+
"origin_addresses" : [ "Karl Johans gate, Oslo, Norway", "Askerveien 1, 1384 Asker, Norway" ],
|
7
|
+
"rows" : [
|
8
|
+
{
|
9
|
+
"elements" : [
|
10
|
+
{
|
11
|
+
"distance" : {
|
12
|
+
"text" : "2.0 km",
|
13
|
+
"value" : 2032
|
14
|
+
},
|
15
|
+
"duration" : {
|
16
|
+
"text" : "6 mins",
|
17
|
+
"value" : 367
|
18
|
+
},
|
19
|
+
"status" : "OK"
|
20
|
+
},
|
21
|
+
{
|
22
|
+
"distance" : {
|
23
|
+
"text" : "30.3 km",
|
24
|
+
"value" : 30345
|
25
|
+
},
|
26
|
+
"duration" : {
|
27
|
+
"text" : "32 mins",
|
28
|
+
"value" : 1924
|
29
|
+
},
|
30
|
+
"status" : "OK"
|
31
|
+
}
|
32
|
+
]
|
33
|
+
},
|
34
|
+
{
|
35
|
+
"elements" : [
|
36
|
+
{
|
37
|
+
"distance" : {
|
38
|
+
"text" : "21.2 km",
|
39
|
+
"value" : 21246
|
40
|
+
},
|
41
|
+
"duration" : {
|
42
|
+
"text" : "22 mins",
|
43
|
+
"value" : 1315
|
44
|
+
},
|
45
|
+
"status" : "OK"
|
46
|
+
},
|
47
|
+
{
|
48
|
+
"distance" : {
|
49
|
+
"text" : "9.1 km",
|
50
|
+
"value" : 9103
|
51
|
+
},
|
52
|
+
"duration" : {
|
53
|
+
"text" : "15 mins",
|
54
|
+
"value" : 878
|
55
|
+
},
|
56
|
+
"status" : "OK"
|
57
|
+
}
|
58
|
+
]
|
59
|
+
}
|
60
|
+
],
|
61
|
+
"status" : "OK"
|
62
|
+
}
|