geocoder 1.1.3 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of geocoder might be problematic. Click here for more details.

Files changed (45) hide show
  1. data/CHANGELOG.rdoc +12 -0
  2. data/README.md +580 -0
  3. data/examples/autoexpire_cache.rb +30 -0
  4. data/lib/geocoder.rb +9 -85
  5. data/lib/geocoder/calculations.rb +1 -1
  6. data/lib/geocoder/cli.rb +9 -10
  7. data/lib/geocoder/configuration.rb +3 -1
  8. data/lib/geocoder/lookup.rb +81 -0
  9. data/lib/geocoder/lookups/base.rb +20 -32
  10. data/lib/geocoder/lookups/bing.rb +12 -8
  11. data/lib/geocoder/lookups/freegeoip.rb +5 -5
  12. data/lib/geocoder/lookups/geocoder_ca.rb +13 -9
  13. data/lib/geocoder/lookups/google.rb +19 -7
  14. data/lib/geocoder/lookups/google_premier.rb +8 -7
  15. data/lib/geocoder/lookups/mapquest.rb +5 -23
  16. data/lib/geocoder/lookups/nominatim.rb +16 -13
  17. data/lib/geocoder/lookups/test.rb +8 -6
  18. data/lib/geocoder/lookups/yahoo.rb +49 -10
  19. data/lib/geocoder/lookups/yandex.rb +15 -8
  20. data/lib/geocoder/models/mongoid.rb +0 -1
  21. data/lib/geocoder/query.rb +88 -0
  22. data/lib/geocoder/results/mapquest.rb +2 -61
  23. data/lib/geocoder/results/nominatim.rb +24 -3
  24. data/lib/geocoder/sql.rb +104 -0
  25. data/lib/geocoder/stores/active_record.rb +68 -140
  26. data/lib/geocoder/stores/mongo_base.rb +2 -2
  27. data/lib/geocoder/version.rb +1 -1
  28. data/test/active_record_test.rb +15 -0
  29. data/test/calculations_test.rb +7 -0
  30. data/test/error_handling_test.rb +7 -7
  31. data/test/fixtures/yahoo_madison_square_garden.json +49 -43
  32. data/test/fixtures/yahoo_v1_madison_square_garden.json +46 -0
  33. data/test/fixtures/yahoo_v1_no_results.json +10 -0
  34. data/test/https_test.rb +2 -2
  35. data/test/integration/smoke_test.rb +6 -4
  36. data/test/lookup_test.rb +13 -6
  37. data/test/query_test.rb +34 -0
  38. data/test/result_test.rb +1 -1
  39. data/test/services_test.rb +48 -7
  40. data/test/test_helper.rb +64 -49
  41. data/test/test_mode_test.rb +0 -1
  42. metadata +13 -7
  43. data/README.rdoc +0 -552
  44. data/test/fixtures/yahoo_garbage.json +0 -50
  45. data/test/input_handling_test.rb +0 -43
@@ -1,66 +1,7 @@
1
1
  require 'geocoder/results/base'
2
+ require 'geocoder/results/nominatim'
2
3
 
3
4
  module Geocoder::Result
4
- class Mapquest < Base
5
-
6
- def house_number
7
- @data['address']['house_number']
8
- end
9
-
10
- def address
11
- @data['display_name']
12
- end
13
-
14
- def street
15
- @data['address']['road']
16
- end
17
-
18
- def city
19
- @data['address']['city']
20
- end
21
-
22
- def village
23
- @data['address']['villiage']
24
- end
25
-
26
- def town
27
- @data['address']['town']
28
- end
29
-
30
- def state
31
- @data['address']['state']
32
- end
33
-
34
- alias_method :state_code, :state
35
-
36
- def postal_code
37
- @data['address']['postcode']
38
- end
39
-
40
- def county
41
- @data['address']['county']
42
- end
43
-
44
- def country
45
- @data['address']['country']
46
- end
47
-
48
- def country_code
49
- @data['address']['country_code']
50
- end
51
-
52
- def coordinates
53
- [@data['lat'].to_f, @data['lon'].to_f]
54
- end
55
-
56
- def self.response_attributes
57
- %w[place_id boundingbox license polygonpoints display_name class type stadium suburb]
58
- end
59
-
60
- response_attributes.each do |a|
61
- define_method a do
62
- @data[a]
63
- end
64
- end
5
+ class Mapquest < Nominatim
65
6
  end
66
7
  end
