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.
- checksums.yaml +7 -0
- data/.gitignore +25 -0
- data/.rspec +2 -0
- data/.travis.yml +36 -0
- data/CHANGELOG.md +68 -0
- data/Gemfile +12 -0
- data/{MIT-LICENSE → LICENSE.txt} +5 -3
- data/README.md +160 -0
- data/Rakefile +6 -0
- data/acts_as_geocodable.gemspec +27 -0
- data/gemfiles/rails30.gemfile +15 -0
- data/gemfiles/rails31.gemfile +14 -0
- data/gemfiles/rails32.gemfile +14 -0
- data/gemfiles/rails40.gemfile +14 -0
- data/gemfiles/rails41.gemfile +14 -0
- data/lib/acts_as_geocodable.rb +104 -105
- data/lib/acts_as_geocodable/geocode.rb +8 -8
- data/lib/acts_as_geocodable/geocoding.rb +3 -3
- data/lib/acts_as_geocodable/remote_location.rb +8 -8
- data/lib/acts_as_geocodable/version.rb +1 -1
- data/lib/generators/acts_as_geocodable/USAGE +1 -1
- data/lib/generators/acts_as_geocodable/acts_as_geocodable_generator.rb +5 -6
- data/lib/generators/acts_as_geocodable/templates/migration.rb +15 -15
- data/spec/acts_as_geocodable_generator_spec.rb +23 -0
- data/spec/acts_as_geocodable_spec.rb +318 -0
- data/spec/db/database.yml.example +8 -0
- data/spec/db/schema.rb +59 -0
- data/spec/geocode_spec.rb +84 -0
- data/spec/spec_helper.rb +31 -0
- data/spec/support/factories.rb +110 -0
- data/spec/support/geocoder.rb +52 -0
- data/spec/support/models.rb +35 -0
- metadata +122 -67
- data/CHANGELOG +0 -18
- data/README.textile +0 -129
@@ -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, :
|
5
|
-
t.column "longitude", :decimal, :
|
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"], :
|
16
|
-
add_index "geocodes", ["latitude"], :
|
17
|
-
add_index "geocodes", ["query"], :
|
18
|
-
add_index "geocodes", ["locality"], :
|
19
|
-
add_index "geocodes", ["region"], :
|
20
|
-
add_index "geocodes", ["postal_code"], :
|
21
|
-
add_index "geocodes", ["country"], :
|
22
|
-
add_index "geocodes", ["precision"], :
|
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"], :
|
32
|
-
add_index "geocodings", ["geocode_id"], :
|
33
|
-
add_index "geocodings", ["geocodable_id"], :
|
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
|
38
|
-
drop_table
|
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
|
data/spec/db/schema.rb
ADDED
@@ -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
|