Skip to content

Commit

Permalink
Merge pull request #118 from GhostGroup/add-postgis-support
Browse files Browse the repository at this point in the history
Add postgis support
  • Loading branch information
bleonard authored Aug 14, 2017
2 parents 87dd492 + c9bf4f9 commit 29a9ca5
Show file tree
Hide file tree
Showing 13 changed files with 229 additions and 3 deletions.
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ services:
before_script:
- mysql -e 'create database makara_test;'
- psql -c 'create database makara_test;' -U postgres
- psql -c 'create extension postgis;' -U postgres

rvm:
- 2.0
Expand Down
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ gem 'rack', '1.6.0'

gem 'mysql2', :platform => :ruby
gem 'pg', :platform => :ruby
gem 'activerecord-postgis-adapter', :platform => :ruby
gem 'rgeo', :platform => :ruby

gem 'activerecord-jdbcmysql-adapter', :platform => :jruby
gem 'activerecord-jdbcpostgresql-adapter', :platform => :jruby

2 changes: 2 additions & 0 deletions gemfiles/ar30.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ gem 'i18n', '~> 0.5.0'
gem 'mysql2', '0.2.11', :platform => :ruby
gem 'activerecord-jdbcmysql-adapter', :platform => :jruby
gem 'activerecord-jdbcpostgresql-adapter', :platform => :jruby
gem 'activerecord-postgis-adapter', :platform => :ruby
gem 'rgeo', :platform => :ruby


rmajor, rminor, rpatch = RUBY_VERSION.split(/[^\d]/)[0..2].map(&:to_i)
Expand Down
2 changes: 2 additions & 0 deletions gemfiles/ar31.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ gem 'i18n', '~> 0.6.0'
gem 'mysql2', '~> 0.3.10', :platform => :ruby
gem 'activerecord-jdbcmysql-adapter', :platform => :jruby
gem 'activerecord-jdbcpostgresql-adapter', :platform => :jruby
gem 'activerecord-postgis-adapter', :platform => :ruby
gem 'rgeo', :platform => :ruby

rmajor, rminor, rpatch = RUBY_VERSION.split(/[^\d]/)[0..2].map(&:to_i)

Expand Down
2 changes: 2 additions & 0 deletions gemfiles/ar32.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ gem 'i18n', '~> 0.6.0'
gem 'mysql2', '~> 0.3.10', :platform => :ruby
gem 'activerecord-jdbcmysql-adapter', :platform => :jruby
gem 'activerecord-jdbcpostgresql-adapter', :platform => :jruby
gem 'activerecord-postgis-adapter', :platform => :ruby
gem 'rgeo', :platform => :ruby

rmajor, rminor, rpatch = RUBY_VERSION.split(/[^\d]/)[0..2].map(&:to_i)

Expand Down
2 changes: 2 additions & 0 deletions gemfiles/ar40.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ gem 'mysql2', '~> 0.3.10', :platform => :ruby
gem 'activerecord-jdbcmysql-adapter', :platform => :jruby
gem 'pg', :platform => :ruby
gem 'activerecord-jdbcpostgresql-adapter', :platform => :jruby
gem 'activerecord-postgis-adapter', :platform => :ruby
gem 'rgeo', :platform => :ruby
2 changes: 2 additions & 0 deletions gemfiles/ar41.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ gem 'mysql2', '~> 0.3.10', :platform => :ruby
gem 'activerecord-jdbcmysql-adapter', :platform => :jruby
gem 'pg', :platform => :ruby
gem 'activerecord-jdbcpostgresql-adapter', :platform => :jruby
gem 'activerecord-postgis-adapter', :platform => :ruby
gem 'rgeo', :platform => :ruby
2 changes: 2 additions & 0 deletions gemfiles/ar42.gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ gem 'mysql2', '~> 0.3.10', :platform => :ruby
gem 'activerecord-jdbcmysql-adapter', :platform => :jruby
gem 'pg', :platform => :ruby
gem 'activerecord-jdbcpostgresql-adapter', :platform => :jruby
gem 'activerecord-postgis-adapter', :platform => :ruby
gem 'rgeo', :platform => :ruby
41 changes: 41 additions & 0 deletions lib/active_record/connection_adapters/makara_postgis_adapter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
require 'active_record/connection_adapters/makara_abstract_adapter'
require 'active_record/connection_adapters/postgis_adapter'

if ActiveRecord::VERSION::MAJOR >= 4

module ActiveRecord
module ConnectionHandling
def makara_postgis_connection(config)
ActiveRecord::ConnectionAdapters::MakaraPostgisAdapter.new(config)
end
end
end

else

module ActiveRecord
class Base
def self.makara_postgis_connection(config)
ActiveRecord::ConnectionAdapters::MakaraPostgisAdapter.new(config)
end
end
end

end


module ActiveRecord
module ConnectionAdapters
class MakaraPostgisAdapter < ActiveRecord::ConnectionAdapters::MakaraAbstractAdapter
def self.visitor_for(*args)
ActiveRecord::ConnectionAdapters::PostGISAdapter.visitor_for(*args)
end

protected

def active_record_connection_for(config)
::ActiveRecord::Base.postgis_connection(config)
end
end
end
end
150 changes: 150 additions & 0 deletions spec/active_record/connection_adapters/makara_postgis_adapter_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# RGeo doesn't play well with JRuby and to avoid complicated test setup
# we're only testing ActiveRecord version ~> 4.2
if RUBY_ENGINE == 'ruby' &&
ActiveRecord::VERSION::MAJOR == 4 &&
ActiveRecord::VERSION::MINOR >= 2

