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