@@ -5,8 +5,9 @@ module Geocoder::Result
5
5
 
6
6
  def poi
7
7
  %w[stadium bus_stop tram_stop].each do |key|
8
- @data['address'][key] if @data['address'].key?(key)
8
+ return @data['address'][key] if @data['address'].key?(key)
9
9
  end
10
+ return nil
10
11
  end
11
12
 
12
13
  def house_number
@@ -69,14 +70,34 @@ module Geocoder::Result
69
70
  [@data['lat'].to_f, @data['lon'].to_f]
70
71
  end
71
72
 
73
+ def place_class
74
+ @data['class']
75
+ end
76
+
77
+ def place_type
78
+ @data['type']
79
+ end
80
+
72
81
  def self.response_attributes
73
82
  %w[place_id osm_type osm_id boundingbox license
74
83
  polygonpoints display_name class type stadium]
75
84
  end
76
85
 
86
+ def class
87
+ warn "DEPRECATION WARNING: The 'class' method of Geocoder::Result::Nominatim objects is deprecated and will be removed in Geocoder version 1.2.0. Please use 'place_class' instead."
88
+ @data['class']
89
+ end
90
+
91
+ def type
92
+ warn "DEPRECATION WARNING: The 'type' method of Geocoder::Result::Nominatim objects is deprecated and will be removed in Geocoder version 1.2.0. Please use 'place_type' instead."
93
+ @data['type']
94
+ end
95
+
77
96
  response_attributes.each do |a|
78
- define_method a do
79
- @data[a]
97
+ unless method_defined?(a)
98
+ define_method a do
99
+ @data[a]
100
+ end
80
101
  end
81
102
  end
82
103
  end
