geokit-rails3 0.0.3

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 (46) hide show
  1. data/.gitignore +30 -0
  2. data/CHANGELOG.rdoc +46 -0
  3. data/CONFIG.markdown +67 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +89 -0
  6. data/MIT-LICENSE +20 -0
  7. data/README.markdown +554 -0
  8. data/Rakefile +17 -0
  9. data/geokit-rails3.gemspec +29 -0
  10. data/lib/geokit-rails.rb +10 -0
  11. data/lib/geokit-rails3/acts_as_mappable.rb +433 -0
  12. data/lib/geokit-rails3/adapters/abstract.rb +31 -0
  13. data/lib/geokit-rails3/adapters/mysql.rb +22 -0
  14. data/lib/geokit-rails3/adapters/postgresql.rb +22 -0
  15. data/lib/geokit-rails3/adapters/sqlserver.rb +43 -0
  16. data/lib/geokit-rails3/core_extensions.rb +14 -0
  17. data/lib/geokit-rails3/defaults.rb +21 -0
  18. data/lib/geokit-rails3/geocoder_control.rb +18 -0
  19. data/lib/geokit-rails3/ip_geocode_lookup.rb +44 -0
  20. data/lib/geokit-rails3/railtie.rb +38 -0
  21. data/lib/geokit-rails3/version.rb +3 -0
  22. data/test/acts_as_mappable_test.rb +473 -0
  23. data/test/boot.rb +32 -0
  24. data/test/database.yml +20 -0
  25. data/test/fixtures/companies.yml +7 -0
  26. data/test/fixtures/custom_locations.yml +54 -0
  27. data/test/fixtures/locations.yml +54 -0
  28. data/test/fixtures/mock_addresses.yml +17 -0
  29. data/test/fixtures/mock_families.yml +2 -0
  30. data/test/fixtures/mock_houses.yml +9 -0
  31. data/test/fixtures/mock_organizations.yml +5 -0
  32. data/test/fixtures/mock_people.yml +5 -0
  33. data/test/fixtures/stores.yml +0 -0
  34. data/test/models/company.rb +3 -0
  35. data/test/models/custom_location.rb +12 -0
  36. data/test/models/location.rb +4 -0
  37. data/test/models/mock_address.rb +4 -0
  38. data/test/models/mock_family.rb +3 -0
  39. data/test/models/mock_house.rb +3 -0
  40. data/test/models/mock_organization.rb +4 -0
  41. data/test/models/mock_person.rb +4 -0
  42. data/test/models/store.rb +3 -0
  43. data/test/schema.rb +60 -0
  44. data/test/tasks.rake +38 -0
  45. data/test/test_helper.rb +23 -0
  46. metadata +236 -0
