Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding support for DataMapper as a model #811

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions Gemfile
Expand Up @@ -3,6 +3,7 @@ source "https://rubygems.org"
group :development, :test do
gem 'rake'
gem 'mongoid', '2.6.0'
gem 'data_mapper', '1.2.0'
gem 'bson_ext', platforms: :ruby
gem 'geoip'
gem 'rubyzip'
Expand Down
1 change: 1 addition & 0 deletions lib/geocoder.rb
Expand Up @@ -10,6 +10,7 @@
require "geocoder/models/active_record" if defined?(::ActiveRecord)
require "geocoder/models/mongoid" if defined?(::Mongoid)
require "geocoder/models/mongo_mapper" if defined?(::MongoMapper)
require "geocoder/models/data_mapper" if defined?(::DataMapper)

module Geocoder

Expand Down
65 changes: 65 additions & 0 deletions lib/geocoder/models/data_mapper.rb
@@ -0,0 +1,65 @@
require 'geocoder/models/base'

module Geocoder
module Model
module DataMapper
include Base

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,
:latitude => options[:latitude] || :latitude,
:longitude => options[:longitude] || :longitude,
:geocode_block => block,
:units => options[:units],
:method => options[:method],
:lookup => options[:lookup],
:language => options[:language]
)
end

##
# Set attribute names and include the Geocoder module.
#
def reverse_geocoded_by(latitude_attr, longitude_attr, options = {}, &block)
geocoder_init(
:reverse_geocode => true,
:fetched_address => options[:address] || :address,
:latitude => latitude_attr,
:longitude => longitude_attr,
:reverse_block => block,
:units => options[:units],
:method => options[:method],
:lookup => options[:lookup],
:language => options[:language]
)
end

private # ----------------------------------------------------------------

def geocoder_init(options)
unless geocoder_initialized?
@geocoder_options = { }
require "geocoder/stores/#{geocoder_file_name}"
include Geocoder::Store.const_get(geocoder_module_name)
end
@geocoder_options.merge! options
end

def geocoder_initialized?
included_modules.include? Geocoder::Store.const_get(geocoder_module_name)
rescue NameError
false
end

def geocoder_file_name; "data_mapper"; end
def geocoder_module_name; "DataMapper"; end
end
end
end
59 changes: 59 additions & 0 deletions lib/geocoder/stores/data_mapper.rb
@@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
require 'geocoder/stores/base'

##
# Add geocoding functionality to any DataMapper::Resource object.
#
module Geocoder::Store
module DataMapper
include Base

##
# Implementation of 'included' hook method.
#
def self.included(base)
base.class_eval do
# DataMapper scope equaliance. See: http://datamapper.org/docs/find.html
def geocoded
all(:conditions => ["latitude IS NOT NULL AND longitude IS NOT NULL"])
end

def not_geocoded
all(:conditions => ["latitude IS NULL OR longitude IS NULL"])
end
end
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|
if r = rs.first
unless r.latitude.nil? or r.longitude.nil?
o.__send__ "#{self.class.geocoder_options[:latitude]}=", r.latitude
o.__send__ "#{self.class.geocoder_options[:longitude]}=", r.longitude
end
r.coordinates
end
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|
if r = rs.first
unless r.address.nil?
o.__send__ "#{self.class.geocoder_options[:fetched_address]}=", r.address
end
r.address
end
end
end

end
end
35 changes: 35 additions & 0 deletions test/data_mapper_test_helper.rb
@@ -0,0 +1,35 @@
require 'rubygems'
require 'test/unit'
require 'test_helper'
require 'data_mapper'
require 'geocoder/models/data_mapper'

$LOAD_PATH.unshift(File.dirname(__FILE__))
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))

DataMapper::Logger.new($stdout, :debug)

##
# Geocoded model.
#
class PlaceUsingDataMapper
include Geocoder::Model::DataMapper
include DataMapper::Resource

property :name, String
property :address, String
property :latitude, Decimal
property :longitude, Decimal

geocoded_by :address

def coordinates
[latitude, longitude]
end

def initialize(name, address)
super()
attribute_set :name, name
attribute_set :address, address
end
end
31 changes: 31 additions & 0 deletions test/unit/data_mapper_test.rb
@@ -0,0 +1,31 @@
# encoding: utf-8
$: << File.join(File.dirname(__FILE__), "..")
require 'data_mapper_test_helper'

class DataMapperTest < GeocoderTestCase
def test_geocoded_check
p = PlaceUsingDataMapper.new(*geocoded_object_params(:msg))
p.latitude = 40.750354
p.longitude = -73.993371
assert p.geocoded?
end

def test_distance_to_returns_float
p = PlaceUsingDataMapper.new(*geocoded_object_params(:msg))
p.latitude = 40.750354
p.longitude = -73.993371
assert p.distance_to([30, -94]).is_a?(Float)
end

def test_model_configuration
p = PlaceUsingDataMapper.new(*geocoded_object_params(:msg))
p.latitude = 0
p.longitude = 0

PlaceUsingDataMapper.geocoded_by :address, :coordinates => :coordinates, :units => :km
assert_equal 111, p.distance_to([0,1]).round

PlaceUsingDataMapper.geocoded_by :address, :coordinates => :coordinates, :units => :mi
assert_equal 69, p.distance_to([0,1]).round
end
end