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,63 @@
|
|
1
|
+
module GoogleDistanceMatrix
|
2
|
+
class UrlBuilder
|
3
|
+
BASE_URL = "maps.googleapis.com/maps/api/distancematrix/json"
|
4
|
+
DELIMITER = CGI.escape("|")
|
5
|
+
MAX_URL_SIZE = 2048
|
6
|
+
|
7
|
+
attr_reader :matrix
|
8
|
+
delegate :configuration, to: :matrix
|
9
|
+
|
10
|
+
def initialize(matrix)
|
11
|
+
@matrix = matrix
|
12
|
+
|
13
|
+
fail InvalidMatrix.new matrix if matrix.invalid?
|
14
|
+
end
|
15
|
+
|
16
|
+
def url
|
17
|
+
@url ||= build_url
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
def build_url
|
24
|
+
url = [protocol, BASE_URL, "?", get_params_string].join
|
25
|
+
|
26
|
+
if sign_url?
|
27
|
+
url = GoogleBusinessApiUrlSigner.add_signature(url, configuration.google_business_api_private_key)
|
28
|
+
end
|
29
|
+
|
30
|
+
if url.length > MAX_URL_SIZE
|
31
|
+
fail MatrixUrlTooLong.new url, MAX_URL_SIZE
|
32
|
+
end
|
33
|
+
|
34
|
+
url
|
35
|
+
end
|
36
|
+
|
37
|
+
def sign_url?
|
38
|
+
configuration.google_business_api_client_id.present? and
|
39
|
+
configuration.google_business_api_private_key.present?
|
40
|
+
end
|
41
|
+
|
42
|
+
def get_params_string
|
43
|
+
params.to_a.map { |key_value| key_value.join("=") }.join("&")
|
44
|
+
end
|
45
|
+
|
46
|
+
def params
|
47
|
+
places_to_param_config = {lat_lng_scale: configuration.lat_lng_scale}
|
48
|
+
|
49
|
+
configuration.to_param.merge(
|
50
|
+
origins: matrix.origins.map { |o| escape o.to_param(places_to_param_config) }.join(DELIMITER),
|
51
|
+
destinations: matrix.destinations.map { |d| escape d.to_param(places_to_param_config) }.join(DELIMITER),
|
52
|
+
)
|
53
|
+
end
|
54
|
+
|
55
|
+
def protocol
|
56
|
+
configuration.protocol + "://"
|
57
|
+
end
|
58
|
+
|
59
|
+
def escape(string)
|
60
|
+
CGI.escape string
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GoogleDistanceMatrix::Client, :request_recordings do
|
4
|
+
let(:origin_1) { GoogleDistanceMatrix::Place.new address: "Karl Johans gate, Oslo" }
|
5
|
+
let(:destination_1) { GoogleDistanceMatrix::Place.new address: "Drammensveien 1, Oslo" }
|
6
|
+
let(:matrix) { GoogleDistanceMatrix::Matrix.new(origins: [origin_1], destinations: [destination_1]) }
|
7
|
+
|
8
|
+
let(:url_builder) { GoogleDistanceMatrix::UrlBuilder.new matrix }
|
9
|
+
let(:url) { url_builder.url }
|
10
|
+
|
11
|
+
subject { GoogleDistanceMatrix::Client.new }
|
12
|
+
|
13
|
+
describe "success" do
|
14
|
+
before { stub_request(:get, url).to_return body: recorded_request_for(:success) }
|
15
|
+
|
16
|
+
it "makes the request" do
|
17
|
+
expect(subject.get(url_builder.url).body).to eq recorded_request_for(:success).read
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "client errors" do
|
22
|
+
describe "server issues 4xx client error" do
|
23
|
+
it "wraps the error http response" do
|
24
|
+
stub_request(:get, url).to_return status: [400, "Client error"]
|
25
|
+
expect { subject.get(url_builder.url) }.to raise_error GoogleDistanceMatrix::ClientError
|
26
|
+
end
|
27
|
+
|
28
|
+
it "wraps uri too long error" do
|
29
|
+
stub_request(:get, url).to_return status: [414, "Client error"]
|
30
|
+
expect { subject.get(url_builder.url) }.to raise_error GoogleDistanceMatrix::MatrixUrlTooLong
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
described_class::CLIENT_ERRORS.each do |error|
|
35
|
+
it "wraps '#{error}' client error" do
|
36
|
+
stub_request(:get, url).to_return body: JSON.generate({status: error})
|
37
|
+
expect { subject.get(url_builder.url) }.to raise_error GoogleDistanceMatrix::ClientError
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe "request errors" do
|
43
|
+
describe "server error" do
|
44
|
+
before { stub_request(:get, url).to_return status: [500, "Internal Server Error"] }
|
45
|
+
|
46
|
+
it "wraps the error http response" do
|
47
|
+
expect { subject.get(url_builder.url) }.to raise_error GoogleDistanceMatrix::RequestError
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "timeout" do
|
52
|
+
before { stub_request(:get, url).to_timeout }
|
53
|
+
|
54
|
+
it "wraps the error from Net::HTTP" do
|
55
|
+
expect { subject.get(url_builder.url).body }.to raise_error GoogleDistanceMatrix::RequestError
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe "server error" do
|
60
|
+
before { stub_request(:get, url).to_return status: [999, "Unknown"] }
|
61
|
+
|
62
|
+
it "wraps the error http response" do
|
63
|
+
expect { subject.get(url_builder.url) }.to raise_error GoogleDistanceMatrix::RequestError
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GoogleDistanceMatrix::Configuration do
|
4
|
+
subject { described_class.new }
|
5
|
+
|
6
|
+
describe "Validations" do
|
7
|
+
it { should ensure_inclusion_of(:sensor).in_array([true, false]) }
|
8
|
+
|
9
|
+
it { should ensure_inclusion_of(:mode).in_array(["driving", "walking", "bicycling"]) }
|
10
|
+
it { should allow_value(nil).for(:mode) }
|
11
|
+
|
12
|
+
it { should ensure_inclusion_of(:avoid).in_array(["tolls", "highways"]) }
|
13
|
+
it { should allow_value(nil).for(:avoid) }
|
14
|
+
|
15
|
+
it { should ensure_inclusion_of(:units).in_array(["metric", "imperial"]) }
|
16
|
+
it { should allow_value(nil).for(:units) }
|
17
|
+
|
18
|
+
it { should ensure_inclusion_of(:protocol).in_array(["http", "https"]) }
|
19
|
+
end
|
20
|
+
|
21
|
+
|
22
|
+
describe "defaults" do
|
23
|
+
its(:sensor) { should be_false }
|
24
|
+
its(:mode) { should eq "driving" }
|
25
|
+
its(:avoid) { should be_nil }
|
26
|
+
its(:units) { should eq "metric" }
|
27
|
+
its(:lat_lng_scale) { should eq 5 }
|
28
|
+
its(:protocol) { should eq "http" }
|
29
|
+
|
30
|
+
its(:google_business_api_client_id) { should be_nil }
|
31
|
+
its(:google_business_api_private_key) { should be_nil }
|
32
|
+
|
33
|
+
its(:logger) { should be_nil }
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
describe "#to_param" do
|
38
|
+
described_class::ATTRIBUTES.each do |attr|
|
39
|
+
it "includes #{attr}" do
|
40
|
+
subject[attr] = "foo"
|
41
|
+
expect(subject.to_param[attr]).to eq subject.public_send(attr)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "does not include #{attr} when it is blank" do
|
45
|
+
subject[attr] = nil
|
46
|
+
expect(subject.to_param.with_indifferent_access).to_not have_key attr
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
described_class::API_DEFAULTS.each_pair do |attr, default_value|
|
51
|
+
it "does not include #{attr} when it equals what is default for API" do
|
52
|
+
subject[attr] = default_value
|
53
|
+
|
54
|
+
expect(subject.to_param.with_indifferent_access).to_not have_key attr
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
it "includes client if google_business_api_client_id has been set" do
|
59
|
+
subject.google_business_api_client_id = "123"
|
60
|
+
expect(subject.to_param['client']).to eq "123"
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GoogleDistanceMatrix::Logger do
|
4
|
+
context "without a logger backend" do
|
5
|
+
subject { described_class.new }
|
6
|
+
|
7
|
+
described_class::LEVELS.each do |level|
|
8
|
+
it "logging #{level} does not fail" do
|
9
|
+
subject.public_send level, "log msg"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
context "with a logger backend" do
|
15
|
+
let(:backend) { mock }
|
16
|
+
|
17
|
+
subject { described_class.new backend }
|
18
|
+
|
19
|
+
described_class::LEVELS.each do |level|
|
20
|
+
describe level do
|
21
|
+
it "sends log message to the backend" do
|
22
|
+
backend.should_receive(level).with("[google_distance_matrix] log msg")
|
23
|
+
subject.public_send level, "log msg"
|
24
|
+
end
|
25
|
+
|
26
|
+
it "supports sending in a tag" do
|
27
|
+
backend.should_receive(level).with("[google_distance_matrix] [client] log msg")
|
28
|
+
subject.public_send level, "log msg", tag: :client
|
29
|
+
end
|
30
|
+
|
31
|
+
it "supports sending in multiple tags" do
|
32
|
+
backend.should_receive(level).with("[google_distance_matrix] [client] [request] log msg")
|
33
|
+
subject.public_send level, "log msg", tag: ['client', 'request']
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,169 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GoogleDistanceMatrix::Matrix 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
|
+
let(:destination_2) { GoogleDistanceMatrix::Place.new address: "Skjellestadhagen, Heggedal" }
|
9
|
+
|
10
|
+
let(:url_builder) { GoogleDistanceMatrix::UrlBuilder.new subject }
|
11
|
+
let(:url) { url_builder.url }
|
12
|
+
|
13
|
+
subject do
|
14
|
+
described_class.new(
|
15
|
+
origins: [origin_1, origin_2],
|
16
|
+
destinations: [destination_1, destination_2]
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
describe "#initialize" do
|
21
|
+
it "takes a list of origins" do
|
22
|
+
matrix = described_class.new origins: [origin_1, origin_2]
|
23
|
+
expect(matrix.origins).to include origin_1, origin_2
|
24
|
+
end
|
25
|
+
|
26
|
+
it "takes a list of destinations" do
|
27
|
+
matrix = described_class.new destinations: [destination_1, destination_2]
|
28
|
+
expect(matrix.destinations).to include destination_1, destination_2
|
29
|
+
end
|
30
|
+
|
31
|
+
it "has a default configuration" do
|
32
|
+
expect(subject.configuration).to be_present
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#configuration" do
|
37
|
+
it "is by default set from default_configuration" do
|
38
|
+
config = mock
|
39
|
+
config.stub(:dup).and_return config
|
40
|
+
GoogleDistanceMatrix.should_receive(:default_configuration).and_return config
|
41
|
+
|
42
|
+
expect(described_class.new.configuration).to eq config
|
43
|
+
end
|
44
|
+
|
45
|
+
it "has it's own configuration" do
|
46
|
+
expect {
|
47
|
+
subject.configure { |c| c.sensor = !GoogleDistanceMatrix.default_configuration.sensor }
|
48
|
+
}.to_not change(GoogleDistanceMatrix.default_configuration, :sensor)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "has a configurable configuration :-)" do
|
52
|
+
expect {
|
53
|
+
subject.configure { |c| c.sensor = !GoogleDistanceMatrix.default_configuration.sensor }
|
54
|
+
}.to change(subject.configuration, :sensor).to !GoogleDistanceMatrix.default_configuration.sensor
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
%w[origins destinations].each do |attr|
|
59
|
+
let(:place) { GoogleDistanceMatrix::Place.new address: "My street" }
|
60
|
+
|
61
|
+
describe "##{attr}" do
|
62
|
+
it "can receive places" do
|
63
|
+
subject.public_send(attr) << place
|
64
|
+
expect(subject.public_send(attr)).to include place
|
65
|
+
end
|
66
|
+
|
67
|
+
it "does not same place twice" do
|
68
|
+
expect {
|
69
|
+
2.times { subject.public_send(attr) << place }
|
70
|
+
}.to change(subject.public_send(attr), :length).by 1
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
%w[
|
76
|
+
route_for
|
77
|
+
route_for!
|
78
|
+
routes_for
|
79
|
+
routes_for!
|
80
|
+
shortest_route_by_duration_to
|
81
|
+
shortest_route_by_duration_to!
|
82
|
+
shortest_route_by_distance_to
|
83
|
+
shortest_route_by_distance_to!
|
84
|
+
].each do |method|
|
85
|
+
it "delegates #{method} to routes_finder" do
|
86
|
+
finder = mock
|
87
|
+
result = mock
|
88
|
+
|
89
|
+
subject.stub(:routes_finder).and_return finder
|
90
|
+
|
91
|
+
finder.should_receive(method).and_return result
|
92
|
+
expect(subject.public_send(method)).to eq result
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe "#data", :request_recordings do
|
97
|
+
context "success" do
|
98
|
+
let!(:api_request_stub) { stub_request(:get, url).to_return body: recorded_request_for(:success) }
|
99
|
+
|
100
|
+
it "loads from Google's API" do
|
101
|
+
subject.data
|
102
|
+
api_request_stub.should have_been_requested
|
103
|
+
end
|
104
|
+
|
105
|
+
it "does not load twice" do
|
106
|
+
2.times { subject.data }
|
107
|
+
api_request_stub.should have_been_requested
|
108
|
+
end
|
109
|
+
|
110
|
+
it "contains one row" do
|
111
|
+
expect(subject.data.length).to eq 2
|
112
|
+
end
|
113
|
+
|
114
|
+
it "contains two columns each row" do
|
115
|
+
expect(subject.data[0].length).to eq 2
|
116
|
+
expect(subject.data[1].length).to eq 2
|
117
|
+
end
|
118
|
+
|
119
|
+
it "assigns correct origin on routes in the data" do
|
120
|
+
expect(subject.data[0][0].origin).to eq origin_1
|
121
|
+
expect(subject.data[0][1].origin).to eq origin_1
|
122
|
+
|
123
|
+
expect(subject.data[1][0].origin).to eq origin_2
|
124
|
+
expect(subject.data[1][1].origin).to eq origin_2
|
125
|
+
end
|
126
|
+
|
127
|
+
it "assigns correct destination on routes in the data" do
|
128
|
+
expect(subject.data[0][0].destination).to eq destination_1
|
129
|
+
expect(subject.data[0][1].destination).to eq destination_2
|
130
|
+
|
131
|
+
expect(subject.data[1][0].destination).to eq destination_1
|
132
|
+
expect(subject.data[1][1].destination).to eq destination_2
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
context "some elements is not OK" do
|
137
|
+
let!(:api_request_stub) { stub_request(:get, url).to_return body: recorded_request_for(:zero_results) }
|
138
|
+
|
139
|
+
it "loads from Google's API" do
|
140
|
+
subject.data
|
141
|
+
api_request_stub.should have_been_requested
|
142
|
+
end
|
143
|
+
|
144
|
+
it "as loaded route with errors correctly" do
|
145
|
+
route = subject.data[0][1]
|
146
|
+
|
147
|
+
expect(route.status).to eq "zero_results"
|
148
|
+
expect(route.duration_in_seconds).to be_nil
|
149
|
+
end
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
describe "#reload" do
|
154
|
+
before do
|
155
|
+
subject.stub(:load_matrix).and_return { ['loaded'] }
|
156
|
+
subject.data.clear
|
157
|
+
end
|
158
|
+
|
159
|
+
it "reloads matrix' data from the API" do
|
160
|
+
expect {
|
161
|
+
subject.reload
|
162
|
+
}.to change(subject, :data).from([]).to ['loaded']
|
163
|
+
end
|
164
|
+
|
165
|
+
it "is chainable" do
|
166
|
+
expect(subject.reload.data).to eq ['loaded']
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,93 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe GoogleDistanceMatrix::Place do
|
4
|
+
let(:address) { "Karl Johans gate, Oslo" }
|
5
|
+
let(:lat) { 1.4 }
|
6
|
+
let(:lng) { 2.2 }
|
7
|
+
|
8
|
+
describe "#initialize" do
|
9
|
+
it "builds with an address" do
|
10
|
+
place = described_class.new(address: address)
|
11
|
+
expect(place.address).to eq address
|
12
|
+
end
|
13
|
+
|
14
|
+
it "builds with lat lng" do
|
15
|
+
place = described_class.new(lat: lat, lng: lng)
|
16
|
+
expect(place.lat).to eq lat
|
17
|
+
expect(place.lng).to eq lng
|
18
|
+
end
|
19
|
+
|
20
|
+
it "builds with an object responding to lat and lng" do
|
21
|
+
point = mock lat: 1, lng: 2
|
22
|
+
place = described_class.new(point)
|
23
|
+
|
24
|
+
expect(place.lat).to eq point.lat
|
25
|
+
expect(place.lng).to eq point.lng
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
it "keeps a record of the object it built itself from" do
|
30
|
+
point = mock lat: 1, lng: 2
|
31
|
+
place = described_class.new(point)
|
32
|
+
|
33
|
+
expect(place.extracted_attributes_from).to eq point
|
34
|
+
end
|
35
|
+
it "builds with an object responding to address" do
|
36
|
+
object = mock address: address
|
37
|
+
place = described_class.new(object)
|
38
|
+
|
39
|
+
expect(place.address).to eq object.address
|
40
|
+
end
|
41
|
+
|
42
|
+
it "builds with an object responding to lat, lng and address" do
|
43
|
+
object = mock lat: 1, lng:2, address: address
|
44
|
+
place = described_class.new(object)
|
45
|
+
|
46
|
+
expect(place.lat).to eq object.lat
|
47
|
+
expect(place.lng).to eq object.lng
|
48
|
+
expect(place.address).to be_nil
|
49
|
+
end
|
50
|
+
|
51
|
+
it "fails if no valid attributes given" do
|
52
|
+
expect { described_class.new }.to raise_error ArgumentError
|
53
|
+
expect { described_class.new(lat: lat) }.to raise_error ArgumentError
|
54
|
+
expect { described_class.new(lng: lng) }.to raise_error ArgumentError
|
55
|
+
end
|
56
|
+
|
57
|
+
it "fails if both address, lat ang lng is given" do
|
58
|
+
expect { described_class.new(address: address, lat: lat, lng: lng) }.to raise_error ArgumentError
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe "#to_param" do
|
63
|
+
context "with address" do
|
64
|
+
subject { described_class.new address: address }
|
65
|
+
|
66
|
+
its(:to_param) { should eq address }
|
67
|
+
end
|
68
|
+
|
69
|
+
context "with lat lng" do
|
70
|
+
subject { described_class.new lng: lng, lat: lat }
|
71
|
+
|
72
|
+
its(:to_param) { should eq "#{lat},#{lng}" }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "#equal?" do
|
77
|
+
it "is considered equal when address is the same" do
|
78
|
+
expect(described_class.new(address: address)).to be_eql described_class.new(address: address)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "is considered equal when lat and lng are the same" do
|
82
|
+
expect(described_class.new(lat: lat, lng: lng)).to be_eql described_class.new(lat: lat, lng: lng)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "is not considered equal when address differs" do
|
86
|
+
expect(described_class.new(address: address)).to_not be_eql described_class.new(address: address + ", Norway")
|
87
|
+
end
|
88
|
+
|
89
|
+
it "is not considered equal when lat or lng differs" do
|
90
|
+
expect(described_class.new(lat: lat, lng: lng)).to_not be_eql described_class.new(lat: lat, lng: lng + 1)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|