google_distance_matrix 0.0.1
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.
- 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
|
+
}
|