@@ -0,0 +1,31 @@
1
+ module Geokit
2
+ module Adapters
3
+ class Abstract
4
+ class NotImplementedError < StandardError ; end
5
+
6
+ cattr_accessor :loaded
7
+
8
+ class << self
9
+ def load(klass) ; end
10
+ end
11
+
12
+ def initialize(klass)
13
+ @owner = klass
14
+ end
15
+
16
+ def method_missing(method, *args, &block)
17
+ return @owner.send(method, *args, &block) if @owner.respond_to?(method)
18
+ super
19
+ end
20
+
21
+ def sphere_distance_sql(lat, lng, multiplier)
22
+ raise NotImplementedError, '#sphere_distance_sql is not implemented'
23
+ end
24
+
25
+ def flat_distance_sql(origin, lat_degree_units, lng_degree_units)
26
+ raise NotImplementedError, '#flat_distance_sql is not implemented'
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,22 @@
1
+ module Geokit
2
+ module Adapters
3
+ class MySQL < Abstract
4
+
5
+ def sphere_distance_sql(lat, lng, multiplier)
6
+ %|
7
+ (ACOS(least(1,COS(#{lat})*COS(#{lng})*COS(RADIANS(#{qualified_lat_column_name}))*COS(RADIANS(#{qualified_lng_column_name}))+
8
+ COS(#{lat})*SIN(#{lng})*COS(RADIANS(#{qualified_lat_column_name}))*SIN(RADIANS(#{qualified_lng_column_name}))+
9
+ SIN(#{lat})*SIN(RADIANS(#{qualified_lat_column_name}))))*#{multiplier})
10
+ |
11
+ end
12
+
13
+ def flat_distance_sql(origin, lat_degree_units, lng_degree_units)
14
+ %|
15
+ SQRT(POW(#{lat_degree_units}*(#{origin.lat}-#{qualified_lat_column_name}),2)+
16
+ POW(#{lng_degree_units}*(#{origin.lng}-#{qualified_lng_column_name}),2))
17
+ |
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ module Geokit
2
+ module Adapters
3
+ class PostgreSQL < Abstract
4
+
5
+ def sphere_distance_sql(lat, lng, multiplier)
6
+ %|
7
+ (ACOS(least(1,COS(#{lat})*COS(#{lng})*COS(RADIANS(#{qualified_lat_column_name}))*COS(RADIANS(#{qualified_lng_column_name}))+
8
+ COS(#{lat})*SIN(#{lng})*COS(RADIANS(#{qualified_lat_column_name}))*SIN(RADIANS(#{qualified_lng_column_name}))+
9
+ SIN(#{lat})*SIN(RADIANS(#{qualified_lat_column_name}))))*#{multiplier})
10
+ |
11
+ end
12
+
13
+ def flat_distance_sql(origin, lat_degree_units, lng_degree_units)
14
+ %|
15
+ SQRT(POW(#{lat_degree_units}*(#{origin.lat}-#{qualified_lat_column_name}),2)+
16
+ POW(#{lng_degree_units}*(#{origin.lng}-#{qualified_lng_column_name}),2))
17
+ |
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,43 @@
1
+ module Geokit
2
+ module Adapters
3
+ class SQLServer < Abstract
4
+
5
+ class << self
6
+
7
+ def load(klass)
8
+ klass.connection.execute <<-EOS
9
+ if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[geokit_least]') and xtype in (N'FN', N'IF', N'TF'))
10
+ drop function [dbo].[geokit_least]
11
+ EOS
12
+
13
+ klass.connection.execute <<-EOS
14
+ CREATE FUNCTION [dbo].geokit_least (@value1 float,@value2 float) RETURNS float AS BEGIN
15
+ return (SELECT CASE WHEN @value1 < @value2 THEN @value1 ELSE @value2 END) END
16
+ EOS
17
+ self.loaded = true
18
+ end
19
+
20
+ end
21
+
22
+ def initialize(*args)
23
+ super(*args)
24
+ end
25
+
26
+ def sphere_distance_sql(lat, lng, multiplier)
27
+ %|
28
+ (ACOS([dbo].geokit_least(1,COS(#{lat})*COS(#{lng})*COS(RADIANS(#{qualified_lat_column_name}))*COS(RADIANS(#{qualified_lng_column_name}))+
29
+ COS(#{lat})*SIN(#{lng})*COS(RADIANS(#{qualified_lat_column_name}))*SIN(RADIANS(#{qualified_lng_column_name}))+
30
+ SIN(#{lat})*SIN(RADIANS(#{qualified_lat_column_name}))))*#{multiplier})
31
+ |
32
+ end
33
+
34
+ def flat_distance_sql(origin, lat_degree_units, lng_degree_units)
35
+ %|
36
+ SQRT(POWER(#{lat_degree_units}*(#{origin.lat}-#{qualified_lat_column_name}),2)+
37
+ POWER(#{lng_degree_units}*(#{origin.lng}-#{qualified_lng_column_name}),2))
38
+ |
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,14 @@
1
+ # Extend Array with a sort_by_distance method.
2
+ class Array
3
+ # This method creates a "distance" attribute on each object, calculates the
4
+ # distance from the passed origin, and finally sorts the array by the
5
+ # resulting distance.
6
+ def sort_by_distance_from(origin, opts={})
7
+ distance_attribute_name = opts.delete(:distance_attribute_name) || 'distance'
8
+ self.each do |e|
9
+ e.class.send(:attr_accessor, distance_attribute_name) if !e.respond_to?("#{distance_attribute_name}=")
10
+ e.send("#{distance_attribute_name}=", e.distance_to(origin,opts))
11
+ end
12
+ self.sort!{|a,b|a.send(distance_attribute_name) <=> b.send(distance_attribute_name)}
13
+ end
14
+ end
@@ -0,0 +1,21 @@
1
+ module Geokit
2
+ # These defaults are used in Geokit::Mappable.distance_to and in acts_as_mappable
3
+ @@default_units = :miles
4
+ @@default_formula = :sphere
5
+
6
+ [:default_units, :default_formula].each do |sym|
7
+ class_eval <<-EOS, __FILE__, __LINE__
8
+ def self.#{sym}
9
+ if defined?(#{sym.to_s.upcase})
10
+ #{sym.to_s.upcase}
11
+ else
12
+ @@#{sym}
13
+ end
14
+ end
15
+
16
+ def self.#{sym}=(obj)
17
+ @@#{sym} = obj
18
+ end
19
+ EOS
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ require 'active_support/concern'
2
+
3
+ module Geokit
4
+ module GeocoderControl
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ if self.respond_to? :before_filter
9
+ self.send :before_filter, :set_geokit_domain
10
+ end
11
+ end
12
+
13
+ def set_geokit_domain
14
+ Geokit::Geocoders::domain = request.domain
15
+ logger.debug("Geokit is using the domain: #{Geokit::Geocoders::domain}")
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,44 @@
1
+ require 'yaml'
2
+ require 'active_support/concern'
3
+
4
+ module Geokit
5
+ # Contains a class method geocode_ip_address which can be used to enable automatic geocoding
6
+ # for request IP addresses. The geocoded information is stored in a cookie and in the
7
+ # session to minimize web service calls. The point of the helper is to enable location-based
8
+ # websites to have a best-guess for new visitors.
9
+ module IpGeocodeLookup
10
+ extend ActiveSupport::Concern
11
+
12
+ # Class method to mix into active record.
13
+ module ClassMethods # :nodoc:
14
+ def geocode_ip_address(filter_options = {})
15
+ before_filter :store_ip_location, filter_options
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ # Places the IP address' geocode location into the session if it
22
+ # can be found. Otherwise, looks for a geo location cookie and
23
+ # uses that value. The last resort is to call the web service to
24
+ # get the value.
25
+ def store_ip_location
26
+ session[:geo_location] ||= retrieve_location_from_cookie_or_service
27
+ cookies[:geo_location] = { :value => session[:geo_location].to_yaml, :expires => 30.days.from_now } if session[:geo_location]
28
+ end
29
+
30
+ # Uses the stored location value from the cookie if it exists. If
31
+ # no cookie exists, calls out to the web service to get the location.
32
+ def retrieve_location_from_cookie_or_service
33
+ return YAML.load(cookies[:geo_location]) if cookies[:geo_location]
34
+ location = Geocoders::MultiGeocoder.geocode(get_ip_address)
35
+ return location.success ? location : nil
36
+ end
37
+
38
+ # Returns the real ip address, though this could be the localhost ip
39
+ # address. No special handling here anymore.
40
+ def get_ip_address
41
+ request.remote_ip
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,38 @@
1
+ require 'geokit-rails3'
2
+ require 'rails'
3
+
4
+ module Geokit
5
+
6
+ class Railtie < Rails::Railtie
7
+
8
+ config.geokit = ActiveSupport::OrderedOptions.new
9
+ config.geokit.geocoders = ActiveSupport::OrderedOptions.new
10
+
11
+ initializer 'geokit-rails3.insert_into_active_record' do
12
+ ActiveSupport.on_load :active_record do
13
+ ActiveRecord::Base.send(:include, Geokit::ActsAsMappable::Glue)
14
+ Geokit::Geocoders.logger = ActiveRecord::Base.logger
15
+ end
16
+ end
17
+
18
+ initializer 'geokit-rails3.insert_into_action_controller' do
19
+ ActiveSupport.on_load :action_controller do
20
+ ActionController::Base.send(:include, Geokit::GeocoderControl)
21
+ ActionController::Base.send(:include, GeoKit::IpGeocodeLookup)
22
+ end
23
+ end
24
+
25
+ config.after_initialize do |app|
26
+ options = app.config.geokit
27
+ geocoders_options = options.delete(:geocoders)
28
+
29
+ options.each do |k,v|
30
+ Geokit::send("#{k}=", v)
31
+ end
32
+ geocoders_options.each do |k,v|
33
+ Geokit::Geocoders::send("#{k}=", v)
34
+ end
35
+ end
36
+ end
37
+
38
+ end
@@ -0,0 +1,3 @@
1
+ module GeokitRails3
2
+ VERSION = "0.0.3"
3
+ end
@@ -0,0 +1,473 @@
1
+ require 'test_helper'
2
+
3
+ Geokit::Geocoders::provider_order = [:google, :us]
4
+
5
+ class ActsAsMappableTest < GeokitTestCase
6
+
7
+ LOCATION_A_IP = "217.10.83.5"
8
+
9
+ def setup
10
+ @location_a = GeoKit::GeoLoc.new
11
+ @location_a.lat = 32.918593
12
+ @location_a.lng = -96.958444
13
+ @location_a.city = "Irving"
14
+ @location_a.state = "TX"
15
+ @location_a.country_code = "US"
16
+ @location_a.success = true
17
+
18
+ @sw = GeoKit::LatLng.new(32.91663,-96.982841)
19
+ @ne = GeoKit::LatLng.new(32.96302,-96.919495)
20
+ @bounds_center=GeoKit::LatLng.new((@sw.lat+@ne.lat)/2,(@sw.lng+@ne.lng)/2)
21
+
22
+ @starbucks = companies(:starbucks)
23
+ @loc_a = locations(:a)
24
+ @custom_loc_a = custom_locations(:a)
25
+ @loc_e = locations(:e)
26
+ @custom_loc_e = custom_locations(:e)
27
+
28
+ @barnes_and_noble = mock_organizations(:barnes_and_noble)
29
+ @address = mock_addresses(:address_barnes_and_noble)
30
+ end
31
+
32
+ def test_override_default_units_the_hard_way
33
+ Location.default_units = :kms
34
+ locations = Location.find(:all, :origin => @loc_a, :conditions => "distance < 3.97")
35
+ assert_equal 5, locations.size
36
+ locations = Location.count(:origin => @loc_a, :conditions => "distance < 3.97")
37
+ assert_equal 5, locations
38
+ Location.default_units = :miles
39
+ end
40
+
41
+ def test_include
42
+ locations = Location.find(:all, :origin => @loc_a, :include => :company, :conditions => "company_id = 1")
43
+ assert !locations.empty?
44
+ assert_equal 1, locations[0].company.id
45
+ assert_equal 'Starbucks', locations[0].company.name
46
+ end
47
+
48
+ def test_distance_between_geocoded
49
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with("Irving, TX").returns(@location_a)
50
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with("San Francisco, CA").returns(@location_a)
51
+ assert_equal 0, Location.distance_between("Irving, TX", "San Francisco, CA")
52
+ end
53
+
54
+ def test_distance_to_geocoded
55
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with("Irving, TX").returns(@location_a)
56
+ assert_equal 0, @custom_loc_a.distance_to("Irving, TX")
57
+ end
58
+
59
+ def test_distance_to_geocoded_error
60
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with("Irving, TX").returns(GeoKit::GeoLoc.new)
61
+ assert_raise(GeoKit::Geocoders::GeocodeError) { @custom_loc_a.distance_to("Irving, TX") }
62
+ end
63
+
64
+ def test_custom_attributes_distance_calculations
65
+ assert_equal 0, @custom_loc_a.distance_to(@loc_a)
66
+ assert_equal 0, CustomLocation.distance_between(@custom_loc_a, @loc_a)
67
+ end
68
+
69
+ def test_distance_column_in_select
70
+ locations = Location.find(:all, :origin => @loc_a, :order => "distance ASC")
71
+ assert_equal 6, locations.size
72
+ assert_equal 0, @loc_a.distance_to(locations.first)
73
+ assert_in_delta 3.97, @loc_a.distance_to(locations.last, :units => :miles, :formula => :sphere), 0.01
74
+ end
75
+
76
+ def test_find_with_distance_condition
77
+ locations = Location.find(:all, :origin => @loc_a, :conditions => "distance < 3.97")
78
+ assert_equal 5, locations.size
79
+ locations = Location.count(:origin => @loc_a, :conditions => "distance < 3.97")
80
+ assert_equal 5, locations
81
+ end
82
+
83
+ def test_find_with_distance_condition_with_units_override
84
+ locations = Location.find(:all, :origin => @loc_a, :units => :kms, :conditions => "distance < 6.387")
85
+ assert_equal 5, locations.size
86
+ locations = Location.count(:origin => @loc_a, :units => :kms, :conditions => "distance < 6.387")
87
+ assert_equal 5, locations
88
+ end
89
+
90
+ def test_find_with_distance_condition_with_formula_override
91
+ locations = Location.find(:all, :origin => @loc_a, :formula => :flat, :conditions => "distance < 6.387")
92
+ assert_equal 6, locations.size
93
+ locations = Location.count(:origin => @loc_a, :formula => :flat, :conditions => "distance < 6.387")
94
+ assert_equal 6, locations
95
+ end
96
+
97
+ def test_find_within
98
+ locations = Location.find_within(3.97, :origin => @loc_a)
99
+ assert_equal 5, locations.size
100
+ locations = Location.count_within(3.97, :origin => @loc_a)
101
+ assert_equal 5, locations
102
+ end
103
+
104
+ def test_find_within_with_token
105
+ locations = Location.find(:all, :within => 3.97, :origin => @loc_a)
106
+ assert_equal 5, locations.size
107
+ locations = Location.count(:within => 3.97, :origin => @loc_a)
108
+ assert_equal 5, locations
109
+ end
110
+
111
+ def test_find_within_with_coordinates
112
+ locations = Location.find_within(3.97, :origin =>[@loc_a.lat,@loc_a.lng])
113
+ assert_equal 5, locations.size
114
+ locations = Location.count_within(3.97, :origin =>[@loc_a.lat,@loc_a.lng])
115
+ assert_equal 5, locations
116
+ end
117
+
118
+ def test_find_with_compound_condition
119
+ locations = Location.find(:all, :origin => @loc_a, :conditions => "distance < 5 and city = 'Coppell'")
120
+ assert_equal 2, locations.size
121
+ locations = Location.count(:origin => @loc_a, :conditions => "distance < 5 and city = 'Coppell'")
122
+ assert_equal 2, locations
123
+ end
124
+
125
+ def test_find_with_secure_compound_condition
126
+ locations = Location.find(:all, :origin => @loc_a, :conditions => ["distance < ? and city = ?", 5, 'Coppell'])
127
+ assert_equal 2, locations.size
128
+ locations = Location.count(:origin => @loc_a, :conditions => ["distance < ? and city = ?", 5, 'Coppell'])
129
+ assert_equal 2, locations
130
+ end
131
+
132
+ def test_find_beyond
133
+ locations = Location.find_beyond(3.95, :origin => @loc_a)
134
+ assert_equal 1, locations.size
135
+ locations = Location.count_beyond(3.95, :origin => @loc_a)
136
+ assert_equal 1, locations
137
+ end
138
+
139
+ def test_find_beyond_with_token
140
+ locations = Location.find(:all, :beyond => 3.95, :origin => @loc_a)
141
+ assert_equal 1, locations.size
142
+ locations = Location.count(:beyond => 3.95, :origin => @loc_a)
143
+ assert_equal 1, locations
144
+ end
145
+
146
+ def test_find_beyond_with_coordinates
147
+ locations = Location.find_beyond(3.95, :origin =>[@loc_a.lat, @loc_a.lng])
148
+ assert_equal 1, locations.size
149
+ locations = Location.count_beyond(3.95, :origin =>[@loc_a.lat, @loc_a.lng])
150
+ assert_equal 1, locations
151
+ end
152
+
153
+ def test_find_range_with_token
154
+ locations = Location.find(:all, :range => 0..10, :origin => @loc_a)
155
+ assert_equal 6, locations.size
156
+ locations = Location.count(:range => 0..10, :origin => @loc_a)
157
+ assert_equal 6, locations
158
+ end
159
+
160
+ def test_find_range_with_token_with_conditions
161
+ locations = Location.find(:all, :origin => @loc_a, :range => 0..10, :conditions => ["city = ?", 'Coppell'])
162
+ assert_equal 2, locations.size
163
+ locations = Location.count(:origin => @loc_a, :range => 0..10, :conditions => ["city = ?", 'Coppell'])
164
+ assert_equal 2, locations
165
+ end
166
+
167
+ def test_find_range_with_token_with_hash_conditions
168
+ locations = Location.find(:all, :origin => @loc_a, :range => 0..10, :conditions => {:city => 'Coppell'})
169
+ assert_equal 2, locations.size
170
+ locations = Location.count(:origin => @loc_a, :range => 0..10, :conditions => {:city => 'Coppell'})
171
+ assert_equal 2, locations
172
+ end
173
+
174
+ def test_find_range_with_token_excluding_end
175
+ locations = Location.find(:all, :range => 0...10, :origin => @loc_a)
176
+ assert_equal 6, locations.size
177
+ locations = Location.count(:range => 0...10, :origin => @loc_a)
178
+ assert_equal 6, locations
179
+ end
180
+
181
+ def test_find_nearest
182
+ assert_equal @loc_a, Location.find_nearest(:origin => @loc_a)
183
+ end
184
+
185
+ def test_find_nearest_through_find
186
+ assert_equal @loc_a, Location.find(:nearest, :origin => @loc_a)
187
+ end
188
+
189
+ def test_find_nearest_with_coordinates
190
+ assert_equal @loc_a, Location.find_nearest(:origin =>[@loc_a.lat, @loc_a.lng])
191
+ end
192
+
193
+ def test_find_farthest
194
+ assert_equal @loc_e, Location.find_farthest(:origin => @loc_a)
195
+ end
196
+
197
+ def test_find_farthest_through_find
198
+ assert_equal @loc_e, Location.find(:farthest, :origin => @loc_a)
199
+ end
200
+
201
+ def test_find_farthest_with_coordinates
202
+ assert_equal @loc_e, Location.find_farthest(:origin =>[@loc_a.lat, @loc_a.lng])
203
+ end
204
+
205
+ def test_scoped_distance_column_in_select
206
+ locations = @starbucks.locations.find(:all, :origin => @loc_a, :order => "distance ASC")
207
+ assert_equal 5, locations.size
208
+ assert_equal 0, @loc_a.distance_to(locations.first)
209
+ assert_in_delta 3.97, @loc_a.distance_to(locations.last, :units => :miles, :formula => :sphere), 0.01
210
+ end
211
+
212
+ def test_scoped_find_with_distance_condition
213
+ locations = @starbucks.locations.find(:all, :origin => @loc_a, :conditions => "distance < 3.97")
214
+ assert_equal 4, locations.size
215
+ locations = @starbucks.locations.count(:origin => @loc_a, :conditions => "distance < 3.97")
216
+ assert_equal 4, locations
217
+ end
218
+
219
+ def test_scoped_find_within
220
+ locations = @starbucks.locations.find_within(3.97, :origin => @loc_a)
221
+ assert_equal 4, locations.size
222
+ locations = @starbucks.locations.count_within(3.97, :origin => @loc_a)
223
+ assert_equal 4, locations
224
+ end
225
+
226
+ def test_scoped_find_with_compound_condition
227
+ locations = @starbucks.locations.find(:all, :origin => @loc_a, :conditions => "distance < 5 and city = 'Coppell'")
228
+ assert_equal 2, locations.size
229
+ locations = @starbucks.locations.count( :origin => @loc_a, :conditions => "distance < 5 and city = 'Coppell'")
230
+ assert_equal 2, locations
231
+ end
232
+
233
+ def test_scoped_find_beyond
234
+ locations = @starbucks.locations.find_beyond(3.95, :origin => @loc_a)
235
+ assert_equal 1, locations.size
236
+ locations = @starbucks.locations.count_beyond(3.95, :origin => @loc_a)
237
+ assert_equal 1, locations
238
+ end
239
+
240
+ def test_scoped_find_nearest
241
+ assert_equal @loc_a, @starbucks.locations.find_nearest(:origin => @loc_a)
242
+ end
243
+
244
+ def test_scoped_find_farthest
245
+ assert_equal @loc_e, @starbucks.locations.find_farthest(:origin => @loc_a)
246
+ end
247
+
248
+ def test_ip_geocoded_distance_column_in_select
249
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with(LOCATION_A_IP).returns(@location_a)
250
+ locations = Location.find(:all, :origin => LOCATION_A_IP, :order => "distance ASC")
251
+ assert_equal 6, locations.size
252
+ assert_equal 0, @loc_a.distance_to(locations.first)
253
+ assert_in_delta 3.97, @loc_a.distance_to(locations.last, :units => :miles, :formula => :sphere), 0.01
254
+ end
255
+
256
+ def test_ip_geocoded_find_with_distance_condition
257
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with(LOCATION_A_IP).returns(@location_a)
258
+ locations = Location.find(:all, :origin => LOCATION_A_IP, :conditions => "distance < 3.97")
259
+ assert_equal 5, locations.size
260
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with(LOCATION_A_IP).returns(@location_a)
261
+ locations = Location.count(:origin => LOCATION_A_IP, :conditions => "distance < 3.97")
262
+ assert_equal 5, locations
263
+ end
264
+
265
+ def test_ip_geocoded_find_within
266
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with(LOCATION_A_IP).returns(@location_a)
267
+ locations = Location.find_within(3.97, :origin => LOCATION_A_IP)
268
+ assert_equal 5, locations.size
269
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with(LOCATION_A_IP).returns(@location_a)
270
+ locations = Location.count_within(3.97, :origin => LOCATION_A_IP)
271
+ assert_equal 5, locations
272
+ end
273
+
274
+ def test_ip_geocoded_find_with_compound_condition
275
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with(LOCATION_A_IP).returns(@location_a)
276
+ locations = Location.find(:all, :origin => LOCATION_A_IP, :conditions => "distance < 5 and city = 'Coppell'")
277
+ assert_equal 2, locations.size
278
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with(LOCATION_A_IP).returns(@location_a)
279
+ locations = Location.count(:origin => LOCATION_A_IP, :conditions => "distance < 5 and city = 'Coppell'")
280
+ assert_equal 2, locations
281
+ end
282
+
283
+ def test_ip_geocoded_find_with_secure_compound_condition
284
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with(LOCATION_A_IP).returns(@location_a)
285
+ locations = Location.find(:all, :origin => LOCATION_A_IP, :conditions => ["distance < ? and city = ?", 5, 'Coppell'])
286
+ assert_equal 2, locations.size
287
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with(LOCATION_A_IP).returns(@location_a)
288
+ locations = Location.count(:origin => LOCATION_A_IP, :conditions => ["distance < ? and city = ?", 5, 'Coppell'])
289
+ assert_equal 2, locations
290
+ end
291
+
292
+ def test_ip_geocoded_find_beyond
293
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with(LOCATION_A_IP).returns(@location_a)
294
+ locations = Location.find_beyond(3.95, :origin => LOCATION_A_IP)
295
+ assert_equal 1, locations.size
296
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with(LOCATION_A_IP).returns(@location_a)
297
+ locations = Location.count_beyond(3.95, :origin => LOCATION_A_IP)
298
+ assert_equal 1, locations
299
+ end
300
+
301
+ def test_ip_geocoded_find_nearest
302
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with(LOCATION_A_IP).returns(@location_a)
303
+ assert_equal @loc_a, Location.find_nearest(:origin => LOCATION_A_IP)
304
+ end
305
+
306
+ def test_ip_geocoded_find_farthest
307
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with(LOCATION_A_IP).returns(@location_a)
308
+ assert_equal @loc_e, Location.find_farthest(:origin => LOCATION_A_IP)
309
+ end
310
+
311
+ def test_ip_geocoder_exception
312
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with('127.0.0.1').returns(GeoKit::GeoLoc.new)
313
+ assert_raises GeoKit::Geocoders::GeocodeError do
314
+ Location.find_farthest(:origin => '127.0.0.1')
315
+ end
316
+ end
317
+
318
+ def test_address_geocode
319
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with('Irving, TX').returns(@location_a)
320
+ locations = Location.find(:all, :origin => 'Irving, TX', :conditions => ["distance < ? and city = ?", 5, 'Coppell'])
321
+ assert_equal 2, locations.size
322
+ end
323
+
324
+ def test_find_with_custom_distance_condition
325
+ locations = CustomLocation.find(:all, :origin => @loc_a, :conditions => "dist < 3.97")
326
+ assert_equal 5, locations.size
327
+ locations = CustomLocation.count(:origin => @loc_a, :conditions => "dist < 3.97")
328
+ assert_equal 5, locations
329
+ end
330
+
331
+ def test_find_with_custom_distance_condition_using_custom_origin
332
+ locations = CustomLocation.find(:all, :origin => @custom_loc_a, :conditions => "dist < 3.97")
333
+ assert_equal 5, locations.size
334
+ locations = CustomLocation.count(:origin => @custom_loc_a, :conditions => "dist < 3.97")
335
+ assert_equal 5, locations
336
+ end
337
+
338
+ def test_find_within_with_custom
339
+ locations = CustomLocation.find_within(3.97, :origin => @loc_a)
340
+ assert_equal 5, locations.size
341
+ locations = CustomLocation.count_within(3.97, :origin => @loc_a)
342
+ assert_equal 5, locations
343
+ end
344
+
345
+ def test_find_within_with_coordinates_with_custom
346
+ locations = CustomLocation.find_within(3.97, :origin =>[@loc_a.lat, @loc_a.lng])
347
+ assert_equal 5, locations.size
348
+ locations = CustomLocation.count_within(3.97, :origin =>[@loc_a.lat, @loc_a.lng])
349
+ assert_equal 5, locations
350
+ end
351
+
352
+ def test_find_with_compound_condition_with_custom
353
+ locations = CustomLocation.find(:all, :origin => @loc_a, :conditions => "dist < 5 and city = 'Coppell'")
354
+ assert_equal 1, locations.size
355
+ locations = CustomLocation.count(:origin => @loc_a, :conditions => "dist < 5 and city = 'Coppell'")
356
+ assert_equal 1, locations
357
+ end
358
+
359
+ def test_find_with_secure_compound_condition_with_custom
360
+ locations = CustomLocation.find(:all, :origin => @loc_a, :conditions => ["dist < ? and city = ?", 5, 'Coppell'])
361
+ assert_equal 1, locations.size
362
+ locations = CustomLocation.count(:origin => @loc_a, :conditions => ["dist < ? and city = ?", 5, 'Coppell'])
363
+ assert_equal 1, locations
364
+ end
365
+
366
+ def test_find_beyond_with_custom
367
+ locations = CustomLocation.find_beyond(3.95, :origin => @loc_a)
368
+ assert_equal 1, locations.size
369
+ locations = CustomLocation.count_beyond(3.95, :origin => @loc_a)
370
+ assert_equal 1, locations
371
+ end
372
+
373
+ def test_find_beyond_with_coordinates_with_custom
374
+ locations = CustomLocation.find_beyond(3.95, :origin =>[@loc_a.lat, @loc_a.lng])
375
+ assert_equal 1, locations.size
376
+ locations = CustomLocation.count_beyond(3.95, :origin =>[@loc_a.lat, @loc_a.lng])
377
+ assert_equal 1, locations
378
+ end
379
+
380
+ def test_find_nearest_with_custom
381
+ assert_equal @custom_loc_a, CustomLocation.find_nearest(:origin => @loc_a)
382
+ end
383
+
384
+ def test_find_nearest_with_coordinates_with_custom
385
+ assert_equal @custom_loc_a, CustomLocation.find_nearest(:origin =>[@loc_a.lat, @loc_a.lng])
386
+ end
387
+
388
+ def test_find_farthest_with_custom
389
+ assert_equal @custom_loc_e, CustomLocation.find_farthest(:origin => @loc_a)
390
+ end
391
+
392
+ def test_find_farthest_with_coordinates_with_custom
393
+ assert_equal @custom_loc_e, CustomLocation.find_farthest(:origin =>[@loc_a.lat, @loc_a.lng])
394
+ end
395
+
396
+ def test_find_with_array_origin
397
+ locations = Location.find(:all, :origin =>[@loc_a.lat,@loc_a.lng], :conditions => "distance < 3.97")
398
+ assert_equal 5, locations.size
399
+ locations = Location.count(:origin =>[@loc_a.lat,@loc_a.lng], :conditions => "distance < 3.97")
400
+ assert_equal 5, locations
401
+ end
402
+
403
+
404
+ # Bounding box tests
405
+
406
+ def test_find_within_bounds
407
+ locations = Location.find_within_bounds([@sw,@ne])
408
+ assert_equal 2, locations.size
409
+ locations = Location.count_within_bounds([@sw,@ne])
410
+ assert_equal 2, locations
411
+ end
412
+
413
+ def test_find_within_bounds_ordered_by_distance
414
+ locations = Location.find_within_bounds([@sw,@ne], :origin=>@bounds_center, :order=>'distance asc')
415
+ assert_equal locations[0], locations(:d)
416
+ assert_equal locations[1], locations(:a)
417
+ end
418
+
419
+ def test_find_within_bounds_with_token
420
+ locations = Location.find(:all, :bounds=>[@sw,@ne])
421
+ assert_equal 2, locations.size
422
+ locations = Location.count(:bounds=>[@sw,@ne])
423
+ assert_equal 2, locations
424
+ end
425
+
426
+ def test_find_within_bounds_with_string_conditions
427
+ locations = Location.find(:all, :bounds=>[@sw,@ne], :conditions=>"id !=#{locations(:a).id}")
428
+ assert_equal 1, locations.size
429
+ end
430
+
431
+ def test_find_within_bounds_with_array_conditions
432
+ locations = Location.find(:all, :bounds=>[@sw,@ne], :conditions=>["id != ?", locations(:a).id])
433
+ assert_equal 1, locations.size
434
+ end
435
+
436
+ def test_find_within_bounds_with_hash_conditions
437
+ locations = Location.find(:all, :bounds=>[@sw,@ne], :conditions=>{:id => locations(:a).id})
438
+ assert_equal 1, locations.size
439
+ end
440
+
441
+ def test_auto_geocode
442
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with("Irving, TX").returns(@location_a)
443
+ store=Store.new(:address=>'Irving, TX')
444
+ store.save
445
+ assert_equal store.lat,@location_a.lat
446
+ assert_equal store.lng,@location_a.lng
447
+ assert_equal 0, store.errors.size
448
+ end
449
+
450
+ def test_auto_geocode_failure
451
+ GeoKit::Geocoders::MultiGeocoder.expects(:geocode).with("BOGUS").returns(GeoKit::GeoLoc.new)
452
+ store=Store.new(:address=>'BOGUS')
453
+ store.save
454
+ assert store.new_record?
455
+ assert_equal 1, store.errors.size
456
+ end
457
+
458
+ # Test :through
459
+
460
+ def test_find_with_through
461
+ organizations = MockOrganization.find(:all, :origin => @location_a, :order => 'distance ASC')
462
+ assert_equal 2, organizations.size
463
+ organizations = MockOrganization.count(:origin => @location_a, :conditions => "distance < 3.97")
464
+ assert_equal 1, organizations
465
+ end
466
+
467
+ def test_find_with_through_with_hash
468
+ people = MockPerson.find(:all, :origin => @location_a, :order => 'distance ASC')
469
+ assert_equal 2, people.size
470
+ people = MockPerson.count(:origin => @location_a, :conditions => "distance < 3.97")
471
+ assert_equal 2, people
472
+ end
473
+ end