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

Add postgis support #116

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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ services:
before_script:
- mysql -e 'create database makara_test;'
- psql -c 'create database makara_test;' -U postgres
- psql -c 'create extension postgis;' -U postgres

before_install:
- gem update --system 2.1.11
- gem --version
- gem --version
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