acts_as_geocodable 2.0.3 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,8 +1,8 @@
1
1
  class AddGeocodableTables < ActiveRecord::Migration
2
2
  def self.up
3
3
  create_table "geocodes" do |t|
4
- t.column "latitude", :decimal, :precision => 15, :scale => 12
5
- t.column "longitude", :decimal, :precision => 15, :scale => 12
4
+ t.column "latitude", :decimal, precision: 15, scale: 12
5
+ t.column "longitude", :decimal, precision: 15, scale: 12
6
6
  t.column "query", :string
7
7
  t.column "street", :string
8
8
  t.column "locality", :string
@@ -12,14 +12,14 @@ class AddGeocodableTables < ActiveRecord::Migration
12
12
  t.column "precision", :string
13
13
  end
14
14
 
15
- add_index "geocodes", ["longitude"], :name => "geocodes_longitude_index"
16
- add_index "geocodes", ["latitude"], :name => "geocodes_latitude_index"
17
- add_index "geocodes", ["query"], :name => "geocodes_query_index", :unique => true
18
- add_index "geocodes", ["locality"], :name => "geocodes_locality_index"
19
- add_index "geocodes", ["region"], :name => "geocodes_region_index"
20
- add_index "geocodes", ["postal_code"], :name => "geocodes_postal_code_index"
21
- add_index "geocodes", ["country"], :name => "geocodes_country_index"
22
- add_index "geocodes", ["precision"], :name => "geocodes_precision_index"
15
+ add_index "geocodes", ["longitude"], name: "geocodes_longitude_index"
16
+ add_index "geocodes", ["latitude"], name: "geocodes_latitude_index"
17
+ add_index "geocodes", ["query"], name: "geocodes_query_index", unique: true
18
+ add_index "geocodes", ["locality"], name: "geocodes_locality_index"
19
+ add_index "geocodes", ["region"], name: "geocodes_region_index"
20
+ add_index "geocodes", ["postal_code"], name: "geocodes_postal_code_index"
21
+ add_index "geocodes", ["country"], name: "geocodes_country_index"
22
+ add_index "geocodes", ["precision"], name: "geocodes_precision_index"
23
23
 
24
24
 
25
25
  create_table "geocodings" do |t|
@@ -28,13 +28,13 @@ class AddGeocodableTables < ActiveRecord::Migration
28
28
  t.column "geocodable_type", :string
29
29
  end
30
30
 
31
- add_index "geocodings", ["geocodable_type"], :name => "geocodings_geocodable_type_index"
32
- add_index "geocodings", ["geocode_id"], :name => "geocodings_geocode_id_index"
33
- add_index "geocodings", ["geocodable_id"], :name => "geocodings_geocodable_id_index"
31
+ add_index "geocodings", ["geocodable_type"], name: "geocodings_geocodable_type_index"
32
+ add_index "geocodings", ["geocode_id"], name: "geocodings_geocode_id_index"
33
+ add_index "geocodings", ["geocodable_id"], name: "geocodings_geocodable_id_index"
34
34
  end
35
35
 
36
36
  def self.down
37
- drop_table :geocodes
38
- drop_table :geocodings
37
+ drop_table :geocodes
38
+ drop_table :geocodings
39
39
  end
40
40
  end
