module ActsAsGeocodable
Constants
- VERSION
Public Instance Methods
acts_as_geocodable(options = {})
click to toggle source
Make a model geocodable.
class Event < ActiveRecord::Base acts_as_geocodable end
Options¶ ↑
-
:address
: A hash that maps geocodable attirbutes (:street
,:locality
,:region
,:postal_code
,:country
) to your model's address fields, or a symbol to store the entire address in one field -
:normalize_address
: If set to true, you address fields will be updated using the address fields returned by the geocoder. (Default isfalse
) -
:units
: Default units-:miles
or:kilometers
-used for distance calculations and queries. (Default is:miles
)
# File lib/acts_as_geocodable.rb, line 33 def acts_as_geocodable(options = {}) options = { address: { street: :street, locality: :locality, region: :region, postal_code: :postal_code, country: :country}, normalize_address: false, distance_column: "distance", units: :miles }.merge(options) class_attribute :acts_as_geocodable_options self.acts_as_geocodable_options = options define_callbacks :geocoding if ActiveRecord::VERSION::MAJOR >= 4 has_one :geocoding, -> { includes :geocode }, as: :geocodable, dependent: :destroy else has_one :geocoding, as: :geocodable, include: :geocode, dependent: :destroy end after_save :attach_geocode # Would love to do a simpler scope here, like: # scope :with_geocode_fields, includes(:geocoding) # But we need to use select() and it would get overwritten. scope :with_geocode_fields, lambda { joins("JOIN geocodings ON #{table_name}.#{primary_key} = geocodings.geocodable_id AND geocodings.geocodable_type = '#{model_name}' JOIN geocodes ON geocodings.geocode_id = geocodes.id") } # Use ActiveRecord ARel style syntax for finding records. # # Model.origin("Chicago, IL", within: 10) # # a +distance+ attribute indicating the distance # to the origin is added to each of the results: # # Model.origin("Portland, OR").first.distance #=> 388.383 # # == Options # # * <tt>origin</tt>: A Geocode, String, or geocodable model that specifies # the origin # * <tt>:within</tt>: Limit to results within this radius of the origin # * <tt>:beyond</tt>: Limit to results outside of this radius from the origin # * <tt>:units</tt>: Units to use for <tt>:within</tt> or <tt>:beyond</tt>. # Default is <tt>:miles</tt> unless specified otherwise in the +acts_as_geocodable+ # declaration. # scope :origin, lambda { |*args| origin = location_to_geocode(args[0]) options = { units: acts_as_geocodable_options[:units], }.merge(args[1] || {}) distance_sql = sql_for_distance(origin, options[:units]) scope = with_geocode_fields.select("#{table_name}.*, #{distance_sql} AS #{acts_as_geocodable_options[:distance_column]}") scope = scope.where("#{distance_sql} > #{options[:beyond]}") if options[:beyond] if options[:within] scope = scope.where("(geocodes.latitude = :lat AND geocodes.longitude = :long) OR (#{distance_sql} <= #{options[:within]})", { lat: origin.latitude, long: origin.longitude }) end scope } scope :near, -> { order("#{acts_as_geocodable_options[:distance_column]} ASC") } scope :far, -> { order("#{acts_as_geocodable_options[:distance_column]} DESC") } include ActsAsGeocodable::Model end