From b25029376f6b175cf4d37ccc009369656eaca32a Mon Sep 17 00:00:00 2001 From: Steve Agalloco <steve.agalloco@gmail.com> Date: Sun, 15 May 2011 22:15:03 -0400 Subject: [PATCH] added support for mongo_mapper --- lib/geocoder.rb | 1 + lib/geocoder/models/mongo_mapper.rb | 24 +++++++++ lib/geocoder/models/mongodb_base.rb | 55 ++++++++++++++++++++ lib/geocoder/models/mongoid.rb | 27 +--------- lib/geocoder/stores/mongo_mapper.rb | 79 +++++++++++++++++++++++++++++ 5 files changed, 161 insertions(+), 25 deletions(-) create mode 100644 lib/geocoder/models/mongo_mapper.rb create mode 100644 lib/geocoder/models/mongodb_base.rb create mode 100644 lib/geocoder/stores/mongo_mapper.rb diff --git a/lib/geocoder.rb b/lib/geocoder.rb index 928bc637..4e648617 100644 --- a/lib/geocoder.rb +++ b/lib/geocoder.rb @@ -4,6 +4,7 @@ require "geocoder/cache" require "geocoder/request" require "geocoder/models/active_record" require "geocoder/models/mongoid" +require "geocoder/models/mongo_mapper" module Geocoder extend self diff --git a/lib/geocoder/models/mongo_mapper.rb b/lib/geocoder/models/mongo_mapper.rb new file mode 100644 index 00000000..89f1c7c7 --- /dev/null +++ b/lib/geocoder/models/mongo_mapper.rb @@ -0,0 +1,24 @@ +require 'geocoder/models/base' +require 'geocoder/models/mongodb_base' + +module Geocoder + module Model + module MongoMapper + include Base + include MongoDBBase + + def self.included(base); base.extend(self); end + + private # -------------------------------------------------------------- + + def geocoder_file_name; "mongo_mapper"; end + def geocoder_module_name; "MongoMapper"; end + + def geocoder_init(options) + super(options) + ensure_index [[ geocoder_options[:coordinates], Mongo::GEO2D ]], + :min => -180, :max => 180 # create 2d index + end + end + end +end diff --git a/lib/geocoder/models/mongodb_base.rb b/lib/geocoder/models/mongodb_base.rb new file mode 100644 index 00000000..eee7ce4e --- /dev/null +++ b/lib/geocoder/models/mongodb_base.rb @@ -0,0 +1,55 @@ +require 'geocoder' + +module Geocoder + + ## + # Methods for invoking Geocoder in a model. + # + module Model + module MongoDBBase + + ## + # Set attribute names and include the Geocoder module. + # + def geocoded_by(address_attr, options = {}, &block) + geocoder_init( + :geocode => true, + :user_address => address_attr, + :coordinates => options[:coordinates] || :coordinates, + :geocode_block => block + ) + end + + ## + # Set attribute names and include the Geocoder module. + # + def reverse_geocoded_by(coordinates_attr, options = {}, &block) + geocoder_init( + :reverse_geocode => true, + :fetched_address => options[:address] || :address, + :coordinates => coordinates_attr, + :reverse_block => block + ) + end + + private # ---------------------------------------------------------------- + + def geocoder_init(options) + unless geocoder_initialized? + @geocoder_options = {} + require "geocoder/stores/#{geocoder_file_name}" + include eval("Geocoder::Store::" + geocoder_module_name) + end + @geocoder_options.merge! options + end + + def geocoder_initialized? + begin + included_modules.include? eval("Geocoder::Store::" + geocoder_module_name) + rescue NameError + false + end + end + end + end +end diff --git a/lib/geocoder/models/mongoid.rb b/lib/geocoder/models/mongoid.rb index a3d2b1ff..a6e40673 100644 --- a/lib/geocoder/models/mongoid.rb +++ b/lib/geocoder/models/mongoid.rb @@ -1,37 +1,14 @@ require 'geocoder/models/base' +require 'geocoder/models/mongodb_base' module Geocoder module Model module Mongoid include Base + include MongoDBBase def self.included(base); base.extend(self); end - ## - # Set attribute names and include the Geocoder module. - # - def geocoded_by(address_attr, options = {}, &block) - geocoder_init( - :geocode => true, - :user_address => address_attr, - :coordinates => options[:coordinates] || :coordinates, - :geocode_block => block - ) - end - - ## - # Set attribute names and include the Geocoder module. - # - def reverse_geocoded_by(coordinates_attr, options = {}, &block) - geocoder_init( - :reverse_geocode => true, - :fetched_address => options[:address] || :address, - :coordinates => coordinates_attr, - :reverse_block => block - ) - end - - private # -------------------------------------------------------------- def geocoder_file_name; "mongoid"; end diff --git a/lib/geocoder/stores/mongo_mapper.rb b/lib/geocoder/stores/mongo_mapper.rb new file mode 100644 index 00000000..0b1b44e6 --- /dev/null +++ b/lib/geocoder/stores/mongo_mapper.rb @@ -0,0 +1,79 @@ +require 'geocoder/stores/base' + +module Geocoder::Store + module MongoMapper + include Base + + def self.included(base) + base.class_eval do + + scope :geocoded, lambda { + where(geocoder_options[:coordinates].ne => nil) + } + + scope :not_geocoded, lambda { + where(geocoder_options[:coordinates] => nil) + } + + scope :near, lambda{ |location, *args| + coords = Geocoder::Calculations.extract_coordinates(location) + radius = args.size > 0 ? args.shift : 20 + options = args.size > 0 ? args.shift : {} + + # Use BSON::OrderedHash if Ruby's hashes are unordered. + # Conditions must be in order required by indexes (see mongo gem). + empty = RUBY_VERSION.split('.')[1].to_i < 9 ? BSON::OrderedHash.new : {} + + conds = empty.clone + conds[:coordinates] = empty.clone + conds[:coordinates]["$nearSphere"] = coords.reverse + conds[:coordinates]["$maxDistance"] = \ + Geocoder::Calculations.distance_to_radians(radius, options[:units] || :mi) + + if obj = options[:exclude] + conds[:_id.ne] = obj.id + end + where(conds) + } + end + end + + ## + # Coordinates [lat,lon] of the object. + # This method always returns coordinates in lat,lon order, + # even though internally they are stored in the opposite order. + # + def to_coordinates + coords = send(self.class.geocoder_options[:coordinates]) + coords.is_a?(Array) ? coords.reverse : [] + end + + ## + # Look up coordinates and assign to +latitude+ and +longitude+ attributes + # (or other as specified in +geocoded_by+). Returns coordinates (array). + # + def geocode + do_lookup(false) do |o,rs| + r = rs.first + unless r.coordinates.nil? + o.send :write_attribute, self.class.geocoder_options[:coordinates], r.coordinates.reverse + end + r.coordinates + end + end + + ## + # Look up address and assign to +address+ attribute (or other as specified + # in +reverse_geocoded_by+). Returns address (string). + # + def reverse_geocode + do_lookup(true) do |o,rs| + r = rs.first + unless r.address.nil? + o.send :write_attribute, self.class.geocoder_options[:fetched_address], r.address + end + r.address + end + end + end +end -- GitLab