@@ -0,0 +1,23 @@
1
+ require "spec_helper"
2
+ require "rails/generators"
3
+
4
+ describe "ActsAsGeocodableGenerator" do
5
+ before do
6
+ FileUtils.mkdir_p(fake_rails_root)
7
+ @original_files = file_list
8
+ end
9
+
10
+ after do
11
+ FileUtils.rm_r(fake_rails_root)
12
+ end
13
+
14
+ private
15
+
16
+ def fake_rails_root
17
+ File.join(File.dirname(__FILE__), "rails_root")
18
+ end
19
+
20
+ def file_list
21
+ Dir.glob(File.join(fake_rails_root, "*"))
22
+ end
23
+ end
@@ -0,0 +1,318 @@
1
+ require "spec_helper"
2
+
3
+ describe ActsAsGeocodable do
4
+ before do
5
+ @white_house = FactoryGirl.create(:whitehouse)
6
+ @saugatuck = FactoryGirl.create(:saugatuck)
7
+ end
8
+
9
+ describe "geocode" do
10
+ it "should be the geocode from the geocoding" do
11
+ expect(@white_house.geocode).to eq(@white_house.geocoding.geocode)
12
+ end
13
+
14
+ it "should be nil without a geocoding" do
15
+ expect(Vacation.new.geocode).to be_nil
16
+ end
17
+ end
18
+
19
+ describe "to_location" do
20
+ it "should return a graticule location" do
21
+ expect(@white_house.to_location).to be_kind_of(Graticule::Location)
22
+ end
23
+ end
24
+
25
+ describe "with address normalization" do
26
+ before do
27
+ Vacation.acts_as_geocodable_options[:normalize_address] = true
28
+
29
+ allow(Geocode.geocoder).to receive(:locate).and_return(
30
+ Graticule::Location.new(locality: "San Clemente", region: "CA")
31
+ )
32
+ end
33
+
34
+ it "should update address fields with result" do
35
+ vacation = Vacation.create! locality: "sanclemente", region: "ca"
36
+ expect(vacation.locality).to eq("San Clemente")
37
+ expect(vacation.region).to eq("CA")
38
+ end
39
+
40
+ it "should update address blob" do
41
+ allow(Geocode.geocoder).to receive(:locate).and_return(
42
+ Graticule::Location.new(locality: "Grand Rapids", region: "MI", country: "US")
43
+ )
44
+
45
+ vacation = AddressBlobVacation.create! address: "grand rapids, mi"
46
+ expect(vacation.address).to eq("Grand Rapids, MI US")
47
+ end
48
+ end
49
+
50
+ describe "without address normalization" do
51
+ before do
52
+ Vacation.acts_as_geocodable_options[:normalize_address] = false
53
+
54
+ allow(Geocode.geocoder).to receive(:locate).and_return(
55
+ Graticule::Location.new(locality: "Portland", region: "OR", postal_code: "97212")
56
+ )
57
+
58
+ @vacation = Vacation.create! locality: "portland", region: "or"
59
+ end
60
+
61
+ it "should not update address attributes" do
62
+ expect(@vacation.locality).to eq("portland")
63
+ expect(@vacation.region).to eq("or")
64
+ end
65
+
66
+ it "should fill in blank attributes" do
67
+ expect(@vacation.postal_code).to eq("97212")
68
+ end
69
+ end
70
+
71
+ describe "with blank location attributes" do
72
+ it "should not create geocode" do
73
+ expect(Geocode.geocoder).not_to receive(:locate)
74
+ expect {
75
+ expect(Vacation.create!(locality: "\n", region: " ").geocoding).to be_nil
76
+ }.not_to change { Geocode.count }
77
+ end
78
+
79
+ it "should destroy existing geocoding" do
80
+ [:name, :street, :locality, :region, :postal_code].each do |attribute|
81
+ @white_house.send("#{attribute}=", nil)
82
+ end
83
+
84
+ expect(lambda { @white_house.save! }).to change(Geocoding, :count).by(-1)
85
+ @white_house.reload
86
+ expect(@white_house.geocoding).to be_nil
87
+ end
88
+ end
89
+
90
+ describe "on save" do
91
+ it "should not create geocode without changes" do
92
+ expect(@white_house.geocoding).not_to be_nil
93
+ original_geocode = @white_house.geocode
94
+ expect { @white_house.save!; @white_house.reload }.not_to change { Geocode.count }
95
+ expect(@white_house.geocode).to eq(original_geocode)
96
+ end
97
+
98
+ it "should not create geocoding without changes" do
99
+ expect(@white_house.geocoding).not_to be_nil
100
+ original_geocode = @white_house.geocode
101
+ expect { @white_house.save!; @white_house.reload }.not_to change { Geocoding.count }
102
+ expect(@white_house.geocode).to eq(original_geocode)
103
+ end
104
+ end
105
+
106
+ describe "on save with an existing geocode" do
107
+ before do
108
+ @white_house.attributes = { street: "", locality: "Saugatuck", region: "MI", postal_code: "" }
109
+ end
110
+
111
+ it "should destroy the old geocoding, create a new one, and leave the count the same" do
112
+ expect { @white_house.save! }.not_to change { Geocoding.count }
113
+ end
114
+
115
+ it "should set the new geocode" do
116
+ @white_house.save!
117
+ expect(@white_house.geocode.postal_code.to_s).to eq("49406")
118
+ end
119
+ end
120
+
121
+ describe "validates_as_geocodable" do
122
+ before do
123
+ @model = Class.new(Vacation)
124
+ @vacation = @model.new locality: "Saugatuck", region: "MI"
125
+ end
126
+
127
+ it "should be valid with geocodable address" do
128
+ @model.validates_as_geocodable
129
+ expect(@vacation).to be_valid
130
+ end
131
+
132
+ it "should be invalid without geocodable address" do
133
+ @model.validates_as_geocodable
134
+ expect(Geocode.geocoder).to receive(:locate).and_raise(Graticule::Error)
135
+ expect(@vacation).not_to be_valid
136
+ expect(@vacation.errors.size).to eq(1)
137
+ expect(@vacation.errors[:base]).to include("Address could not be geocoded.")
138
+ end
139
+
140
+ it "should be valid with the same precision" do
141
+ @model.validates_as_geocodable precision: :street
142
+ expect(Geocode.geocoder).to receive(:locate).and_return(Graticule::Location.new(precision: "street"))
143
+ expect(@vacation).to be_valid
144
+ end
145
+
146
+ it "should be valid with a higher precision" do
147
+ @model.validates_as_geocodable precision: :region
148
+ expect(Geocode.geocoder).to receive(:locate).and_return(Graticule::Location.new(precision: "street"))
149
+ expect(@vacation).to be_valid
150
+ end
151
+
152
+ it "should be invalid with a lower precision" do
153
+ @model.validates_as_geocodable precision: :street
154
+ expect(Geocode.geocoder).to receive(:locate).and_return(Graticule::Location.new(precision: "region"))
155
+ expect(@vacation).not_to be_valid
156
+ expect(@vacation.errors[:base]).to include("Address could not be geocoded.")
157
+ end
158
+
159
+ it "should allow nil" do
160
+ @model.validates_as_geocodable allow_nil: true
161
+ expect(@model.new).to be_valid
162
+ end
163
+
164
+ it "should be invalid if validation block returns false" do
165
+ expect(Geocode.geocoder).to receive(:locate).and_return(Graticule::Location.new(country: "CA"))
166
+ staycation = Staycation.new locality: "Saugatuck", region: "MI"
167
+ expect(staycation).not_to be_valid
168
+ end
169
+
170
+ it "should be valid if validation block returns true" do
171
+ expect(Geocode.geocoder).to receive(:locate).and_return(Graticule::Location.new(country: "US"))
172
+ staycation = Staycation.new locality: "Saugatuck", region: "MI"
173
+ expect(staycation).to be_valid
174
+ end
175
+ end
176
+
177
+ describe "find with origin" do
178
+ it "should add distance to result" do
179
+ expect(Vacation.origin("49406").first.distance).to be_present
180
+ end
181
+ end
182
+
183
+ describe "find within" do
184
+ before do
185
+ @results = Vacation.origin(49406, within: 10).all
186
+ end
187
+
188
+ it "should find locations within radius" do
189
+ expect(@results).to include(@saugatuck)
190
+ end
191
+
192
+ it "should add distance to results" do
193
+ expect(@results.first.distance).to be_present
194
+ end
195
+
196
+ it "should find within" do
197
+ spots = Vacation.origin("49406", within: 3).all
198
+ expect(spots.size).to eq(1)
199
+ expect(spots.first).to eq(@saugatuck)
200
+ end
201
+
202
+ it "should count within" do
203
+ spots_count = Vacation.origin("49406", within: 3).count(:all)
204
+ expect(spots_count).to eq(1)
205
+ end
206
+
207
+ it "should be able to find within kilometers" do
208
+ saugatuck = Vacation.origin(49406, within: 2, units: :kilometers).first
209
+ expect(saugatuck).to eq(@saugatuck)
210
+ end
211
+ end
212
+
213
+ describe "distance_to" do
214
+ before do
215
+ @san_francisco = Vacation.create!(name: "San Francisco", locality: "San Francisco", region: "CA")
216
+ end
217
+
218
+ it "should calculate distance from a string" do
219
+ expect(@san_francisco.distance_to(@saugatuck.geocode.query)).to be_within(2).of(1927)
220
+ end
221
+
222
+ it "should calculate distance from a geocode" do
223
+ expect(@san_francisco.distance_to(@saugatuck.geocode)).to be_within(2).of(1927)
224
+ end
225
+
226
+ it "should calculate distance from a geocodable model" do
227
+ expect(@san_francisco.distance_to(@saugatuck)).to be_within(2).of(1927)
228
+ expect(@saugatuck.distance_to(@san_francisco)).to be_within(2).of(1927)
229
+ end
230
+
231
+ it "should calculate distance in default miles" do
232
+ expect(@san_francisco.distance_to(@saugatuck, units: :miles)).to be_within(2).of(1927)
233
+ end
234
+
235
+ it "should calculate distance in default kilometers" do
236
+ expect(@san_francisco.distance_to(@saugatuck, units: :kilometers)).to be_within(2).of(3101)
237
+ end
238
+
239
+ it "should return nil with invalid geocode" do
240
+ expect(@san_francisco.distance_to(Geocode.new)).to be_nil
241
+ expect(@san_francisco.distance_to(nil)).to be_nil
242
+ end
243
+ end
244
+
245
+ it "should have beyond" do
246
+ spots = Vacation.origin("49406", beyond: 3).all
247
+ expect(spots.first).to eq(@white_house)
248
+ expect(spots.size).to eq(1)
249
+ end
250
+
251
+ it "should have count for beyond" do
252
+ count = Vacation.origin("49406", beyond: 3).count(:all)
253
+ expect(count).to eq(1)
254
+ end
255
+
256
+ it "should find beyond with other units" do
257
+ whitehouse = Vacation.origin("49406", beyond: 3, units: :kilometers).first
258
+ expect(whitehouse).to eq(@white_house)
259
+ expect(whitehouse.distance.to_f).to be_within(1).of(877.554975851074)
260
+ end
261
+
262
+ it "should find nearest" do
263
+ expect(Vacation.origin("49406").nearest).to eq(@saugatuck)
264
+ end
265
+
266
+ it "should find farthest" do
267
+ expect(Vacation.origin("49406").farthest).to eq(@white_house)
268
+ end
269
+
270
+ it "should raise error with find nearest and including" do
271
+ expect(lambda { Vacation.origin("49406").nearest(include: :nearest_city) }).to raise_error(ArgumentError)
272
+ end
273
+
274
+ it "should find with order" do
275
+ expected = [@saugatuck, @white_house]
276
+ actual = Vacation.origin("49406").order("distance").all
277
+ expect(actual).to eq(expected)
278
+ end
279
+
280
+ it "can set the geocode to nil" do
281
+ expect(Vacation.send(:location_to_geocode, nil)).to be_nil
282
+ end
283
+
284
+ it "can convert to a geocode" do
285
+ g = Geocode.new
286
+ expect(Vacation.send(:location_to_geocode, g)).to eql(g)
287
+ end
288
+
289
+ it "can convert a string to a geocode" do
290
+ douglas_geocode = FactoryGirl.create(:douglas_geocode)
291
+ expect(Vacation.send(:location_to_geocode, "49406")).to eq(douglas_geocode)
292
+ end
293
+
294
+ it "can covert a numeric zip to a geocode" do
295
+ douglas_geocode = FactoryGirl.create(:douglas_geocode)
296
+ expect(Vacation.send(:location_to_geocode, 49406)).to eq(douglas_geocode)
297
+ end
298
+
299
+ it "should convert a geocodable to a geocode" do
300
+ expect(Vacation.send(:location_to_geocode, @white_house)).to eq(@white_house.geocode)
301
+ end
302
+
303
+ describe "callbacks" do
304
+ it "should run a callback after geocoding" do
305
+ location = CallbackLocation.new address: "San Francisco"
306
+ expect(location.geocoding).to be_nil
307
+ expect(location).to receive(:done_geocoding).once.and_return(true)
308
+ expect(location.save!).to be_truthy
309
+ end
310
+
311
+ it "should not run callbacks after geocoding if the object is the same" do
312
+ location = CallbackLocation.create(address: "San Francisco")
313
+ expect(location.geocoding).not_to be_nil
314
+ expect(location).not_to receive(:done_geocoding)
315
+ expect(location.save!).to be_truthy
316
+ end
317
+ end
318
+ end
@@ -0,0 +1,8 @@
1
+ mysql:
2
+ adapter: mysql2
3
+ username: root
4
+ database: acts_as_geocodable_test
5
+ postgresql:
6
+ adapter: postgresql
7
+ username: postgres
8
+ database: acts_as_geocodable_test
@@ -0,0 +1,59 @@
1
+ ActiveRecord::Schema.define(version: 1) do
2
+ create_table "geocodes", force: true do |t|
3
+ t.column "latitude", :decimal, precision: 15, scale: 12
4
+ t.column "longitude", :decimal, precision: 15, scale: 12
5
+ t.column "query", :string
6
+ t.column "street", :string
7
+ t.column "locality", :string
8
+ t.column "region", :string
9
+ t.column "postal_code", :string
10
+ t.column "country", :string
11
+ t.column "precision", :string
12
+ end
13
+
14
+ add_index "geocodes", ["query"], name: "geocodes_query_index", unique: true
15
+ add_index "geocodes", ["latitude"], name: "geocodes_latitude_index"
16
+ add_index "geocodes", ["longitude"], name: "geocodes_longitude_index"
17
+
18
+ create_table "geocodings", force: true do |t|
19
+ t.column "geocodable_id", :integer
20
+ t.column "geocode_id", :integer
21
+ t.column "geocodable_type", :string
22
+ end
23
+
24
+ add_index "geocodings", ["geocodable_id"], name: "geocodings_geocodable_id_index"
25
+ add_index "geocodings", ["geocode_id"], name: "geocodings_geocode_id_index"
26
+ add_index "geocodings", ["geocodable_type"], name: "geocodings_geocodable_type_index"
27
+
28
+ create_table "vacations", force: true do |t|
29
+ t.column "name", :string
30
+ t.column "street", :string
31
+ t.column "locality", :string
32
+ t.column "region", :string
33
+ t.column "postal_code", :string
34
+ t.column "city_id", :integer
35
+ end
36
+
37
+ create_table "validated_vacations", force: true do |t|
38
+ t.column "name", :string
39
+ t.column "street", :string
40
+ t.column "locality", :string
41
+ t.column "region", :string
42
+ t.column "postal_code", :string
43
+ end
44
+
45
+ create_table "address_blob_vacations", force: true do |t|
46
+ t.column "name", :string
47
+ t.column "address", :string
48
+ end
49
+
50
+ create_table "callback_locations", force: true do |t|
51
+ t.column "name", :string
52
+ t.column "address", :string
53
+ end
54
+
55
+ create_table "cities", force: true do |t|
56
+ t.column "name", :string
57
+ t.column "zip", :string
58
+ end
59
+ end