acts_as_geocodable 2.0.3 → 2.1.0

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.
@@ -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