google_distance_matrix 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (34) hide show
  1. data/.gitignore +17 -0
  2. data/.rbenv-version +1 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +106 -0
  6. data/Rakefile +1 -0
  7. data/google_distance_matrix.gemspec +30 -0
  8. data/lib/google_distance_matrix.rb +38 -0
  9. data/lib/google_distance_matrix/client.rb +47 -0
  10. data/lib/google_distance_matrix/configuration.rb +68 -0
  11. data/lib/google_distance_matrix/errors.rb +88 -0
  12. data/lib/google_distance_matrix/log_subscriber.rb +14 -0
  13. data/lib/google_distance_matrix/logger.rb +32 -0
  14. data/lib/google_distance_matrix/matrix.rb +122 -0
  15. data/lib/google_distance_matrix/place.rb +101 -0
  16. data/lib/google_distance_matrix/places.rb +43 -0
  17. data/lib/google_distance_matrix/railtie.rb +9 -0
  18. data/lib/google_distance_matrix/route.rb +49 -0
  19. data/lib/google_distance_matrix/routes_finder.rb +149 -0
  20. data/lib/google_distance_matrix/url_builder.rb +63 -0
  21. data/lib/google_distance_matrix/version.rb +3 -0
  22. data/spec/lib/google_distance_matrix/client_spec.rb +67 -0
  23. data/spec/lib/google_distance_matrix/configuration_spec.rb +63 -0
  24. data/spec/lib/google_distance_matrix/logger_spec.rb +38 -0
  25. data/spec/lib/google_distance_matrix/matrix_spec.rb +169 -0
  26. data/spec/lib/google_distance_matrix/place_spec.rb +93 -0
  27. data/spec/lib/google_distance_matrix/places_spec.rb +77 -0
  28. data/spec/lib/google_distance_matrix/route_spec.rb +28 -0
  29. data/spec/lib/google_distance_matrix/routes_finder_spec.rb +190 -0
  30. data/spec/lib/google_distance_matrix/url_builder_spec.rb +105 -0
  31. data/spec/request_recordings/success +62 -0
  32. data/spec/request_recordings/zero_results +57 -0
  33. data/spec/spec_helper.rb +24 -0
  34. 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
+ }