<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array">
    <added>
      <filename>test/integration/app/app/models/category.rb</filename>
    </added>
    <added>
      <filename>test/integration/app/db/migrate/010_create_categories.rb</filename>
    </added>
    <added>
      <filename>test/integration/app/db/migrate/011_categories_sellers.rb</filename>
    </added>
    <added>
      <filename>test/integration/app/test/fixtures/categories.yml</filename>
    </added>
    <added>
      <filename>test/integration/app/test/fixtures/categories_sellers.yml</filename>
    </added>
    <added>
      <filename>test/integration/app/test/unit/category_test.rb</filename>
    </added>
  </added>
  <modified type="array">
    <modified>
      <diff>@@ -1,4 +1,6 @@
 
+v1.9.2. Add geodistance support (Jeremy Seitz, Mark Lane). 
+
 v1.9.1. Add ultrasphinx:index:merge task for index merging, and a note in DEPLOYMENT_NOTES about how to use it.
 
 v1.9. Delta indexing. ERb now supported in .base files. Allow setting the searched indexes at runtime.</diff>
      <filename>CHANGELOG</filename>
    </modified>
    <modified>
      <diff>@@ -37,6 +37,7 @@ test/integration/app/app/helpers/application_helper.rb
 test/integration/app/app/helpers/sellers_helper.rb
 test/integration/app/app/helpers/states_helper.rb
 test/integration/app/app/helpers/users_helper.rb
+test/integration/app/app/models/category.rb
 test/integration/app/app/models/geo/address.rb
 test/integration/app/app/models/geo/country.rb
 test/integration/app/app/models/geo/state.rb
@@ -71,7 +72,6 @@ test/integration/app/config/environments/test.rb
 test/integration/app/config/locomotive.yml
 test/integration/app/config/routes.rb
 test/integration/app/config/ultrasphinx/default.base
-test/integration/app/config/ultrasphinx/development.conf
 test/integration/app/config/ultrasphinx/development.conf.canonical
 test/integration/app/db/migrate/001_create_users.rb
 test/integration/app/db/migrate/002_create_sellers.rb
@@ -82,7 +82,8 @@ test/integration/app/db/migrate/006_add_deleted_to_user.rb
 test/integration/app/db/migrate/007_add_lat_and_long_to_address.rb
 test/integration/app/db/migrate/008_add_mission_statement_to_seller.rb
 test/integration/app/db/migrate/009_create_countries.rb
-test/integration/app/db/schema.rb
+test/integration/app/db/migrate/010_create_categories.rb
+test/integration/app/db/migrate/011_categories_sellers.rb
 test/integration/app/doc/README_FOR_APP
 test/integration/app/public/404.html
 test/integration/app/public/500.html
@@ -115,6 +116,8 @@ test/integration/app/script/process/spawner
 test/integration/app/script/runner
 test/integration/app/script/server
 test/integration/app/test/fixtures/addresses.yml
+test/integration/app/test/fixtures/categories.yml
+test/integration/app/test/fixtures/categories_sellers.yml
 test/integration/app/test/fixtures/countries.yml
 test/integration/app/test/fixtures/sellers.yml
 test/integration/app/test/fixtures/states.yml
@@ -125,6 +128,7 @@ test/integration/app/test/functional/states_controller_test.rb
 test/integration/app/test/functional/users_controller_test.rb
 test/integration/app/test/test_helper.rb
 test/integration/app/test/unit/address_test.rb
+test/integration/app/test/unit/category_test.rb
 test/integration/app/test/unit/country_test.rb
 test/integration/app/test/unit/seller_test.rb
 test/integration/app/test/unit/state_test.rb
@@ -153,6 +157,7 @@ vendor/riddle/README
 vendor/riddle/spec/fixtures/data/anchor.bin
 vendor/riddle/spec/fixtures/data/any.bin
 vendor/riddle/spec/fixtures/data/boolean.bin
+vendor/riddle/spec/fixtures/data/comment.bin
 vendor/riddle/spec/fixtures/data/distinct.bin
 vendor/riddle/spec/fixtures/data/field_weights.bin
 vendor/riddle/spec/fixtures/data/filter.bin
@@ -166,6 +171,8 @@ vendor/riddle/spec/fixtures/data/filter_range_exclude.bin
 vendor/riddle/spec/fixtures/data/group.bin
 vendor/riddle/spec/fixtures/data/index.bin
 vendor/riddle/spec/fixtures/data/index_weights.bin
+vendor/riddle/spec/fixtures/data/keywords_with_hits.bin
+vendor/riddle/spec/fixtures/data/keywords_without_hits.bin
 vendor/riddle/spec/fixtures/data/phrase.bin
 vendor/riddle/spec/fixtures/data/rank_mode.bin
 vendor/riddle/spec/fixtures/data/simple.bin
@@ -179,6 +186,7 @@ vendor/riddle/spec/fixtures/sql/conf.example.yml
 vendor/riddle/spec/fixtures/sql/data.sql
 vendor/riddle/spec/fixtures/sql/structure.sql
 vendor/riddle/spec/functional/excerpt_spec.rb
+vendor/riddle/spec/functional/keywords_spec.rb
 vendor/riddle/spec/functional/search_spec.rb
 vendor/riddle/spec/functional/update_spec.rb
 vendor/riddle/spec/spec_helper.rb</diff>
      <filename>Manifest</filename>
    </modified>
    <modified>
      <diff>@@ -32,6 +32,7 @@ Features include:
 * spellcheck
 * faceting on text, date, and numeric fields
 * field weighting, merging, and aliases
