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,7 +1,10 @@
1
- require 'graticule'
2
- require 'acts_as_geocodable/geocoding'
3
- require 'acts_as_geocodable/geocode'
4
- require 'acts_as_geocodable/remote_location'
1
+ require "active_record"
2
+ require "active_support"
3
+ require "graticule"
4
+
5
+ require "acts_as_geocodable/geocoding"
6
+ require "acts_as_geocodable/geocode"
7
+ require "acts_as_geocodable/remote_location"
5
8
 
6
9
  module ActiveSupport::Callbacks::ClassMethods
7
10
  def without_callback(*args, &block)
@@ -11,7 +14,7 @@ module ActiveSupport::Callbacks::ClassMethods
11
14
  end
12
15
  end
13
16
 
14
- module ActsAsGeocodable #:nodoc:
17
+ module ActsAsGeocodable
15
18
  # Make a model geocodable.
16
19
  #
17
20
  # class Event < ActiveRecord::Base
@@ -29,25 +32,24 @@ module ActsAsGeocodable #:nodoc:
29
32
  #
30
33
  def acts_as_geocodable(options = {})
31
34
  options = {
32
- :address => {
33
- :street => :street, :locality => :locality, :region => :region,
34
- :postal_code => :postal_code, :country => :country},
35
- :normalize_address => false,
36
- :distance_column => 'distance',
37
- :units => :miles
35
+ address: {
36
+ street: :street, locality: :locality, region: :region,
37
+ postal_code: :postal_code, country: :country},
38
+ normalize_address: false,
39
+ distance_column: "distance",
40
+ units: :miles
38
41
  }.merge(options)
39
42
 
40
- if ActiveRecord::VERSION::MAJOR >= 3
41
- class_attribute :acts_as_geocodable_options
42
- self.acts_as_geocodable_options = options
43
- else
44
- write_inheritable_attribute :acts_as_geocodable_options, options
45
- class_inheritable_reader :acts_as_geocodable_options
46
- end
43
+ class_attribute :acts_as_geocodable_options
44
+ self.acts_as_geocodable_options = options
47
45
 
48
46
  define_callbacks :geocoding
49
47
 
50
- has_one :geocoding, :as => :geocodable, :include => :geocode, :dependent => :destroy
48
+ if ActiveRecord::VERSION::MAJOR >= 4
49
+ has_one :geocoding, -> { includes :geocode }, as: :geocodable, dependent: :destroy
50
+ else
51
+ has_one :geocoding, as: :geocodable, include: :geocode, dependent: :destroy
52
+ end
51
53
 
52
54
  after_save :attach_geocode
53
55
 
@@ -63,7 +65,7 @@ module ActsAsGeocodable #:nodoc:
63
65
 
64
66
  # Use ActiveRecord ARel style syntax for finding records.
65
67
  #
66
- # Model.origin("Chicago, IL", :within => 10)
68
+ # Model.origin("Chicago, IL", within: 10)
67
69
  #
68
70
  # a +distance+ attribute indicating the distance
69
71
  # to the origin is added to each of the results:
@@ -80,34 +82,35 @@ module ActsAsGeocodable #:nodoc:
80
82
  # Default is <tt>:miles</tt> unless specified otherwise in the +acts_as_geocodable+
81
83
  # declaration.
82
84
  #
