diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9db588f --- /dev/null +++ b/.gitignore @@ -0,0 +1,11 @@ +/coverage +/tmp +/pkg +/Gemfile.local + +*.lock +*.log +*.sqlite3 +!gemfiles/**/*.sqlite3 +.DS_Store +*/.DS_Store diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..83ca640 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,16 @@ +# This file was auto-generated by the schema_dev tool, based on the data in +# ./schema_dev.yml +# Please do not edit this file; any changes will be overwritten next time +# schema_dev gets run. +--- +sudo: false +rvm: +- 2.1.5 +gemfile: +- gemfiles/activerecord-4.2/Gemfile.postgresql +env: POSTGRESQL_DB_USER=postgres +addons: + postgresql: '9.4' +before_script: bundle exec rake create_databases +after_script: bundle exec rake drop_databases +script: bundle exec rake travis diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..24e22d1 --- /dev/null +++ b/Gemfile @@ -0,0 +1,6 @@ +source "http://rubygems.org" + +gemspec + +File.exist?(gemfile_local = File.expand_path('../Gemfile.local', __FILE__)) and eval File.read(gemfile_local), binding, gemfile_local + diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..20f85c4 --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,22 @@ +Copyright (c) 2015 Stenver Jerkku + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/README.md b/README.md index 31d5ee1..5a04bfa 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,123 @@ -# schema_plus_multischema -Provides supports for using multiple schemas within a single database +[![Gem Version](https://badge.fury.io/rb/schema_plus_multischema.svg)](http://badge.fury.io/rb/schema_plus_multischema) +[![Build Status](https://secure.travis-ci.org/SchemaPlus/schema_plus_multischema.svg)](http://travis-ci.org/SchemaPlus/schema_plus_multischema) +[![Coverage Status](https://img.shields.io/coveralls/SchemaPlus/schema_plus_multischema.svg)](https://coveralls.io/r/SchemaPlus/schema_plus_multischema) +[![Dependency Status](https://gemnasium.com/lomba/schema_plus_multischema.svg)](https://gemnasium.com/SchemaPlus/schema_plus_multischema) + +# SchemaPlusMultischema + +Schema plus multischema is a schema plus extension for postgresql that adds support for multiple schemas for schema dumps. + +SchemaPlusMultischema is part of the [SchemaPlus](https://github.com/SchemaPlus/) family of Ruby on Rails ActiveRecord extension gems. + +## Installation + + + +As usual: + +```ruby +gem "schema_plus_multischema" # in a Gemfile +gem.add_dependency "schema_plus_multischema" # in a .gemspec +``` + + + +## Compatibility + +SchemaPlusMultischema is tested on: + + + +* ruby **2.1.5** with activerecord **4.2**, using **postgresql** + + + +## Usage + +Using schema plus multiple schemas is easy - simply require it before you do a schema dump. If everything works, then the schema dump should include schema names before table names + +For example, lets say we have a table ```wallets``` in schema ```private``` and table ```users``` in schema ```public```. Each user has 1 wallet. Without this gem, the schemadump would look like this: + +``` +create_table 'wallets' do +end + +create_table 'users' do |t| + t.integer :wallet_id +end +``` + +With this gem, the schemadump will look like this: + +``` +create_table 'private.wallets' do +end + +create_table 'public.users' do |t| + t.integer :wallet_id, null: false +end + +``` + +The schema plus multischema also works with schema plus foreign keys. If schema plus foreign keys is enabled, the output will look like this: + +``` +create_table 'private.wallets' do +end + +create_table 'public.users' do |t| + t.integer :wallet_id, null: false, foreign_key: {references: "private.wallets", name: "fk_public_users_wallet_id", on_update: :no_action, on_delete: :cascade} +end +``` + +## History + +* 0.1.0 - Initial release + +## Development & Testing + +Are you interested in contributing to SchemaPlusMultischema? Thanks! Please follow +the standard protocol: fork, feature branch, develop, push, and issue pull +request. + +Some things to know about to help you develop and test: + + + +* **schema_dev**: SchemaPlus::Multischema uses [schema_dev](https://github.com/SchemaPlus/schema_dev) to + facilitate running rspec tests on the matrix of ruby, activerecord, and database + versions that the gem supports, both locally and on + [travis-ci](http://travis-ci.org/SchemaPlus/schema_plus_multischema) + + To to run rspec locally on the full matrix, do: + + $ schema_dev bundle install + $ schema_dev rspec + + You can also run on just one configuration at a time; For info, see `schema_dev --help` or the [schema_dev](https://github.com/SchemaPlus/schema_dev) README. + + The matrix of configurations is specified in `schema_dev.yml` in + the project root. + + + + + + +* **schema_plus_core**: SchemaPlus::Multischema uses the SchemaPlus::Core API that + provides middleware callback stacks to make it easy to extend + ActiveRecord's behavior. If that API is missing something you need for + your contribution, please head over to + [schema_plus_core](https://github.com/SchemaPlus/schema_plus_core) and open + an issue or pull request. + + + + + +* **schema_monkey**: SchemaPlus::Multischema is implemented as a + [schema_monkey](https://github.com/SchemaPlus/schema_monkey) client, + using [schema_monkey](https://github.com/SchemaPlus/schema_monkey)'s + convention-based protocols for extending ActiveRecord and using middleware stacks. + + diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..edbae60 --- /dev/null +++ b/Rakefile @@ -0,0 +1,9 @@ +require 'bundler' +Bundler::GemHelper.install_tasks + +require 'schema_dev/tasks' + +task :default => :spec + +require 'rspec/core/rake_task' +RSpec::Core::RakeTask.new(:spec) diff --git a/gemfiles/Gemfile.base b/gemfiles/Gemfile.base new file mode 100644 index 0000000..9831e95 --- /dev/null +++ b/gemfiles/Gemfile.base @@ -0,0 +1,4 @@ +source 'https://rubygems.org' +gemspec :path => File.expand_path('..', __FILE__) + +File.exist?(gemfile_local = File.expand_path('../Gemfile.local', __FILE__)) and eval File.read(gemfile_local), binding, gemfile_local diff --git a/gemfiles/activerecord-4.2/Gemfile.base b/gemfiles/activerecord-4.2/Gemfile.base new file mode 100644 index 0000000..39ee838 --- /dev/null +++ b/gemfiles/activerecord-4.2/Gemfile.base @@ -0,0 +1,3 @@ +eval File.read File.expand_path('../../Gemfile.base', __FILE__) + +gem "activerecord", "~> 4.2.0" diff --git a/gemfiles/activerecord-4.2/Gemfile.postgresql b/gemfiles/activerecord-4.2/Gemfile.postgresql new file mode 100644 index 0000000..2c00e63 --- /dev/null +++ b/gemfiles/activerecord-4.2/Gemfile.postgresql @@ -0,0 +1,10 @@ +require "pathname" +eval(Pathname.new(__FILE__).dirname.join("Gemfile.base").read, binding) + +platform :ruby do + gem "pg" +end + +platform :jruby do + gem 'activerecord-jdbcpostgresql-adapter' +end \ No newline at end of file diff --git a/lib/schema_plus_multischema.rb b/lib/schema_plus_multischema.rb new file mode 100644 index 0000000..3aa9b04 --- /dev/null +++ b/lib/schema_plus_multischema.rb @@ -0,0 +1,11 @@ +require 'schema_plus/core' + +require_relative 'schema_plus_multischema/version' +require_relative 'schema_plus_multischema/active_record/connection_adapters/postgresql_adapter' + +module SchemaPlusMultischema + module ActiveRecord + end +end + +SchemaMonkey.register SchemaPlusMultischema diff --git a/lib/schema_plus_multischema/active_record/connection_adapters/postgresql_adapter.rb b/lib/schema_plus_multischema/active_record/connection_adapters/postgresql_adapter.rb new file mode 100644 index 0000000..9cc06a6 --- /dev/null +++ b/lib/schema_plus_multischema/active_record/connection_adapters/postgresql_adapter.rb @@ -0,0 +1,30 @@ +module SchemaPlusMultischema + module ActiveRecord + module ConnectionAdapters + module PostgresqlAdapter + + # Returns the list of all tables in the schema search path or a specified schema. + def tables(name = nil) + select_tablenames_with_schemas <<-SQL + SELECT schemaname, tablename + FROM pg_tables + WHERE schemaname = ANY(current_schemas(false)) + SQL + end + + def select_tablenames_with_schemas(arel) + arel, binds = binds_from_relation arel, [] + sql = to_sql(arel, binds) + execute_and_clear(sql, 'SCHEMA', binds) do |result| + if result.nfields > 0 + rows = result.column_values(0).count + (0..(rows - 1)).map{ |i| "#{result.column_values(0)[i]}.#{result.column_values(1)[i]}" } + else + [] + end + end + end + end + end + end +end diff --git a/lib/schema_plus_multischema/version.rb b/lib/schema_plus_multischema/version.rb new file mode 100644 index 0000000..52dfe5b --- /dev/null +++ b/lib/schema_plus_multischema/version.rb @@ -0,0 +1,3 @@ +module SchemaPlusMultischema + VERSION = "0.1.0" +end diff --git a/schema_dev.yml b/schema_dev.yml new file mode 100644 index 0000000..b04506c --- /dev/null +++ b/schema_dev.yml @@ -0,0 +1,6 @@ +ruby: + - 2.1.5 +activerecord: + - 4.2 +db: + - postgresql diff --git a/schema_multiple_schemas.gemspec b/schema_multiple_schemas.gemspec new file mode 100644 index 0000000..857639a --- /dev/null +++ b/schema_multiple_schemas.gemspec @@ -0,0 +1,29 @@ +# coding: utf-8 +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'schema_plus_multischema/version' + +Gem::Specification.new do |gem| + gem.name = "schema_plus_multischema" + gem.version = SchemaPlusMultischema::VERSION + gem.authors = ["Stenver Jerkku"] + gem.email = ["stenver1010@gmail.com"] + gem.summary = %q{Adds support for multiple schemas in activerecord when using Postgres} + gem.homepage = "https://github.com/SchemaPlus/schema_plus_multischema" + gem.license = "MIT" + + gem.files = `git ls-files -z`.split("\x0") + gem.executables = gem.files.grep(%r{^bin/}) { |f| File.basename(f) } + gem.test_files = gem.files.grep(%r{^(test|spec|features)/}) + gem.require_paths = ["lib"] + + gem.add_dependency "activerecord", "~> 4.2" + gem.add_dependency "schema_plus_core", "~> 0.5" + + gem.add_development_dependency "bundler", "~> 1.7" + gem.add_development_dependency "rake", "~> 10.0" + gem.add_development_dependency "rspec", "~> 3.0" + gem.add_development_dependency "schema_dev", "~> 3.5", ">= 3.5.1" + gem.add_development_dependency "simplecov" + gem.add_development_dependency "simplecov-gem-profile" +end diff --git a/spec/schema_dumper_spec.rb b/spec/schema_dumper_spec.rb new file mode 100644 index 0000000..021f361 --- /dev/null +++ b/spec/schema_dumper_spec.rb @@ -0,0 +1,72 @@ +require 'spec_helper' + +describe 'Schema dump' do + before(:each) do + ActiveRecord::Migration.suppress_messages do + + ActiveRecord::Schema.define do + connection.schema_search_path='first,second' + connection.tables.each do |table| drop_table table, force: :cascade end + + execute <<-SQL + CREATE SCHEMA IF NOT EXISTS first; + CREATE TABLE first.dogs + ( + id INTEGER PRIMARY KEY + ); + SQL + + execute <<-SQL + CREATE SCHEMA IF NOT EXISTS second; + CREATE TABLE second.dogs + ( + id INTEGER PRIMARY KEY + ); + SQL + + execute <<-SQL + CREATE SCHEMA IF NOT EXISTS second; + CREATE TABLE first.owners + ( + id INTEGER PRIMARY KEY, + dog_id INTEGER NOT NULL + ); + CREATE INDEX fk__first_owners_second_dogs ON first.owners USING btree (dog_id); + + ALTER TABLE ONLY first.owners + ADD CONSTRAINT fk_first_owners_dog_id FOREIGN KEY (dog_id) REFERENCES second.dogs(id) ON DELETE CASCADE; + SQL + + execute <<-SQL + CREATE SCHEMA IF NOT EXISTS second; + CREATE TABLE no_schema_prefix + ( + id INTEGER PRIMARY KEY + ); + SQL + end + end + end + + def dump_schema(opts={}) + stream = StringIO.new + ActiveRecord::SchemaDumper.ignore_tables = Array.wrap(opts[:ignore]) || [] + ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, stream) + stream.string + end + + it 'should dump tables which are created without schema prefix' do + expect(dump_schema).to include('create_table "first.no_schema_prefix"') + end + + it 'should dump tables with same names from different schemas' do + expect(dump_schema).to include('create_table "first.dogs"') + expect(dump_schema).to include('create_table "second.dogs"') + end + + context 'when foreign key schema plus gem required' do + it 'should dump foreign key references with schema names' do + expect(dump_schema).to include('foreign_key: {references: "second.dogs", name: "fk_first_owners_dog_id"') + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..ed212ad --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,24 @@ +require 'simplecov' +require 'simplecov-gem-profile' +require 'pry' +SimpleCov.start "gem" + +$LOAD_PATH.unshift(File.dirname(__FILE__)) +$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib')) + +require 'rspec' +require 'active_record' +require 'schema_plus_multischema' +require 'schema_dev/rspec' +require 'schema_plus_foreign_keys' + +SchemaDev::Rspec.setup + +Dir[File.dirname(__FILE__) + "/support/**/*.rb"].each {|f| require f} + +RSpec.configure do |config| + config.warnings = true +end + + +SimpleCov.command_name "[ruby#{RUBY_VERSION}-activerecord#{::ActiveRecord.version}-#{ActiveRecord::Base.connection.adapter_name}]"