@@ -0,0 +1,104 @@
1
+ module Geocoder
2
+ module Sql
3
+ extend self
4
+
5
+ ##
6
+ # Distance calculation for use with a database that supports POWER(),
7
+ # SQRT(), PI(), and trigonometric functions SIN(), COS(), ASIN(),
8
+ # ATAN2(), DEGREES(), and RADIANS().
9
+ #
10
+ # Based on the excellent tutorial at:
11
+ # http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
12
+ #
13
+ def full_distance(latitude, longitude, lat_attr, lon_attr, options = {})
14
+ earth = Geocoder::Calculations.earth_radius(options[:units] || :mi)
15
+
16
+ "#{earth} * 2 * ASIN(SQRT(" +
17
+ "POWER(SIN((#{latitude.to_f} - #{lat_attr}) * PI() / 180 / 2), 2) + " +
18
+ "COS(#{latitude.to_f} * PI() / 180) * COS(#{lat_attr} * PI() / 180) * " +
19
+ "POWER(SIN((#{longitude.to_f} - #{lon_attr}) * PI() / 180 / 2), 2)" +
20
+ "))"
21
+ end
22
+
23
+ ##
24
+ # Distance calculation for use with a database without trigonometric
25
+ # functions, like SQLite. Approach is to find objects within a square
26
+ # rather than a circle, so results are very approximate (will include
27
+ # objects outside the given radius).
28
+ #
29
+ # Distance and bearing calculations are *extremely inaccurate*. To be
30
+ # clear: this only exists to provide interface consistency. Results
31
+ # are not intended for use in production!
32
+ #
33
+ def approx_distance(latitude, longitude, lat_attr, lon_attr, options = {})
34
+ dx = Geocoder::Calculations.longitude_degree_distance(30, options[:units] || :mi)
35
+ dy = Geocoder::Calculations.latitude_degree_distance(options[:units] || :mi)
36
+
37
+ # sin of 45 degrees = average x or y component of vector
38
+ factor = Math.sin(Math::PI / 4)
39
+
40
+ "(#{dy} * ABS(#{lat_attr} - #{latitude.to_f}) * #{factor}) + " +
41
+ "(#{dx} * ABS(#{lon_attr} - #{longitude.to_f}) * #{factor})"
42
+ end
43
+
44
+ def within_bounding_box(sw_lat, sw_lng, ne_lat, ne_lng, lat_attr, lon_attr)
45
+ spans = "#{lat_attr} BETWEEN #{sw_lat} AND #{ne_lat} AND "
46
+ # handle box that spans 180 longitude
47
+ if sw_lng.to_f > ne_lng.to_f
48
+ spans + "#{lon_attr} BETWEEN #{sw_lng} AND 180 OR " +
49
+ "#{lon_attr} BETWEEN -180 AND #{ne_lng}"
50
+ else
51
+ spans + "#{lon_attr} BETWEEN #{sw_lng} AND #{ne_lng}"
52
+ end
53
+ end
54
+
55
+ ##
56
+ # Fairly accurate bearing calculation. Takes a latitude, longitude,
57
+ # and an options hash which must include a :bearing value
58
+ # (:linear or :spherical).
59
+ #
60
+ # Based on:
61
+ # http://www.beginningspatial.com/calculating_bearing_one_point_another
62
+ #
63
+ def full_bearing(latitude, longitude, lat_attr, lon_attr, options = {})
64
+ case options[:bearing]
65
+ when :linear
66
+ "CAST(" +
67
+ "DEGREES(ATAN2( " +
68
+ "RADIANS(#{lon_attr} - #{longitude.to_f}), " +
69
+ "RADIANS(#{lat_attr} - #{latitude.to_f})" +
70
+ ")) + 360 " +
71
+ "AS decimal) % 360"
72
+ when :spherical
73
+ "CAST(" +
74
+ "DEGREES(ATAN2( " +
75
+ "SIN(RADIANS(#{lon_attr} - #{longitude.to_f})) * " +
76
+ "COS(RADIANS(#{lat_attr})), (" +
77
+ "COS(RADIANS(#{latitude.to_f})) * SIN(RADIANS(#{lat_attr}))" +
78
+ ") - (" +
79
+ "SIN(RADIANS(#{latitude.to_f})) * COS(RADIANS(#{lat_attr})) * " +
80
+ "COS(RADIANS(#{lon_attr} - #{longitude.to_f}))" +
81
+ ")" +
82
+ ")) + 360 " +
83
+ "AS decimal) % 360"
84
+ end
85
+ end
86
+
87
+ ##
88
+ # Totally lame bearing calculation. Basically useless except that it
89
+ # returns *something* in databases without trig functions.
90
+ #
91
+ def approx_bearing(latitude, longitude, lat_attr, lon_attr, options = {})
92
+ "CASE " +
93
+ "WHEN (#{lat_attr} >= #{latitude.to_f} AND " +
94
+ "#{lon_attr} >= #{longitude.to_f}) THEN 45.0 " +
95
+ "WHEN (#{lat_attr} < #{latitude.to_f} AND " +
96
+ "#{lon_attr} >= #{longitude.to_f}) THEN 135.0 " +
97
+ "WHEN (#{lat_attr} < #{latitude.to_f} AND " +
98
+ "#{lon_attr} < #{longitude.to_f}) THEN 225.0 " +
99
+ "WHEN (#{lat_attr} >= #{latitude.to_f} AND " +
100
+ "#{lon_attr} < #{longitude.to_f}) THEN 315.0 " +
101
+ "END"
102
+ end
103
+ end
104
+ end
@@ -1,3 +1,4 @@
1
+ require 'geocoder/sql'
1
2
  require 'geocoder/stores/base'
2
3
 
3
4
  ##
@@ -36,11 +37,13 @@ module Geocoder::Store
36
37
  if Geocoder::Calculations.coordinates_present?(latitude, longitude)
37
38
  near_scope_options(latitude, longitude, *args)
38
39
  else
39
- where(false_condition) # no results if no lat/lon given
40
+ # If no lat/lon given we don't want any results, but we still
41
+ # need distance and bearing columns so you can add, for example:
42
+ # .order("distance")
43
+ select(select_clause(nil, "NULL", "NULL")).where(false_condition)
40
44
  end
41
45
  }
42
46
 
43
-
44
47
  ##
45
48
  # Find all objects within the area of a given bounding box.
46
49
  # Bounds must be an array of locations specifying the southwest
@@ -49,14 +52,15 @@ module Geocoder::Store
49
52
  #
50
53
  scope :within_bounding_box, lambda{ |bounds|
51
54
  sw_lat, sw_lng, ne_lat, ne_lng = bounds.flatten if bounds
52
- return where(false_condition) unless sw_lat && sw_lng && ne_lat && ne_lng
53
- spans = "#{geocoder_options[:latitude]} BETWEEN #{sw_lat} AND #{ne_lat} AND "
54
- spans << if sw_lng > ne_lng # Handle a box that spans 180
55
- "#{geocoder_options[:longitude]} BETWEEN #{sw_lng} AND 180 OR #{geocoder_options[:longitude]} BETWEEN -180 AND #{ne_lng}"
55
+ if sw_lat && sw_lng && ne_lat && ne_lng
56
+ {:conditions => Geocoder::Sql.within_bounding_box(
57
+ sw_lat, sw_lng, ne_lat, ne_lng,
58
+ full_column_name(geocoder_options[:latitude]),
59
+ full_column_name(geocoder_options[:longitude])
60
+ )}
56
61
  else
57
- "#{geocoder_options[:longitude]} BETWEEN #{sw_lng} AND #{ne_lng}"
62
+ select(select_clause(nil, "NULL", "NULL")).where(false_condition)
58
63
  end
59
- { :conditions => spans }
60
64
  }
