geokit / geokit-rails

Official Geokit plugin for Rails/ActiveRecord. Provides location-based goodness for your Rails app. Requires the Geokit gem.
MIT License
1.57k stars 245 forks source link

Autoloading of adapters fails with "undefined method 'loaded' for Mysql2:Class. #130

Open danshep opened 6 years ago

danshep commented 6 years ago
     NoMethodError:
       undefined method `loaded' for Mysql2:Class
     # org/jruby/RubyBasicObject.java:1657:in `method_missing'
     # C:/jruby-9.1.15.0/lib/ruby/gems/shared/gems/geokit-rails-2.3.0/lib/geokit-rails/acts_as_mappable.rb:111:in `geokit_finder_adapter'
     # C:/jruby-9.1.15.0/lib/ruby/gems/shared/gems/geokit-rails-2.3.0/lib/geokit-rails/acts_as_mappable.rb:343:in `sphere_distance_sql'
     # C:/jruby-9.1.15.0/lib/ruby/gems/shared/gems/geokit-rails-2.3.0/lib/geokit-rails/acts_as_mappable.rb:213:in `distance_sql'

This is due to faulty logic in geokit_finder_adapter:

      # A proxy to an instance of a finder adapter, inferred from the connection's adapter.
      def geokit_finder_adapter
        @geokit_finder_adapter ||= begin
          unless Adapters.const_defined?(connection.adapter_name.camelcase)
            filename = connection.adapter_name.downcase
            require File.join("geokit-rails", "adapters", filename)
          end
          klass = Adapters.const_get(connection.adapter_name.camelcase)
          if klass.class == Module
            # For some reason Mysql2 adapter was defined in Adapters.constants but was Module instead of a Class
            filename = connection.adapter_name.downcase
            require File.join("geokit-rails", "adapters", filename)
            # Re-init the klass after require
            klass = Adapters.const_get(connection.adapter_name.camelcase)
          end
          klass.load(self) unless klass.loaded || skip_loading
          klass.new(self)
        rescue LoadError
          raise UnsupportedAdapter, "`#{connection.adapter_name.downcase}` is not a supported adapter."
        end
      end

The call to const_defined and const_get should pass the extra 'false' parameter - otherwise they will find the top-level '::Mysql2' constant and return that instead. ie:

irb(main):001:0> module X1; module X2; end; end
irb(main):004:0> module X2; end
irb(main):005:0> module X3; end
irb(main):006:0> X3.const_defined?('X2')
=> true
irb(main):007:0> X3.const_defined?('X2', false)
=> false
irb(main):008:0> X1.const_defined?('X2', false)
=> true