require 'spec_helper'
require 'rgeo'
require 'activerecord-postgis-adapter'
require 'active_record/connection_adapters/postgis_adapter'

describe 'MakaraPostgisAdapter' do
let(:db_username){ ENV['TRAVIS'] ? 'postgres' : `whoami`.chomp }

let(:config) do
base = YAML.load_file(File.expand_path('spec/support/postgis_database.yml'))['test']
base['username'] = db_username
base
end

let(:connection) { ActiveRecord::Base.connection }

before :each do
ActiveRecord::Base.clear_all_connections!
change_context
end

it 'should allow a connection to be established' do
ActiveRecord::Base.establish_connection(config)
expect(ActiveRecord::Base.connection)
.to be_instance_of(ActiveRecord::ConnectionAdapters::MakaraPostgisAdapter)
end

context 'with the connection established and schema loaded' do
before do
ActiveRecord::Base.establish_connection(config)
load(File.dirname(__FILE__) + '/../../support/schema.rb')
load(File.dirname(__FILE__) + '/../../support/postgis_schema.rb')
change_context
RGeo::ActiveRecord::SpatialFactoryStore.instance.tap do |config|
# By default, use the GEOS implementation for spatial columns.
config.default = RGeo::Geos.factory_generator

# But use a geographic implementation for point columns.
config.register(RGeo::Geographic.spherical_factory(srid: 4326), geo_type: "point")
end
end

let(:town_class) do
Class.new(ActiveRecord::Base) do
self.table_name = :towns
end
end

it 'should have one master and two slaves' do
expect(connection.master_pool.connection_count).to eq(1)
expect(connection.slave_pool.connection_count).to eq(2)
end

it 'should allow real queries to work' do
connection.execute('INSERT INTO users (name) VALUES (\'John\')')

connection.master_pool.connections.each do |master|
expect(master).to receive(:execute).never
end

change_context
res = connection.execute('SELECT name FROM users ORDER BY id DESC LIMIT 1')

expect(res.to_a[0]['name']).to eq('John')
end

it 'should send SET operations to each connection' do
connection.master_pool.connections.each do |con|
expect(con).to receive(:execute).with("SET TimeZone = 'UTC'").once
end

connection.slave_pool.connections.each do |con|
expect(con).to receive(:execute).with("SET TimeZone = 'UTC'").once
end
connection.execute("SET TimeZone = 'UTC'")
end

it 'should send reads to the slave' do
# ensure the next connection will be the first one
connection.slave_pool
.strategy
.instance_variable_set('@current_idx',
connection.slave_pool.connections.length)

con = connection.slave_pool.connections.first
expect(con).to receive(:execute).with('SELECT * FROM users').once

connection.execute('SELECT * FROM users')
end

it 'should send writes to master' do
con = connection.master_pool.connections.first
expect(con).to receive(:execute).with('UPDATE users SET name = "bob" WHERE id = 1')
connection.execute('UPDATE users SET name = "bob" WHERE id = 1')
end

it 'should interpret points correctly' do
town_class.create!(location: 'Point(1 2)')
town = town_class.last
expect(town.location.x).to eq 1
expect(town.location.y).to eq 2
end
end

context 'without live connections' do
it 'should raise errors on read or write' do
allow(ActiveRecord::Base).to receive(:postgis_connection).and_raise(StandardError.new('could not connect to server: Connection refused'))

ActiveRecord::Base.establish_connection(config)
expect { connection.execute('SELECT * FROM users') }.to raise_error(Makara::Errors::NoConnectionsAvailable)
expect { connection.execute('INSERT INTO users (name) VALUES (\'John\')') }.to raise_error(Makara::Errors::NoConnectionsAvailable)
end
end

context 'with only master connection' do
it 'should not raise errors on read and write' do
custom_config = config.deep_dup
custom_config['makara']['connections'].select{|h| h['role'] == 'slave' }.each{|h| h['port'] = '1'}

ActiveRecord::Base.establish_connection(custom_config)
load(File.dirname(__FILE__) + '/../../support/schema.rb')

connection.execute('SELECT * FROM users')
connection.execute('INSERT INTO users (name) VALUES (\'John\')')
end
end

context 'with only slave connection' do
it 'should raise error only on write' do
ActiveRecord::Base.establish_connection(config)
load(File.dirname(__FILE__) + '/../../support/schema.rb')
ActiveRecord::Base.clear_all_connections!

custom_config = config.deep_dup
custom_config['makara']['connections'].select{|h| h['role'] == 'master' }.each{|h| h['port'] = '1'}

ActiveRecord::Base.establish_connection(custom_config)

connection.execute('SELECT * FROM users')
expect { connection.execute('INSERT INTO users (name) VALUES (\'John\')') }.to raise_error(Makara::Errors::NoConnectionsAvailable)
end
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,11 @@

let(:db_username){ ENV['TRAVIS'] ? 'postgres' : `whoami`.chomp }

let(:config){
let(:config) do
base = YAML.load_file(File.expand_path('spec/support/postgresql_database.yml'))['test']
base['username'] = db_username
base
}
end

let(:connection) { ActiveRecord::Base.connection }

Expand Down
13 changes: 13 additions & 0 deletions spec/support/postgis_database.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
test:
adapter: 'makara_postgis'
database: 'makara_test'
username: 'root'
password: ''

makara:
blacklist_duration: 2
master_ttl: 5
connections:
- role: master
- role: slave
- role: slave
8 changes: 8 additions & 0 deletions spec/support/postgis_schema.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@

ActiveRecord::Schema.define(:version => 20160518161227) do
execute "create extension if not exists postgis"

create_table "towns", :force => true do |t|
t.st_point "location"
end
end

0 comments on commit 29a9ca5

Please sign in to comment.