61
65
  end
62
66
  end
@@ -69,7 +73,7 @@ module Geocoder::Store
69
73
  def distance_from_sql(location, *args)
70
74
  latitude, longitude = Geocoder::Calculations.extract_coordinates(location)
71
75
  if Geocoder::Calculations.coordinates_present?(latitude, longitude)
72
- distance_from_sql_options(latitude, longitude, *args)
76
+ distance_sql(latitude, longitude, *args)
73
77
  end
74
78
  end
75
79
 
@@ -90,163 +94,87 @@ module Geocoder::Store
90
94
  # set to false for no bearing calculation.
91
95
  # See Geocoder::Configuration to know how configure default method.
92
96
  # * +:select+ - string with the SELECT SQL fragment (e.g. “id, name”)
93
- # * +:order+ - column(s) for ORDER BY SQL clause; default is distance
97
+ # * +:order+ - column(s) for ORDER BY SQL clause; default is distance;
98
+ # set to false or nil to omit the ORDER BY clause
94
99
  # * +:exclude+ - an object to exclude (used by the +nearbys+ method)
95
100
  #
96
101
  def near_scope_options(latitude, longitude, radius = 20, options = {})
102
+ options[:units] ||= (geocoder_options[:units] || Geocoder::Configuration.units)
103
+ bearing = bearing_sql(latitude, longitude, options)
104
+ distance = distance_sql(latitude, longitude, options)
97
105
  if using_sqlite?
98
- approx_near_scope_options(latitude, longitude, radius, options)
99
- else
100
- full_near_scope_options(latitude, longitude, radius, options)
101
- end
102
- end
103
-
104
- def distance_from_sql_options(latitude, longitude, options = {})
105
- if using_sqlite?
106
- approx_distance_from_sql(latitude, longitude, options)
106
+ b = Geocoder::Calculations.bounding_box([latitude, longitude], radius, options)
107
+ args = b + [
108
+ full_column_name(geocoder_options[:latitude]),
109
+ full_column_name(geocoder_options[:longitude])
110
+ ]
111
+ conditions = Geocoder::Sql.within_bounding_box(*args)
107
112
  else
108
- full_distance_from_sql(latitude, longitude, options)
113
+ conditions = ["#{distance} <= ?", radius]
109
114
  end
115
+ {
116
+ :select => select_clause(options[:select], distance, bearing),
117
+ :conditions => add_exclude_condition(conditions, options[:exclude]),
118
+ :order => options.include?(:order) ? options[:order] : "distance ASC"
119
+ }
110
120
  end
111
121
 
112
122
  ##
113
- # Scope options hash for use with a database that supports POWER(),
114
- # SQRT(), PI(), and trigonometric functions SIN(), COS(), ASIN(),
115
- # ATAN2(), DEGREES(), and RADIANS().
123
+ # SQL for calculating distance based on the current database's
124
+ # capabilities (trig functions?).
116
125
  #