+* sorting and filtering by geodistance
 * &lt;tt&gt;belongs_to&lt;/tt&gt; and &lt;tt&gt;has_many&lt;/tt&gt; includes
 * drop-in compatibility with will_paginate[http://err.lighthouseapp.com/projects/466/home]
 * drop-in compatibility with Interlock[http://blog.evanweaver.com/files/doc/fauna/interlock/]</diff>
      <filename>README</filename>
    </modified>
    <modified>
      <diff>@@ -34,7 +34,7 @@ searchd
 {  
   # What interface the search daemon should listen on and where to store its logs
   address = 0.0.0.0
-  port = 3312
+  port = 3313
   seamless_rotate = 1
   log = &lt;%= path %&gt;log/searchd.log
   query_log = &lt;%= path %&gt;log/query.log
@@ -52,7 +52,7 @@ client
   
   # How your application connects to the search daemon (not necessarily the same as above)
   server_host = localhost
-  server_port = 3312
+  server_port = 3313
 }
 
 # Individual SQL source options</diff>
      <filename>examples/default.base</filename>
    </modified>
    <modified>
      <diff>@@ -42,18 +42,49 @@ The hash lets you customize internal aspects of the search.
 &lt;tt&gt;:weights&lt;/tt&gt;:: A hash. Text-field names and associated query weighting. The default weight for every field is 1.0. Example: &lt;tt&gt;:weights =&gt; {'title' =&gt; 2.0}&lt;/tt&gt;
 &lt;tt&gt;:filters&lt;/tt&gt;:: A hash. Names of numeric or date fields and associated values. You can use a single value, an array of values, or a range. (See the bottom of the ActiveRecord::Base page for an example.)
 &lt;tt&gt;:facets&lt;/tt&gt;:: An array of fields for grouping/faceting. You can access the returned facet values and their result counts with the &lt;tt&gt;facets&lt;/tt&gt; method.
+&lt;tt&gt;:location&lt;/tt&gt;:: A hash. Specify the names of your latititude and longitude attributes as declared in your is_indexed calls. To sort the results by distance, set &lt;tt&gt;:sort_mode =&gt; 'extended'&lt;/tt&gt; and &lt;tt&gt;:sort_by =&gt; 'distance asc'.&lt;/tt&gt;
 &lt;tt&gt;:indexes&lt;/tt&gt;:: An array of indexes to search. Currently only &lt;tt&gt;Ultrasphinx::MAIN_INDEX&lt;/tt&gt; and &lt;tt&gt;Ultrasphinx::DELTA_INDEX&lt;/tt&gt; are available. Defaults to both; changing this is rarely needed.
 
+== Query Defaults
+
 Note that you can set up your own query defaults in &lt;tt&gt;environment.rb&lt;/tt&gt;: 
   
   self.class.query_defaults = HashWithIndifferentAccess.new({
-    :per_page =&gt; 10,
+    :per_page =&gt; 10,c
     :sort_mode =&gt; 'relevance',
     :weights =&gt; {'title' =&gt; 2.0}
   })
 
 = Advanced features
 
+== Geographic distance
+
+If you pass a &lt;tt&gt;:location&lt;/tt&gt; Hash, distance from the location in meters will be available in your result records via the &lt;tt&gt;distance&lt;/tt&gt; accessor:
+
+  @search = Ultrasphinx::Search.new(:class_names =&gt; 'Point', 
+            :query =&gt; 'pizza',
+            :sort_mode =&gt; 'extended',
+            :sort_by =&gt; 'distance',
+            :location =&gt; {
+              :lat =&gt; 40.3,
+              :long =&gt; -73.6
+            })
+            
+   @search.run.first.distance #=&gt; 1402.4
+
+Note that Sphinx expects lat/long to be indexed as radians. If you have degrees in your database, do the conversion in the &lt;tt&gt;is_indexed&lt;/tt&gt; as so:
+  
+    is_indexed 'fields' =&gt; [
+        'name', 
+        'description',
+        {:field =&gt; 'lat', :function_sql =&gt; &quot;RADIANS(?)&quot;}, 
+        {:field =&gt; 'lng', :function_sql =&gt; &quot;RADIANS(?)&quot;}
+      ]
+
+Then, set &lt;tt&gt;Ultrasphinx::Search.client_options[:location][:units] = 'degrees'&lt;/tt&gt;.
+
+The &lt;tt&gt;:double&lt;/tt&gt; column type is recommended for storing location data.
+
 == Interlock integration
   
 Ultrasphinx uses the &lt;tt&gt;find_all_by_id&lt;/tt&gt; method to instantiate records. If you set &lt;tt&gt;with_finders: true&lt;/tt&gt; in {Interlock's}[http://blog.evanweaver.com/files/doc/fauna/interlock] &lt;tt&gt;config/memcached.yml&lt;/tt&gt;, Interlock overrides &lt;tt&gt;find_all_by_id&lt;/tt&gt; with a caching version.
@@ -108,7 +139,12 @@ Note that your database is never changed by anything Ultrasphinx does.
       :weights =&gt; {},
       :class_names =&gt; [],
       :filters =&gt; {},
-      :facets =&gt; []
+      :facets =&gt; [],
+      :location =&gt; HashWithIndifferentAccess.new({
+        :lat_attribute_name  =&gt; 'lat',
+        :long_attribute_name =&gt; 'lng',
+        :units =&gt; 'radians'
+      })
     })
     
     cattr_accessor :excerpting_options
@@ -262,12 +298,19 @@ Note that your database is never changed by anything Ultrasphinx does.
       opts = Hash[HashWithIndifferentAccess.new(opts._deep_dup._coerce_basic_types)]
       unless self.class.query_defaults.instance_of? Hash
         self.class.query_defaults = Hash[self.class.query_defaults]
+        self.class.query_defaults['location'] = Hash[self.class.query_defaults['location']]
+        
         self.class.client_options = Hash[self.class.client_options]
         self.class.excerpting_options = Hash[self.class.excerpting_options]
         self.class.excerpting_options['content_methods'].map! {|ary| ary.map {|m| m.to_s}}
       end    
+
+      # We need an annoying deep merge on the :location parameter
+      opts['location'].reverse_merge!(self.class.query_defaults['location']) if opts['location']
+
+      # Merge the rest of the defaults      
+      @options = self.class.query_defaults.merge(opts)
       
-      @options = self.class.query_defaults.merge(opts)            
       @options['query'] = @options['query'].to_s
       @options['class_names'] = Array(@options['class_names'])
       @options['facets'] = Array(@options['facets'])</diff>
      <filename>lib/ultrasphinx/search.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,6 +2,9 @@
 module Ultrasphinx
   class Search
     module Internals
+    
+      INFINITY = 1/0.0
+    
       include Associations
 
       # These methods are kept stateless to ease debugging
@@ -11,6 +14,8 @@ module Ultrasphinx
       def build_request_with_options opts
       
         request = Riddle::Client.new
+        
+        # Basic options
         request.instance_eval do          
           @server = Ultrasphinx::CLIENT_SETTINGS['server_host']
           @port = Ultrasphinx::CLIENT_SETTINGS['server_port']          
@@ -20,8 +25,29 @@ module Ultrasphinx
           @max_matches = [@offset + @limit + Ultrasphinx::Search.client_options['max_matches_offset'], MAX_MATCHES].min
         end
           
+        # Geosearch location
+        loc = opts['location']
+        loc.stringify_keys!
+        lat, long = loc['lat'], loc['long']
+        if lat and long
+          # Convert degrees to radians, if requested
+          if loc['units'] == 'degrees'
+            lat = degrees_to_radians(lat)
+            long = degrees_to_radians(long)
+          end
+          # Set the location/anchor point
+          request.set_anchor(loc['lat_attribute_name'], lat, loc['long_attribute_name'], long)
+        end
+                  
         # Sorting
         sort_by = opts['sort_by']
+        if options['location']
+          case sort_by
+            when &quot;distance asc&quot;, &quot;distance&quot; then sort_by = &quot;@geodist asc&quot;
+            when &quot;distance desc&quot; then sort_by = &quot;@geodist desc&quot;
+          end
+        end
+        
         # Use the additional sortable column if it is a text type
         sort_by += &quot;_sortable&quot; if Fields.instance.types[sort_by] == &quot;text&quot;
         
@@ -65,13 +91,19 @@ module Ultrasphinx
         end          
 
         # Extract raw filters 
-        # XXX This is poorly done. We should coerce based on the Field types, not the value class
+        # XXX This is poorly done. We should coerce based on the Field types, not the value class.
+        # That would also allow us to move numeric filters from the query string into the hash.
         Array(opts['filters']).each do |field, value|          
-          field = field.to_s
-          type = Fields.instance.types[field]
-          unless type
-            raise UsageError, &quot;field #{field.inspect} is invalid&quot;
+
+          field = field.to_s          
+          type = Fields.instance.types[field]             
+          
+          # Special derived attribute
+          if field == 'distance' and options['location']
+            field, type = '@geodist', 'float'
           end
+
+          raise UsageError, &quot;field #{field.inspect} is invalid&quot; unless type
           
           begin
             case value
@@ -292,6 +324,16 @@ module Ultrasphinx
             end
           end
         end
+
+        # Add an accessor for distance, if requested
+        if self.options['location']['lat'] and self.options['location']['long']
+          results.each_with_index do |result, index|
+            if result
+              distance = (response[:matches][index][:attributes]['@geodist'] or INFINITY)
+              result.instance_variable_get('@attributes')['distance'] = distance
+            end
+          end
+        end
         
         results.compact!
         
@@ -335,6 +377,10 @@ module Ultrasphinx
         # Also removes apostrophes in the middle of words so that they don't get split in two.
         s.gsub(/(^|\s)(AND|OR|NOT|\@\w+)(\s|$)/i, &quot;&quot;).gsub(/(\w)\'(\w)/, '\1\2')
       end 
+      
+      def degrees_to_radians(value)
+        Math::PI * value / 180.0
+      end
     
     end
   end  </diff>
      <filename>lib/ultrasphinx/search/internals.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,7 +2,7 @@ class AddressesController &lt; ApplicationController
   # GET /addresses
   # GET /addresses.xml
   def index
-    @addresses = Address.find(:all)
+    @addresses = Geo::Address.find(:all)
 
     respond_to do |format|
       format.html # index.html.erb
@@ -13,7 +13,7 @@ class AddressesController &lt; ApplicationController
   # GET /addresses/1
   # GET /addresses/1.xml
   def show
-    @address = Address.find(params[:id])
+    @address = Geo::Address.find(params[:id])
 
     respond_to do |format|
       format.html # show.html.erb
@@ -24,7 +24,7 @@ class AddressesController &lt; ApplicationController
   # GET /addresses/new
   # GET /addresses/new.xml
   def new
-    @address = Address.new
+    @address = Geo::Address.new
 
     respond_to do |format|
       format.html # new.html.erb
@@ -34,18 +34,18 @@ class AddressesController &lt; ApplicationController
 
   # GET /addresses/1/edit
   def edit
-    @address = Address.find(params[:id])
+    @address = Geo::Address.find(params[:id])
   end
 
   # POST /addresses
   # POST /addresses.xml
   def create
-    @address = Address.new(params[:address])
+    @address = Geo::Address.new(params[:address])
 
     respond_to do |format|
       if @address.save
         flash[:notice] = 'Address was successfully created.'
-        format.html { redirect_to(@address) }
+        format.html { redirect_to(address_path(@address)) }
         format.xml  { render :xml =&gt; @address, :status =&gt; :created, :location =&gt; @address }
       else
         format.html { render :action =&gt; &quot;new&quot; }
@@ -57,12 +57,12 @@ class AddressesController &lt; ApplicationController
   # PUT /addresses/1
   # PUT /addresses/1.xml
   def update
-    @address = Address.find(params[:id])
+    @address = Geo::Address.find(params[:id])
 
     respond_to do |format|
       if @address.update_attributes(params[:address])
         flash[:notice] = 'Address was successfully updated.'
-        format.html { redirect_to(@address) }
+        format.html { redirect_to(address_path(@address)) }
         format.xml  { head :ok }
       else
         format.html { render :action =&gt; &quot;edit&quot; }
@@ -74,7 +74,7 @@ class AddressesController &lt; ApplicationController
   # DELETE /addresses/1
   # DELETE /addresses/1.xml
   def destroy
-    @address = Address.find(params[:id])
+    @address = Geo::Address.find(params[:id])
     @address.destroy
 
     respond_to do |format|</diff>
      <filename>test/integration/app/app/controllers/addresses_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,7 +2,7 @@ class StatesController &lt; ApplicationController
   # GET /states
   # GET /states.xml
   def index
-    @states = State.find(:all)
+    @states = Geo::State.find(:all)
 
     respond_to do |format|
       format.html # index.html.erb
@@ -13,7 +13,7 @@ class StatesController &lt; ApplicationController
   # GET /states/1
   # GET /states/1.xml
   def show
-    @state = State.find(params[:id])
+    @state = Geo::State.find(params[:id])
 
     respond_to do |format|
       format.html # show.html.erb
@@ -24,7 +24,7 @@ class StatesController &lt; ApplicationController
   # GET /states/new
   # GET /states/new.xml
   def new
-    @state = State.new
+    @state = Geo::State.new
 
     respond_to do |format|
       format.html # new.html.erb
@@ -34,18 +34,18 @@ class StatesController &lt; ApplicationController
 
   # GET /states/1/edit
   def edit
-    @state = State.find(params[:id])
+    @state = Geo::State.find(params[:id])
   end
 
   # POST /states
   # POST /states.xml
   def create
-    @state = State.new(params[:state])
+    @state = Geo::State.new(params[:state])
 
     respond_to do |format|
       if @state.save
         flash[:notice] = 'State was successfully created.'
-        format.html { redirect_to(@state) }
+        format.html { redirect_to(state_path(@state)) }
         format.xml  { render :xml =&gt; @state, :status =&gt; :created, :location =&gt; @state }
       else
         format.html { render :action =&gt; &quot;new&quot; }
@@ -57,12 +57,12 @@ class StatesController &lt; ApplicationController
   # PUT /states/1
   # PUT /states/1.xml
   def update
-    @state = State.find(params[:id])
+    @state = Geo::State.find(params[:id])
 
     respond_to do |format|
       if @state.update_attributes(params[:state])
         flash[:notice] = 'State was successfully updated.'
-        format.html { redirect_to(@state) }
+        format.html { redirect_to(state_path(@state)) }
         format.xml  { head :ok }
       else
         format.html { render :action =&gt; &quot;edit&quot; }
@@ -74,7 +74,7 @@ class StatesController &lt; ApplicationController
   # DELETE /states/1
   # DELETE /states/1.xml
   def destroy
-    @state = State.find(params[:id])
+    @state = Geo::State.find(params[:id])
     @state.destroy
 
     respond_to do |format|</diff>
      <filename>test/integration/app/app/controllers/states_controller.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,7 +2,7 @@ class Geo::Address &lt; ActiveRecord::Base
   belongs_to :user
   belongs_to :state, :class_name =&gt; 'Geo::State'
   
-  is_indexed 'fields' =&gt; ['name'],
+  is_indexed 'fields' =&gt; ['name', {:field =&gt; 'lat', :function_sql =&gt; &quot;RADIANS(?)&quot;}, {:field =&gt; 'lng', :function_sql =&gt; &quot;RADIANS(?)&quot;}],
     'concatenate' =&gt; [{'fields' =&gt; ['line_1', 'line_2', 'city', 'province_region', 'zip_postal_code'], 'as' =&gt; 'content'}],
     'include' =&gt; [{'association_name' =&gt; 'state', 'field' =&gt; 'name', 'as' =&gt; 'state'}],
     'delta' =&gt; true</diff>
      <filename>test/integration/app/app/models/geo/address.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,11 +2,11 @@
 
 &lt;%= error_messages_for :address %&gt;
 
-&lt;% form_for(@address) do |f| %&gt;
+&lt;% form_for @address, :url =&gt; address_path(@address) do |f| %&gt;
   &lt;p&gt;
     &lt;%= f.submit &quot;Update&quot; %&gt;
   &lt;/p&gt;
 &lt;% end %&gt;
 
-&lt;%= link_to 'Show', @address %&gt; |
+&lt;%= link_to 'Show', address_path(@address) %&gt; |
 &lt;%= link_to 'Back', addresses_path %&gt;</diff>
      <filename>test/integration/app/app/views/addresses/edit.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -6,9 +6,9 @@
 
 &lt;% for address in @addresses %&gt;
   &lt;tr&gt;
-    &lt;td&gt;&lt;%= link_to 'Show', address %&gt;&lt;/td&gt;
+    &lt;td&gt;&lt;%= link_to 'Show', address_path(address) %&gt;&lt;/td&gt;
     &lt;td&gt;&lt;%= link_to 'Edit', edit_address_path(address) %&gt;&lt;/td&gt;
-    &lt;td&gt;&lt;%= link_to 'Destroy', address, :confirm =&gt; 'Are you sure?', :method =&gt; :delete %&gt;&lt;/td&gt;
+    &lt;td&gt;&lt;%= link_to 'Destroy', address_path(address), :confirm =&gt; 'Are you sure?', :method =&gt; :delete %&gt;&lt;/td&gt;
   &lt;/tr&gt;
 &lt;% end %&gt;
 &lt;/table&gt;</diff>
      <filename>test/integration/app/app/views/addresses/index.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -2,7 +2,7 @@
 
 &lt;%= error_messages_for :address %&gt;
 
-&lt;% form_for(@address) do |f| %&gt;
+&lt;% form_for @address, :url =&gt; address_path(@address) do |f| %&gt;
   &lt;p&gt;
     &lt;%= f.submit &quot;Create&quot; %&gt;
   &lt;/p&gt;</diff>
      <filename>test/integration/app/app/views/addresses/new.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -2,11 +2,11 @@
 
 &lt;%= error_messages_for :state %&gt;
 
-&lt;% form_for(@state) do |f| %&gt;
+&lt;% form_for @state, :url =&gt; state_path(@state) do |f| %&gt;
   &lt;p&gt;
     &lt;%= f.submit &quot;Update&quot; %&gt;
   &lt;/p&gt;
 &lt;% end %&gt;
 
-&lt;%= link_to 'Show', @state %&gt; |
+&lt;%= link_to 'Show', state_path(@state) %&gt; |
 &lt;%= link_to 'Back', states_path %&gt;</diff>
      <filename>test/integration/app/app/views/states/edit.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -7,9 +7,9 @@
 &lt;% for state in @states %&gt;
   &lt;tr&gt;
     &lt;td&gt;&lt;%= state.name %&gt;&lt;/td&gt;
-    &lt;td&gt;&lt;%= link_to 'Show', state %&gt;&lt;/td&gt;
+    &lt;td&gt;&lt;%= link_to 'Show', state_path(state) %&gt;&lt;/td&gt;
     &lt;td&gt;&lt;%= link_to 'Edit', edit_state_path(state) %&gt;&lt;/td&gt;
-    &lt;td&gt;&lt;%= link_to 'Destroy', state, :confirm =&gt; 'Are you sure?', :method =&gt; :delete %&gt;&lt;/td&gt;
+    &lt;td&gt;&lt;%= link_to 'Destroy', state_path(state), :confirm =&gt; 'Are you sure?', :method =&gt; :delete %&gt;&lt;/td&gt;
   &lt;/tr&gt;
 &lt;% end %&gt;
 &lt;/table&gt;</diff>
      <filename>test/integration/app/app/views/states/index.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -2,7 +2,7 @@
 
 &lt;%= error_messages_for :state %&gt;
 
-&lt;% form_for(@state) do |f| %&gt;
+&lt;% form_for @state, :url =&gt; state_path(@state) do |f| %&gt;
   &lt;p&gt;
     &lt;%= f.submit &quot;Create&quot; %&gt;
   &lt;/p&gt;</diff>
      <filename>test/integration/app/app/views/states/new.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -7,9 +7,9 @@
 &lt;% for user in @users %&gt;
   &lt;tr&gt;
 	&lt;td&gt;&lt;%= h(user.login) %&gt;&lt;/td&gt;
-	&lt;td&gt;&lt;%= h(user.address.line_1) %&gt;&lt;/td&gt;
-	&lt;td&gt;&lt;%= h(user.address.city) %&gt;&lt;/td&gt;
-	&lt;td&gt;&lt;%= h(user.address.state.name) %&gt;&lt;/td&gt;
+	&lt;td&gt;&lt;%= h(user.address.line_1) if user.address %&gt;&lt;/td&gt;
+	&lt;td&gt;&lt;%= h(user.address.city) if user.address %&gt;&lt;/td&gt;
+	&lt;td&gt;&lt;%= h(user.address.state.name) if user.address %&gt;&lt;/td&gt;
     &lt;td&gt;&lt;%= link_to 'Show', user %&gt;&lt;/td&gt;
     &lt;td&gt;&lt;%= link_to 'Edit', edit_user_path(user) %&gt;&lt;/td&gt;
     &lt;td&gt;&lt;%= link_to 'Destroy', user, :confirm =&gt; 'Are you sure?', :method =&gt; :delete %&gt;&lt;/td&gt;</diff>
      <filename>test/integration/app/app/views/users/index.html.erb</filename>
    </modified>
    <modified>
      <diff>@@ -11,5 +11,6 @@ Rails::Initializer.run do |config|
 end
 
 Ultrasphinx::Search.client_options[&quot;finder_methods&quot;].unshift(&quot;custom_find&quot;)
+Ultrasphinx::Search.query_defaults[&quot;location&quot;][&quot;units&quot;] = &quot;degrees&quot;
 
 # Dependencies.log_activity = true</diff>
      <filename>test/integration/app/config/environment.rb</filename>
    </modified>
    <modified>
      <diff>@@ -36,7 +36,7 @@ searchd
   
   # What interface the search daemon should listen on 
   address = 0.0.0.0
-  port = 3312
+  port = 3313
   seamless_rotate = 1
   
   # Where to to store the logs
@@ -55,7 +55,7 @@ client
   dictionary_name = &lt;%= dictionary = 'ap' %&gt;
   # How your application connects to the search daemon (not necessarily the same as above)
   server_host = localhost
-  server_port = 3312
+  server_port = 3313
 }
 
 source</diff>
      <filename>test/integration/app/config/ultrasphinx/default.base</filename>
    </modified>
    <modified>
      <diff>@@ -1,5 +1,5 @@
 
-# Auto-generated at Thu Mar 20 22:33:23 -0400 2008.
+# Auto-generated at Sun Mar 23 17:16:38 -0400 2008.
 # Hand modifications will be overwritten.
 # /Users/eweaver/Desktop/projects/ub/vendor/plugins/ultrasphinx/test/integration/app/config/ultrasphinx/default.base
 indexer {
@@ -10,7 +10,7 @@ searchd {
   read_timeout = 5
   max_children = 300
   log = /tmp/sphinx/searchd.log
-  port = 3312
+  port = 3313
   max_matches = 100000
   query_log = /tmp/sphinx/query.log
   seamless_rotate = 1
@@ -35,7 +35,7 @@ sql_host = localhost
 sql_pass = 
 sql_user = root
 sql_query_range = SELECT MIN(id) , MAX(id) FROM states 
-sql_query = SELECT (states.id * 5 + 0) AS id, CAST(GROUP_CONCAT(DISTINCT addresses.name  SEPARATOR ' ') AS CHAR) AS address_name, 0 AS capitalization, 'Geo::State' AS class, 0 AS class_id, '' AS company, 0 AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_name_sortable, '' AS company_two, 0 AS company_two_facet, '' AS content, 18000 AS created_at, 0 AS deleted, '' AS email, '' AS login, '' AS mission_statement, '' AS mission_statement_sortable, '' AS name, '' AS state, 0 AS user_id FROM states LEFT OUTER JOIN addresses AS addresses ON states.id = addresses.state_id WHERE states.id &gt;= $start AND states.id &lt;= $end GROUP BY states.id
+sql_query = SELECT (states.id * 5 + 0) AS id, CAST(GROUP_CONCAT(DISTINCT addresses.name  SEPARATOR ' ') AS CHAR) AS address_name, 0 AS capitalization, 'Geo::State' AS class, 0 AS class_id, '' AS company, 0 AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_name_sortable, '' AS company_two, 0 AS company_two_facet, '' AS content, 18000 AS created_at, 0 AS deleted, '' AS email, 0 AS lat, 0 AS lng, '' AS login, '' AS mission_statement, '' AS mission_statement_sortable, '' AS name, '' AS state, 0 AS user_id FROM states LEFT OUTER JOIN addresses AS addresses ON states.id = addresses.state_id WHERE states.id &gt;= $start AND states.id &lt;= $end GROUP BY states.id
 
 sql_attr_float = capitalization
 sql_attr_uint = class_id
@@ -45,6 +45,8 @@ sql_attr_str2ordinal = company_name_sortable
 sql_attr_uint = company_two_facet
 sql_attr_timestamp = created_at
 sql_attr_bool = deleted
+sql_attr_float = lat
+sql_attr_float = lng
 sql_attr_str2ordinal = mission_statement_sortable
 sql_attr_uint = user_id
 sql_query_info = SELECT * FROM states WHERE states.id = (($id - 0) / 5)
@@ -68,7 +70,7 @@ sql_host = localhost
 sql_pass = 
 sql_user = root
 sql_query_range = SELECT MIN(id) , MAX(id) FROM sellers 
-sql_query = SELECT (sellers.id * 5 + 1) AS id, '' AS address_name, sellers.capitalization AS capitalization, 'Seller' AS class, 1 AS class_id, '' AS company, 0 AS company_facet, sellers.company_name AS company_name, CAST(CRC32(sellers.company_name) AS unsigned) AS company_name_facet, company_name AS company_name_sortable, '' AS company_two, 0 AS company_two_facet, '' AS content, UNIX_TIMESTAMP(sellers.created_at) AS created_at, 0 AS deleted, '' AS email, '' AS login, sellers.mission_statement AS mission_statement, mission_statement AS mission_statement_sortable, '' AS name, '' AS state, sellers.user_id AS user_id FROM sellers WHERE sellers.id &gt;= $start AND sellers.id &lt;= $end GROUP BY sellers.id
+sql_query = SELECT (sellers.id * 5 + 1) AS id, '' AS address_name, sellers.capitalization AS capitalization, 'Seller' AS class, 1 AS class_id, '' AS company, 0 AS company_facet, sellers.company_name AS company_name, CAST(CRC32(sellers.company_name) AS unsigned) AS company_name_facet, company_name AS company_name_sortable, '' AS company_two, 0 AS company_two_facet, '' AS content, UNIX_TIMESTAMP(sellers.created_at) AS created_at, 0 AS deleted, '' AS email, 0 AS lat, 0 AS lng, '' AS login, sellers.mission_statement AS mission_statement, mission_statement AS mission_statement_sortable, '' AS name, '' AS state, sellers.user_id AS user_id FROM sellers WHERE sellers.id &gt;= $start AND sellers.id &lt;= $end GROUP BY sellers.id
 
 sql_attr_float = capitalization
 sql_attr_uint = class_id
@@ -78,6 +80,8 @@ sql_attr_str2ordinal = company_name_sortable
 sql_attr_uint = company_two_facet
 sql_attr_timestamp = created_at
 sql_attr_bool = deleted
+sql_attr_float = lat
+sql_attr_float = lng
 sql_attr_str2ordinal = mission_statement_sortable
 sql_attr_uint = user_id
 sql_query_info = SELECT * FROM sellers WHERE sellers.id = (($id - 1) / 5)
@@ -101,7 +105,7 @@ sql_host = localhost
 sql_pass = 
 sql_user = root
 sql_query_range = SELECT MIN(id) , MAX(id) FROM addresses 
-sql_query = SELECT (addresses.id * 5 + 2) AS id, '' AS address_name, 0 AS capitalization, 'Geo::Address' AS class, 2 AS class_id, '' AS company, 0 AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_name_sortable, '' AS company_two, 0 AS company_two_facet, CONCAT_WS(' ', addresses.line_1, addresses.line_2, addresses.city, addresses.province_region, addresses.zip_postal_code) AS content, 18000 AS created_at, 0 AS deleted, '' AS email, '' AS login, '' AS mission_statement, '' AS mission_statement_sortable, addresses.name AS name, state.name AS state, 0 AS user_id FROM addresses LEFT OUTER JOIN states AS state ON state.id = addresses.state_id WHERE addresses.id &gt;= $start AND addresses.id &lt;= $end GROUP BY addresses.id
+sql_query = SELECT (addresses.id * 5 + 2) AS id, '' AS address_name, 0 AS capitalization, 'Geo::Address' AS class, 2 AS class_id, '' AS company, 0 AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_name_sortable, '' AS company_two, 0 AS company_two_facet, CONCAT_WS(' ', addresses.line_1, addresses.line_2, addresses.city, addresses.province_region, addresses.zip_postal_code) AS content, 18000 AS created_at, 0 AS deleted, '' AS email, RADIANS(addresses.lat) AS lat, RADIANS(addresses.lng) AS lng, '' AS login, '' AS mission_statement, '' AS mission_statement_sortable, addresses.name AS name, state.name AS state, 0 AS user_id FROM addresses LEFT OUTER JOIN states AS state ON state.id = addresses.state_id WHERE addresses.id &gt;= $start AND addresses.id &lt;= $end GROUP BY addresses.id
 
 sql_attr_float = capitalization
 sql_attr_uint = class_id
@@ -111,6 +115,8 @@ sql_attr_str2ordinal = company_name_sortable
 sql_attr_uint = company_two_facet
 sql_attr_timestamp = created_at
 sql_attr_bool = deleted
+sql_attr_float = lat
+sql_attr_float = lng
 sql_attr_str2ordinal = mission_statement_sortable
 sql_attr_uint = user_id
 sql_query_info = SELECT * FROM addresses WHERE addresses.id = (($id - 2) / 5)
@@ -134,7 +140,7 @@ sql_host = localhost
 sql_pass = 
 sql_user = root
 sql_query_range = SELECT MIN(id) , MAX(id) FROM users 
-sql_query = SELECT (users.id * 5 + 3) AS id, '' AS address_name, 0 AS capitalization, 'User' AS class, 3 AS class_id, specific_seller.company_name AS company, CAST(CRC32(specific_seller.company_name) AS unsigned) AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_name_sortable, REPLACE(sellers_two.company_name, '6', ' replacement ') AS company_two, CAST(CRC32(REPLACE(sellers_two.company_name, '6', ' replacement ')) AS unsigned) AS company_two_facet, '' AS content, 18000 AS created_at, users.deleted AS deleted, users.email AS email, users.login AS login, '' AS mission_statement, '' AS mission_statement_sortable, '' AS name, '' AS state, 0 AS user_id FROM users LEFT OUTER JOIN sellers AS specific_seller ON users.id = specific_seller.user_id LEFT OUTER JOIN sellers AS sellers_two ON users.id = sellers_two.user_id WHERE users.id &gt;= $start AND users.id &lt;= $end AND (deleted = '0') GROUP BY users.id
+sql_query = SELECT (users.id * 5 + 3) AS id, '' AS address_name, 0 AS capitalization, 'User' AS class, 3 AS class_id, specific_seller.company_name AS company, CAST(CRC32(specific_seller.company_name) AS unsigned) AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_name_sortable, REPLACE(sellers_two.company_name, '6', ' replacement ') AS company_two, CAST(CRC32(REPLACE(sellers_two.company_name, '6', ' replacement ')) AS unsigned) AS company_two_facet, '' AS content, 18000 AS created_at, users.deleted AS deleted, users.email AS email, 0 AS lat, 0 AS lng, users.login AS login, '' AS mission_statement, '' AS mission_statement_sortable, '' AS name, '' AS state, 0 AS user_id FROM users LEFT OUTER JOIN sellers AS specific_seller ON users.id = specific_seller.user_id LEFT OUTER JOIN sellers AS sellers_two ON users.id = sellers_two.user_id WHERE users.id &gt;= $start AND users.id &lt;= $end AND (deleted = '0') GROUP BY users.id
 
 sql_attr_float = capitalization
 sql_attr_uint = class_id
@@ -144,6 +150,8 @@ sql_attr_str2ordinal = company_name_sortable
 sql_attr_uint = company_two_facet
 sql_attr_timestamp = created_at
 sql_attr_bool = deleted
+sql_attr_float = lat
+sql_attr_float = lng
 sql_attr_str2ordinal = mission_statement_sortable
 sql_attr_uint = user_id
 sql_query_info = SELECT * FROM users WHERE users.id = (($id - 3) / 5)
@@ -167,7 +175,7 @@ sql_host = localhost
 sql_pass = 
 sql_user = root
 sql_query_range = SELECT MIN(id) , MAX(id) FROM countries 
-sql_query = SELECT (countries.id * 5 + 4) AS id, '' AS address_name, 0 AS capitalization, 'Geo::Country' AS class, 4 AS class_id, '' AS company, 0 AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_name_sortable, '' AS company_two, 0 AS company_two_facet, '' AS content, 18000 AS created_at, 0 AS deleted, '' AS email, '' AS login, '' AS mission_statement, '' AS mission_statement_sortable, countries.name AS name, '' AS state, 0 AS user_id FROM countries WHERE countries.id &gt;= $start AND countries.id &lt;= $end GROUP BY countries.id
+sql_query = SELECT (countries.id * 5 + 4) AS id, '' AS address_name, 0 AS capitalization, 'Geo::Country' AS class, 4 AS class_id, '' AS company, 0 AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_name_sortable, '' AS company_two, 0 AS company_two_facet, '' AS content, 18000 AS created_at, 0 AS deleted, '' AS email, 0 AS lat, 0 AS lng, '' AS login, '' AS mission_statement, '' AS mission_statement_sortable, countries.name AS name, '' AS state, 0 AS user_id FROM countries WHERE countries.id &gt;= $start AND countries.id &lt;= $end GROUP BY countries.id
 
 sql_attr_float = capitalization
 sql_attr_uint = class_id
@@ -177,6 +185,8 @@ sql_attr_str2ordinal = company_name_sortable
 sql_attr_uint = company_two_facet
 sql_attr_timestamp = created_at
 sql_attr_bool = deleted
+sql_attr_float = lat
+sql_attr_float = lng
 sql_attr_str2ordinal = mission_statement_sortable
 sql_attr_uint = user_id
 sql_query_info = SELECT * FROM countries WHERE countries.id = (($id - 4) / 5)
@@ -223,7 +233,7 @@ sql_host = localhost
 sql_pass = 
 sql_user = root
 sql_query_range = SELECT MIN(id) , MAX(id) FROM sellers WHERE sellers.updated_at &gt; DATE_SUB(NOW(), INTERVAL 88200 SECOND)
-sql_query = SELECT (sellers.id * 5 + 1) AS id, '' AS address_name, sellers.capitalization AS capitalization, 'Seller' AS class, 1 AS class_id, '' AS company, 0 AS company_facet, sellers.company_name AS company_name, CAST(CRC32(sellers.company_name) AS unsigned) AS company_name_facet, company_name AS company_name_sortable, '' AS company_two, 0 AS company_two_facet, '' AS content, UNIX_TIMESTAMP(sellers.created_at) AS created_at, 0 AS deleted, '' AS email, '' AS login, sellers.mission_statement AS mission_statement, mission_statement AS mission_statement_sortable, '' AS name, '' AS state, sellers.user_id AS user_id FROM sellers WHERE sellers.id &gt;= $start AND sellers.id &lt;= $end AND sellers.updated_at &gt; DATE_SUB(NOW(), INTERVAL 88200 SECOND) GROUP BY sellers.id
+sql_query = SELECT (sellers.id * 5 + 1) AS id, '' AS address_name, sellers.capitalization AS capitalization, 'Seller' AS class, 1 AS class_id, '' AS company, 0 AS company_facet, sellers.company_name AS company_name, CAST(CRC32(sellers.company_name) AS unsigned) AS company_name_facet, company_name AS company_name_sortable, '' AS company_two, 0 AS company_two_facet, '' AS content, UNIX_TIMESTAMP(sellers.created_at) AS created_at, 0 AS deleted, '' AS email, 0 AS lat, 0 AS lng, '' AS login, sellers.mission_statement AS mission_statement, mission_statement AS mission_statement_sortable, '' AS name, '' AS state, sellers.user_id AS user_id FROM sellers WHERE sellers.id &gt;= $start AND sellers.id &lt;= $end AND sellers.updated_at &gt; DATE_SUB(NOW(), INTERVAL 88200 SECOND) GROUP BY sellers.id
 
 sql_attr_float = capitalization
 sql_attr_uint = class_id
@@ -233,6 +243,8 @@ sql_attr_str2ordinal = company_name_sortable
 sql_attr_uint = company_two_facet
 sql_attr_timestamp = created_at
 sql_attr_bool = deleted
+sql_attr_float = lat
+sql_attr_float = lng
 sql_attr_str2ordinal = mission_statement_sortable
 sql_attr_uint = user_id
 sql_query_info = SELECT * FROM sellers WHERE sellers.id = (($id - 1) / 5)
@@ -256,7 +268,7 @@ sql_host = localhost
 sql_pass = 
 sql_user = root
 sql_query_range = SELECT MIN(id) , MAX(id) FROM addresses 
-sql_query = SELECT (addresses.id * 5 + 2) AS id, '' AS address_name, 0 AS capitalization, 'Geo::Address' AS class, 2 AS class_id, '' AS company, 0 AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_name_sortable, '' AS company_two, 0 AS company_two_facet, CONCAT_WS(' ', addresses.line_1, addresses.line_2, addresses.city, addresses.province_region, addresses.zip_postal_code) AS content, 18000 AS created_at, 0 AS deleted, '' AS email, '' AS login, '' AS mission_statement, '' AS mission_statement_sortable, addresses.name AS name, state.name AS state, 0 AS user_id FROM addresses LEFT OUTER JOIN states AS state ON state.id = addresses.state_id WHERE addresses.id &gt;= $start AND addresses.id &lt;= $end GROUP BY addresses.id
+sql_query = SELECT (addresses.id * 5 + 2) AS id, '' AS address_name, 0 AS capitalization, 'Geo::Address' AS class, 2 AS class_id, '' AS company, 0 AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_name_sortable, '' AS company_two, 0 AS company_two_facet, CONCAT_WS(' ', addresses.line_1, addresses.line_2, addresses.city, addresses.province_region, addresses.zip_postal_code) AS content, 18000 AS created_at, 0 AS deleted, '' AS email, RADIANS(addresses.lat) AS lat, RADIANS(addresses.lng) AS lng, '' AS login, '' AS mission_statement, '' AS mission_statement_sortable, addresses.name AS name, state.name AS state, 0 AS user_id FROM addresses LEFT OUTER JOIN states AS state ON state.id = addresses.state_id WHERE addresses.id &gt;= $start AND addresses.id &lt;= $end GROUP BY addresses.id
 
 sql_attr_float = capitalization
 sql_attr_uint = class_id
@@ -266,6 +278,8 @@ sql_attr_str2ordinal = company_name_sortable
 sql_attr_uint = company_two_facet
 sql_attr_timestamp = created_at
 sql_attr_bool = deleted
+sql_attr_float = lat
+sql_attr_float = lng
 sql_attr_str2ordinal = mission_statement_sortable
 sql_attr_uint = user_id
 sql_query_info = SELECT * FROM addresses WHERE addresses.id = (($id - 2) / 5)
@@ -289,7 +303,7 @@ sql_host = localhost
 sql_pass = 
 sql_user = root
 sql_query_range = SELECT MIN(id) , MAX(id) FROM users WHERE users.created_at &gt; DATE_SUB(NOW(), INTERVAL 88200 SECOND)
-sql_query = SELECT (users.id * 5 + 3) AS id, '' AS address_name, 0 AS capitalization, 'User' AS class, 3 AS class_id, specific_seller.company_name AS company, CAST(CRC32(specific_seller.company_name) AS unsigned) AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_name_sortable, REPLACE(sellers_two.company_name, '6', ' replacement ') AS company_two, CAST(CRC32(REPLACE(sellers_two.company_name, '6', ' replacement ')) AS unsigned) AS company_two_facet, '' AS content, 18000 AS created_at, users.deleted AS deleted, users.email AS email, users.login AS login, '' AS mission_statement, '' AS mission_statement_sortable, '' AS name, '' AS state, 0 AS user_id FROM users LEFT OUTER JOIN sellers AS specific_seller ON users.id = specific_seller.user_id LEFT OUTER JOIN sellers AS sellers_two ON users.id = sellers_two.user_id WHERE users.id &gt;= $start AND users.id &lt;= $end AND (deleted = '0') AND users.created_at &gt; DATE_SUB(NOW(), INTERVAL 88200 SECOND) GROUP BY users.id
+sql_query = SELECT (users.id * 5 + 3) AS id, '' AS address_name, 0 AS capitalization, 'User' AS class, 3 AS class_id, specific_seller.company_name AS company, CAST(CRC32(specific_seller.company_name) AS unsigned) AS company_facet, '' AS company_name, 0 AS company_name_facet, '' AS company_name_sortable, REPLACE(sellers_two.company_name, '6', ' replacement ') AS company_two, CAST(CRC32(REPLACE(sellers_two.company_name, '6', ' replacement ')) AS unsigned) AS company_two_facet, '' AS content, 18000 AS created_at, users.deleted AS deleted, users.email AS email, 0 AS lat, 0 AS lng, users.login AS login, '' AS mission_statement, '' AS mission_statement_sortable, '' AS name, '' AS state, 0 AS user_id FROM users LEFT OUTER JOIN sellers AS specific_seller ON users.id = specific_seller.user_id LEFT OUTER JOIN sellers AS sellers_two ON users.id = sellers_two.user_id WHERE users.id &gt;= $start AND users.id &lt;= $end AND (deleted = '0') AND users.created_at &gt; DATE_SUB(NOW(), INTERVAL 88200 SECOND) GROUP BY users.id
 
 sql_attr_float = capitalization
 sql_attr_uint = class_id
@@ -299,6 +313,8 @@ sql_attr_str2ordinal = company_name_sortable
 sql_attr_uint = company_two_facet
 sql_attr_timestamp = created_at
 sql_attr_bool = deleted
+sql_attr_float = lat
+sql_attr_float = lng
 sql_attr_str2ordinal = mission_statement_sortable
 sql_attr_uint = user_id
 sql_query_info = SELECT * FROM users WHERE users.id = (($id - 3) / 5)</diff>
      <filename>test/integration/app/config/ultrasphinx/development.conf.canonical</filename>
    </modified>
    <modified>
      <diff>@@ -1,11 +1,11 @@
 class AddLatAndLongToAddress &lt; ActiveRecord::Migration
   def self.up
-    add_column :addresses, :lat, :float
-    add_column :addresses, :long, :float
+    add_column :addresses, :lat, :double
+    add_column :addresses, :lng, :double
   end
 
   def self.down
     remove_column :addresses, :lat
-    remove_column :addresses, :long
+    remove_column :addresses, :lng
   end
 end</diff>
      <filename>test/integration/app/db/migrate/007_add_lat_and_long_to_address.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,13 +1,84 @@
-&lt;% 1.upto(40) do |num| %&gt;
+# Waikiki Aquarium&#8206;Address:
+# 2777 Kalakaua Ave
+# Honolulu, HI 96815
+# (21.265915, -157.821256)
+# 0.972 miles to Diamond Head State Park
+# 10.147 miles to Aloha Stadium
+# 10.880 miles to Kailua Beach Park
+&lt;% 1.upto(10) do |num| %&gt;
 &lt;%=&quot;address#{num}:&quot; %&gt;
   id: &lt;%= num %&gt;
   user_id: &lt;%= num %&gt;
-  name: &lt;%= &quot;Chicago Branch Office #{num}&quot; %&gt;
-  line_1: &quot;3344 Airport Road&quot;
+  name: &lt;%= &quot;Waikiki Aquarium #{num}&quot; %&gt;
+  line_1: &quot;2777 Kalakaua Ave&quot;
   line_2: &quot;Suite 122&quot;
-  city: &quot;Chicago&quot;
+  city: &quot;Honolulu&quot;
   state_id: 15
-  province_region: &quot;Cook County&quot;
-  zip_postal_code: &quot;43554&quot;
+  province_region: &quot;Honolulu County&quot;
+  zip_postal_code: &quot;96815&quot;
   country_id: 2
+  lat: &lt;%= 21.265915 %&gt;
+  lng: &lt;%= -157.821256 %&gt;
+&lt;% end %&gt;
+
+# Diamond Head State Monument
+# 4200 Diamond Head Rd
+# Honolulu, HI 96816
+# (21.2627, -157.80655)
+# 10.563 miles to Kailua Beach Park
+&lt;% 11.upto(20) do |num| %&gt;
+&lt;%=&quot;address#{num}:&quot; %&gt;
+  id: &lt;%= num %&gt;
+  user_id: &lt;%= num %&gt;
+  name: &lt;%= &quot;Diamond Head State Monument #{num}&quot; %&gt;
+  line_1: &quot;4200 Diamond Head Rd&quot;
+  line_2: &quot;Suite 122&quot;
+  city: &quot;Honolulu&quot;
+  state_id: 15
+  province_region: &quot;Honolulu County&quot;
+  zip_postal_code: &quot;96816&quot;
+  country_id: 2
+  lat: &lt;%= 21.2627 %&gt;
+  lng: &lt;%= -157.80655 %&gt;
+&lt;% end %&gt;
+
+# Aloha Stadium&#8206;Address:
+# 99-500 Salt Lake Blvd
+# Aiea, HI 96701
+# (21.370824, -157.931578)
+# 13.366 miles to Kailua Beach Park
+&lt;% 21.upto(30) do |num| %&gt;
+&lt;%=&quot;address#{num}:&quot; %&gt;
+  id: &lt;%= num %&gt;
+  user_id: &lt;%= num %&gt;
+  name: &lt;%= &quot;Aloha Stadium #{num}&quot; %&gt;
+  line_1: &quot;99-500 Salt Lake Blvd&quot;
+  line_2: &quot;Suite 122&quot;
+  city: &quot;Aiea&quot;
+  state_id: 15
+  province_region: &quot;Honolulu County&quot;
+  zip_postal_code: &quot;96701&quot;
+  country_id: 2
+  lat: &lt;%= 21.370824 %&gt;
+  lng: &lt;%= -157.80655 %&gt;
+&lt;% end %&gt;
+
+# Kailua Beach Park&#8206;Address:
+# 450 Kawailoa Road
+# Honolulu, HI 96734
+# (21.39567059288111, -157.72552013397217)
+&lt;% 31.upto(40) do |num| %&gt;
+&lt;%=&quot;address#{num}:&quot; %&gt;
+  id: &lt;%= num %&gt;
+  user_id: &lt;%= num %&gt;
+  name: &lt;%= &quot;Kailua Beach Park #{num}&quot; %&gt;
+  line_1: &quot;450 Kawailoa Road&quot;
+  line_2: &quot;Suite 122&quot;
+  city: &quot;Honolulu&quot;
+  state_id: 15
+  province_region: &quot;Honolulu County&quot;
+  zip_postal_code: &quot;96734&quot;
+  country_id: 2
+  lat: &lt;%= 21.39567059288111 %&gt;
+  lng: &lt;%= -157.72552013397217 %&gt;
 &lt;% end %&gt;
\ No newline at end of file</diff>
      <filename>test/integration/app/test/fixtures/addresses.yml</filename>
    </modified>
    <modified>
      <diff>@@ -25,8 +25,8 @@ class AddressesControllerTest &lt; Test::Unit::TestCase
   end
 
   def test_should_create_address
-    assert_difference('Address.count') do
-      post :create, :address =&gt; { }
+    assert_difference('Geo::Address.count') do
+      post :create, :address =&gt; { :user_id =&gt; 1, :state_id =&gt; 1, :country_id =&gt; 1 }
     end
 
     assert_redirected_to address_path(assigns(:address))
@@ -48,10 +48,15 @@ class AddressesControllerTest &lt; Test::Unit::TestCase
   end
 
   def test_should_destroy_address
-    assert_difference('Address.count', -1) do
-      delete :destroy, :id =&gt; 1
+    new_address = Geo::Address.new(:user_id =&gt; 1, :name =&gt; 'test', :city =&gt; 'test', :state_id =&gt; 15, :country_id =&gt; 2)
+    
+    assert_difference('Geo::Address.count', +1) do
+      new_address.save
     end
 
+    assert_difference('Geo::Address.count', -1) do
+      delete :destroy, :id =&gt; new_address.id
+    end
     assert_redirected_to addresses_path
   end
 end</diff>
      <filename>test/integration/app/test/functional/addresses_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -26,7 +26,7 @@ class SellersControllerTest &lt; Test::Unit::TestCase
 
   def test_should_create_seller
     assert_difference('Seller.count') do
-      post :create, :seller =&gt; { }
+      post :create, :seller =&gt; { :user_id =&gt; 1 }
     end
 
     assert_redirected_to seller_path(assigns(:seller))
@@ -48,8 +48,15 @@ class SellersControllerTest &lt; Test::Unit::TestCase
   end
 
   def test_should_destroy_seller
+    new_user = User.create(:login =&gt; 'test', :email =&gt; 'test@test.com')
+    new_seller = Seller.new(:user_id =&gt; new_user.id, :company_name =&gt; 'test')
+    
+    assert_difference('Seller.count', +1) do
+      new_seller.save
+    end
+
     assert_difference('Seller.count', -1) do
-      delete :destroy, :id =&gt; 1
+      delete :destroy, :id =&gt; new_seller.id
     end
 
     assert_redirected_to sellers_path</diff>
      <filename>test/integration/app/test/functional/sellers_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -25,8 +25,8 @@ class StatesControllerTest &lt; Test::Unit::TestCase
   end
 
   def test_should_create_state
-    assert_difference('State.count') do
-      post :create, :state =&gt; { }
+    assert_difference('Geo::State.count') do
+      post :create, :state =&gt; { :name =&gt; 'test', :abbreviation =&gt; 'te' }
     end
 
     assert_redirected_to state_path(assigns(:state))
@@ -48,10 +48,16 @@ class StatesControllerTest &lt; Test::Unit::TestCase
   end
 
   def test_should_destroy_state
-    assert_difference('State.count', -1) do
-      delete :destroy, :id =&gt; 1
+    new_state = Geo::State.new(:name =&gt; 'test', :abbreviation =&gt; 'te')
+    
+    assert_difference('Geo::State.count', +1) do
+      new_state.save
     end
 
+    assert_difference('Geo::State.count', -1) do
+      delete :destroy, :id =&gt; new_state.id
+    end
+    
     assert_redirected_to states_path
   end
 end</diff>
      <filename>test/integration/app/test/functional/states_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -48,10 +48,15 @@ class UsersControllerTest &lt; Test::Unit::TestCase
   end
 
   def test_should_destroy_user
-    assert_difference('User.count', -1) do
-      delete :destroy, :id =&gt; 1
+    new_user = User.new(:login =&gt; 'test', :email =&gt; 'test@test.com')
+    
+    assert_difference('User.count', +1) do
+      new_user.save
     end
 
+    assert_difference('User.count', -1) do
+      delete :destroy, :id =&gt; new_user.id
+    end
     assert_redirected_to users_path
   end
 end</diff>
      <filename>test/integration/app/test/functional/users_controller_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 require File.dirname(__FILE__) + '/../test_helper'
 
-class AddressTest &lt; Test::Unit::TestCase
+class Geo::AddressTest &lt; Test::Unit::TestCase
   fixtures :addresses
 
   # Replace this with your real tests.</diff>
      <filename>test/integration/app/test/unit/address_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 require File.dirname(__FILE__) + '/../test_helper'
 
-class CountryTest &lt; ActiveSupport::TestCase
+class Geo::CountryTest &lt; ActiveSupport::TestCase
   # Replace this with your real tests.
   def test_truth
     assert true</diff>
      <filename>test/integration/app/test/unit/country_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 require File.dirname(__FILE__) + '/../test_helper'
 
-class StatesTest &lt; Test::Unit::TestCase
+class Geo::StatesTest &lt; Test::Unit::TestCase
   fixtures :states
 
   # Replace this with your real tests.</diff>
      <filename>test/integration/app/test/unit/state_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -1,6 +1,6 @@
 require File.dirname(__FILE__) + '/../test_helper'
 
-class UserTest &lt; Test::Unit::TestCase
+class Person::UserTest &lt; Test::Unit::TestCase
   fixtures :users
 
   # Replace this with your real tests.</diff>
      <filename>test/integration/app/test/unit/user_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -11,7 +11,7 @@ class SearchTest &lt; Test::Unit::TestCase
     assert_nothing_raised do
       @s = S.new(:query =&gt; 'seller').run
     end
-    assert_equal 20, @s.results.size
+    assert_equal 20, @s.size
   end  
   
   def test_with_subtotals_option
@@ -34,6 +34,7 @@ class SearchTest &lt; Test::Unit::TestCase
   end
   
   def test_ignore_missing_records_option
+    S.client_options['distance'] = false # must disable geodistance or line 57 will bomb
     @s = S.new(:per_page =&gt; 1).run
     @record = @s.first
     assert_equal 1, @s.size
@@ -50,9 +51,8 @@ class SearchTest &lt; Test::Unit::TestCase
     end 
     assert_equal 0, @s.size
     assert_equal 1, @s.per_page
-
     S.client_options['ignore_missing_records'] = false
-    
+  
     # Re-insert the record... ugh
     @new_record = @record.class.new(@record.attributes)
     @new_record.id = @record.id
@@ -253,7 +253,7 @@ class SearchTest &lt; Test::Unit::TestCase
   end
 
   def test_included_text_facet_without_association_sql
-    # there are 40 users, but only 20 sellers. So you get 20 facets + 1 nil with 20 items
+    # There are 40 users, but only 20 sellers. So you get 20 facets + 1 nil with 20 items
     @s = Ultrasphinx::Search.new(:class_names =&gt; 'User', :facets =&gt; ['company']).run
     assert_equal(
       (Seller.count + 1), 
@@ -262,8 +262,9 @@ class SearchTest &lt; Test::Unit::TestCase
   end
 
   def test_included_text_facet_with_association_sql
-    # XXX there are 40 users but only 20 sellers, but the replace function from user deletes 
-    # User #6 and 16 (why?). There is also a nil facet that gets added for a total of 19 objects
+    # XXX there are 40 users but only 20 sellers, but the replace function from User deletes 
+    # User #6 and 16 (why? Hash collision?). There is also a nil facet that gets added for a 
+    # total of 19 objects
     @s = Ultrasphinx::Search.new(:class_names =&gt; 'User', :facets =&gt; ['company_two']).run
     assert_equal(
       (Seller.count - 1), 
@@ -317,4 +318,64 @@ class SearchTest &lt; Test::Unit::TestCase
     assert_match /strong/, @excerpted_item.name
   end
   
+  def test_distance_ascending
+    # (21.289453, -157.842783) is Ala Moana Shopping Center, 1450 Ala Moana Blvd, Honolulu HI 96814
+    @s = Ultrasphinx::Search.new(:class_names =&gt; 'Geo::Address', 
+          :query =&gt; 'Honolulu',
+          :per_page =&gt; 40,
+          :sort_mode =&gt; 'extended',
+          :sort_by =&gt; 'distance',
+          :location =&gt; {
+            :lat =&gt; 21.289453,
+            :long =&gt; -157.842783
+          })
+    @s.run
+    # This should return all items in the database, sorted in increasing distance
+    assert_equal 40, @s.size
+    assert_match /Waikiki Aquarium/, @s.first.name
+    assert_match /Waikiki Aquarium/, @s[1].name
+    assert_in_delta 3439, @s.first.distance, 10
+  end
+  
+  def test_distance_filter
+    # (21.289453, -157.842783) is Ala Moana Shopping Center, 1450 Ala Moana Blvd, Honolulu HI 96814
+    @s = Ultrasphinx::Search.new(:class_names =&gt; 'Geo::Address', 
+          :query =&gt; 'Honolulu',
+          :per_page =&gt; 40,
+          :sort_mode =&gt; 'extended',
+          :sort_by =&gt; 'distance',
+          :filters =&gt; {'distance' =&gt; 0..5000},
+          :location =&gt; {
+            :lat =&gt; 21.289453,
+            :long =&gt; -157.842783
+          })
+    @s.run
+
+    @s.each do |obj|
+      # This should return only those items within 5000 meters of Ala Moana, which 
+      # is only Waikiki Aquarium and Diamond Head.
+      assert_match /Waikiki Aquarium|Diamond Head/, obj.name
+    end
+        
+    assert_in_delta 3439, @s.first.distance, 10 # Closest item should be Waikiki at 3439 meters
+  end
+  
+  def test_distance_decending
+    # (21.289453, -157.842783) is Ala Moana Shopping Center, 1450 Ala Moana Blvd, Honolulu HI 96814
+    @s = Ultrasphinx::Search.new(:class_names =&gt; 'Geo::Address', 
+          :query =&gt; 'Honolulu',
+          :per_page =&gt; 40,
+          :sort_mode =&gt; 'extended',
+          :sort_by =&gt; 'distance desc',
+          :location =&gt; {
+            :lat =&gt; 21.289453,
+            :long =&gt; -157.842783
+          })
+    @s.run
+    
+    # Ids should come back in reverse order because the fixtures have the farther locations later in the file
+    assert_equal 40, @s.size
+    assert_match /Kailua Beach Park/, @s.first.name 
+    assert_in_delta 16940, @s.first.distance, 40
+  end
 end
\ No newline at end of file</diff>
      <filename>test/integration/search_test.rb</filename>
    </modified>
    <modified>
      <diff>@@ -2,14 +2,25 @@
 # Setup integration system for the integration suite
 
 Dir.chdir &quot;#{File.dirname(__FILE__)}/integration/app/&quot; do
-  system(&quot;killall searchd&quot;)
-  system(&quot;rm -rf /tmp/sphinx&quot;)
+
+  pid_file = '/tmp/sphinx/searchd.pid'
+  if File.exist? pid_file
+    pid = File.read(pid_file).to_i
+    system(&quot;kill #{pid}&quot;); sleep(2); system(&quot;kill -9 #{pid}&quot;)  
+  end
+  
+  system(&quot;rm -rf /tmp/sphinx&quot;)  
   system(&quot;rm -rf config/ultrasphinx/development.conf&quot;)
+
   Dir.chdir &quot;vendor/plugins&quot; do
-    system(&quot;rm ultrasphinx; ln -s ../../../../../ ultrasphinx&quot;)
+    system(&quot;rm ultrasphinx&quot;)
+    system(&quot;ln -s ../../../../../ ultrasphinx&quot;)
   end
+  
+  system(&quot;rake db:drop&quot;)
   system(&quot;rake db:create&quot;)
   system(&quot;rake db:migrate db:fixtures:load&quot;)
+
   system(&quot;rake us:boot&quot;)
   system(&quot;rm /tmp/ultrasphinx-stopwords.txt&quot;)
   system(&quot;rake ultrasphinx:spelling:build&quot;)</diff>
      <filename>test/setup.rb</filename>
    </modified>
    <modified>
      <diff>@@ -0,0 +1,8 @@
+
+# Tear down integration system for the integration suite
+
+Dir.chdir &quot;#{File.dirname(__FILE__)}/integration/app/&quot; do  
+  # Remove the symlink created by the setup method, for people with tools
+  # that can't handle recursive directories (Textmate).
+  system(&quot;rm vendor/plugins/ultrasphinx&quot;) unless ENV['USER'] == 'eweaver'
+end
\ No newline at end of file</diff>
      <filename>test/teardown.rb</filename>
    </modified>
    <modified>
      <diff>@@ -21,6 +21,6 @@ end
 
 require 'integration/app/config/environment'
 
-Dir.chdir &quot;#{HERE}/integration/app&quot; do
-  system(&quot;rake us:start&quot;)
-end
+#Dir.chdir &quot;#{HERE}/integration/app&quot; do
+#  system(&quot;rake us:start&quot;)
+#end</diff>
      <filename>test/test_helper.rb</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>8ebc11ed86a4ccbd3573c5e164de412abc065ff8</id>
    </parent>
  </parents>
  <author>
    <name>Evan Weaver</name>
    <email>evan@cloudbur.st</email>
  </author>
  <url>http://github.com/fauna/ultrasphinx/commit/807632c8caa807b9826b475964bf3f97d2ac1f2c</url>
  <id>807632c8caa807b9826b475964bf3f97d2ac1f2c</id>
  <committed-date>2008-03-23T23:21:30-07:00</committed-date>
  <authored-date>2008-03-23T23:21:30-07:00</authored-date>
  <message>Clean up and apply geodistance patch (Jeremy Seitz, Mark Lane).</message>
  <tree>7d6f963dd49aa0a292761bca00af0f6a778fbdc7</tree>
  <committer>
    <name>Evan Weaver</name>
    <email>evan@cloudbur.st</email>
  </committer>
</commit>