83
- scope :origin, lambda {|*args|
85
+ scope :origin, lambda { |*args|
84
86
  origin = location_to_geocode(args[0])
85
87
  options = {
86
- :units => acts_as_geocodable_options[:units],
88
+ units: acts_as_geocodable_options[:units],
87
89
  }.merge(args[1] || {})
88
90
  distance_sql = sql_for_distance(origin, options[:units])
89
91
 
90
92
  scope = with_geocode_fields.select("#{table_name}.*, #{distance_sql} AS
91
93
  #{acts_as_geocodable_options[:distance_column]}")
92
94
 
93
- scope = scope.where("#{distance_sql} > #{options[:beyond]}") if options[:beyond]
95
+ scope = scope.where("#{distance_sql} > #{options[:beyond]}") if options[:beyond]
94
96
  if options[:within]
95
- scope = scope.where("(geocodes.latitude = :lat AND geocodes.longitude = :long) OR (#{distance_sql} <= #{options[:within]})", {:lat => origin.latitude, :long => origin.longitude})
97
+ scope = scope.where("(geocodes.latitude = :lat AND geocodes.longitude = :long) OR (#{distance_sql} <= #{options[:within]})", { lat: origin.latitude, long: origin.longitude })
96
98
  end
97
99
  scope
98
100
  }
99
101
 
100
- scope :near, order("#{acts_as_geocodable_options[:distance_column]} ASC")
101
- scope :far, order("#{acts_as_geocodable_options[:distance_column]} DESC")
102
+ scope :near, -> { order("#{acts_as_geocodable_options[:distance_column]} ASC") }
103
+ scope :far, -> { order("#{acts_as_geocodable_options[:distance_column]} DESC") }
102
104
 
103
105
  include ActsAsGeocodable::Model
104
106
  end
105
107
 
106
108
  module Model
107
- extend ActiveSupport::Concern
109
+ def self.included(base)
110
+ base.extend(ClassMethods)
111
+ end
108
112
 
109
113
  module ClassMethods
110
-
111
114
  # Find the nearest location to the given origin
112
115
  #
113
116
  # Model.origin("Grand Rapids, MI").nearest
@@ -128,7 +131,7 @@ module ActsAsGeocodable #:nodoc:
128
131
  def location_to_geocode(location)
129
132
  case location
130
133
  when Geocode then location
131
- when InstanceMethods then location.geocode
134
+ when ActsAsGeocodable::Model then location.geocode
132
135
  when String, Fixnum then Geocode.find_or_create_by_query(location.to_s)
133
136
  end
134
137
  end
@@ -149,120 +152,116 @@ module ActsAsGeocodable #:nodoc:
149
152
  # end
150
153
  #
151
154
  def validates_as_geocodable(options = {})
152
- options = options.reverse_merge :message => "Address could not be geocoded.", :allow_nil => false
155
+ options = options.reverse_merge message: "Address could not be geocoded.", allow_nil: false
153
156
  validate do |model|
154
157
  is_blank = model.to_location.attributes.except(:precision).all?(&:blank?)
155
158
  unless options[:allow_nil] && is_blank
156
- geocode = model.send :attach_geocode
159
+ geocode = model.send(:attach_geocode)
157
160
  if !geocode ||
158
161
  (options[:precision] && geocode.precision < options[:precision]) ||
159
162
  (block_given? && yield(geocode) == false)
160
- model.errors.add :base, options[:message]
163
+ model.errors.add(:base, options[:message])
161
164
  end
162
165
  end
163
166
  end
164
167
  end
165
168
 
166
- private
169
+ private
167
170
 
168
171
  def sql_for_distance(origin, units = acts_as_geocodable_options[:units])
169
172
  Graticule::Distance::Spherical.to_sql(
170
- :latitude => origin.latitude,
171
- :longitude => origin.longitude,
172
- :latitude_column => "geocodes.latitude",
173
- :longitude_column => "geocodes.longitude",
174
- :units => units
173
+ latitude: origin.latitude,
174
+ longitude: origin.longitude,
175
+ latitude_column: "geocodes.latitude",
176
+ longitude_column: "geocodes.longitude",
177
+ units: units
175
178
  )
176
179
  end
177
180
  end
178
181
 
179
- module InstanceMethods
180
-
181
- # Get the geocode for this model
182
- def geocode
183
- geocoding.geocode if geocoding
184
- end
182
+ # Get the geocode for this model
183
+ def geocode
184
+ geocoding.geocode if geocoding
185
+ end
185
186
 
186
- # Create a Graticule::Location
187
- def to_location
188
- Graticule::Location.new.tap do |location|
189
- [:street, :locality, :region, :postal_code, :country].each do |attr|
190
- location.send "#{attr}=", geo_attribute(attr)
191
- end
187
+ # Create a Graticule::Location
188
+ def to_location
189
+ Graticule::Location.new.tap do |location|
190
+ [:street, :locality, :region, :postal_code, :country].each do |attr|
191
+ location.send("#{attr}=", geo_attribute(attr))
192
192
  end
193
193
  end
194
+ end
194
195
 
195
- # Get the distance to the given destination. The destination can be an
196
- # acts_as_geocodable model, a Geocode, or a string
197
- #
198
- # myhome.distance_to "Chicago, IL"
199
- # myhome.distance_to "49423"
200
- # myhome.distance_to other_model
201
- #
202
- # == Options
203
- # * <tt>:units</tt>: <tt>:miles</tt> or <tt>:kilometers</tt>
204
- # * <tt>:formula</tt>: The formula to use to calculate the distance. This can
205
- # be any formula supported by Graticule. The default is <tt>:haversine</tt>.
206
- #
207
- def distance_to(destination, options = {})
208
- units = options[:units] || acts_as_geocodable_options[:units]
209
- formula = options[:formula] || :haversine
196
+ # Get the distance to the given destination. The destination can be an
197
+ # acts_as_geocodable model, a Geocode, or a string
198
+ #
199
+ # myhome.distance_to "Chicago, IL"
200
+ # myhome.distance_to "49423"
201
+ # myhome.distance_to other_model
202
+ #
203
+ # == Options
204
+ # * <tt>:units</tt>: <tt>:miles</tt> or <tt>:kilometers</tt>
205
+ # * <tt>:formula</tt>: The formula to use to calculate the distance. This can
206
+ # be any formula supported by Graticule. The default is <tt>:haversine</tt>.
207
+ #
208
+ def distance_to(destination, options = {})
209
+ units = options[:units] || acts_as_geocodable_options[:units]
210
+ formula = options[:formula] || :haversine
210
211
 
211
- geocode = self.class.location_to_geocode(destination)
212
- self.geocode.distance_to(geocode, units, formula)
213
- end
212
+ geocode = self.class.location_to_geocode(destination)
213
+ self.geocode.distance_to(geocode, units, formula)
214
+ end
214
215
 
215
216
  protected
216
217
 
217
- # Perform the geocoding
218
- def attach_geocode
219
- new_geocode = Geocode.find_or_create_by_location self.to_location unless self.to_location.blank?
220
- if new_geocode && self.geocode != new_geocode
221
- run_callbacks :geocoding do
222
- self.geocoding = Geocoding.new :geocode => new_geocode
223
- self.update_address self.acts_as_geocodable_options[:normalize_address]
224
- end
225
- elsif !new_geocode && self.geocoding
226
- self.geocoding.destroy
218
+ # Perform the geocoding
219
+ def attach_geocode
220
+ new_geocode = Geocode.find_or_create_by_location(self.to_location) unless self.to_location.blank?
221
+ if new_geocode && self.geocode != new_geocode
222
+ run_callbacks :geocoding do
223
+ self.geocoding = Geocoding.new(geocode: new_geocode)
224
+ self.update_address self.acts_as_geocodable_options[:normalize_address]
227
225
  end
228
- new_geocode
229
- rescue Graticule::Error => e
230
- logger.warn e.message
226
+ elsif !new_geocode && self.geocoding
227
+ self.geocoding.destroy
231
228
  end
229
+ new_geocode
230
+ rescue Graticule::Error => error
231
+ logger.warn error.message
232
+ end
232
233
 
233
-
234
- def update_address(force = false) #:nodoc:
235
- unless self.geocode.blank?
236
- if self.acts_as_geocodable_options[:address].is_a? Symbol
237
- method = self.acts_as_geocodable_options[:address]
234
+ def update_address(force = false)
235
+ unless self.geocode.blank?
236
+ if self.acts_as_geocodable_options[:address].is_a? Symbol
237
+ method = self.acts_as_geocodable_options[:address]
238
+ if self.respond_to?("#{method}=") && (self.send(method).blank? || force)
239
+ self.send("#{method}=", self.geocode.to_location.to_s)
240
+ end
241
+ else
242
+ self.acts_as_geocodable_options[:address].each do |attribute,method|
238
243
  if self.respond_to?("#{method}=") && (self.send(method).blank? || force)
239
- self.send "#{method}=", self.geocode.to_location.to_s
240
- end
241
- else
242
- self.acts_as_geocodable_options[:address].each do |attribute,method|
243
- if self.respond_to?("#{method}=") && (self.send(method).blank? || force)
244
- self.send "#{method}=", self.geocode.send(attribute)
245
- end
244
+ self.send("#{method}=", self.geocode.send(attribute))
246
245
  end
247
246
  end
247
+ end
248
248
 
249
- self.class.without_callback(:save, :after, :attach_geocode) do
250
- save
251
- end
249
+ self.class.without_callback(:save, :after, :attach_geocode) do
250
+ save
252
251
  end
253
252
  end
253
+ end
254
254
 
255
- def geo_attribute(attr_key) #:nodoc:
256
- if self.acts_as_geocodable_options[:address].is_a? Symbol
257
- attr_name = self.acts_as_geocodable_options[:address]
258
- attr_key == :street ? self.send(attr_name) : nil
259
- else
260
- attr_name = self.acts_as_geocodable_options[:address][attr_key]
261
- attr_name && self.respond_to?(attr_name) ? self.send(attr_name) : nil
262
- end
255
+ def geo_attribute(attr_key)
256
+ if self.acts_as_geocodable_options[:address].is_a? Symbol
257
+ attr_name = self.acts_as_geocodable_options[:address]
258
+ attr_key == :street ? self.send(attr_name) : nil
259
+ else
260
+ attr_name = self.acts_as_geocodable_options[:address][attr_key]
261
+ attr_name && self.respond_to?(attr_name) ? self.send(attr_name) : nil
263
262
  end
264
263
  end
265
264
  end
266
265
  end
267
266
 
268
- ActiveRecord::Base.send :extend, ActsAsGeocodable
267
+ ActiveRecord::Base.send(:extend, ActsAsGeocodable)
@@ -1,5 +1,5 @@
1
1
  class Geocode < ActiveRecord::Base
2
- has_many :geocodings, :dependent => :destroy
2
+ has_many :geocodings, dependent: :destroy
3
3
 
4
4
  validates_uniqueness_of :query
5
5
 
@@ -20,7 +20,7 @@ class Geocode < ActiveRecord::Base
20
20
  end
21
21
 
22
22
  def self.create_by_query(query)
23
- create geocoder.locate(query).attributes.merge(:query => query)
23
+ create geocoder.locate(query).attributes.merge(query: query)
24
24
  end
25
25
 
26
26
  def self.find_or_create_by_location(location)
@@ -28,9 +28,9 @@ class Geocode < ActiveRecord::Base
28
28
  end
29
29
 
30
30
  def self.create_from_location(location)
31
- create geocoder.locate(location).attributes.merge(:query => location.to_s)
32
- rescue Graticule::Error => e
33
- logger.warn e.message
31
+ create geocoder.locate(location).attributes.merge(query: location.to_s)
32
+ rescue Graticule::Error => error
33
+ logger.warn error.message
34
34
  nil
35
35
  end
36
36
 
@@ -47,7 +47,7 @@ class Geocode < ActiveRecord::Base
47
47
  end
48
48
 
49
49
  def on(geocodable)
50
- geocodings.create :geocodable => geocodable
50
+ geocodings.create(geocodable: geocodable)
51
51
  end
52
52
 
53
53
  def coordinates
@@ -60,6 +60,6 @@ class Geocode < ActiveRecord::Base
60
60
 
61
61
  # Create a Graticule::Location
62
62
  def to_location
63
- Graticule::Location.new(attributes.except('id', 'query'))
63
+ Graticule::Location.new(attributes.except("id", "query"))
64
64
  end
65
- end
65
+ end
@@ -1,12 +1,12 @@
1
1
  class Geocoding < ActiveRecord::Base
2
2
  belongs_to :geocode
3
- belongs_to :geocodable, :polymorphic => true
3
+ belongs_to :geocodable, polymorphic: true
4
4
 
5
5
  def self.geocoded_class(geocodable)
6
6
  ActiveRecord::Base.send(:class_name_of_active_record_descendant, geocodable.class).to_s
7
7
  end
8
-
8
+
9
9
  def self.find_geocodable(geocoded_class, geocoded_id)
10
10
  geocoded_class.constantize.find(geocoded_id)
11
11
  end
12
- end
12
+ end
@@ -1,20 +1,20 @@
1
- module ActsAsGeocodable #:nodoc:
2
- module RemoteLocation #:nodoc:
1
+ require "action_controller"
3
2
 
3
+ module ActsAsGeocodable
4
+ module RemoteLocation
4
5
  # Get the remote location of the request IP using http://hostip.info
5
6
  def remote_location
6
- if request.remote_ip == '127.0.0.1'
7
+ if request.remote_ip == "127.0.0.1"
7
8
  # otherwise people would complain that it doesn't work
8
- Graticule::Location.new(:locality => 'localhost')
9
+ Graticule::Location.new(locality: "localhost")
9
10
  else
10
11
  Graticule.service(:host_ip).new.locate(request.remote_ip)
11
12
  end
12
- rescue Graticule::Error => e
13
- logger.warn "An error occurred while looking up the location of '#{request.remote_ip}': #{e.message}"
13
+ rescue Graticule::Error => error
14
+ logger.warn "An error occurred while looking up the location of '#{request.remote_ip}': #{error.message}"
14
15
  nil
15
16
  end
16
-
17
17
  end
18
18
  end
19
19
 
20
- ActionController::Base.send :include, ActsAsGeocodable::RemoteLocation
20
+ ActionController::Base.send(:include, ActsAsGeocodable::RemoteLocation)
@@ -1,3 +1,3 @@
1
1
  module ActsAsGeocodable
2
- VERSION = '2.0.3' unless defined?(::ActsAsGeocodable::VERSION)
2
+ VERSION = "2.1.0" unless defined?(::ActsAsGeocodable::VERSION)
3
3
  end
@@ -9,4 +9,4 @@ Example:
9
9
  rails generate acts_as_geocodable
10
10
 
11
11
  With 4 existing migrations, this will create an AddGeocodableTables migration in the
12
- file db/migrate/5_add_geocodable_tables.rb
12
+ file db/migrate/5_add_geocodable_tables.rb
@@ -1,16 +1,16 @@
1
- require 'rails/generators'
2
- require 'rails/generators/migration'
1
+ require "rails/generators"
2
+ require "rails/generators/migration"
3
3
 
4
4
  class ActsAsGeocodableGenerator < Rails::Generators::Base
5
5
  include Rails::Generators::Migration
6
6
 
7
7
  def self.source_root
8
- @source_root ||= File.join(File.dirname(__FILE__), 'templates')
8
+ @source_root ||= File.join(File.dirname(__FILE__), "templates")
9
9
  end
10
10
 
11
11
  # Implement the required interface for Rails::Generators::Migration.
12
12
  #
13
- def self.next_migration_number(dirname) #:nodoc:
13
+ def self.next_migration_number(dirname)
14
14
  next_migration_number = current_migration_number(dirname) + 1
15
15
  if ActiveRecord::Base.timestamped_migrations
16
16
  [Time.now.utc.strftime("%Y%m%d%H%M%S"), "%.14d" % next_migration_number].max
@@ -21,8 +21,7 @@ class ActsAsGeocodableGenerator < Rails::Generators::Base
21
21
 
22
22
  def create_migration_file
23
23
  if defined?(ActiveRecord)
24
- migration_template 'migration.rb', 'db/migrate/add_geocodable_tables.rb'
24
+ migration_template "migration.rb", "db/migrate/add_geocodable_tables.rb"
25
25
  end
26
26
  end
27
-
28
27
  end