117
- # Bearing calculation based on:
118
- # http://www.beginningspatial.com/calculating_bearing_one_point_another
119
- #
120
- def full_near_scope_options(latitude, longitude, radius, options)
121
- lat_attr = geocoder_options[:latitude]
122
- lon_attr = geocoder_options[:longitude]
123
- options[:bearing] ||= (options[:method] ||
124
- geocoder_options[:method] ||
125
- Geocoder::Configuration.distances)
126
- bearing = case options[:bearing]
127
- when :linear
128
- "CAST(" +
129
- "DEGREES(ATAN2( " +
130
- "RADIANS(#{full_column_name(lon_attr)} - #{longitude}), " +
131
- "RADIANS(#{full_column_name(lat_attr)} - #{latitude})" +
132
- ")) + 360 " +
133
- "AS decimal) % 360"
134
- when :spherical
135
- "CAST(" +
136
- "DEGREES(ATAN2( " +
137
- "SIN(RADIANS(#{full_column_name(lon_attr)} - #{longitude})) * " +
138
- "COS(RADIANS(#{full_column_name(lat_attr)})), (" +
139
- "COS(RADIANS(#{latitude})) * SIN(RADIANS(#{full_column_name(lat_attr)}))" +
140
- ") - (" +
141
- "SIN(RADIANS(#{latitude})) * COS(RADIANS(#{full_column_name(lat_attr)})) * " +
142
- "COS(RADIANS(#{full_column_name(lon_attr)} - #{longitude}))" +
143
- ")" +
144
- ")) + 360 " +
145
- "AS decimal) % 360"
146
- end
147
- options[:units] ||= (geocoder_options[:units] || Geocoder::Configuration.units)
148
- distance = full_distance_from_sql(latitude, longitude, options)
149
- conditions = ["#{distance} <= ?", radius]
150
- default_near_scope_options(latitude, longitude, radius, options).merge(
151
- :select => "#{options[:select] || full_column_name("*")}, " +
152
- "#{distance} AS distance" +
153
- (bearing ? ", #{bearing} AS bearing" : ""),
154
- :conditions => add_exclude_condition(conditions, options[:exclude])
126
+ def distance_sql(latitude, longitude, options = {})
127
+ method_prefix = using_sqlite? ? "approx" : "full"
128
+ Geocoder::Sql.send(
129
+ method_prefix + "_distance",
130
+ latitude, longitude,
131
+ full_column_name(geocoder_options[:latitude]),
132
+ full_column_name(geocoder_options[:longitude]),
133
+ options
155
134
  )
156
135
  end
157
136
 
158
-
159
- # Distance calculations based on the excellent tutorial at:
160
- # http://www.scribd.com/doc/2569355/Geo-Distance-Search-with-MySQL
161
-
162
- def full_distance_from_sql(latitude, longitude, options)
163
- lat_attr = geocoder_options[:latitude]
164
- lon_attr = geocoder_options[:longitude]
165
-
166
- earth = Geocoder::Calculations.earth_radius(options[:units] || :mi)
167
-
168
- "#{earth} * 2 * ASIN(SQRT(" +
169
- "POWER(SIN((#{latitude} - #{full_column_name(lat_attr)}) * PI() / 180 / 2), 2) + " +
170
- "COS(#{latitude} * PI() / 180) * COS(#{full_column_name(lat_attr)} * PI() / 180) * " +
171
- "POWER(SIN((#{longitude} - #{full_column_name(lon_attr)}) * PI() / 180 / 2), 2) ))"
172
- end
173
-
174
- def approx_distance_from_sql(latitude, longitude, options)
175
- lat_attr = geocoder_options[:latitude]
176
- lon_attr = geocoder_options[:longitude]
177
-
178
- dx = Geocoder::Calculations.longitude_degree_distance(30, options[:units] || :mi)
179
- dy = Geocoder::Calculations.latitude_degree_distance(options[:units] || :mi)
180
-
181
- # sin of 45 degrees = average x or y component of vector
182
- factor = Math.sin(Math::PI / 4)
183
-
184
- "(#{dy} * ABS(#{full_column_name(lat_attr)} - #{latitude}) * #{factor}) + " +
185
- "(#{dx} * ABS(#{full_column_name(lon_attr)} - #{longitude}) * #{factor})"
186
- end
187
-
188
137
  ##
189
- # Scope options hash for use with a database without trigonometric
190
- # functions, like SQLite. Approach is to find objects within a square
191
- # rather than a circle, so results are very approximate (will include
192
- # objects outside the given radius).
138
+ # SQL for calculating bearing based on the current database's
139
+ # capabilities (trig functions?).
193
140
  #
194
- # Distance and bearing calculations are *extremely inaccurate*. They
195
- # only exist for interface consistency--not intended for production!
196
- #
197
- def approx_near_scope_options(latitude, longitude, radius, options)
198
- lat_attr = geocoder_options[:latitude]
199
- lon_attr = geocoder_options[:longitude]
200
- unless options.include?(:bearing)
201
- options[:bearing] = (options[:method] || \
202
- geocoder_options[:method] || \
203
- Geocoder::Configuration.distances)
141
+ def bearing_sql(latitude, longitude, options = {})
142
+ if !options.include?(:bearing)
143
+ options[:bearing] = Geocoder::Configuration.distances
204
144
  end
205
145
  if options[:bearing]
206
- bearing = "CASE " +
207
- "WHEN (#{full_column_name(lat_attr)} >= #{latitude} AND #{full_column_name(lon_attr)} >= #{longitude}) THEN 45.0 " +
208
- "WHEN (#{full_column_name(lat_attr)} < #{latitude} AND #{full_column_name(lon_attr)} >= #{longitude}) THEN 135.0 " +
209
- "WHEN (#{full_column_name(lat_attr)} < #{latitude} AND #{full_column_name(lon_attr)} < #{longitude}) THEN 225.0 " +
210
- "WHEN (#{full_column_name(lat_attr)} >= #{latitude} AND #{full_column_name(lon_attr)} < #{longitude}) THEN 315.0 " +
211
- "END"
212
- else
213
- bearing = false
146
+ method_prefix = using_sqlite? ? "approx" : "full"
147
+ Geocoder::Sql.send(
148
+ method_prefix + "_bearing",
149
+ latitude, longitude,
150
+ full_column_name(geocoder_options[:latitude]),
151
+ full_column_name(geocoder_options[:longitude]),
152
+ options
153
+ )
214
154
  end
215
-
216
- options[:units] ||= (geocoder_options[:units] || Geocoder::Configuration.units)
217
- distance = approx_distance_from_sql(latitude, longitude, options)
218
-
219
- b = Geocoder::Calculations.bounding_box([latitude, longitude], radius, options)
220
- conditions = [
221
- "#{full_column_name(lat_attr)} BETWEEN ? AND ? AND #{full_column_name(lon_attr)} BETWEEN ? AND ?"] +
222
- [b[0], b[2], b[1], b[3]
223
- ]
224
- default_near_scope_options(latitude, longitude, radius, options).merge(
225
- :select => "#{options[:select] || full_column_name("*")}, " +
226
- "#{distance} AS distance" +
227
- (bearing ? ", #{bearing} AS bearing" : ""),
228
- :conditions => add_exclude_condition(conditions, options[:exclude])
229
- )
230
155
  end
231
156
 
232
157
  ##
233
- # Options used for any near-like scope.
158
+ # Generate the SELECT clause.
234
159
  #
235
- def default_near_scope_options(latitude, longitude, radius, options)
236
- {
237
- :order => options[:order] || "distance",
238
- :limit => options[:limit],
239
- :offset => options[:offset]
240
- }
160
+ def select_clause(columns, distance, bearing = nil)
161
+ if columns == :geo_only
162
+ clause = ""
163
+ else
164
+ clause = (columns || full_column_name("*")) + ", "
165
+ end
166
+ clause + "#{distance} AS distance" +
167
+ (bearing ? ", #{bearing} AS bearing" : "")
241
168
  end
242
169
 
243
170
  ##
244
171
  # Adds a condition to exclude a given object by ID.
245
- # The given conditions MUST be an array.
172
+ # Expects conditions as an array or string. Returns array.
246
173
  #
247
174
  def add_exclude_condition(conditions, exclude)
175
+ conditions = [conditions] if conditions.is_a?(String)
248
176
  if exclude
249
- conditions[0] << " AND #{full_column_name(:id)} != ?"
177
+ conditions[0] << " AND #{full_column_name(primary_key)} != ?"
250
178
  conditions << exclude.id
251
179
  end
252
180
  conditions
@@ -280,8 +208,8 @@ module Geocoder::Store
280
208
  do_lookup(false) do |o,rs|
281
209
  if r = rs.first
282
210
  unless r.latitude.nil? or r.longitude.nil?
283
- o.send :write_attribute, self.class.geocoder_options[:latitude], r.latitude
284
- o.send :write_attribute, self.class.geocoder_options[:longitude], r.longitude
211
+ o.__send__ "#{self.class.geocoder_options[:latitude]}=", r.latitude
212
+ o.__send__ "#{self.class.geocoder_options[:longitude]}=", r.longitude
285
213
  end
286
214
  r.coordinates
287
215
  end
@@ -298,7 +226,7 @@ module Geocoder::Store
298
226
  do_lookup(true) do |o,rs|
299
227
  if r = rs.first
300
228
  unless r.address.nil?
301
- o.send :write_attribute, self.class.geocoder_options[:fetched_address], r.address
229
+ o.__send__ "#{self.class.geocoder_options[:fetched_address]}=", r.address
302
230
  end
303
231
  r.address
304
232
  end