From d588643d5ab05bf78ad7b5b26a645b433d344b57 Mon Sep 17 00:00:00 2001 From: Derek Ethier Date: Sat, 9 Apr 2016 22:36:30 -0400 Subject: [PATCH 01/91] Update README.md to include the GRANT usage command to db_enhancements The db_enhancements rake task is great, but the GRANT usage command is important to ensuring that the shared_extensions schema is available. Without this, the extensions created in this space will not be available in tenant schemas. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index dd406442..54d47af1 100644 --- a/README.md +++ b/README.md @@ -316,6 +316,8 @@ namespace :db do ActiveRecord::Base.connection.execute 'CREATE EXTENSION IF NOT EXISTS HSTORE SCHEMA shared_extensions;' # Enable UUID-OSSP ActiveRecord::Base.connection.execute 'CREATE EXTENSION IF NOT EXISTS "uuid-ossp" SCHEMA shared_extensions;' + # Grant usage to public + ActiveRecord::Base.connection.execute 'GRANT usage ON SCHEMA shared_extensions to public;' end end From 721093b4d2eb47165b81538a3d83da741c2d4e99 Mon Sep 17 00:00:00 2001 From: Frank West Date: Thu, 4 Aug 2016 16:16:48 +0000 Subject: [PATCH 02/91] Skip initialization of Apartment during assets:clean This changes skipping the initialization logic to exclude initializing of `assets:clean` and `assets:precompile`. This is an issue occurring when deploying to heroku where apartment attempts to connect to the database when it is not available and causes an error to occur. Fixes #346 --- lib/apartment/railtie.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/apartment/railtie.rb b/lib/apartment/railtie.rb index ae790cda..dffa451b 100644 --- a/lib/apartment/railtie.rb +++ b/lib/apartment/railtie.rb @@ -28,7 +28,7 @@ class Railtie < Rails::Railtie # See the middleware/console declarations below to help with this. Hope to fix that soon. # config.to_prepare do - Apartment::Tenant.init unless ARGV.include? 'assets:precompile' + Apartment::Tenant.init unless ARGV.any? { |arg| arg =~ /\Aassets:(?:precompile|clean)\z/ } end # From b2b68b37f2a4037cc1d223880fb1c32c4b377061 Mon Sep 17 00:00:00 2001 From: Benjamin Borowski Date: Mon, 8 Aug 2016 12:57:39 -0700 Subject: [PATCH 03/91] [DOCS] middleware considerations and general cleanup --- README.md | 83 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 60 insertions(+), 23 deletions(-) diff --git a/README.md b/README.md index 3378b0dc..d9fd5098 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,5 @@ # Apartment + [![Code Climate](https://codeclimate.com/github/influitive/apartment.png)](https://codeclimate.com/github/influitive/apartment) [![Build Status](https://secure.travis-ci.org/influitive/apartment.png?branch=development)](http://travis-ci.org/influitive/apartment) @@ -29,7 +30,6 @@ this poll, we'd greatly appreciated it. gem 'rails', '4.2.1', github: 'influitive/rails', tag: 'v4.2.1.memfix' ``` - ## Installation ### Rails @@ -57,7 +57,9 @@ on a per-user basis, look under "Usage - Switching tenants per request", below. > * for Rails 3.1.x: _Rails ~> 3.1.2_, it contains a [patch](https://github.com/rails/rails/pull/3232) that makes prepared statements work with multiple schemas ## Usage + ### Video Tutorial + How to separate your application data into different accounts or companies. [GoRails #47](https://gorails.com/episodes/multitenancy-with-apartment) @@ -103,6 +105,9 @@ tenant, call switch with no arguments. You can have Apartment route to the appropriate tenant by adding some Rack middleware. Apartment can support many different "Elevators" that can take care of this routing to your data. +**NOTE: when switching tenants per-request, keep in mind that the order of your Rack middleware is important.** +See the [Middleware Considerations](#middleware-considerations) section for more. + The initializer above will generate the appropriate code for the Subdomain elevator by default. You can see this in `config/initializers/apartment.rb` after running that generator. If you're *not* using the generator, you can specify your @@ -114,7 +119,8 @@ manually in your `application.rb` like so require 'apartment/elevators/subdomain' # or 'domain' or 'generic' ``` -**Switch on subdomain** +#### Switch on subdomain + In house, we use the subdomain elevator, which analyzes the subdomain of the request and switches to a tenant schema of the same name. It can be used like so: ```ruby @@ -144,8 +150,9 @@ Apartment::Elevators::Subdomain.excluded_subdomains = ['www'] This functions much in the same way as Apartment.excluded_models. This example will prevent switching your tenant when the subdomain is www. Handy for subdomains like: "public", "www", and "admin" :) -**Switch on first subdomain** -To switch on the first subdomain, which analyzes the chain of subdomains of the request and switches to a tenant schema of the first name in the chain (e.g. owls.birds.animals.com would switch to "owl"). It can be used like so: +#### Switch on first subdomain + +To switch on the first subdomain, which analyzes the chain of subdomains of the request and switches to a tenant schema of the first name in the chain (e.g. owls.birds.animals.com would switch to "owl"). It can be used like so: ```ruby # application.rb @@ -163,9 +170,10 @@ If you want to exclude a domain, for example if you don't want your application Apartment::Elevators::FirstSubdomain.excluded_subdomains = ['www'] ``` -This functions much in the same way as the Subdomain elevator. +This functions much in the same way as the Subdomain elevator. **NOTE:** in fact, at the time of this writing, the `Subdomain` and `FirstSubdomain` elevators both use the first subdomain ([#339](https://github.com/influitive/apartment/issues/339#issuecomment-235578610)). If you need to switch on larger parts of a Subdomain, consider using a Custom Elevator. + +#### Switch on domain -**Switch on domain** To switch based on full domain (excluding subdomains *ie 'www'* and top level domains *ie '.com'* ) use the following: ```ruby @@ -177,7 +185,8 @@ module MyApplication end ``` -**Switch on full host using a hash** +#### Switch on full host using a hash + To switch based on full host with a hash to find corresponding tenant name use the following: ```ruby @@ -189,8 +198,9 @@ module MyApplication end ``` -**Custom Elevator** -A Generic Elevator exists that allows you to pass a `Proc` (or anything that responds to `call`) to the middleware. This Object will be passed in an `ActionDispatch::Request` object when called for you to do your magic. Apartment will use the return value of this proc to switch to the appropriate tenant. Use like so: +#### Custom Elevator + +A Generic Elevator exists that allows you to pass a `Proc` (or anything that responds to `call`) to the middleware. This Object will be passed in an `ActionDispatch::Request` object when called for you to do your magic. Apartment will use the return value of this proc to switch to the appropriate tenant. Use like so: ```ruby # application.rb @@ -225,6 +235,28 @@ class MyCustomElevator < Apartment::Elevators::Generic end ``` +#### Middleware Considerations + +In the examples above, we show the Apartment middleware being appended to the Rack stack with + +```ruby +Rails.application.config.middleware.use 'Apartment::Elevators::Subdomain' +``` + +By default, the Subdomain middleware switches into a Tenant based on the subdomain at the beginning of the request, and when the request is finished, it switches back to the "public" Tenant. This happens in the [Generic](https://github.com/influitive/apartment/blob/development/lib/apartment/elevators/generic.rb#L22) elevator, so all elevators that inherit from this elevator will operate as such. + +It's also good to note that Apartment switches back to the "public" tenant any time an error is raised in your application. + +This works okay for simple applications, but it's important to consider that you may want to maintain the "selected" tenant through different parts of the Rack application stack. For example, the [Devise](https://github.com/plataformatec/devise) gem adds the `Warden::Manager` middleware at the end of the stack in the examples above, our `Apartment::Elevators::Subdomain` middleware would come after it. Trouble is, Apartment resets the selected tenant after the request is finish, so some redirects (e.g. authentication) in Devise will be run in the context of the "public" tenant. The same issue would also effect a gem such as the [better_errors](https://github.com/charliesome/better_errors) gem which inserts a middleware quite early in the Rails middleware stack. + +To resolve this issue, consider adding the Apartment middleware at a location in the Rack stack that makes sense for your needs, e.g.: + +```ruby +Rails.application.config.middleware.insert_before 'Warden::Manager', 'Apartment::Elevators::Subdomain' +``` + +Now work done in the Warden middleware is wrapped in the `Apartment::Tenant.switch` context started in the Generic elevator. + ### Dropping Tenants To drop tenants using Apartment, use the following command: @@ -251,7 +283,7 @@ end ### Excluding models -If you have some models that should always access the 'public' tenant, you can specify this by configuring Apartment using `Apartment.configure`. This will yield a config object for you. You can set excluded models like so: +If you have some models that should always access the 'public' tenant, you can specify this by configuring Apartment using `Apartment.configure`. This will yield a config object for you. You can set excluded models like so: ```ruby config.excluded_models = ["User", "Company"] # these models will not be multi-tenanted, but remain in the global (public) namespace @@ -259,7 +291,7 @@ config.excluded_models = ["User", "Company"] # these models will not be m Note that a string representation of the model name is now the standard so that models are properly constantized when reloaded in development -Rails will always access the 'public' tenant when accessing these models, but note that tables will be created in all schemas. This may not be ideal, but its done this way because otherwise rails wouldn't be able to properly generate the schema.rb file. +Rails will always access the 'public' tenant when accessing these models, but note that tables will be created in all schemas. This may not be ideal, but its done this way because otherwise rails wouldn't be able to properly generate the schema.rb file. > **NOTE - Many-To-Many Excluded Models:** > Since model exclusions must come from referencing a real ActiveRecord model, `has_and_belongs_to_many` is NOT supported. In order to achieve a many-to-many relationship for excluded models, you MUST use `has_many :through`. This way you can reference the join model in the excluded models configuration. @@ -267,6 +299,7 @@ Rails will always access the 'public' tenant when accessing these models, but n ### Postgresql Schemas ## Providing a Different default_schema + By default, ActiveRecord will use `"$user", public` as the default `schema_search_path`. This can be modified if you wish to use a different default schema be setting: ```ruby @@ -276,14 +309,16 @@ config.default_schema = "some_other_schema" With that set, all excluded models will use this schema as the table name prefix instead of `public` and `reset` on `Apartment::Tenant` will return to this schema as well. ## Persistent Schemas -Apartment will normally just switch the `schema_search_path` whole hog to the one passed in. This can lead to problems if you want other schemas to always be searched as well. Enter `persistent_schemas`. You can configure a list of other schemas that will always remain in the search path, while the default gets swapped out: + +Apartment will normally just switch the `schema_search_path` whole hog to the one passed in. This can lead to problems if you want other schemas to always be searched as well. Enter `persistent_schemas`. You can configure a list of other schemas that will always remain in the search path, while the default gets swapped out: ```ruby config.persistent_schemas = ['some', 'other', 'schemas'] ``` ### Installing Extensions into Persistent Schemas -Persistent Schemas have numerous useful applications. [Hstore](http://www.postgresql.org/docs/9.1/static/hstore.html), for instance, is a popular storage engine for Postgresql. In order to use extensions such as Hstore, you have to install it to a specific schema and have that always in the `schema_search_path`. + +Persistent Schemas have numerous useful applications. [Hstore](http://www.postgresql.org/docs/9.1/static/hstore.html), for instance, is a popular storage engine for Postgresql. In order to use extensions such as Hstore, you have to install it to a specific schema and have that always in the `schema_search_path`. When using extensions, keep in mind: * Extensions can only be installed into one schema per database, so we will want to install it into a schema that is always available in the `schema_search_path` @@ -306,7 +341,6 @@ When using extensions, keep in mind: # BEFORE db:migrate. # ################################################## - namespace :db do desc 'Also create shared_extensions Schema' task :extensions => :environment do @@ -330,7 +364,7 @@ end #### 2. Ensure the schema is in Rails' default connection -Next, your `database.yml` file must mimic what you've set for your default and persistent schemas in Apartment. When you run migrations with Rails, it won't know about the extensions schema because Apartment isn't injected into the default connection, it's done on a per-request basis, therefore Rails doesn't know about `hstore` or `uuid-ossp` during migrations. To do so, add the following to your `database.yml` for all environments +Next, your `database.yml` file must mimic what you've set for your default and persistent schemas in Apartment. When you run migrations with Rails, it won't know about the extensions schema because Apartment isn't injected into the default connection, it's done on a per-request basis, therefore Rails doesn't know about `hstore` or `uuid-ossp` during migrations. To do so, add the following to your `database.yml` for all environments ```yaml # database.yml @@ -351,6 +385,7 @@ This would be for a config with `default_schema` set to `public` and `persistent To double check, login to the console of your Heroku app and see if `Apartment.connection.schema_search_path` is `public,hstore` #### 3. Ensure the schema is in the apartment config + ```ruby # config/initializers/apartment.rb ... @@ -359,6 +394,7 @@ config.persistent_schemas = ['shared_extensions'] ``` #### Alternative: Creating schema by default + Another way that we've successfully configured hstore for our applications is to add it into the postgresql template1 database so that every tenant that gets created has it by default. @@ -377,6 +413,7 @@ also contain the tenanted tables, which is an open issue with no real milestone Happy to accept PR's on the matter. #### Alternative: Creating new schemas by using raw SQL dumps + Apartment can be forced to use raw SQL dumps insted of `schema.rb` for creating new schemas. Use this when you are using some extra features in postgres that can't be respresented in `schema.rb`, like materialized views etc. This only applies while using postgres adapter and `config.use_schemas` is set to `true`. @@ -387,12 +424,11 @@ Enable this option with: config.use_sql = true ``` - ### Managing Migrations In order to migrate all of your tenants (or postgresql schemas) you need to provide a list -of dbs to Apartment. You can make this dynamic by providing a Proc object to be called on migrations. -This object should yield an array of string representing each tenant name. Example: +of dbs to Apartment. You can make this dynamic by providing a Proc object to be called on migrations. +This object should yield an array of string representing each tenant name. Example: ```ruby # Dynamically get tenant names to migrate @@ -418,8 +454,8 @@ Note that you can disable the default migrating of all tenants with `db:migrate` ### Handling Environments By default, when not using postgresql schemas, Apartment will prepend the environment to the tenant name -to ensure there is no conflict between your environments. This is mainly for the benefit of your development -and test environments. If you wish to turn this option off in production, you could do something like: +to ensure there is no conflict between your environments. This is mainly for the benefit of your development +and test environments. If you wish to turn this option off in production, you could do something like: ```ruby config.prepend_environment = !Rails.env.production? @@ -454,7 +490,8 @@ end ``` ## Delayed::Job -### Has been removed... See apartment-sidekiq for a better backgrounding experience + +Has been removed. See [apartment-sidekiq](https://github.com/influitive/apartment-sidekiq) for a better backgrounding experience. ## Contributing @@ -462,8 +499,8 @@ end * Copy them into the same directory but with the name `database.yml` * Edit them to fit your own settings * Rake tasks (see the Rakefile) will help you setup your dbs necessary to run tests -* Please issue pull requests to the `development` branch. All development happens here, master is used for releases -* Ensure that your code is accompanied with tests. No code will be merged without tests +* Please issue pull requests to the `development` branch. All development happens here, master is used for releases. +* Ensure that your code is accompanied with tests. No code will be merged without tests * If you're looking to help, check out the TODO file for some upcoming changes I'd like to implement in Apartment. From efd28e37dcf16c101b5159d1851fd18f72496633 Mon Sep 17 00:00:00 2001 From: Mike Campbell Date: Tue, 9 Aug 2016 09:27:57 +0100 Subject: [PATCH 04/91] Fix for AbstractAdapter's DB name escaping in drop_command --- lib/apartment/adapters/abstract_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/apartment/adapters/abstract_adapter.rb b/lib/apartment/adapters/abstract_adapter.rb index ce7e0d72..a1e534b1 100644 --- a/lib/apartment/adapters/abstract_adapter.rb +++ b/lib/apartment/adapters/abstract_adapter.rb @@ -159,7 +159,7 @@ def process_excluded_model(excluded_model) def drop_command(conn, tenant) # connection.drop_database note that drop_database will not throw an exception, so manually execute - conn.execute("DROP DATABASE #{environmentify(tenant)}") + conn.execute("DROP DATABASE #{conn.quote_table_name(environmentify(tenant))}") end # Create the tenant From b142428a5353cadfe7a716ecc9decd6c99f7682e Mon Sep 17 00:00:00 2001 From: Mike Campbell Date: Mon, 14 Nov 2016 12:20:06 +0000 Subject: [PATCH 05/91] Don't retain connection used during gem initialisation When the gem is initialised and a schema adapter is being used, the adapter will initialise the schema_search_path which involves touching the database, and thus checking out a connection for the current thread. For threaded web servers, it is not expected that the main thread has a database connection, so you typically set the connection pool size equal to the number of web server threads. This patch checks the connection back in to the pool after init so that a web server thread can pick it up and use it. --- lib/apartment/railtie.rb | 6 +++++- spec/examples/generic_adapter_examples.rb | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/lib/apartment/railtie.rb b/lib/apartment/railtie.rb index dffa451b..2d25d5ec 100644 --- a/lib/apartment/railtie.rb +++ b/lib/apartment/railtie.rb @@ -28,7 +28,11 @@ class Railtie < Rails::Railtie # See the middleware/console declarations below to help with this. Hope to fix that soon. # config.to_prepare do - Apartment::Tenant.init unless ARGV.any? { |arg| arg =~ /\Aassets:(?:precompile|clean)\z/ } + unless ARGV.any? { |arg| arg =~ /\Aassets:(?:precompile|clean)\z/ } + Apartment.connection_class.connection_pool.with_connection do + Apartment::Tenant.init + end + end end # diff --git a/spec/examples/generic_adapter_examples.rb b/spec/examples/generic_adapter_examples.rb index f90cb28f..078036f7 100644 --- a/spec/examples/generic_adapter_examples.rb +++ b/spec/examples/generic_adapter_examples.rb @@ -8,6 +8,25 @@ Apartment.append_environment = false } + describe "#init" do + it "should not retain a connection after railtie" do + # this test should work on rails >= 4, the connection pool code is + # completely different for 3.2 so we'd have to have a messy conditional.. + unless Rails::VERSION::MAJOR < 4 + ActiveRecord::Base.connection_pool.disconnect! + + Apartment::Railtie.config.to_prepare_blocks.map(&:call) + + num_available_connections = Apartment.connection_class.connection_pool + .instance_variable_get(:@available) + .instance_variable_get(:@queue) + .size + + expect(num_available_connections).to eq(1) + end + end + end + # # Creates happen already in our before_filter # From aeab03c42eb66d19a17e08cfb86681b1dc0f4ace Mon Sep 17 00:00:00 2001 From: kakipo Date: Sat, 10 Dec 2016 12:00:03 +0900 Subject: [PATCH 06/91] Accept DB config --- lib/apartment/adapters/abstract_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/apartment/adapters/abstract_adapter.rb b/lib/apartment/adapters/abstract_adapter.rb index a1e534b1..0ae35924 100644 --- a/lib/apartment/adapters/abstract_adapter.rb +++ b/lib/apartment/adapters/abstract_adapter.rb @@ -175,7 +175,7 @@ def create_tenant(tenant) end def create_tenant_command(conn, tenant) - conn.create_database(environmentify(tenant)) + conn.create_database(environmentify(tenant), @config) end # Connect to new tenant From bae62c551f24940060ef233287c8ae904b046787 Mon Sep 17 00:00:00 2001 From: humancopy Date: Thu, 2 Mar 2017 18:24:36 +0100 Subject: [PATCH 07/91] Use public_suffix for smarter subdomain parsing (#309) * Use public_suffix for smarter subdomain parsing * Check validity of domain and add tests for localhost and IP * Remove unnecessary TLD length test --- README.md | 9 ------- apartment.gemspec | 1 + lib/apartment.rb | 4 --- lib/apartment/elevators/subdomain.rb | 19 +++++++++++--- lib/apartment/railtie.rb | 1 - spec/unit/config_spec.rb | 7 ------ spec/unit/elevators/subdomain_spec.rb | 36 +++++++++++++++++++++------ 7 files changed, 44 insertions(+), 33 deletions(-) diff --git a/README.md b/README.md index d9fd5098..5691bb56 100644 --- a/README.md +++ b/README.md @@ -132,15 +132,6 @@ module MyApplication end ``` -By default, the subdomain elevator assumes that the parent domain consists of two segments, e.g. 'example.com'. If this isn't the case, you can adjust the `tld_length` (top level domain length) configuration variable, which defaults to 1. For example, if you are using 'localhost' in development: -```ruby -# config/initializers/apartment.rb -Apartment.configure do |config| - ... - config.tld_length = 0 if Rails.env == 'development' -end -``` - If you want to exclude a domain, for example if you don't want your application to treat www like a subdomain, in an initializer in your application, you can set the following: ```ruby diff --git a/apartment.gemspec b/apartment.gemspec index e58eef44..004cea2d 100644 --- a/apartment.gemspec +++ b/apartment.gemspec @@ -37,6 +37,7 @@ Gem::Specification.new do |s| # must be >= 3.1.2 due to bug in prepared_statements s.add_dependency 'activerecord', '>= 3.1.2', '< 6.0' s.add_dependency 'rack', '>= 1.3.6' + s.add_dependency 'public_suffix', '~> 2.0.5' s.add_development_dependency 'appraisal' s.add_development_dependency 'rake', '~> 0.9' diff --git a/lib/apartment.rb b/lib/apartment.rb index f86184b3..027e9c0a 100644 --- a/lib/apartment.rb +++ b/lib/apartment.rb @@ -75,10 +75,6 @@ def seed_data_file @seed_data_file = "#{Rails.root}/db/seeds.rb" end - def tld_length - @tld_length || 1 - end - # Reset all the config for Apartment def reset (ACCESSOR_METHODS + WRITER_METHODS).each{|method| remove_instance_variable(:"@#{method}") if instance_variable_defined?(:"@#{method}") } diff --git a/lib/apartment/elevators/subdomain.rb b/lib/apartment/elevators/subdomain.rb index b68aa55e..97f82ab8 100644 --- a/lib/apartment/elevators/subdomain.rb +++ b/lib/apartment/elevators/subdomain.rb @@ -1,4 +1,5 @@ require 'apartment/elevators/generic' +require 'public_suffix' module Apartment module Elevators @@ -38,13 +39,23 @@ def subdomain(host) end def subdomains(host) - return [] unless named_host?(host) + host_valid?(host) ? parse_host(host) : [] + end + + def host_valid?(host) + !ip_host?(host) && domain_valid?(host) + end + + def ip_host?(host) + !/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$/.match(host).nil? + end - host.split('.')[0..-(Apartment.tld_length + 2)] + def domain_valid?(host) + PublicSuffix.valid?(host, ignore_private: true) end - def named_host?(host) - !(host.nil? || /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.match(host)) + def parse_host(host) + (PublicSuffix.parse(host).trd || '').split('.') end end end diff --git a/lib/apartment/railtie.rb b/lib/apartment/railtie.rb index 2d25d5ec..f011cbfd 100644 --- a/lib/apartment/railtie.rb +++ b/lib/apartment/railtie.rb @@ -17,7 +17,6 @@ class Railtie < Rails::Railtie config.seed_after_create = false config.prepend_environment = false config.append_environment = false - config.tld_length = 1 end ActiveRecord::Migrator.migrations_paths = Rails.application.paths['db/migrate'].to_a diff --git a/spec/unit/config_spec.rb b/spec/unit/config_spec.rb index 2ccb9a60..44901952 100644 --- a/spec/unit/config_spec.rb +++ b/spec/unit/config_spec.rb @@ -50,13 +50,6 @@ def tenant_names_from_array(names) expect(Apartment.seed_after_create).to be true end - it "should set tld_length" do - Apartment.configure do |config| - config.tld_length = 2 - end - expect(Apartment.tld_length).to eq(2) - end - context "databases" do let(:users_conf_hash) { { port: 5444 } } diff --git a/spec/unit/elevators/subdomain_spec.rb b/spec/unit/elevators/subdomain_spec.rb index b8655a2b..cdc9a3db 100644 --- a/spec/unit/elevators/subdomain_spec.rb +++ b/spec/unit/elevators/subdomain_spec.rb @@ -6,7 +6,7 @@ subject(:elevator){ described_class.new(Proc.new{}) } describe "#parse_tenant_name" do - context "assuming tld_length of 1" do + context "assuming one tld" do it "should parse subdomain" do request = ActionDispatch::Request.new('HTTP_HOST' => 'foo.bar.com') expect(elevator.parse_tenant_name(request)).to eq('foo') @@ -18,13 +18,7 @@ end end - context "assuming tld_length of 2" do - before do - Apartment.configure do |config| - config.tld_length = 2 - end - end - + context "assuming two tlds" do it "should parse subdomain in the third level domain" do request = ActionDispatch::Request.new('HTTP_HOST' => 'foo.bar.co.uk') expect(elevator.parse_tenant_name(request)).to eq("foo") @@ -35,6 +29,32 @@ expect(elevator.parse_tenant_name(request)).to be_nil end end + + context "assuming two subdomains" do + it "should parse two subdomains in the two level domain" do + request = ActionDispatch::Request.new('HTTP_HOST' => 'foo.xyz.bar.com') + elevator.parse_tenant_name(request).should == "foo" + end + + it "should parse two subdomains in the third level domain" do + request = ActionDispatch::Request.new('HTTP_HOST' => 'foo.xyz.bar.co.uk') + elevator.parse_tenant_name(request).should == "foo" + end + end + + context "assuming localhost" do + it "should return nil for localhost" do + request = ActionDispatch::Request.new('HTTP_HOST' => 'localhost') + elevator.parse_tenant_name(request).should be_nil + end + end + + context "assuming ip address" do + it "should return nil for an ip address" do + request = ActionDispatch::Request.new('HTTP_HOST' => '127.0.0.1') + elevator.parse_tenant_name(request).should be_nil + end + end end describe "#call" do From 18fab38c75f0d02921b04c371635e59c33dfcb90 Mon Sep 17 00:00:00 2001 From: Cairo Noleto Date: Sun, 12 Mar 2017 16:44:36 -0300 Subject: [PATCH 08/91] Removing deprecated methods. --- apartment.gemspec | 16 ------- lib/apartment.rb | 21 --------- lib/apartment/adapters/abstract_adapter.rb | 43 +++---------------- lib/apartment/elevators/generic.rb | 21 +-------- lib/apartment/tenant.rb | 10 ----- spec/apartment_spec.rb | 6 +-- spec/dummy/config/application.rb | 2 +- .../config/initializers/apartment.rb | 2 +- spec/examples/generic_adapter_examples.rb | 17 -------- 9 files changed, 10 insertions(+), 128 deletions(-) diff --git a/apartment.gemspec b/apartment.gemspec index 004cea2d..dbb1a27e 100644 --- a/apartment.gemspec +++ b/apartment.gemspec @@ -18,22 +18,6 @@ Gem::Specification.new do |s| s.homepage = %q{https://github.com/influitive/apartment} s.licenses = ["MIT"] - s.post_install_message = <<-MSG - ******************************** - - Apartment Deprecation Warning - - `Apartment::Tenant.process` has been deprecated in favour of `Apartment::Tenant.switch`. - You must now always pass a block to `switch`. - - To get the previous `switch` behaviour where you can switch to a tenant - without a block, use `Apartment::Tenant.switch!`. - This is to indicate that your call actually has a side affect of changing - the scope of your queries to that tenant. - - ******************************** - MSG - # must be >= 3.1.2 due to bug in prepared_statements s.add_dependency 'activerecord', '>= 3.1.2', '< 6.0' s.add_dependency 'rack', '>= 1.3.6' diff --git a/lib/apartment.rb b/lib/apartment.rb index 027e9c0a..38ea5188 100644 --- a/lib/apartment.rb +++ b/lib/apartment.rb @@ -3,7 +3,6 @@ require 'forwardable' require 'active_record' require 'apartment/tenant' -require 'apartment/deprecation' module Apartment @@ -80,26 +79,6 @@ def reset (ACCESSOR_METHODS + WRITER_METHODS).each{|method| remove_instance_variable(:"@#{method}") if instance_variable_defined?(:"@#{method}") } end - def database_names - Apartment::Deprecation.warn "[Deprecation Warning] `database_names` is now deprecated, please use `tenant_names`" - tenant_names - end - - def database_names=(names) - Apartment::Deprecation.warn "[Deprecation Warning] `database_names=` is now deprecated, please use `tenant_names=`" - self.tenant_names=(names) - end - - def use_postgres_schemas - Apartment::Deprecation.warn "[Deprecation Warning] `use_postgresql_schemas` is now deprecated, please use `use_schemas`" - use_schemas - end - - def use_postgres_schemas=(to_use_or_not_to_use) - Apartment::Deprecation.warn "[Deprecation Warning] `use_postgresql_schemas=` is now deprecated, please use `use_schemas=`" - self.use_schemas = to_use_or_not_to_use - end - def extract_tenant_config return {} unless @tenant_names values = @tenant_names.respond_to?(:call) ? @tenant_names.call : @tenant_names diff --git a/lib/apartment/adapters/abstract_adapter.rb b/lib/apartment/adapters/abstract_adapter.rb index 0ae35924..760603d0 100644 --- a/lib/apartment/adapters/abstract_adapter.rb +++ b/lib/apartment/adapters/abstract_adapter.rb @@ -1,5 +1,3 @@ -require 'apartment/deprecation' - module Apartment module Adapters class AbstractAdapter @@ -34,24 +32,6 @@ def create(tenant) end end - # Get the current tenant name - # - # @return {String} current tenant name - # - def current_database - Apartment::Deprecation.warn "[Deprecation Warning] `current_database` is now deprecated, please use `current`" - current - end - - # Get the current tenant name - # - # @return {String} current tenant name - # - def current_tenant - Apartment::Deprecation.warn "[Deprecation Warning] `current_tenant` is now deprecated, please use `current`" - current - end - # Note alias_method here doesn't work with inheritence apparently ?? # def current @@ -99,25 +79,14 @@ def switch!(tenant = nil) # @param {String?} tenant to connect to # def switch(tenant = nil) - if block_given? - begin - previous_tenant = current - switch!(tenant) - yield - - ensure - switch!(previous_tenant) rescue reset - end - else - Apartment::Deprecation.warn("[Deprecation Warning] `switch` now requires a block reset to the default tenant after the block. Please use `switch!` instead if you don't want this") + begin + previous_tenant = current switch!(tenant) - end - end + yield - # [Deprecated] - def process(tenant = nil, &block) - Apartment::Deprecation.warn("[Deprecation Warning] `process` is now deprecated. Please use `switch`") - switch(tenant, &block) + ensure + switch!(previous_tenant) rescue reset + end end # Iterate over all tenants, switch to tenant and yield tenant name diff --git a/lib/apartment/elevators/generic.rb b/lib/apartment/elevators/generic.rb index 30367cf3..21349378 100644 --- a/lib/apartment/elevators/generic.rb +++ b/lib/apartment/elevators/generic.rb @@ -1,6 +1,5 @@ require 'rack/request' require 'apartment/tenant' -require 'apartment/deprecation' module Apartment module Elevators @@ -10,7 +9,7 @@ class Generic def initialize(app, processor = nil) @app = app - @processor = processor || parse_method + @processor = processor || method(:parse_tenant_name) end def call(env) @@ -25,27 +24,9 @@ def call(env) end end - def parse_database_name(request) - deprecation_warning - parse_tenant_name(request) - end - def parse_tenant_name(request) raise "Override" end - - def parse_method - if self.class.instance_methods(false).include? :parse_database_name - deprecation_warning - method(:parse_database_name) - else - method(:parse_tenant_name) - end - end - - def deprecation_warning - Apartment::Deprecation.warn "[DEPRECATED::Apartment] Use #parse_tenant_name instead of #parse_database_name -> #{self.class.name}" - end end end end diff --git a/lib/apartment/tenant.rb b/lib/apartment/tenant.rb index b236b9d9..7a4e33ca 100644 --- a/lib/apartment/tenant.rb +++ b/lib/apartment/tenant.rb @@ -1,5 +1,4 @@ require 'forwardable' -require 'apartment/deprecation' module Apartment # The main entry point to Apartment functions @@ -64,13 +63,4 @@ def config @config ||= Apartment.connection_config end end - - def self.const_missing(const_name) - if const_name == :Database - Apartment::Deprecation.warn "`Apartment::Database` has been deprecated. Use `Apartment::Tenant` instead." - Tenant - else - super - end - end end diff --git a/spec/apartment_spec.rb b/spec/apartment_spec.rb index c319d300..b00e0b2d 100644 --- a/spec/apartment_spec.rb +++ b/spec/apartment_spec.rb @@ -8,8 +8,4 @@ it "should be a valid app" do expect(::Rails.application).to be_a(Dummy::Application) end - - it "should deprecate Apartment::Database in favor of Apartment::Tenant" do - expect(Apartment::Database).to eq(Apartment::Tenant) - end -end \ No newline at end of file +end diff --git a/spec/dummy/config/application.rb b/spec/dummy/config/application.rb index b61781c8..e8f51ed9 100644 --- a/spec/dummy/config/application.rb +++ b/spec/dummy/config/application.rb @@ -17,7 +17,7 @@ class Application < Rails::Application require 'apartment/elevators/subdomain' require 'apartment/elevators/domain' - config.middleware.use 'Apartment::Elevators::Subdomain' + config.middleware.use Apartment::Elevators::Subdomain # Custom directories with classes and modules you want to be autoloadable. config.autoload_paths += %W(#{config.root}/lib) diff --git a/spec/dummy_engine/config/initializers/apartment.rb b/spec/dummy_engine/config/initializers/apartment.rb index addb1cf7..a1367900 100644 --- a/spec/dummy_engine/config/initializers/apartment.rb +++ b/spec/dummy_engine/config/initializers/apartment.rb @@ -48,4 +48,4 @@ # Rails.application.config.middleware.use 'Apartment::Elevators::Domain' -Rails.application.config.middleware.use 'Apartment::Elevators::Subdomain' +Rails.application.config.middleware.use Apartment::Elevators::Subdomain diff --git a/spec/examples/generic_adapter_examples.rb b/spec/examples/generic_adapter_examples.rb index 078036f7..d77166ba 100644 --- a/spec/examples/generic_adapter_examples.rb +++ b/spec/examples/generic_adapter_examples.rb @@ -102,23 +102,6 @@ subject.switch(db1){ subject.drop(db2) } }.to_not raise_error end - - it "warns if no block is given, but calls switch!" do - expect(Apartment::Deprecation).to receive(:warn) - - subject.switch(db1) - expect(subject.current).to eq(db1) - end - end - - describe "#process" do - it "is deprecated" do - expect(Apartment::Deprecation).to receive(:warn) - - subject.process(db1) do - expect(subject.current).to eq(db1) - end - end end describe "#reset" do From 86d52cd1732316145df14293f2e5b1a7d0b3f82e Mon Sep 17 00:00:00 2001 From: Mike Campbell Date: Mon, 13 Mar 2017 09:57:06 +0000 Subject: [PATCH 09/91] Add Rails ~5.0.0 to travis and remove ~3.2.0 --- .travis.yml | 8 +++++--- gemfiles/rails_3_2.gemfile | 13 ------------- 2 files changed, 5 insertions(+), 16 deletions(-) delete mode 100644 gemfiles/rails_3_2.gemfile diff --git a/.travis.yml b/.travis.yml index f694ec4a..1f006220 100644 --- a/.travis.yml +++ b/.travis.yml @@ -6,10 +6,10 @@ rvm: - 2.3.1 - jruby-9.0.5.0 gemfile: - - gemfiles/rails_3_2.gemfile - gemfiles/rails_4_0.gemfile - gemfiles/rails_4_1.gemfile - gemfiles/rails_4_2.gemfile + - gemfiles/rails_5_0.gemfile bundler_args: --without local before_install: - gem install bundler -v '> 1.5.0' @@ -18,6 +18,8 @@ env: RUBY_FREE_MIN: 200000 matrix: exclude: - - rvm: 2.2.0 - gemfile: gemfiles/rails_3_2.gemfile + - rvm: 2.0.0 + gemfile: gemfiles/rails_5_0.gemfile + - rvm: 2.1.9 + gemfile: gemfiles/rails_5_0.gemfile fast_finish: true diff --git a/gemfiles/rails_3_2.gemfile b/gemfiles/rails_3_2.gemfile deleted file mode 100644 index d18ea9dd..00000000 --- a/gemfiles/rails_3_2.gemfile +++ /dev/null @@ -1,13 +0,0 @@ -# This file was generated by Appraisal - -source "http://rubygems.org" - -gem "rails", "~> 3.2.0" -gem "test-unit", "~> 3.0" - -group :local do - gem "pry" - gem "guard-rspec", "~> 4.2" -end - -gemspec :path => "../" From 17ef2fa3194b954d774b193b45f36cec7b3a897e Mon Sep 17 00:00:00 2001 From: Giorgos Vrettos Date: Tue, 11 Apr 2017 00:09:30 +0300 Subject: [PATCH 10/91] Fix error when passing strings to the middleware stack --- README.md | 12 ++++++------ .../apartment/install/templates/apartment.rb | 8 ++++---- spec/dummy_engine/config/initializers/apartment.rb | 4 ++-- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/README.md b/README.md index 5691bb56..c5f2ccce 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ In house, we use the subdomain elevator, which analyzes the subdomain of the req # application.rb module MyApplication class Application < Rails::Application - config.middleware.use 'Apartment::Elevators::Subdomain' + config.middleware.use Apartment::Elevators::Subdomain end end ``` @@ -149,7 +149,7 @@ To switch on the first subdomain, which analyzes the chain of subdomains of the # application.rb module MyApplication class Application < Rails::Application - config.middleware.use 'Apartment::Elevators::FirstSubdomain' + config.middleware.use Apartment::Elevators::FirstSubdomain end end ``` @@ -171,7 +171,7 @@ To switch based on full domain (excluding subdomains *ie 'www'* and top level do # application.rb module MyApplication class Application < Rails::Application - config.middleware.use 'Apartment::Elevators::Domain' + config.middleware.use Apartment::Elevators::Domain end end ``` @@ -184,7 +184,7 @@ To switch based on full host with a hash to find corresponding tenant name use t # application.rb module MyApplication class Application < Rails::Application - config.middleware.use 'Apartment::Elevators::HostHash', {'example.com' => 'example_tenant'} + config.middleware.use Apartment::Elevators::HostHash, {'example.com' => 'example_tenant'} end end ``` @@ -198,7 +198,7 @@ A Generic Elevator exists that allows you to pass a `Proc` (or anything that res module MyApplication class Application < Rails::Application # Obviously not a contrived example - config.middleware.use 'Apartment::Elevators::Generic', Proc.new { |request| request.host.reverse } + config.middleware.use Apartment::Elevators::Generic, Proc.new { |request| request.host.reverse } end end ``` @@ -231,7 +231,7 @@ end In the examples above, we show the Apartment middleware being appended to the Rack stack with ```ruby -Rails.application.config.middleware.use 'Apartment::Elevators::Subdomain' +Rails.application.config.middleware.use Apartment::Elevators::Subdomain ``` By default, the Subdomain middleware switches into a Tenant based on the subdomain at the beginning of the request, and when the request is finished, it switches back to the "public" Tenant. This happens in the [Generic](https://github.com/influitive/apartment/blob/development/lib/apartment/elevators/generic.rb#L22) elevator, so all elevators that inherit from this elevator will operate as such. diff --git a/lib/generators/apartment/install/templates/apartment.rb b/lib/generators/apartment/install/templates/apartment.rb index 90bf4abc..d7c78ad1 100644 --- a/lib/generators/apartment/install/templates/apartment.rb +++ b/lib/generators/apartment/install/templates/apartment.rb @@ -83,10 +83,10 @@ # Setup a custom Tenant switching middleware. The Proc should return the name of the Tenant that # you want to switch to. -# Rails.application.config.middleware.use 'Apartment::Elevators::Generic', lambda { |request| +# Rails.application.config.middleware.use Apartment::Elevators::Generic, lambda { |request| # request.host.split('.').first # } -# Rails.application.config.middleware.use 'Apartment::Elevators::Domain' -Rails.application.config.middleware.use 'Apartment::Elevators::Subdomain' -# Rails.application.config.middleware.use 'Apartment::Elevators::FirstSubdomain' +# Rails.application.config.middleware.use Apartment::Elevators::Domain +Rails.application.config.middleware.use Apartment::Elevators::Subdomain +# Rails.application.config.middleware.use Apartment::Elevators::FirstSubdomain diff --git a/spec/dummy_engine/config/initializers/apartment.rb b/spec/dummy_engine/config/initializers/apartment.rb index a1367900..611f5322 100644 --- a/spec/dummy_engine/config/initializers/apartment.rb +++ b/spec/dummy_engine/config/initializers/apartment.rb @@ -42,10 +42,10 @@ ## # Elevator Configuration -# Rails.application.config.middleware.use 'Apartment::Elevators::Generic', lambda { |request| +# Rails.application.config.middleware.use Apartment::Elevators::Generic, lambda { |request| # # TODO: supply generic implementation # } -# Rails.application.config.middleware.use 'Apartment::Elevators::Domain' +# Rails.application.config.middleware.use Apartment::Elevators::Domain Rails.application.config.middleware.use Apartment::Elevators::Subdomain From 43e05109269211c8bc67cb0cce863ee8b31a693b Mon Sep 17 00:00:00 2001 From: Fernando Morgenstern Date: Tue, 27 Jun 2017 09:15:55 -0300 Subject: [PATCH 11/91] Keep query cache config after switching databases When use_schemas = false, switching databases would always disable query cache. Even if it was previously enabled. connect_to_new method was changed to check the current configuration and keep it after the new connection is established. --- lib/apartment/adapters/abstract_adapter.rb | 4 + spec/integration/query_caching_spec.rb | 86 ++++++++++++++++------ 2 files changed, 67 insertions(+), 23 deletions(-) diff --git a/lib/apartment/adapters/abstract_adapter.rb b/lib/apartment/adapters/abstract_adapter.rb index 760603d0..18e80df6 100644 --- a/lib/apartment/adapters/abstract_adapter.rb +++ b/lib/apartment/adapters/abstract_adapter.rb @@ -152,8 +152,12 @@ def create_tenant_command(conn, tenant) # @param {String} tenant Database name # def connect_to_new(tenant) + query_cache_enabled = ActiveRecord::Base.connection.query_cache_enabled + Apartment.establish_connection multi_tenantify(tenant) Apartment.connection.active? # call active? to manually check if this connection is valid + + Apartment.connection.enable_query_cache! if query_cache_enabled rescue *rescuable_exceptions => exception Apartment::Tenant.reset if reset_on_connection_exception? raise_connect_error!(tenant, exception) diff --git a/spec/integration/query_caching_spec.rb b/spec/integration/query_caching_spec.rb index 8030bbf3..7bf08996 100644 --- a/spec/integration/query_caching_spec.rb +++ b/spec/integration/query_caching_spec.rb @@ -1,41 +1,81 @@ require 'spec_helper' describe 'query caching' do - let(:db_names) { [db1, db2] } + describe 'when use_schemas = true' do + let(:db_names) { [db1, db2] } - before do - Apartment.configure do |config| - config.excluded_models = ["Company"] - config.tenant_names = lambda{ Company.pluck(:database) } - config.use_schemas = true + before do + Apartment.configure do |config| + config.excluded_models = ["Company"] + config.tenant_names = lambda{ Company.pluck(:database) } + config.use_schemas = true + end + + Apartment::Tenant.reload!(config) + + db_names.each do |db_name| + Apartment::Tenant.create(db_name) + Company.create database: db_name + end + end + + after do + db_names.each{ |db| Apartment::Tenant.drop(db) } + Apartment::Tenant.reset + Company.delete_all + end + + it 'clears the ActiveRecord::QueryCache after switching databases' do + db_names.each do |db_name| + Apartment::Tenant.switch! db_name + User.create! name: db_name + end + + ActiveRecord::Base.connection.enable_query_cache! + + Apartment::Tenant.switch! db_names.first + expect(User.find_by_name(db_names.first).name).to eq(db_names.first) + + Apartment::Tenant.switch! db_names.last + expect(User.find_by_name(db_names.first)).to be_nil end + end - Apartment::Tenant.reload!(config) + describe 'when use_schemas = false' do + let(:db_name) { db1 } + + before do + Apartment.configure do |config| + config.excluded_models = ["Company"] + config.tenant_names = lambda{ Company.pluck(:database) } + config.use_schemas = false + end + + Apartment::Tenant.reload!(config) - db_names.each do |db_name| Apartment::Tenant.create(db_name) Company.create database: db_name end - end - after do - db_names.each{ |db| Apartment::Tenant.drop(db) } - Apartment::Tenant.reset - Company.delete_all - end + after do + # Avoid cannot drop the currently open database. Maybe there is a better way to handle this. + Apartment::Tenant.switch! 'template1' - it 'clears the ActiveRecord::QueryCache after switching databases' do - db_names.each do |db_name| - Apartment::Tenant.switch! db_name - User.create! name: db_name + Apartment::Tenant.drop(db_name) + Apartment::Tenant.reset + Company.delete_all end - ActiveRecord::Base.connection.enable_query_cache! + it "configuration value is kept after switching databases" do + ActiveRecord::Base.connection.enable_query_cache! - Apartment::Tenant.switch! db_names.first - expect(User.find_by_name(db_names.first).name).to eq(db_names.first) + Apartment::Tenant.switch! db_name + expect(Apartment.connection.query_cache_enabled).to be true + + ActiveRecord::Base.connection.disable_query_cache! - Apartment::Tenant.switch! db_names.last - expect(User.find_by_name(db_names.first)).to be_nil + Apartment::Tenant.switch! db_name + expect(Apartment.connection.query_cache_enabled).to be false + end end end From 3b634053803bdc6d26eeffc23f07b2f45df70252 Mon Sep 17 00:00:00 2001 From: meganemura Date: Sun, 18 Jun 2017 23:32:56 +0900 Subject: [PATCH 12/91] Cache bundled gems in Travis CI --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 1f006220..319be006 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,3 +23,4 @@ matrix: - rvm: 2.1.9 gemfile: gemfiles/rails_5_0.gemfile fast_finish: true +cache: bundler From fa3ebf80bb61b0a90d2e8ea0f1c488e622df7247 Mon Sep 17 00:00:00 2001 From: meganemura Date: Fri, 16 Jun 2017 01:06:03 +0900 Subject: [PATCH 13/91] Specify the Rails release to inheriting AR::Migration class --- spec/dummy/db/migrate/20110613152810_create_dummy_models.rb | 3 ++- spec/dummy/db/migrate/20111202022214_create_table_books.rb | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/spec/dummy/db/migrate/20110613152810_create_dummy_models.rb b/spec/dummy/db/migrate/20110613152810_create_dummy_models.rb index ce8375f4..d1f620eb 100644 --- a/spec/dummy/db/migrate/20110613152810_create_dummy_models.rb +++ b/spec/dummy/db/migrate/20110613152810_create_dummy_models.rb @@ -1,4 +1,5 @@ -class CreateDummyModels < ActiveRecord::Migration +migration_class = (ActiveRecord::VERSION::MAJOR >= 5) ? ActiveRecord::Migration[4.2] : ActiveRecord::Migration +class CreateDummyModels < migration_class def self.up create_table :companies do |t| t.boolean :dummy diff --git a/spec/dummy/db/migrate/20111202022214_create_table_books.rb b/spec/dummy/db/migrate/20111202022214_create_table_books.rb index ddaba1ad..e841e2dd 100644 --- a/spec/dummy/db/migrate/20111202022214_create_table_books.rb +++ b/spec/dummy/db/migrate/20111202022214_create_table_books.rb @@ -1,4 +1,5 @@ -class CreateTableBooks < ActiveRecord::Migration +migration_class = (ActiveRecord::VERSION::MAJOR >= 5) ? ActiveRecord::Migration[4.2] : ActiveRecord::Migration +class CreateTableBooks < migration_class def up create_table :books do |t| t.string :name From 50d0f6ac69442eab7bbadaf988986378be067f52 Mon Sep 17 00:00:00 2001 From: meganemura Date: Sat, 17 Jun 2017 01:14:52 +0900 Subject: [PATCH 14/91] Add docker-compose.yml for development easily --- docker-compose.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 docker-compose.yml diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 00000000..937e2b3b --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,14 @@ +version: '2' +services: + postgresql: + image: postgres:9.2 + environment: + POSTGRES_PASSWORD: "" + ports: + - "5432:5432" + mysql: + image: mysql:5.7 + environment: + MYSQL_ALLOW_EMPTY_PASSWORD: "yes" + ports: + - "3306:3306" From ecd5f269cfa28ce4bf91bb9329ea75a9e365906d Mon Sep 17 00:00:00 2001 From: meganemura Date: Sat, 17 Jun 2017 22:08:46 +0900 Subject: [PATCH 15/91] Specify username in spec/dummy/config/database.yml.sample --- spec/dummy/config/database.yml.sample | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spec/dummy/config/database.yml.sample b/spec/dummy/config/database.yml.sample index 80c550c3..afc79694 100644 --- a/spec/dummy/config/database.yml.sample +++ b/spec/dummy/config/database.yml.sample @@ -25,6 +25,7 @@ development: test: adapter: postgresql database: apartment_postgresql_test + username: postgres min_messages: WARNING pool: 5 timeout: 5000 @@ -32,7 +33,8 @@ test: development: adapter: postgresql database: apartment_postgresql_development + username: postgres min_messages: WARNING pool: 5 timeout: 5000 -<% end %> \ No newline at end of file +<% end %> From 57fb6b8153d0af44c5790112c46424d7bcdb0753 Mon Sep 17 00:00:00 2001 From: meganemura Date: Sat, 17 Jun 2017 01:43:13 +0900 Subject: [PATCH 16/91] Respect spec/config/database.yml configurations to connect server on docker container. --- Rakefile | 27 +++++++++++++++++++++++---- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/Rakefile b/Rakefile index 8bb751d0..996e60e0 100644 --- a/Rakefile +++ b/Rakefile @@ -51,7 +51,13 @@ namespace :postgres do desc 'Build the PostgreSQL test databases' task :build_db do - %x{ createdb -E UTF8 #{pg_config['database']} -U#{pg_config['username']} } rescue "test db already exists" + params = [] + params << "-E UTF8" + params << pg_config['database'] + params << "-U#{pg_config['username']}" + params << "-h#{pg_config['host']}" if pg_config['host'] + params << "-p#{pg_config['port']}" if pg_config['port'] + %x{ createdb #{params.join(' ')} } rescue "test db already exists" ActiveRecord::Base.establish_connection pg_config ActiveRecord::Migrator.migrate('spec/dummy/db/migrate') end @@ -59,7 +65,12 @@ namespace :postgres do desc "drop the PostgreSQL test database" task :drop_db do puts "dropping database #{pg_config['database']}" - %x{ dropdb #{pg_config['database']} -U#{pg_config['username']} } + params = [] + params << pg_config['database'] + params << "-U#{pg_config['username']}" + params << "-h#{pg_config['host']}" if pg_config['host'] + params << "-p#{pg_config['port']}" if pg_config['port'] + %x{ dropdb #{params.join(' ')} } end end @@ -70,7 +81,11 @@ namespace :mysql do desc 'Build the MySQL test databases' task :build_db do - %x{ mysqladmin -u #{my_config['username']} --password=#{my_config['password']} create #{my_config['database']} } rescue "test db already exists" + params = [] + params << "-h #{my_config['host']}" if my_config['host'] + params << "-u #{my_config['username']}" if my_config['username'] + params << "-p#{my_config['password']}" if my_config['password'] + %x{ mysqladmin #{params.join(' ')} create #{my_config['database']} } rescue "test db already exists" ActiveRecord::Base.establish_connection my_config ActiveRecord::Migrator.migrate('spec/dummy/db/migrate') end @@ -78,7 +93,11 @@ namespace :mysql do desc "drop the MySQL test database" task :drop_db do puts "dropping database #{my_config['database']}" - %x{ mysqladmin -u #{my_config['username']} --password=#{my_config['password']} drop #{my_config['database']} --force} + params = [] + params << "-h #{my_config['host']}" if my_config['host'] + params << "-u #{my_config['username']}" if my_config['username'] + params << "-p#{my_config['password']}" if my_config['password'] + %x{ mysqladmin #{params.join(' ')} drop #{my_config['database']} --force} end end From d15ba64a0bc5a192e56fda9b76b517dda1068029 Mon Sep 17 00:00:00 2001 From: meganemura Date: Mon, 3 Jul 2017 13:45:57 +0900 Subject: [PATCH 17/91] PostgreSQL < 9.3 does not respect a new search_path for prepared statements --- lib/apartment/adapters/postgresql_adapter.rb | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/lib/apartment/adapters/postgresql_adapter.rb b/lib/apartment/adapters/postgresql_adapter.rb index 544aa334..de972ac7 100644 --- a/lib/apartment/adapters/postgresql_adapter.rb +++ b/lib/apartment/adapters/postgresql_adapter.rb @@ -68,6 +68,13 @@ def connect_to_new(tenant = nil) @current = tenant.to_s Apartment.connection.schema_search_path = full_search_path + # When the PostgreSQL version is < 9.3, + # there is a issue for prepared statement with changing search_path. + # https://www.postgresql.org/docs/9.3/static/sql-prepare.html + if postgresql_version < 90300 + Apartment.connection.clear_cache! + end + rescue *rescuable_exceptions raise TenantNotFound, "One of the following schema(s) is invalid: \"#{tenant}\" #{full_search_path}" end @@ -87,6 +94,12 @@ def full_search_path def persistent_schemas [@current, Apartment.persistent_schemas].flatten end + + def postgresql_version + # ActiveRecord::ConnectionAdapters::PostgreSQLAdapter#postgresql_version is + # public from Rails 5.0. + Apartment.connection.send(:postgresql_version) + end end # Another Adapter for Postgresql when using schemas and SQL From 7fa0400c486eedb6d8fda3eeec726551346e190c Mon Sep 17 00:00:00 2001 From: meganemura Date: Mon, 3 Jul 2017 16:33:49 +0900 Subject: [PATCH 18/91] Fix a spec about Apartment::Tenant using postgresql --- spec/tenant_spec.rb | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spec/tenant_spec.rb b/spec/tenant_spec.rb index 68e36ab7..d2f409e6 100644 --- a/spec/tenant_spec.rb +++ b/spec/tenant_spec.rb @@ -59,8 +59,7 @@ describe "#adapter" do it "should load postgresql adapter" do - subject.adapter - expect(Apartment::Adapters::PostgresqlAdapter).to be_a(Class) + expect(subject.adapter).to be_a(Apartment::Adapters::PostgresqlSchemaAdapter) end it "raises exception with invalid adapter specified" do From b70f2b7dc873f7af572483084b06d614a2bb2ca2 Mon Sep 17 00:00:00 2001 From: meganemura Date: Mon, 3 Jul 2017 22:41:09 +0900 Subject: [PATCH 19/91] Fix rarely failing specs caused by retaining per model connection --- spec/adapters/mysql2_adapter_spec.rb | 8 ++++++++ spec/examples/connection_adapter_examples.rb | 8 ++++++++ spec/examples/schema_adapter_examples.rb | 8 ++++++++ spec/tenant_spec.rb | 8 ++++++++ 4 files changed, 32 insertions(+) diff --git a/spec/adapters/mysql2_adapter_spec.rb b/spec/adapters/mysql2_adapter_spec.rb index c3037a3a..78f7b589 100644 --- a/spec/adapters/mysql2_adapter_spec.rb +++ b/spec/adapters/mysql2_adapter_spec.rb @@ -32,6 +32,14 @@ def tenant_names end end + after do + # Apartment::Tenant.init creates per model connection. + # Remove the connection after testing not to unintentionally keep the connection across tests. + Apartment.excluded_models.each do |excluded_model| + excluded_model.constantize.remove_connection + end + end + it "should process model exclusions" do Apartment::Tenant.init diff --git a/spec/examples/connection_adapter_examples.rb b/spec/examples/connection_adapter_examples.rb index 1d2b9aa2..a7d4ac9b 100644 --- a/spec/examples/connection_adapter_examples.rb +++ b/spec/examples/connection_adapter_examples.rb @@ -6,6 +6,14 @@ let(:default_tenant){ subject.switch{ ActiveRecord::Base.connection.current_database } } describe "#init" do + after do + # Apartment::Tenant.init creates per model connection. + # Remove the connection after testing not to unintentionally keep the connection across tests. + Apartment.excluded_models.each do |excluded_model| + excluded_model.constantize.remove_connection + end + end + it "should process model exclusions" do Apartment.configure do |config| config.excluded_models = ["Company"] diff --git a/spec/examples/schema_adapter_examples.rb b/spec/examples/schema_adapter_examples.rb index 1800e86d..5eed5091 100644 --- a/spec/examples/schema_adapter_examples.rb +++ b/spec/examples/schema_adapter_examples.rb @@ -15,6 +15,14 @@ end end + after do + # Apartment::Tenant.init creates per model connection. + # Remove the connection after testing not to unintentionally keep the connection across tests. + Apartment.excluded_models.each do |excluded_model| + excluded_model.constantize.remove_connection + end + end + it "should process model exclusions" do Apartment::Tenant.init diff --git a/spec/tenant_spec.rb b/spec/tenant_spec.rb index d2f409e6..12b5f4f3 100644 --- a/spec/tenant_spec.rb +++ b/spec/tenant_spec.rb @@ -136,6 +136,14 @@ subject.init end + after do + # Apartment::Tenant.init creates per model connection. + # Remove the connection after testing not to unintentionally keep the connection across tests. + Apartment.excluded_models.each do |excluded_model| + excluded_model.constantize.remove_connection + end + end + it "should create excluded models in public schema" do subject.reset # ensure we're on public schema count = Company.count + x.times{ Company.create } From 705bdfc2c5e6e13505f232e936fc65bb7f1556da Mon Sep 17 00:00:00 2001 From: meganemura Date: Tue, 4 Jul 2017 00:18:46 +0900 Subject: [PATCH 20/91] Specify nokogiri version only if ruby < 2.1 for bundling capybara (the development dependency) depends on xpath, and xpath depends on nokogiri. --- apartment.gemspec | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apartment.gemspec b/apartment.gemspec index dbb1a27e..038d1f62 100644 --- a/apartment.gemspec +++ b/apartment.gemspec @@ -41,4 +41,9 @@ Gem::Specification.new do |s| s.add_development_dependency 'pg', '>= 0.11.0' s.add_development_dependency 'sqlite3' end + + if RUBY_VERSION < '2.1.0' + # capybara depends on xpath depends on nokogiri + s.add_development_dependency 'nokogiri', '< 1.7.0' + end end From d19eb1c9eac14fedbe765b83c5552c735365841e Mon Sep 17 00:00:00 2001 From: meganemura Date: Tue, 4 Jul 2017 10:49:55 +0900 Subject: [PATCH 21/91] Remove a bitdeli badge [ci skip] We could not reach to the bitdeli.com currently. --- README.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/README.md b/README.md index c5f2ccce..cc3258de 100644 --- a/README.md +++ b/README.md @@ -498,5 +498,3 @@ Has been removed. See [apartment-sidekiq](https://github.com/influitive/apartmen ## License Apartment is released under the [MIT License](http://www.opensource.org/licenses/MIT). - -[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/influitive/apartment/trend.png)](https://bitdeli.com/free "Bitdeli Badge") From ea7fb05d3026415076fa6db2ea26e9b394ee11a4 Mon Sep 17 00:00:00 2001 From: meganemura Date: Wed, 5 Jul 2017 05:36:58 +0900 Subject: [PATCH 22/91] Use JRuby (>= 9.1.9.0) to run Rails (>= 4.1) Exclude Rails (>= 5.0) with JRuby, because of the activerecord-jdbc-adapter is unstable for Rails 5.x. --- .travis.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.travis.yml b/.travis.yml index 319be006..ce0de033 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,6 +5,7 @@ rvm: - 2.2.4 - 2.3.1 - jruby-9.0.5.0 + - jruby-9.1.9.0 gemfile: - gemfiles/rails_4_0.gemfile - gemfiles/rails_4_1.gemfile @@ -22,5 +23,11 @@ matrix: gemfile: gemfiles/rails_5_0.gemfile - rvm: 2.1.9 gemfile: gemfiles/rails_5_0.gemfile + - rvm: jruby-9.0.5.0 + gemfile: gemfiles/rails_4_2.gemfile + - rvm: jruby-9.0.5.0 + gemfile: gemfiles/rails_5_0.gemfile + - rvm: jruby-9.1.9.0 + gemfile: gemfiles/rails_5_0.gemfile fast_finish: true cache: bundler From 1633f600985686d5e38a6243f5e91ee3f6088501 Mon Sep 17 00:00:00 2001 From: meganemura Date: Tue, 4 Jul 2017 15:37:41 +0900 Subject: [PATCH 23/91] Use svg badges Reference: - https://docs.codeclimate.com/docs/overview#section-badges - https://codeclimate.com/github/influitive/apartment/badges - https://docs.travis-ci.com/user/status-images/ --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c5f2ccce..67271362 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # Apartment -[![Code Climate](https://codeclimate.com/github/influitive/apartment.png)](https://codeclimate.com/github/influitive/apartment) -[![Build Status](https://secure.travis-ci.org/influitive/apartment.png?branch=development)](http://travis-ci.org/influitive/apartment) +[![Code Climate](https://codeclimate.com/github/influitive/apartment/badges/gpa.svg)](https://codeclimate.com/github/influitive/apartment) +[![Build Status](https://travis-ci.org/influitive/apartment.svg?branch=development)](https://travis-ci.org/influitive/apartment) *Multitenancy for Rails and ActiveRecord* From c9664e2bb4a5a4196c6439411670802bafe9297f Mon Sep 17 00:00:00 2001 From: meganemura Date: Sun, 2 Jul 2017 16:59:06 +0900 Subject: [PATCH 24/91] Fix typo (respresented -> represented) git grep -l respresented | xargs sed -e "s/respresented/represented/g" [ci skip] --- README.md | 2 +- lib/generators/apartment/install/templates/apartment.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d416008b..67abca94 100644 --- a/README.md +++ b/README.md @@ -405,7 +405,7 @@ Happy to accept PR's on the matter. #### Alternative: Creating new schemas by using raw SQL dumps -Apartment can be forced to use raw SQL dumps insted of `schema.rb` for creating new schemas. Use this when you are using some extra features in postgres that can't be respresented in `schema.rb`, like materialized views etc. +Apartment can be forced to use raw SQL dumps insted of `schema.rb` for creating new schemas. Use this when you are using some extra features in postgres that can't be represented in `schema.rb`, like materialized views etc. This only applies while using postgres adapter and `config.use_schemas` is set to `true`. (Note: this option doesn't use `db/structure.sql`, it creates SQL dump by executing `pg_dump`) diff --git a/lib/generators/apartment/install/templates/apartment.rb b/lib/generators/apartment/install/templates/apartment.rb index d7c78ad1..e00953b4 100644 --- a/lib/generators/apartment/install/templates/apartment.rb +++ b/lib/generators/apartment/install/templates/apartment.rb @@ -58,7 +58,7 @@ # config.use_schemas = true # Apartment can be forced to use raw SQL dumps instead of schema.rb for creating new schemas. - # Use this when you are using some extra features in PostgreSQL that can't be respresented in + # Use this when you are using some extra features in PostgreSQL that can't be represented in # schema.rb, like materialized views etc. (only applies with use_schemas set to true). # (Note: this option doesn't use db/structure.sql, it creates SQL dump by executing pg_dump) # From e344f953a3f4332de7ec6f5a096ad74868efbb7f Mon Sep 17 00:00:00 2001 From: meganemura Date: Thu, 6 Jul 2017 17:31:50 +0900 Subject: [PATCH 25/91] Support Rails 5.1 (#453) * Add Rails 5.1 to CI * Exclude builds for Rails 5.1 with JRuby Because of the activerecord-jdbc-adapter compatibility. * `#schema_exists?` uses `#quote` method from Rails 5.1.0 Apartment currently allows numeric `tenant` argument for `PostgresqlSchemaAdapter#connect_to_new`. Rails (from 5.1.0) detects type of argument for `ActiveRecord::ConnectionAdapters::PostgresqlSchemaAdapter#schema_exists?`, therefore numeric argument is not quoted. So we have to convert it to string in apartment to treat it as schema name. https://github.com/rails/rails/commit/2f8d22f292a322a54812c5be5bd2fa5ee119d411#diff-6e377cf054cb4121e6f207bd7c14b492R62 https://github.com/rails/rails/blob/310918f6a194bf5752fe1025930881756f5d8a8e/activerecord/lib/active_record/connection_adapters/abstract/quoting.rb#L183 * Make models pristine after loading the dummy app Loading dummy applications affects table_name of each excluded models defined in `spec/dummy/config/initializers/apartment.rb`. To make them pristine, we need to execute some code after loading. --- .travis.yml | 9 +++++++++ Appraisals | 17 ++++++++--------- Rakefile | 2 +- gemfiles/rails_4_0.gemfile | 2 +- gemfiles/rails_4_1.gemfile | 2 +- gemfiles/rails_4_2.gemfile | 2 +- gemfiles/rails_5_0.gemfile | 2 +- gemfiles/rails_5_1.gemfile | 12 ++++++++++++ lib/apartment/adapters/postgresql_adapter.rb | 2 +- spec/spec_helper.rb | 12 ++++++++++++ 10 files changed, 47 insertions(+), 15 deletions(-) create mode 100644 gemfiles/rails_5_1.gemfile diff --git a/.travis.yml b/.travis.yml index ce0de033..1ceb867e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ gemfile: - gemfiles/rails_4_1.gemfile - gemfiles/rails_4_2.gemfile - gemfiles/rails_5_0.gemfile + - gemfiles/rails_5_1.gemfile bundler_args: --without local before_install: - gem install bundler -v '> 1.5.0' @@ -21,13 +22,21 @@ matrix: exclude: - rvm: 2.0.0 gemfile: gemfiles/rails_5_0.gemfile + - rvm: 2.0.0 + gemfile: gemfiles/rails_5_1.gemfile - rvm: 2.1.9 gemfile: gemfiles/rails_5_0.gemfile + - rvm: 2.1.9 + gemfile: gemfiles/rails_5_1.gemfile - rvm: jruby-9.0.5.0 gemfile: gemfiles/rails_4_2.gemfile - rvm: jruby-9.0.5.0 gemfile: gemfiles/rails_5_0.gemfile + - rvm: jruby-9.0.5.0 + gemfile: gemfiles/rails_5_1.gemfile - rvm: jruby-9.1.9.0 gemfile: gemfiles/rails_5_0.gemfile + - rvm: jruby-9.1.9.0 + gemfile: gemfiles/rails_5_1.gemfile fast_finish: true cache: bundler diff --git a/Appraisals b/Appraisals index f9236f80..76f642b2 100644 --- a/Appraisals +++ b/Appraisals @@ -1,20 +1,19 @@ -appraise "rails-3-2" do - gem "rails", "~> 3.2" - gem "test-unit", "~> 3.0" -end - appraise "rails-4-0" do - gem "rails", "~> 4.0" + gem "rails", "~> 4.0.0" end appraise "rails-4-1" do - gem "rails", "~> 4.1" + gem "rails", "~> 4.1.0" end appraise "rails-4-2" do - gem "rails", "~> 4.2" + gem "rails", "~> 4.2.0" end appraise "rails-5-0" do - gem "rails", "~> 5.0" + gem "rails", "~> 5.0.0" +end + +appraise "rails-5-1" do + gem "rails", "5.1.1" end diff --git a/Rakefile b/Rakefile index 996e60e0..7c1ee70c 100644 --- a/Rakefile +++ b/Rakefile @@ -9,7 +9,7 @@ require "rspec/core/rake_task" RSpec::Core::RakeTask.new(:spec => %w{ db:copy_credentials db:test:prepare }) do |spec| spec.pattern = "spec/**/*_spec.rb" - # spec.rspec_opts = '--order rand:16996' + # spec.rspec_opts = '--order rand:47078' end namespace :spec do diff --git a/gemfiles/rails_4_0.gemfile b/gemfiles/rails_4_0.gemfile index a7a4f7d7..e62f28d3 100644 --- a/gemfiles/rails_4_0.gemfile +++ b/gemfiles/rails_4_0.gemfile @@ -9,4 +9,4 @@ group :local do gem "guard-rspec", "~> 4.2" end -gemspec :path => "../" +gemspec path: "../" diff --git a/gemfiles/rails_4_1.gemfile b/gemfiles/rails_4_1.gemfile index 9eb9a07a..0bb23143 100644 --- a/gemfiles/rails_4_1.gemfile +++ b/gemfiles/rails_4_1.gemfile @@ -9,4 +9,4 @@ group :local do gem "guard-rspec", "~> 4.2" end -gemspec :path => "../" +gemspec path: "../" diff --git a/gemfiles/rails_4_2.gemfile b/gemfiles/rails_4_2.gemfile index 43d74179..e1520f82 100644 --- a/gemfiles/rails_4_2.gemfile +++ b/gemfiles/rails_4_2.gemfile @@ -9,4 +9,4 @@ group :local do gem "guard-rspec", "~> 4.2" end -gemspec :path => "../" +gemspec path: "../" diff --git a/gemfiles/rails_5_0.gemfile b/gemfiles/rails_5_0.gemfile index 5467e3da..290afb96 100644 --- a/gemfiles/rails_5_0.gemfile +++ b/gemfiles/rails_5_0.gemfile @@ -9,4 +9,4 @@ group :local do gem "guard-rspec", "~> 4.2" end -gemspec :path => "../" +gemspec path: "../" diff --git a/gemfiles/rails_5_1.gemfile b/gemfiles/rails_5_1.gemfile new file mode 100644 index 00000000..c3efd90f --- /dev/null +++ b/gemfiles/rails_5_1.gemfile @@ -0,0 +1,12 @@ +# This file was generated by Appraisal + +source "http://rubygems.org" + +gem "rails", "5.1.1" + +group :local do + gem "pry" + gem "guard-rspec", "~> 4.2" +end + +gemspec path: "../" diff --git a/lib/apartment/adapters/postgresql_adapter.rb b/lib/apartment/adapters/postgresql_adapter.rb index de972ac7..c912f61d 100644 --- a/lib/apartment/adapters/postgresql_adapter.rb +++ b/lib/apartment/adapters/postgresql_adapter.rb @@ -63,7 +63,7 @@ def drop_command(conn, tenant) # def connect_to_new(tenant = nil) return reset if tenant.nil? - raise ActiveRecord::StatementInvalid.new("Could not find schema #{tenant}") unless Apartment.connection.schema_exists? tenant + raise ActiveRecord::StatementInvalid.new("Could not find schema #{tenant}") unless Apartment.connection.schema_exists?(tenant.to_s) @current = tenant.to_s Apartment.connection.schema_search_path = full_search_path diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index e63635cb..73cca8f4 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -4,6 +4,18 @@ ENV["RAILS_ENV"] = "test" require File.expand_path("../dummy/config/environment.rb", __FILE__) + +# Loading dummy applications affects table_name of each excluded models +# defined in `spec/dummy/config/initializers/apartment.rb`. +# To make them pristine, we need to execute below lines. +Apartment.excluded_models.each do |model| + klass = model.constantize + + Apartment.connection_class.remove_connection(klass) + klass.clear_all_connections! + klass.reset_table_name +end + require "rspec/rails" require 'capybara/rspec' require 'capybara/rails' From eafb3ef358cce5beb4bf24ca20ab2842be164b77 Mon Sep 17 00:00:00 2001 From: meganemura Date: Wed, 19 Jul 2017 17:26:19 +0900 Subject: [PATCH 26/91] Test against Ruby head and Rails master (#457) Test against Ruby head and Rails master --- .travis.yml | 22 ++++++++++++++++++++++ Appraisals | 4 ++++ gemfiles/rails_master.gemfile | 12 ++++++++++++ 3 files changed, 38 insertions(+) create mode 100644 gemfiles/rails_master.gemfile diff --git a/.travis.yml b/.travis.yml index 1ceb867e..c4f21b05 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,8 @@ rvm: - 2.1.9 - 2.2.4 - 2.3.1 + - 2.4.1 + - ruby-head - jruby-9.0.5.0 - jruby-9.1.9.0 gemfile: @@ -12,6 +14,7 @@ gemfile: - gemfiles/rails_4_2.gemfile - gemfiles/rails_5_0.gemfile - gemfiles/rails_5_1.gemfile + - gemfiles/rails_master.gemfile bundler_args: --without local before_install: - gem install bundler -v '> 1.5.0' @@ -19,24 +22,43 @@ env: RUBY_GC_MALLOC_LIMIT: 90000000 RUBY_FREE_MIN: 200000 matrix: + allow_failures: + - rvm: ruby-head + - gemfile: gemfiles/rails_master.gemfile exclude: - rvm: 2.0.0 gemfile: gemfiles/rails_5_0.gemfile - rvm: 2.0.0 gemfile: gemfiles/rails_5_1.gemfile + - rvm: 2.0.0 + gemfile: gemfiles/rails_master.gemfile - rvm: 2.1.9 gemfile: gemfiles/rails_5_0.gemfile - rvm: 2.1.9 gemfile: gemfiles/rails_5_1.gemfile + - rvm: 2.1.9 + gemfile: gemfiles/rails_master.gemfile + - rvm: 2.4.1 + gemfile: gemfiles/rails_4_0.gemfile + - rvm: 2.4.1 + gemfile: gemfiles/rails_4_1.gemfile + - rvm: ruby-head + gemfile: gemfiles/rails_4_0.gemfile + - rvm: ruby-head + gemfile: gemfiles/rails_4_1.gemfile - rvm: jruby-9.0.5.0 gemfile: gemfiles/rails_4_2.gemfile - rvm: jruby-9.0.5.0 gemfile: gemfiles/rails_5_0.gemfile - rvm: jruby-9.0.5.0 gemfile: gemfiles/rails_5_1.gemfile + - rvm: jruby-9.0.5.0 + gemfile: gemfiles/rails_master.gemfile - rvm: jruby-9.1.9.0 gemfile: gemfiles/rails_5_0.gemfile - rvm: jruby-9.1.9.0 gemfile: gemfiles/rails_5_1.gemfile + - rvm: jruby-9.1.9.0 + gemfile: gemfiles/rails_master.gemfile fast_finish: true cache: bundler diff --git a/Appraisals b/Appraisals index 76f642b2..a5a1765a 100644 --- a/Appraisals +++ b/Appraisals @@ -17,3 +17,7 @@ end appraise "rails-5-1" do gem "rails", "5.1.1" end + +appraise "rails-master" do + gem "rails", git: 'https://github.com/rails/rails.git' +end diff --git a/gemfiles/rails_master.gemfile b/gemfiles/rails_master.gemfile new file mode 100644 index 00000000..27f6952d --- /dev/null +++ b/gemfiles/rails_master.gemfile @@ -0,0 +1,12 @@ +# This file was generated by Appraisal + +source "http://rubygems.org" + +gem "rails", git: "https://github.com/rails/rails.git" + +group :local do + gem "pry" + gem "guard-rspec", "~> 4.2" +end + +gemspec path: "../" From 163961baad5a661ec1a48c84a72c71ea1dcf3be6 Mon Sep 17 00:00:00 2001 From: Omer Gertel Date: Wed, 19 Jul 2017 13:59:37 +0300 Subject: [PATCH 27/91] Fix create for libpq 9.6 and older db (#418) When Postgres library (libpq) 9.6 is installed on the server, `pg_dump` adds an SQL command to SET `idle_in_transaction_session_timeout`, which is new to 9.6, so it fails when running against older versions of the Postgres database. I've added the parameter to the blacklist. --- lib/apartment/adapters/postgresql_adapter.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/apartment/adapters/postgresql_adapter.rb b/lib/apartment/adapters/postgresql_adapter.rb index c912f61d..777446cb 100644 --- a/lib/apartment/adapters/postgresql_adapter.rb +++ b/lib/apartment/adapters/postgresql_adapter.rb @@ -107,7 +107,8 @@ class PostgresqlSchemaFromSqlAdapter < PostgresqlSchemaAdapter PSQL_DUMP_BLACKLISTED_STATEMENTS= [ /SET search_path/i, # overridden later - /SET lock_timeout/i # new in postgresql 9.3 + /SET lock_timeout/i, # new in postgresql 9.3 + /SET idle_in_transaction_session_timeout/i, # new in postgresql 9.6 ] def import_database_schema From 0c800840eb1eb517ea24e12214e080b92b91a5c6 Mon Sep 17 00:00:00 2001 From: meganemura Date: Thu, 6 Jul 2017 16:50:48 +0900 Subject: [PATCH 28/91] Provide a specific error to `raise_error` matcher https://travis-ci.org/influitive/apartment/jobs/255212791#L703 --- spec/tenant_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/tenant_spec.rb b/spec/tenant_spec.rb index 12b5f4f3..261185a6 100644 --- a/spec/tenant_spec.rb +++ b/spec/tenant_spec.rb @@ -67,7 +67,7 @@ expect { Apartment::Tenant.adapter - }.to raise_error + }.to raise_error(RuntimeError) end context "threadsafety" do From 0b5a9ffda2b04661a01c6423a4544ab73c135db6 Mon Sep 17 00:00:00 2001 From: meganemura Date: Thu, 20 Jul 2017 18:28:47 +0900 Subject: [PATCH 29/91] Replace deprecated constants https://travis-ci.org/influitive/apartment/jobs/255212791#L437-L438 --- lib/apartment/adapters/postgresql_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/apartment/adapters/postgresql_adapter.rb b/lib/apartment/adapters/postgresql_adapter.rb index 777446cb..c7fcbc25 100644 --- a/lib/apartment/adapters/postgresql_adapter.rb +++ b/lib/apartment/adapters/postgresql_adapter.rb @@ -18,7 +18,7 @@ class PostgresqlAdapter < AbstractAdapter private def rescue_from - PGError + PG::Error end end From 31285d4e624a2f8e55532c6f2eb30da2eb0f6ff0 Mon Sep 17 00:00:00 2001 From: meganemura Date: Thu, 20 Jul 2017 18:31:52 +0900 Subject: [PATCH 30/91] Repalce should matchers with expect matchers https://travis-ci.org/influitive/apartment/jobs/255212791#L791 --- spec/unit/elevators/subdomain_spec.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spec/unit/elevators/subdomain_spec.rb b/spec/unit/elevators/subdomain_spec.rb index cdc9a3db..1df11466 100644 --- a/spec/unit/elevators/subdomain_spec.rb +++ b/spec/unit/elevators/subdomain_spec.rb @@ -33,26 +33,26 @@ context "assuming two subdomains" do it "should parse two subdomains in the two level domain" do request = ActionDispatch::Request.new('HTTP_HOST' => 'foo.xyz.bar.com') - elevator.parse_tenant_name(request).should == "foo" + expect(elevator.parse_tenant_name(request)).to eq("foo") end it "should parse two subdomains in the third level domain" do request = ActionDispatch::Request.new('HTTP_HOST' => 'foo.xyz.bar.co.uk') - elevator.parse_tenant_name(request).should == "foo" + expect(elevator.parse_tenant_name(request)).to eq("foo") end end context "assuming localhost" do it "should return nil for localhost" do request = ActionDispatch::Request.new('HTTP_HOST' => 'localhost') - elevator.parse_tenant_name(request).should be_nil + expect(elevator.parse_tenant_name(request)).to be_nil end end context "assuming ip address" do it "should return nil for an ip address" do request = ActionDispatch::Request.new('HTTP_HOST' => '127.0.0.1') - elevator.parse_tenant_name(request).should be_nil + expect(elevator.parse_tenant_name(request)).to be_nil end end end From 33cc016c87d0ce83fa1ac8982196d492a3373f05 Mon Sep 17 00:00:00 2001 From: meganemura Date: Thu, 20 Jul 2017 18:44:13 +0900 Subject: [PATCH 31/91] Use RUBY_GC_HEAP_FREE_SLOTS instead of obsolete RUBY_FREE_MIN https://docs.ruby-lang.org/en/2.4.0/NEWS-2_1_0.html https://travis-ci.org/influitive/apartment/jobs/255212791#L311 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index c4f21b05..703ee7a6 100644 --- a/.travis.yml +++ b/.travis.yml @@ -20,7 +20,7 @@ before_install: - gem install bundler -v '> 1.5.0' env: RUBY_GC_MALLOC_LIMIT: 90000000 - RUBY_FREE_MIN: 200000 + RUBY_GC_HEAP_FREE_SLOTS: 200000 matrix: allow_failures: - rvm: ruby-head From ccf99cee6ce109461e80b3e81fc3c6e0e6cdf433 Mon Sep 17 00:00:00 2001 From: Alex Pooley Date: Tue, 21 Feb 2017 18:55:19 -0500 Subject: [PATCH 32/91] Kernel#quietly was deprecated in Rails 4.2 --- lib/tasks/apartment.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/apartment.rake b/lib/tasks/apartment.rake index 3c70df94..844c1550 100644 --- a/lib/tasks/apartment.rake +++ b/lib/tasks/apartment.rake @@ -7,7 +7,7 @@ apartment_namespace = namespace :apartment do tenants.each do |tenant| begin puts("Creating #{tenant} tenant") - quietly { Apartment::Tenant.create(tenant) } + Apartment::Tenant.create(tenant) rescue Apartment::TenantExists => e puts e.message end From 8102fb33da624bb16870c5ce38773771365abbbd Mon Sep 17 00:00:00 2001 From: meganemura Date: Thu, 20 Jul 2017 01:26:26 +0900 Subject: [PATCH 33/91] Removed task dependency --- lib/tasks/apartment.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/tasks/apartment.rake b/lib/tasks/apartment.rake index 844c1550..610d67f5 100644 --- a/lib/tasks/apartment.rake +++ b/lib/tasks/apartment.rake @@ -3,7 +3,7 @@ require 'apartment/migrator' apartment_namespace = namespace :apartment do desc "Create all tenants" - task create: 'db:migrate' do + task :create do tenants.each do |tenant| begin puts("Creating #{tenant} tenant") From b27141268a4f037c8564487ebe8b3e85fee2d3e6 Mon Sep 17 00:00:00 2001 From: meganemura Date: Thu, 20 Jul 2017 23:27:16 +0900 Subject: [PATCH 34/91] Add a gem version badge to README.md https://badge.fury.io/for/rb/apartment --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 67abca94..7c5ee7e5 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,6 @@ # Apartment +[![Gem Version](https://badge.fury.io/rb/apartment.svg)](https://badge.fury.io/rb/apartment) [![Code Climate](https://codeclimate.com/github/influitive/apartment/badges/gpa.svg)](https://codeclimate.com/github/influitive/apartment) [![Build Status](https://travis-ci.org/influitive/apartment.svg?branch=development)](https://travis-ci.org/influitive/apartment) From a2feed6654cb034810dd63db1f2cc1ea66d24247 Mon Sep 17 00:00:00 2001 From: meganemura Date: Thu, 20 Jul 2017 23:28:03 +0900 Subject: [PATCH 35/91] Remove a trailing space --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7c5ee7e5..fdabe803 100644 --- a/README.md +++ b/README.md @@ -235,7 +235,7 @@ In the examples above, we show the Apartment middleware being appended to the Ra Rails.application.config.middleware.use Apartment::Elevators::Subdomain ``` -By default, the Subdomain middleware switches into a Tenant based on the subdomain at the beginning of the request, and when the request is finished, it switches back to the "public" Tenant. This happens in the [Generic](https://github.com/influitive/apartment/blob/development/lib/apartment/elevators/generic.rb#L22) elevator, so all elevators that inherit from this elevator will operate as such. +By default, the Subdomain middleware switches into a Tenant based on the subdomain at the beginning of the request, and when the request is finished, it switches back to the "public" Tenant. This happens in the [Generic](https://github.com/influitive/apartment/blob/development/lib/apartment/elevators/generic.rb#L22) elevator, so all elevators that inherit from this elevator will operate as such. It's also good to note that Apartment switches back to the "public" tenant any time an error is raised in your application. From 8cd4d60b4a104e3ebfc28a162d4b69498ba970df Mon Sep 17 00:00:00 2001 From: meganemura Date: Fri, 21 Jul 2017 11:57:58 +0900 Subject: [PATCH 36/91] Generated initializers/apartment.rb is misguided `Apartment.use_schemas` is also used for configuring mysql2 adapter. --- .../apartment/install/templates/apartment.rb | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/lib/generators/apartment/install/templates/apartment.rb b/lib/generators/apartment/install/templates/apartment.rb index e00953b4..e8c6438e 100644 --- a/lib/generators/apartment/install/templates/apartment.rb +++ b/lib/generators/apartment/install/templates/apartment.rb @@ -49,14 +49,19 @@ # config.tenant_names = lambda { ToDo_Tenant_Or_User_Model.pluck :database } + # PostgreSQL: + # Specifies whether to use PostgreSQL schemas or create a new database per Tenant. + # + # MySQL: + # Specifies whether to switch databases by using `use` statement or re-establish connection. # - # ==> PostgreSQL only options - - # Specifies whether to use PostgreSQL schemas or create a new database per Tenant. # The default behaviour is true. # # config.use_schemas = true + # + # ==> PostgreSQL only options + # Apartment can be forced to use raw SQL dumps instead of schema.rb for creating new schemas. # Use this when you are using some extra features in PostgreSQL that can't be represented in # schema.rb, like materialized views etc. (only applies with use_schemas set to true). From e517fe70ea23a299b565e0ac762e56ea55938978 Mon Sep 17 00:00:00 2001 From: meganemura Date: Fri, 21 Jul 2017 00:29:13 +0900 Subject: [PATCH 37/91] Raise FileNotFound when apartment cannot find the file to be loaded --- lib/apartment.rb | 3 +++ lib/apartment/adapters/abstract_adapter.rb | 12 +++++++----- spec/examples/generic_adapter_examples.rb | 16 ++++++++++++++++ 3 files changed, 26 insertions(+), 5 deletions(-) diff --git a/lib/apartment.rb b/lib/apartment.rb index 38ea5188..6cd21585 100644 --- a/lib/apartment.rb +++ b/lib/apartment.rb @@ -99,6 +99,9 @@ def extract_tenant_config # Raised when apartment cannot find the adapter specified in config/database.yml AdapterNotFound = Class.new(ApartmentError) + # Raised when apartment cannot find the file to be loaded + FileNotFound = Class.new(ApartmentError) + # Tenant specified is unknown TenantNotFound = Class.new(ApartmentError) diff --git a/lib/apartment/adapters/abstract_adapter.rb b/lib/apartment/adapters/abstract_adapter.rb index 18e80df6..00306e39 100644 --- a/lib/apartment/adapters/abstract_adapter.rb +++ b/lib/apartment/adapters/abstract_adapter.rb @@ -116,7 +116,7 @@ def reset # def seed_data # Don't log the output of seeding the db - silence_warnings{ load_or_abort(Apartment.seed_data_file) } if Apartment.seed_data_file + silence_warnings{ load_or_raise(Apartment.seed_data_file) } if Apartment.seed_data_file end alias_method :seed, :seed_data @@ -187,7 +187,7 @@ def environmentify(tenant) def import_database_schema ActiveRecord::Schema.verbose = false # do not log schema load output. - load_or_abort(Apartment.database_schema_file) if Apartment.database_schema_file + load_or_raise(Apartment.database_schema_file) if Apartment.database_schema_file end # Return a new config that is multi-tenanted @@ -206,15 +206,17 @@ def multi_tenantify_with_tenant_db_name(config, tenant) config[:database] = environmentify(tenant) end - # Load a file or abort if it doesn't exists + # Load a file or raise error if it doesn't exists # - def load_or_abort(file) + def load_or_raise(file) if File.exists?(file) load(file) else - abort %{#{file} doesn't exist yet} + raise FileNotFound, "#{file} doesn't exist yet" end end + # Backward compatibility + alias_method :load_or_abort, :load_or_raise # Exceptions to rescue from on db operations # diff --git a/spec/examples/generic_adapter_examples.rb b/spec/examples/generic_adapter_examples.rb index d77166ba..6c1b32ac 100644 --- a/spec/examples/generic_adapter_examples.rb +++ b/spec/examples/generic_adapter_examples.rb @@ -58,6 +58,22 @@ subject.switch(db2){ expect(User.count).to eq(@count + 1) } end + + it "should raise error when the schema.rb is missing unless Apartment.use_sql is set to true" do + next if Apartment.use_sql + + subject.drop(db1) + begin + Dir.mktmpdir do |tmpdir| + Apartment.database_schema_file = "#{tmpdir}/schema.rb" + expect { + subject.create(db1) + }.to raise_error(Apartment::FileNotFound) + end + ensure + Apartment.remove_instance_variable(:@database_schema_file) + end + end end describe "#drop" do From e70887a7dacca2fbf0f36cc555fa844d1efe274e Mon Sep 17 00:00:00 2001 From: Mike Campbell Date: Wed, 26 Jul 2017 10:55:28 +0100 Subject: [PATCH 38/91] Release 2.0.0 --- HISTORY.md | 18 ++++++++++++++++++ lib/apartment/version.rb | 2 +- 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 5a8907f6..6f0e8266 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,21 @@ +# 2.0.0 + * July 26, 2017 + + - Raise FileNotFound rather than abort when loading files [meganemura] + - Add 5.1 support with fixes for deprecations [meganemura] + - Fix tests for 5.x and a host of dev-friendly improvements [meganemura] + - Keep query cache config after switching databases [fernandomm] + - Pass constants not strings to middleware stack (Rails 5) [tzabaman] + - Remove deprecations from 1.0.0 [caironoleto] + - Replace `tld_length` configuration option with PublicSuffix gem for the + subdomain elevator [humancopy] + - Pass full config to create_database to allow :encoding/:collation/etc + [kakipo] + - Don't retain a connection during initialization [mikecmpbll] + - Fix database name escaping in drop_command [mikecmpbll] + - Skip initialization for assets:clean and assets:precompile tasks + [frank-west-iii] + # 1.2.0 * July 28, 2016 diff --git a/lib/apartment/version.rb b/lib/apartment/version.rb index 9b4808b2..aedfbb55 100644 --- a/lib/apartment/version.rb +++ b/lib/apartment/version.rb @@ -1,3 +1,3 @@ module Apartment - VERSION = "1.2.0" + VERSION = "2.0.0" end From 6f074aba4aa5b5caa94c0ebf78d03ec4bad8a0cc Mon Sep 17 00:00:00 2001 From: Yuki Masutomi Date: Wed, 20 Sep 2017 20:28:11 +0900 Subject: [PATCH 39/91] enhance db:drop task --- lib/apartment/tasks/enhancements.rb | 51 ++++++++++++++++++++--------- lib/tasks/apartment.rake | 12 +++++++ spec/tasks/apartment_rake_spec.rb | 9 +++++ 3 files changed, 57 insertions(+), 15 deletions(-) diff --git a/lib/apartment/tasks/enhancements.rb b/lib/apartment/tasks/enhancements.rb index f93b359e..ce93e58a 100644 --- a/lib/apartment/tasks/enhancements.rb +++ b/lib/apartment/tasks/enhancements.rb @@ -3,33 +3,54 @@ module Apartment class RakeTaskEnhancer - - TASKS = %w(db:migrate db:rollback db:migrate:up db:migrate:down db:migrate:redo db:seed) - + + module TASKS + ENHANCE_BEFORE = %w(db:drop) + ENHANCE_AFTER = %w(db:migrate db:rollback db:migrate:up db:migrate:down db:migrate:redo db:seed) + freeze + end + # This is a bit convoluted, but helps solve problems when using Apartment within an engine # See spec/integration/use_within_an_engine.rb - + class << self def enhance! - TASKS.each do |name| + return unless should_enhance? + + # insert task before + TASKS::ENHANCE_BEFORE.each do |name| task = Rake::Task[name] - task.enhance do - if should_enhance? - enhance_task(task) - end - end + enhance_before_task(task) end + + # insert task after + TASKS::ENHANCE_AFTER.each do |name| + task = Rake::Task[name] + enhance_after_task(task) + end + end - + def should_enhance? Apartment.db_migrate_tenants end - - def enhance_task(task) - Rake::Task[task.name.sub(/db:/, 'apartment:')].invoke + + def enhance_before_task(task) + task.enhance([inserted_task_name(task)]) + end + + def enhance_after_task(task) + task.enhance do + Rake::Task[inserted_task_name(task)].invoke + end end + + def inserted_task_name(task) + task.name.sub(/db:/, 'apartment:') + end + end - + end end diff --git a/lib/tasks/apartment.rake b/lib/tasks/apartment.rake index 610d67f5..eb137942 100644 --- a/lib/tasks/apartment.rake +++ b/lib/tasks/apartment.rake @@ -14,6 +14,18 @@ apartment_namespace = namespace :apartment do end end + desc "Drop all tenants" + task :drop do + tenants.each do |tenant| + begin + puts("Dropping #{tenant} tenant") + Apartment::Tenant.drop(tenant) + rescue Apartment::TenantNotFound => e + puts e.message + end + end + end + desc "Migrate all tenants" task :migrate do warn_if_tenants_empty diff --git a/spec/tasks/apartment_rake_spec.rb b/spec/tasks/apartment_rake_spec.rb index 45a7f708..0f86aeee 100644 --- a/spec/tasks/apartment_rake_spec.rb +++ b/spec/tasks/apartment_rake_spec.rb @@ -1,6 +1,7 @@ require 'spec_helper' require 'rake' require 'apartment/migrator' +require 'apartment/tenant' describe "apartment rake tasks" do @@ -116,5 +117,13 @@ @rake['apartment:rollback'].invoke end end + + describe "apartment:drop" do + it "should migrate public and all multi-tenant dbs" do + expect(Apartment::Tenant).to receive(:drop).exactly(tenant_count).times + @rake['apartment:drop'].invoke + end + end + end end From 39fd791bfc2b1eef659d60d92823babe9b1e91a4 Mon Sep 17 00:00:00 2001 From: Sherman Koa Date: Sun, 24 Sep 2017 18:25:24 +0800 Subject: [PATCH 40/91] Add Host Elevator and refine Elevators documentation --- README.md | 36 +++++++- lib/apartment/elevators/domain.rb | 4 +- lib/apartment/elevators/host.rb | 30 +++++++ .../apartment/install/templates/apartment.rb | 2 + spec/unit/elevators/host_spec.rb | 89 +++++++++++++++++++ 5 files changed, 157 insertions(+), 4 deletions(-) create mode 100644 lib/apartment/elevators/host.rb create mode 100644 spec/unit/elevators/host_spec.rb diff --git a/README.md b/README.md index fdabe803..f3faa60d 100644 --- a/README.md +++ b/README.md @@ -117,7 +117,7 @@ manually in your `application.rb` like so ```ruby # config/application.rb -require 'apartment/elevators/subdomain' # or 'domain' or 'generic' +require 'apartment/elevators/subdomain' # or 'domain', 'first_subdomain', 'host' ``` #### Switch on subdomain @@ -155,7 +155,7 @@ module MyApplication end ``` -If you want to exclude a domain, for example if you don't want your application to treate www like a subdomain, in an initializer in your application, you can set the following: +If you want to exclude a domain, for example if you don't want your application to treat www like a subdomain, in an initializer in your application, you can set the following: ```ruby # config/initializers/apartment/subdomain_exclusions.rb @@ -166,7 +166,7 @@ This functions much in the same way as the Subdomain elevator. **NOTE:** in fact #### Switch on domain -To switch based on full domain (excluding subdomains *ie 'www'* and top level domains *ie '.com'* ) use the following: +To switch based on full domain (excluding the 'www' subdomains and top level domains *ie '.com'* ) use the following: ```ruby # application.rb @@ -177,6 +177,11 @@ module MyApplication end ``` +Note that if you have several subdomains, then it will match on the first *non-www* subdomain: +- example.com => example +- www.example.com => example +- a.example.com => a + #### Switch on full host using a hash To switch based on full host with a hash to find corresponding tenant name use the following: @@ -190,6 +195,31 @@ module MyApplication end ``` +#### Switch on full host, ignoring given first subdomains + +To switch based on full host to find corresponding tenant name use the following: + +```ruby +# application.rb +module MyApplication + class Application < Rails::Application + config.middleware.use Apartment::Elevators::Host + end +end +``` + +If you want to exclude a first-subdomain, for example if you don't want your application to include www in the matching, in an initializer in your application, you can set the following: + +```ruby +Apartment::Elevators::Host.ignored_first_subdomains = ['www'] +``` + +With the above set, these would be the results: +- example.com => example.com +- www.example.com => example.com +- a.example.com => a.example.com +- www.a.example.com => a.example.com + #### Custom Elevator A Generic Elevator exists that allows you to pass a `Proc` (or anything that responds to `call`) to the middleware. This Object will be passed in an `ActionDispatch::Request` object when called for you to do your magic. Apartment will use the return value of this proc to switch to the appropriate tenant. Use like so: diff --git a/lib/apartment/elevators/domain.rb b/lib/apartment/elevators/domain.rb index ac877961..5b81bb8a 100644 --- a/lib/apartment/elevators/domain.rb +++ b/lib/apartment/elevators/domain.rb @@ -4,9 +4,11 @@ module Apartment module Elevators # Provides a rack based tenant switching solution based on domain # Assumes that tenant name should match domain - # Parses request host for second level domain + # Parses request host for second level domain, ignoring www # eg. example.com => example # www.example.bc.ca => example + # a.example.bc.ca => a + # # class Domain < Generic diff --git a/lib/apartment/elevators/host.rb b/lib/apartment/elevators/host.rb new file mode 100644 index 00000000..c8cbd2f8 --- /dev/null +++ b/lib/apartment/elevators/host.rb @@ -0,0 +1,30 @@ +require 'apartment/elevators/generic' + +module Apartment + module Elevators + # Provides a rack based tenant switching solution based on the host + # Assumes that tenant name should match host + # Strips/ignores first subdomains in ignored_first_subdomains + # eg. example.com => example.com + # www.example.bc.ca => www.example.bc.ca + # if ignored_first_subdomains = ['www'] + # www.example.bc.ca => example.bc.ca + # www.a.b.c.d.com => a.b.c.d.com + # + class Host < Generic + def self.ignored_first_subdomains + @ignored_first_subdomains ||= [] + end + + def self.ignored_first_subdomains=(arg) + @ignored_first_subdomains = arg + end + + def parse_tenant_name(request) + return nil if request.host.blank? + parts = request.host.split('.') + self.class.ignored_first_subdomains.include?(parts[0]) ? parts.drop(1).join('.') : request.host + end + end + end +end \ No newline at end of file diff --git a/lib/generators/apartment/install/templates/apartment.rb b/lib/generators/apartment/install/templates/apartment.rb index e8c6438e..5e573d2f 100644 --- a/lib/generators/apartment/install/templates/apartment.rb +++ b/lib/generators/apartment/install/templates/apartment.rb @@ -6,6 +6,7 @@ # require 'apartment/elevators/domain' require 'apartment/elevators/subdomain' # require 'apartment/elevators/first_subdomain' +# require 'apartment/elevators/host' # # Apartment Configuration @@ -95,3 +96,4 @@ # Rails.application.config.middleware.use Apartment::Elevators::Domain Rails.application.config.middleware.use Apartment::Elevators::Subdomain # Rails.application.config.middleware.use Apartment::Elevators::FirstSubdomain +# Rails.application.config.middleware.use Apartment::Elevators::Host diff --git a/spec/unit/elevators/host_spec.rb b/spec/unit/elevators/host_spec.rb new file mode 100644 index 00000000..ca3ac20b --- /dev/null +++ b/spec/unit/elevators/host_spec.rb @@ -0,0 +1,89 @@ +require 'spec_helper' +require 'apartment/elevators/host' + +describe Apartment::Elevators::Host do + + subject(:elevator){ described_class.new(Proc.new{}) } + + describe "#parse_tenant_name" do + + it "should return nil when no host" do + request = ActionDispatch::Request.new('HTTP_HOST' => '') + expect(elevator.parse_tenant_name(request)).to be_nil + end + + context "assuming no ignored_first_subdomains" do + context "with 3 parts" do + it "should return the whole host" do + request = ActionDispatch::Request.new('HTTP_HOST' => 'foo.bar.com') + expect(elevator.parse_tenant_name(request)).to eq('foo.bar.com') + end + end + + context "with 6 parts" do + it "should return the whole host" do + request = ActionDispatch::Request.new('HTTP_HOST' => 'one.two.three.foo.bar.com') + expect(elevator.parse_tenant_name(request)).to eq('one.two.three.foo.bar.com') + end + end + end + + context "assuming ignored_first_subdomains is set" do + before { described_class.ignored_first_subdomains = %w{www foo} } + + context "with 3 parts" do + it "should return host without www" do + request = ActionDispatch::Request.new('HTTP_HOST' => 'www.bar.com') + expect(elevator.parse_tenant_name(request)).to eq('bar.com') + end + + it "should return host without foo" do + request = ActionDispatch::Request.new('HTTP_HOST' => 'foo.bar.com') + expect(elevator.parse_tenant_name(request)).to eq('bar.com') + end + end + + context "with 6 parts" do + it "should return host without www" do + request = ActionDispatch::Request.new('HTTP_HOST' => 'www.one.two.three.foo.bar.com') + expect(elevator.parse_tenant_name(request)).to eq('one.two.three.foo.bar.com') + end + + it "should return host without www" do + request = ActionDispatch::Request.new('HTTP_HOST' => 'foo.one.two.three.bar.com') + expect(elevator.parse_tenant_name(request)).to eq('one.two.three.bar.com') + end + end + end + + context "assuming localhost" do + it "should return localhost" do + request = ActionDispatch::Request.new('HTTP_HOST' => 'localhost') + expect(elevator.parse_tenant_name(request)).to eq('localhost') + end + end + + context "assuming ip address" do + it "should return the ip address" do + request = ActionDispatch::Request.new('HTTP_HOST' => '127.0.0.1') + expect(elevator.parse_tenant_name(request)).to eq('127.0.0.1') + end + end + end + + describe "#call" do + it "switches to the proper tenant" do + expect(Apartment::Tenant).to receive(:switch).with('foo.bar.com') + elevator.call('HTTP_HOST' => 'foo.bar.com') + end + + it "ignores ignored_first_subdomains" do + described_class.ignored_first_subdomains = %w{foo} + + expect(Apartment::Tenant).to receive(:switch).with('bar.com') + elevator.call('HTTP_HOST' => 'foo.bar.com') + + described_class.ignored_first_subdomains = nil + end + end +end From 16909532fa9ea3d5ed2a5c4f64f97c802b255f38 Mon Sep 17 00:00:00 2001 From: meganemura Date: Tue, 3 Oct 2017 01:28:02 +0900 Subject: [PATCH 41/91] Ignore `row_security` statements (#479) Ignore `row_security` statements for backward compatibility --- lib/apartment/adapters/postgresql_adapter.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/apartment/adapters/postgresql_adapter.rb b/lib/apartment/adapters/postgresql_adapter.rb index c7fcbc25..f428cb8f 100644 --- a/lib/apartment/adapters/postgresql_adapter.rb +++ b/lib/apartment/adapters/postgresql_adapter.rb @@ -106,8 +106,9 @@ def postgresql_version class PostgresqlSchemaFromSqlAdapter < PostgresqlSchemaAdapter PSQL_DUMP_BLACKLISTED_STATEMENTS= [ - /SET search_path/i, # overridden later - /SET lock_timeout/i, # new in postgresql 9.3 + /SET search_path/i, # overridden later + /SET lock_timeout/i, # new in postgresql 9.3 + /SET row_security/i, # new in postgresql 9.5 /SET idle_in_transaction_session_timeout/i, # new in postgresql 9.6 ] From 7492576354178a436698dbea53c7f537b677480d Mon Sep 17 00:00:00 2001 From: Michikawa Masayoshi Date: Wed, 27 Sep 2017 10:42:18 +0900 Subject: [PATCH 42/91] Add ignore_private option to parse_host I used heroku review app. Heroku review app example URL is "https://example-pr-1234.herokuapp.com". "herokuapp.com" is eTLD, so PublicSuffix parse to trd is nil without ignore_private option. --- lib/apartment/elevators/subdomain.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/apartment/elevators/subdomain.rb b/lib/apartment/elevators/subdomain.rb index 97f82ab8..c625e29b 100644 --- a/lib/apartment/elevators/subdomain.rb +++ b/lib/apartment/elevators/subdomain.rb @@ -55,7 +55,7 @@ def domain_valid?(host) end def parse_host(host) - (PublicSuffix.parse(host).trd || '').split('.') + (PublicSuffix.parse(host, ignore_private: true).trd || '').split('.') end end end From ccc87394b7194dc2ca15b97e6cf9e7ad711f958c Mon Sep 17 00:00:00 2001 From: Michikawa Masayoshi Date: Sat, 9 Sep 2017 10:20:33 +0900 Subject: [PATCH 43/91] Relax version of public_suffix gem --- apartment.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apartment.gemspec b/apartment.gemspec index 038d1f62..00efc363 100644 --- a/apartment.gemspec +++ b/apartment.gemspec @@ -21,7 +21,7 @@ Gem::Specification.new do |s| # must be >= 3.1.2 due to bug in prepared_statements s.add_dependency 'activerecord', '>= 3.1.2', '< 6.0' s.add_dependency 'rack', '>= 1.3.6' - s.add_dependency 'public_suffix', '~> 2.0.5' + s.add_dependency 'public_suffix', '>= 2' s.add_development_dependency 'appraisal' s.add_development_dependency 'rake', '~> 0.9' From 3a2e8cdf534fc17620c567ea09d129318d93f34e Mon Sep 17 00:00:00 2001 From: CrystalBlue998 <33125423+CrystalBlue998@users.noreply.github.com> Date: Fri, 27 Oct 2017 01:54:37 +0530 Subject: [PATCH 44/91] Fixed a typo --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index fdabe803..3ca20be9 100644 --- a/README.md +++ b/README.md @@ -155,7 +155,7 @@ module MyApplication end ``` -If you want to exclude a domain, for example if you don't want your application to treate www like a subdomain, in an initializer in your application, you can set the following: +If you want to exclude a domain, for example if you don't want your application to treat www like a subdomain, in an initializer in your application, you can set the following: ```ruby # config/initializers/apartment/subdomain_exclusions.rb From 91e5d729552518bc8d987b4fd8d13adfa555bc76 Mon Sep 17 00:00:00 2001 From: meganemura Date: Sat, 28 Oct 2017 13:04:30 +0900 Subject: [PATCH 45/91] Fix Apartment::Elevators::Host specs --- spec/unit/elevators/host_spec.rb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spec/unit/elevators/host_spec.rb b/spec/unit/elevators/host_spec.rb index ca3ac20b..96ea057c 100644 --- a/spec/unit/elevators/host_spec.rb +++ b/spec/unit/elevators/host_spec.rb @@ -13,6 +13,8 @@ end context "assuming no ignored_first_subdomains" do + before { allow(described_class).to receive(:ignored_first_subdomains).and_return([]) } + context "with 3 parts" do it "should return the whole host" do request = ActionDispatch::Request.new('HTTP_HOST' => 'foo.bar.com') @@ -29,7 +31,7 @@ end context "assuming ignored_first_subdomains is set" do - before { described_class.ignored_first_subdomains = %w{www foo} } + before { allow(described_class).to receive(:ignored_first_subdomains).and_return(%w{www foo}) } context "with 3 parts" do it "should return host without www" do @@ -73,17 +75,15 @@ describe "#call" do it "switches to the proper tenant" do + allow(described_class).to receive(:ignored_first_subdomains).and_return([]) expect(Apartment::Tenant).to receive(:switch).with('foo.bar.com') elevator.call('HTTP_HOST' => 'foo.bar.com') end it "ignores ignored_first_subdomains" do - described_class.ignored_first_subdomains = %w{foo} - + allow(described_class).to receive(:ignored_first_subdomains).and_return(%w{foo}) expect(Apartment::Tenant).to receive(:switch).with('bar.com') elevator.call('HTTP_HOST' => 'foo.bar.com') - - described_class.ignored_first_subdomains = nil end end end From c6cc88309344662ceca8772378f68520a45b3792 Mon Sep 17 00:00:00 2001 From: meganemura Date: Sun, 29 Oct 2017 13:07:58 +0900 Subject: [PATCH 46/91] Strict a version of public_suffix gem for Ruby < 2.1.0 --- gemfiles/rails_4_0.gemfile | 4 ++++ gemfiles/rails_4_1.gemfile | 4 ++++ gemfiles/rails_4_2.gemfile | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/gemfiles/rails_4_0.gemfile b/gemfiles/rails_4_0.gemfile index e62f28d3..90f9eed8 100644 --- a/gemfiles/rails_4_0.gemfile +++ b/gemfiles/rails_4_0.gemfile @@ -4,6 +4,10 @@ source "http://rubygems.org" gem "rails", "~> 4.0.0" +if Gem::Version.create(RUBY_VERSION) < Gem::Version.create("2.1.0") + gem "public_suffix", "~> 2.0" +end + group :local do gem "pry" gem "guard-rspec", "~> 4.2" diff --git a/gemfiles/rails_4_1.gemfile b/gemfiles/rails_4_1.gemfile index 0bb23143..a4382c59 100644 --- a/gemfiles/rails_4_1.gemfile +++ b/gemfiles/rails_4_1.gemfile @@ -4,6 +4,10 @@ source "http://rubygems.org" gem "rails", "~> 4.1.0" +if Gem::Version.create(RUBY_VERSION) < Gem::Version.create("2.1.0") + gem "public_suffix", "~> 2.0" +end + group :local do gem "pry" gem "guard-rspec", "~> 4.2" diff --git a/gemfiles/rails_4_2.gemfile b/gemfiles/rails_4_2.gemfile index e1520f82..a2c6f8d4 100644 --- a/gemfiles/rails_4_2.gemfile +++ b/gemfiles/rails_4_2.gemfile @@ -4,6 +4,10 @@ source "http://rubygems.org" gem "rails", "~> 4.2.0" +if Gem::Version.create(RUBY_VERSION) < Gem::Version.create("2.1.0") + gem "public_suffix", '~> 2.0' +end + group :local do gem "pry" gem "guard-rspec", "~> 4.2" From 7061ccb224bd88616934401564320c87a5cea5b2 Mon Sep 17 00:00:00 2001 From: Mike Campbell Date: Fri, 3 Nov 2017 16:30:51 +0000 Subject: [PATCH 47/91] Document `switch` in README --- README.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f3faa60d..9c46f2fa 100644 --- a/README.md +++ b/README.md @@ -94,12 +94,17 @@ One can optionally use the full database creation instead if they want, though t To switch tenants using Apartment, use the following command: ```ruby -Apartment::Tenant.switch!('tenant_name') +Apartment::Tenant.switch('tenant_name') do + # ... +end ``` When switch is called, all requests coming to ActiveRecord will be routed to the tenant -you specify (with the exception of excluded models, see below). To return to the 'root' -tenant, call switch with no arguments. +you specify (with the exception of excluded models, see below). The tenant is automatically +switched back at the end of the block to what it was before. + +There is also `switch!` which doesn't take a block, but it's recommended to use `switch`. +To return to the default tenant, you can call `switch` with no arguments. ### Switching Tenants per request From e5ad63b8534ccb9148dd69623bbf7965b78bc6f9 Mon Sep 17 00:00:00 2001 From: Mike Campbell Date: Fri, 3 Nov 2017 16:37:51 +0000 Subject: [PATCH 48/91] Document apartment-activejob --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f3faa60d..4da6ca74 100644 --- a/README.md +++ b/README.md @@ -511,9 +511,9 @@ config.tenant_names = lambda do end ``` -## Delayed::Job +## Background workers -Has been removed. See [apartment-sidekiq](https://github.com/influitive/apartment-sidekiq) for a better backgrounding experience. +See [apartment-sidekiq](https://github.com/influitive/apartment-sidekiq) or [apartment-activejob](https://github.com/influitive/apartment-activejob). ## Contributing From 576837b7db56c7d1a9a40ba728b4e41e8f1c542b Mon Sep 17 00:00:00 2001 From: meganemura Date: Sat, 9 Dec 2017 23:40:26 +0900 Subject: [PATCH 49/91] Exclude jruby + Rails 4.0 builds from Travis-CI bulid matrix --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index 703ee7a6..7035d95a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -46,6 +46,8 @@ matrix: gemfile: gemfiles/rails_4_0.gemfile - rvm: ruby-head gemfile: gemfiles/rails_4_1.gemfile + - rvm: jruby-9.0.5.0 + gemfile: gemfiles/rails_4_0.gemfile - rvm: jruby-9.0.5.0 gemfile: gemfiles/rails_4_2.gemfile - rvm: jruby-9.0.5.0 @@ -54,6 +56,8 @@ matrix: gemfile: gemfiles/rails_5_1.gemfile - rvm: jruby-9.0.5.0 gemfile: gemfiles/rails_master.gemfile + - rvm: jruby-9.1.9.0 + gemfile: gemfiles/rails_4_0.gemfile - rvm: jruby-9.1.9.0 gemfile: gemfiles/rails_5_0.gemfile - rvm: jruby-9.1.9.0 From 3c94975cd85220b999ac7213adaadcb6b56a25a5 Mon Sep 17 00:00:00 2001 From: meganemura Date: Sun, 10 Dec 2017 02:09:59 +0900 Subject: [PATCH 50/91] Drop support for Ruby 2.0 --- apartment.gemspec | 5 ----- gemfiles/rails_4_0.gemfile | 4 ---- gemfiles/rails_4_1.gemfile | 4 ---- gemfiles/rails_4_2.gemfile | 4 ---- 4 files changed, 17 deletions(-) diff --git a/apartment.gemspec b/apartment.gemspec index 00efc363..db182719 100644 --- a/apartment.gemspec +++ b/apartment.gemspec @@ -41,9 +41,4 @@ Gem::Specification.new do |s| s.add_development_dependency 'pg', '>= 0.11.0' s.add_development_dependency 'sqlite3' end - - if RUBY_VERSION < '2.1.0' - # capybara depends on xpath depends on nokogiri - s.add_development_dependency 'nokogiri', '< 1.7.0' - end end diff --git a/gemfiles/rails_4_0.gemfile b/gemfiles/rails_4_0.gemfile index 90f9eed8..e62f28d3 100644 --- a/gemfiles/rails_4_0.gemfile +++ b/gemfiles/rails_4_0.gemfile @@ -4,10 +4,6 @@ source "http://rubygems.org" gem "rails", "~> 4.0.0" -if Gem::Version.create(RUBY_VERSION) < Gem::Version.create("2.1.0") - gem "public_suffix", "~> 2.0" -end - group :local do gem "pry" gem "guard-rspec", "~> 4.2" diff --git a/gemfiles/rails_4_1.gemfile b/gemfiles/rails_4_1.gemfile index a4382c59..0bb23143 100644 --- a/gemfiles/rails_4_1.gemfile +++ b/gemfiles/rails_4_1.gemfile @@ -4,10 +4,6 @@ source "http://rubygems.org" gem "rails", "~> 4.1.0" -if Gem::Version.create(RUBY_VERSION) < Gem::Version.create("2.1.0") - gem "public_suffix", "~> 2.0" -end - group :local do gem "pry" gem "guard-rspec", "~> 4.2" diff --git a/gemfiles/rails_4_2.gemfile b/gemfiles/rails_4_2.gemfile index a2c6f8d4..e1520f82 100644 --- a/gemfiles/rails_4_2.gemfile +++ b/gemfiles/rails_4_2.gemfile @@ -4,10 +4,6 @@ source "http://rubygems.org" gem "rails", "~> 4.2.0" -if Gem::Version.create(RUBY_VERSION) < Gem::Version.create("2.1.0") - gem "public_suffix", '~> 2.0' -end - group :local do gem "pry" gem "guard-rspec", "~> 4.2" From 8480a648879bd4f7976cb17ed3c338b1cb8c1c7d Mon Sep 17 00:00:00 2001 From: meganemura Date: Sun, 10 Dec 2017 02:14:56 +0900 Subject: [PATCH 51/91] Remove rvm related files --- .ruby-gemset | 1 - .ruby-version | 1 - 2 files changed, 2 deletions(-) delete mode 100644 .ruby-gemset delete mode 100644 .ruby-version diff --git a/.ruby-gemset b/.ruby-gemset deleted file mode 100644 index 421ee4a7..00000000 --- a/.ruby-gemset +++ /dev/null @@ -1 +0,0 @@ -apartment diff --git a/.ruby-version b/.ruby-version deleted file mode 100644 index 81af5fe5..00000000 --- a/.ruby-version +++ /dev/null @@ -1 +0,0 @@ -ruby-2.3 From 7d6c169bbd550adb7be3c50695657ecd906a5e16 Mon Sep 17 00:00:00 2001 From: meganemura Date: Sun, 10 Dec 2017 00:13:44 +0900 Subject: [PATCH 52/91] Add an issue template for GitHub --- .github/ISSUE_TEMPLATE.md | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE.md diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 00000000..cdaa92ff --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,21 @@ +## Steps to reproduce + +## Expected behavior + +## Actual behavior + +## System configuration + + + +* Database: (Tell us what database and its version you use.) + +* Apartment version: + +* Apartment config (in `config/initializers/apartment.rb` or so): + + * `use_schemas`: (`true` or `false`) + +* Rails (or ActiveRecord) version: + +* Ruby version: From 81aaa45a19a7530a1fcfe2143f6de8da3bce792d Mon Sep 17 00:00:00 2001 From: Ryan Brunner Date: Thu, 29 Aug 2013 16:41:14 -0400 Subject: [PATCH 53/91] Run migrations in parallel --- README.md | 11 +++++++++++ apartment.gemspec | 1 + lib/apartment.rb | 6 +++++- lib/tasks/apartment.rake | 17 ++++++++++++----- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 1e2e7371..72dbb07e 100644 --- a/README.md +++ b/README.md @@ -480,6 +480,17 @@ Note that you can disable the default migrating of all tenants with `db:migrate` `Apartment.db_migrate_tenants = false` in your `Rakefile`. Note this must be done *before* the rake tasks are loaded. ie. before `YourApp::Application.load_tasks` is called +#### Parallel Migrations + +Apartment supports parallelizing migrations into multiple threads when +you have a large number of tenants. By default, parallel migrations is +turned off. You can enable this by setting `parallel_migration_threads` to +the number of threads you want to use in your initializer. + +Keep in mind that because migrations are going to access the database, +the number of threads indicated here should be less than the pool size +that Rails will use to connect to your database. + ### Handling Environments By default, when not using postgresql schemas, Apartment will prepend the environment to the tenant name diff --git a/apartment.gemspec b/apartment.gemspec index db182719..336fff14 100644 --- a/apartment.gemspec +++ b/apartment.gemspec @@ -22,6 +22,7 @@ Gem::Specification.new do |s| s.add_dependency 'activerecord', '>= 3.1.2', '< 6.0' s.add_dependency 'rack', '>= 1.3.6' s.add_dependency 'public_suffix', '>= 2' + s.add_dependency 'parallel', '>= 0.7.1' s.add_development_dependency 'appraisal' s.add_development_dependency 'rake', '~> 0.9' diff --git a/lib/apartment.rb b/lib/apartment.rb index 6cd21585..e888cf17 100644 --- a/lib/apartment.rb +++ b/lib/apartment.rb @@ -11,7 +11,7 @@ class << self extend Forwardable ACCESSOR_METHODS = [:use_schemas, :use_sql, :seed_after_create, :prepend_environment, :append_environment, :with_multi_server_setup ] - WRITER_METHODS = [:tenant_names, :database_schema_file, :excluded_models, :default_schema, :persistent_schemas, :connection_class, :tld_length, :db_migrate_tenants, :seed_data_file] + WRITER_METHODS = [:tenant_names, :database_schema_file, :excluded_models, :default_schema, :persistent_schemas, :connection_class, :tld_length, :db_migrate_tenants, :seed_data_file, :parallel_migration_threads] attr_accessor(*ACCESSOR_METHODS) attr_writer(*WRITER_METHODS) @@ -51,6 +51,10 @@ def excluded_models def default_schema @default_schema || "public" # TODO 'public' is postgres specific end + + def parallel_migration_threads + @parallel_migration_threads || 0 + end alias :default_tenant :default_schema alias :default_tenant= :default_schema= diff --git a/lib/tasks/apartment.rake b/lib/tasks/apartment.rake index eb137942..2fd94f37 100644 --- a/lib/tasks/apartment.rake +++ b/lib/tasks/apartment.rake @@ -1,4 +1,5 @@ require 'apartment/migrator' +require 'parallel' apartment_namespace = namespace :apartment do @@ -29,7 +30,7 @@ apartment_namespace = namespace :apartment do desc "Migrate all tenants" task :migrate do warn_if_tenants_empty - tenants.each do |tenant| + each_tenant do |tenant| begin puts("Migrating #{tenant} tenant") Apartment::Migrator.migrate tenant @@ -43,7 +44,7 @@ apartment_namespace = namespace :apartment do task :seed do warn_if_tenants_empty - tenants.each do |tenant| + each_tenant do |tenant| begin puts("Seeding #{tenant} tenant") Apartment::Tenant.switch(tenant) do @@ -61,7 +62,7 @@ apartment_namespace = namespace :apartment do step = ENV['STEP'] ? ENV['STEP'].to_i : 1 - tenants.each do |tenant| + each_tenant do |tenant| begin puts("Rolling back #{tenant} tenant") Apartment::Migrator.rollback tenant, step @@ -79,7 +80,7 @@ apartment_namespace = namespace :apartment do version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil raise 'VERSION is required' unless version - tenants.each do |tenant| + each_tenant do |tenant| begin puts("Migrating #{tenant} tenant up") Apartment::Migrator.run :up, tenant, version @@ -96,7 +97,7 @@ apartment_namespace = namespace :apartment do version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil raise 'VERSION is required' unless version - tenants.each do |tenant| + each_tenant do |tenant| begin puts("Migrating #{tenant} tenant down") Apartment::Migrator.run :down, tenant, version @@ -118,6 +119,12 @@ apartment_namespace = namespace :apartment do end end + def each_tenant(&block) + Parallel.each(tenants, in_threads: Apartment.parallel_migration_threads) do |tenant| + block.call(tenant) + end + end + def tenants ENV['DB'] ? ENV['DB'].split(',').map { |s| s.strip } : Apartment.tenant_names || [] end From edf567b3125873241695b458aac286acb8487c97 Mon Sep 17 00:00:00 2001 From: Mike Campbell Date: Mon, 11 Dec 2017 15:40:20 +0000 Subject: [PATCH 54/91] Remove ruby-2.0.0 from travis config --- .travis.yml | 7 ------- 1 file changed, 7 deletions(-) diff --git a/.travis.yml b/.travis.yml index 7035d95a..816c560b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: ruby rvm: - - 2.0.0 - 2.1.9 - 2.2.4 - 2.3.1 @@ -26,12 +25,6 @@ matrix: - rvm: ruby-head - gemfile: gemfiles/rails_master.gemfile exclude: - - rvm: 2.0.0 - gemfile: gemfiles/rails_5_0.gemfile - - rvm: 2.0.0 - gemfile: gemfiles/rails_5_1.gemfile - - rvm: 2.0.0 - gemfile: gemfiles/rails_master.gemfile - rvm: 2.1.9 gemfile: gemfiles/rails_5_0.gemfile - rvm: 2.1.9 From bb1f94c70e95b5b5dda84ecbcfd3b3d043721353 Mon Sep 17 00:00:00 2001 From: meganemura Date: Tue, 12 Dec 2017 02:51:25 +0900 Subject: [PATCH 55/91] Fix an example of middleware --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 1e2e7371..62b3875e 100644 --- a/README.md +++ b/README.md @@ -279,7 +279,7 @@ This works okay for simple applications, but it's important to consider that you To resolve this issue, consider adding the Apartment middleware at a location in the Rack stack that makes sense for your needs, e.g.: ```ruby -Rails.application.config.middleware.insert_before 'Warden::Manager', 'Apartment::Elevators::Subdomain' +Rails.application.config.middleware.insert_before Warden::Manager, Apartment::Elevators::Subdomain ``` Now work done in the Warden middleware is wrapped in the `Apartment::Tenant.switch` context started in the Generic elevator. From 80a21f2e1cdcbe5b0bd976f88c14332657804536 Mon Sep 17 00:00:00 2001 From: Mike Campbell Date: Fri, 15 Dec 2017 15:39:36 +0000 Subject: [PATCH 56/91] Release 2.1.0 --- HISTORY.md | 5 +++++ lib/apartment/version.rb | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 6f0e8266..03f603b8 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,8 @@ +# 2.1.0 + * December 15, 2017 + + - Enhance db:drop task to act on all tenants [kuzukuzu] + # 2.0.0 * July 26, 2017 diff --git a/lib/apartment/version.rb b/lib/apartment/version.rb index aedfbb55..bd56fa54 100644 --- a/lib/apartment/version.rb +++ b/lib/apartment/version.rb @@ -1,3 +1,3 @@ module Apartment - VERSION = "2.0.0" + VERSION = "2.1.0" end From 0fe7a11d92c235ed7d35e33711eee406be9262d1 Mon Sep 17 00:00:00 2001 From: Mike Campbell Date: Fri, 15 Dec 2017 15:45:40 +0000 Subject: [PATCH 57/91] Fix HISTORY.md --- HISTORY.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/HISTORY.md b/HISTORY.md index 03f603b8..77b30b13 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,6 +1,13 @@ # 2.1.0 * December 15, 2017 + - Add `parallel_migration_threads` configuration option for running migrations + in parallel [ryanbrunner] + - Drop Ruby 2.0.0 support [meganemura] + - ignore_private when parsing subdomains with PublicSuffix [michiomochi] + - Ignore row_security statements in psql dumps for backward compatibility + [meganemura] + - "Host" elevator [shrmnk] - Enhance db:drop task to act on all tenants [kuzukuzu] # 2.0.0 From da4be3956b41bbd52f41fd3e9edee308f7109476 Mon Sep 17 00:00:00 2001 From: pavel Date: Mon, 18 Dec 2017 21:31:10 +0100 Subject: [PATCH 58/91] update travis --- .travis.yml | 37 ++++++----------------------------- apartment.gemspec | 6 +++--- gemfiles/rails_4_0.gemfile | 12 ------------ gemfiles/rails_4_1.gemfile | 12 ------------ gemfiles/rails_4_2.gemfile | 7 +++++++ gemfiles/rails_5_0.gemfile | 6 ++++++ gemfiles/rails_5_1.gemfile | 6 ++++++ gemfiles/rails_master.gemfile | 6 ++++++ 8 files changed, 34 insertions(+), 58 deletions(-) delete mode 100644 gemfiles/rails_4_0.gemfile delete mode 100644 gemfiles/rails_4_1.gemfile diff --git a/.travis.yml b/.travis.yml index 816c560b..2afcb007 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,15 +1,12 @@ language: ruby rvm: - 2.1.9 - - 2.2.4 - - 2.3.1 - - 2.4.1 + - 2.2.9 + - 2.3.6 + - 2.4.3 - ruby-head - - jruby-9.0.5.0 - - jruby-9.1.9.0 + - jruby-9.1.15.0 gemfile: - - gemfiles/rails_4_0.gemfile - - gemfiles/rails_4_1.gemfile - gemfiles/rails_4_2.gemfile - gemfiles/rails_5_0.gemfile - gemfiles/rails_5_1.gemfile @@ -31,31 +28,9 @@ matrix: gemfile: gemfiles/rails_5_1.gemfile - rvm: 2.1.9 gemfile: gemfiles/rails_master.gemfile - - rvm: 2.4.1 - gemfile: gemfiles/rails_4_0.gemfile - - rvm: 2.4.1 - gemfile: gemfiles/rails_4_1.gemfile - - rvm: ruby-head - gemfile: gemfiles/rails_4_0.gemfile - - rvm: ruby-head - gemfile: gemfiles/rails_4_1.gemfile - - rvm: jruby-9.0.5.0 - gemfile: gemfiles/rails_4_0.gemfile - - rvm: jruby-9.0.5.0 - gemfile: gemfiles/rails_4_2.gemfile - - rvm: jruby-9.0.5.0 - gemfile: gemfiles/rails_5_0.gemfile - - rvm: jruby-9.0.5.0 - gemfile: gemfiles/rails_5_1.gemfile - - rvm: jruby-9.0.5.0 - gemfile: gemfiles/rails_master.gemfile - - rvm: jruby-9.1.9.0 - gemfile: gemfiles/rails_4_0.gemfile - - rvm: jruby-9.1.9.0 - gemfile: gemfiles/rails_5_0.gemfile - - rvm: jruby-9.1.9.0 + - rvm: jruby-9.1.15.0 gemfile: gemfiles/rails_5_1.gemfile - - rvm: jruby-9.1.9.0 + - rvm: jruby-9.1.15.0 gemfile: gemfiles/rails_master.gemfile fast_finish: true cache: bundler diff --git a/apartment.gemspec b/apartment.gemspec index 336fff14..e02cf189 100644 --- a/apartment.gemspec +++ b/apartment.gemspec @@ -34,12 +34,12 @@ Gem::Specification.new do |s| s.add_development_dependency 'activerecord-jdbc-adapter' s.add_development_dependency 'activerecord-jdbcpostgresql-adapter' s.add_development_dependency 'activerecord-jdbcmysql-adapter' - s.add_development_dependency 'jdbc-postgres', '9.2.1002' + s.add_development_dependency 'jdbc-postgres' s.add_development_dependency 'jdbc-mysql' s.add_development_dependency 'jruby-openssl' else - s.add_development_dependency 'mysql2', '~> 0.3.10' - s.add_development_dependency 'pg', '>= 0.11.0' + s.add_development_dependency 'mysql2' + s.add_development_dependency 'pg' s.add_development_dependency 'sqlite3' end end diff --git a/gemfiles/rails_4_0.gemfile b/gemfiles/rails_4_0.gemfile deleted file mode 100644 index e62f28d3..00000000 --- a/gemfiles/rails_4_0.gemfile +++ /dev/null @@ -1,12 +0,0 @@ -# This file was generated by Appraisal - -source "http://rubygems.org" - -gem "rails", "~> 4.0.0" - -group :local do - gem "pry" - gem "guard-rspec", "~> 4.2" -end - -gemspec path: "../" diff --git a/gemfiles/rails_4_1.gemfile b/gemfiles/rails_4_1.gemfile deleted file mode 100644 index 0bb23143..00000000 --- a/gemfiles/rails_4_1.gemfile +++ /dev/null @@ -1,12 +0,0 @@ -# This file was generated by Appraisal - -source "http://rubygems.org" - -gem "rails", "~> 4.1.0" - -group :local do - gem "pry" - gem "guard-rspec", "~> 4.2" -end - -gemspec path: "../" diff --git a/gemfiles/rails_4_2.gemfile b/gemfiles/rails_4_2.gemfile index e1520f82..bb2f4f26 100644 --- a/gemfiles/rails_4_2.gemfile +++ b/gemfiles/rails_4_2.gemfile @@ -9,4 +9,11 @@ group :local do gem "guard-rspec", "~> 4.2" end +if defined?(JRUBY_VERSION) + gem 'activerecord-jdbc-adapter', '~> 1.3' + gem 'activerecord-jdbcpostgresql-adapter', '~> 1.3' + gem 'activerecord-jdbcmysql-adapter', '~> 1.3' +end + gemspec path: "../" + diff --git a/gemfiles/rails_5_0.gemfile b/gemfiles/rails_5_0.gemfile index 290afb96..f43969c4 100644 --- a/gemfiles/rails_5_0.gemfile +++ b/gemfiles/rails_5_0.gemfile @@ -9,4 +9,10 @@ group :local do gem "guard-rspec", "~> 4.2" end +if defined?(JRUBY_VERSION) + gem 'activerecord-jdbc-adapter', '~> 50.0' + gem 'activerecord-jdbcpostgresql-adapter', '~> 50.0' + gem 'activerecord-jdbcmysql-adapter', '~> 50.0' +end + gemspec path: "../" diff --git a/gemfiles/rails_5_1.gemfile b/gemfiles/rails_5_1.gemfile index c3efd90f..1717ee9d 100644 --- a/gemfiles/rails_5_1.gemfile +++ b/gemfiles/rails_5_1.gemfile @@ -9,4 +9,10 @@ group :local do gem "guard-rspec", "~> 4.2" end +if defined?(JRUBY_VERSION) + gem 'activerecord-jdbc-adapter', '~> 51.0' + gem 'activerecord-jdbcpostgresql-adapter', '~> 51.0' + gem 'activerecord-jdbcmysql-adapter', '~> 51.0' +end + gemspec path: "../" diff --git a/gemfiles/rails_master.gemfile b/gemfiles/rails_master.gemfile index 27f6952d..9ab4b629 100644 --- a/gemfiles/rails_master.gemfile +++ b/gemfiles/rails_master.gemfile @@ -9,4 +9,10 @@ group :local do gem "guard-rspec", "~> 4.2" end +if defined?(JRUBY_VERSION) + gem 'activerecord-jdbc-adapter', '~> 51.0' + gem 'activerecord-jdbcpostgresql-adapter', '~> 51.0' + gem 'activerecord-jdbcmysql-adapter', '~> 51.0' +end + gemspec path: "../" From 16f077d4a7191af4674b079cd8322aceb666794a Mon Sep 17 00:00:00 2001 From: pavel Date: Wed, 27 Dec 2017 22:53:31 +0100 Subject: [PATCH 59/91] add ruby 2.5.0 --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 2afcb007..7e59ba1a 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ rvm: - 2.2.9 - 2.3.6 - 2.4.3 + - 2.5.0 - ruby-head - jruby-9.1.15.0 gemfile: From f2f90dc678c30e5ea9ce71531b0a65322fb51972 Mon Sep 17 00:00:00 2001 From: menorval Date: Sat, 13 Jan 2018 21:05:19 -0600 Subject: [PATCH 60/91] Fixed typo in owl word from Readme file Changed owl to owls just to avoid any misunderstanding. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 72fb4f41..9b3ced2a 100644 --- a/README.md +++ b/README.md @@ -149,7 +149,7 @@ This functions much in the same way as Apartment.excluded_models. This example w #### Switch on first subdomain -To switch on the first subdomain, which analyzes the chain of subdomains of the request and switches to a tenant schema of the first name in the chain (e.g. owls.birds.animals.com would switch to "owl"). It can be used like so: +To switch on the first subdomain, which analyzes the chain of subdomains of the request and switches to a tenant schema of the first name in the chain (e.g. owls.birds.animals.com would switch to "owls"). It can be used like so: ```ruby # application.rb From a637ee67d324d453ef9f449ab41a8d1a3f9223bd Mon Sep 17 00:00:00 2001 From: Mario Young Date: Mon, 29 Jan 2018 23:03:25 -0300 Subject: [PATCH 61/91] Fix exception when main database don't exist --- lib/apartment/railtie.rb | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/lib/apartment/railtie.rb b/lib/apartment/railtie.rb index f011cbfd..09dade09 100644 --- a/lib/apartment/railtie.rb +++ b/lib/apartment/railtie.rb @@ -27,10 +27,14 @@ class Railtie < Rails::Railtie # See the middleware/console declarations below to help with this. Hope to fix that soon. # config.to_prepare do - unless ARGV.any? { |arg| arg =~ /\Aassets:(?:precompile|clean)\z/ } - Apartment.connection_class.connection_pool.with_connection do - Apartment::Tenant.init + begin + unless ARGV.any? { |arg| arg =~ /\Aassets:(?:precompile|clean)\z/ } + Apartment.connection_class.connection_pool.with_connection do + Apartment::Tenant.init + end end + rescue ::ActiveRecord::NoDatabaseError => e + puts e.message end end From 06d80f0c284a04f383377c669127636068a7214b Mon Sep 17 00:00:00 2001 From: Ingus Skaistkalns Date: Wed, 14 Feb 2018 10:26:10 +0200 Subject: [PATCH 62/91] Support ActiveRecord-5.2 migration context --- Rakefile | 16 ++++- lib/apartment/migrator.rb | 26 +++++-- .../apartment_rake_integration_spec.rb | 49 ++++++++++--- spec/unit/migrator_spec.rb | 72 ++++++++++++++----- 4 files changed, 133 insertions(+), 30 deletions(-) diff --git a/Rakefile b/Rakefile index 7c1ee70c..947b8868 100644 --- a/Rakefile +++ b/Rakefile @@ -59,7 +59,7 @@ namespace :postgres do params << "-p#{pg_config['port']}" if pg_config['port'] %x{ createdb #{params.join(' ')} } rescue "test db already exists" ActiveRecord::Base.establish_connection pg_config - ActiveRecord::Migrator.migrate('spec/dummy/db/migrate') + migrate end desc "drop the PostgreSQL test database" @@ -87,7 +87,7 @@ namespace :mysql do params << "-p#{my_config['password']}" if my_config['password'] %x{ mysqladmin #{params.join(' ')} create #{my_config['database']} } rescue "test db already exists" ActiveRecord::Base.establish_connection my_config - ActiveRecord::Migrator.migrate('spec/dummy/db/migrate') + migrate end desc "drop the MySQL test database" @@ -114,3 +114,15 @@ end def my_config config['mysql'] end + +def activerecord_below_5_2? + ActiveRecord.version.release() < Gem::Version.new('5.2.0') +end + +def migrate + if activerecord_below_5_2? + ActiveRecord::Migrator.migrate('spec/dummy/db/migrate') + else + ActiveRecord::MigrationContext.new('spec/dummy/db/migrate').migrate + end +end diff --git a/lib/apartment/migrator.rb b/lib/apartment/migrator.rb index 3d14f342..d8a04047 100644 --- a/lib/apartment/migrator.rb +++ b/lib/apartment/migrator.rb @@ -10,8 +10,12 @@ def migrate(database) Tenant.switch(database) do version = ENV["VERSION"] ? ENV["VERSION"].to_i : nil - ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, version) do |migration| - ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope) + migration_scope_block = -> (migration) { ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope) } + + if activerecord_below_5_2? + ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, version, &migration_scope_block) + else + ActiveRecord::Base.connection.migration_context.migrate(version, &migration_scope_block) end end end @@ -19,15 +23,29 @@ def migrate(database) # Migrate up/down to a specific version def run(direction, database, version) Tenant.switch(database) do - ActiveRecord::Migrator.run(direction, ActiveRecord::Migrator.migrations_paths, version) + if activerecord_below_5_2? + ActiveRecord::Migrator.run(direction, ActiveRecord::Migrator.migrations_paths, version) + else + ActiveRecord::Base.connection.migration_context.run(direction, version) + end end end # rollback latest migration `step` number of times def rollback(database, step = 1) Tenant.switch(database) do - ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step) + if activerecord_below_5_2? + ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step) + else + ActiveRecord::Base.connection.migration_context.rollback(step) + end end end + + private + + def activerecord_below_5_2? + ActiveRecord.version.release() < Gem::Version.new('5.2.0') + end end end diff --git a/spec/integration/apartment_rake_integration_spec.rb b/spec/integration/apartment_rake_integration_spec.rb index e343b3f8..4b9b098d 100644 --- a/spec/integration/apartment_rake_integration_spec.rb +++ b/spec/integration/apartment_rake_integration_spec.rb @@ -47,19 +47,52 @@ Company.delete_all end - describe "#migrate" do - it "should migrate all databases" do - expect(ActiveRecord::Migrator).to receive(:migrate).exactly(company_count).times + context "with ActiveRecord below 5.2.0" do + before do + allow(ActiveRecord::Migrator).to receive(:migrations_paths) { %w(spec/dummy/db/migrate) } + allow(Apartment::Migrator).to receive(:activerecord_below_5_2?) { true } + end + + describe "#migrate" do + it "should migrate all databases" do + expect(ActiveRecord::Migrator).to receive(:migrate).exactly(company_count).times + + @rake['apartment:migrate'].invoke + end + end + + describe "#rollback" do + it "should rollback all dbs" do + expect(ActiveRecord::Migrator).to receive(:rollback).exactly(company_count).times - @rake['apartment:migrate'].invoke + @rake['apartment:rollback'].invoke + end end end - describe "#rollback" do - it "should rollback all dbs" do - expect(ActiveRecord::Migrator).to receive(:rollback).exactly(company_count).times + context "with ActiveRecord above or equal to 5.2.0" do + let(:migration_context_double) { double(:migration_context) } + + before do + allow(Apartment::Migrator).to receive(:activerecord_below_5_2?) { false } + end + + describe "#migrate" do + it "should migrate all databases" do + allow(ActiveRecord::Base.connection).to receive(:migration_context) { migration_context_double } + expect(migration_context_double).to receive(:migrate).exactly(company_count).times + + @rake['apartment:migrate'].invoke + end + end + + describe "#rollback" do + it "should rollback all dbs" do + allow(ActiveRecord::Base.connection).to receive(:migration_context) { migration_context_double } + expect(migration_context_double).to receive(:rollback).exactly(company_count).times - @rake['apartment:rollback'].invoke + @rake['apartment:rollback'].invoke + end end end diff --git a/spec/unit/migrator_spec.rb b/spec/unit/migrator_spec.rb index d5fcaf96..1e67131d 100644 --- a/spec/unit/migrator_spec.rb +++ b/spec/unit/migrator_spec.rb @@ -8,30 +8,70 @@ # Don't need a real switch here, just testing behaviour before { allow(Apartment::Tenant.adapter).to receive(:connect_to_new) } - describe "::migrate" do - it "switches and migrates" do - expect(Apartment::Tenant).to receive(:switch).with(tenant).and_call_original - expect(ActiveRecord::Migrator).to receive(:migrate) + context "with ActiveRecord below 5.2.0" do + before do + allow(ActiveRecord::Migrator).to receive(:migrations_paths) { %w(spec/dummy/db/migrate) } + allow(Apartment::Migrator).to receive(:activerecord_below_5_2?) { true } + end + + describe "::migrate" do + it "switches and migrates" do + expect(Apartment::Tenant).to receive(:switch).with(tenant).and_call_original + expect(ActiveRecord::Migrator).to receive(:migrate) - Apartment::Migrator.migrate(tenant) + Apartment::Migrator.migrate(tenant) + end end - end - describe "::run" do - it "switches and runs" do - expect(Apartment::Tenant).to receive(:switch).with(tenant).and_call_original - expect(ActiveRecord::Migrator).to receive(:run).with(:up, anything, 1234) + describe "::run" do + it "switches and runs" do + expect(Apartment::Tenant).to receive(:switch).with(tenant).and_call_original + expect(ActiveRecord::Migrator).to receive(:run).with(:up, anything, 1234) - Apartment::Migrator.run(:up, tenant, 1234) + Apartment::Migrator.run(:up, tenant, 1234) + end + end + + describe "::rollback" do + it "switches and rolls back" do + expect(Apartment::Tenant).to receive(:switch).with(tenant).and_call_original + expect(ActiveRecord::Migrator).to receive(:rollback).with(anything, 2) + + Apartment::Migrator.rollback(tenant, 2) + end end end - describe "::rollback" do - it "switches and rolls back" do - expect(Apartment::Tenant).to receive(:switch).with(tenant).and_call_original - expect(ActiveRecord::Migrator).to receive(:rollback).with(anything, 2) + context "with ActiveRecord abowe or equal to 5.2.0" do + before do + allow(Apartment::Migrator).to receive(:activerecord_below_5_2?) { false } + end + + describe "::migrate" do + it "switches and migrates" do + expect(Apartment::Tenant).to receive(:switch).with(tenant).and_call_original + expect_any_instance_of(ActiveRecord::MigrationContext).to receive(:migrate) + + Apartment::Migrator.migrate(tenant) + end + end + + describe "::run" do + it "switches and runs" do + expect(Apartment::Tenant).to receive(:switch).with(tenant).and_call_original + expect_any_instance_of(ActiveRecord::MigrationContext).to receive(:run).with(:up, 1234) + + Apartment::Migrator.run(:up, tenant, 1234) + end + end + + describe "::rollback" do + it "switches and rolls back" do + expect(Apartment::Tenant).to receive(:switch).with(tenant).and_call_original + expect_any_instance_of(ActiveRecord::MigrationContext).to receive(:rollback).with(2) - Apartment::Migrator.rollback(tenant, 2) + Apartment::Migrator.rollback(tenant, 2) + end end end end From 581ad0c7f1d5a88e2860eecdd8215bdf92bb58cc Mon Sep 17 00:00:00 2001 From: meganemura Date: Mon, 26 Feb 2018 17:20:06 +0900 Subject: [PATCH 63/91] Specify pg version --- gemfiles/rails_4_2.gemfile | 3 ++- gemfiles/rails_5_0.gemfile | 2 ++ gemfiles/rails_5_1.gemfile | 2 ++ 3 files changed, 6 insertions(+), 1 deletion(-) diff --git a/gemfiles/rails_4_2.gemfile b/gemfiles/rails_4_2.gemfile index bb2f4f26..9c0545fe 100644 --- a/gemfiles/rails_4_2.gemfile +++ b/gemfiles/rails_4_2.gemfile @@ -13,7 +13,8 @@ if defined?(JRUBY_VERSION) gem 'activerecord-jdbc-adapter', '~> 1.3' gem 'activerecord-jdbcpostgresql-adapter', '~> 1.3' gem 'activerecord-jdbcmysql-adapter', '~> 1.3' +else + gem "pg", "< 1.0.0" end gemspec path: "../" - diff --git a/gemfiles/rails_5_0.gemfile b/gemfiles/rails_5_0.gemfile index f43969c4..4d5b1659 100644 --- a/gemfiles/rails_5_0.gemfile +++ b/gemfiles/rails_5_0.gemfile @@ -13,6 +13,8 @@ if defined?(JRUBY_VERSION) gem 'activerecord-jdbc-adapter', '~> 50.0' gem 'activerecord-jdbcpostgresql-adapter', '~> 50.0' gem 'activerecord-jdbcmysql-adapter', '~> 50.0' +else + gem "pg", "< 1.0.0" end gemspec path: "../" diff --git a/gemfiles/rails_5_1.gemfile b/gemfiles/rails_5_1.gemfile index 1717ee9d..956f5199 100644 --- a/gemfiles/rails_5_1.gemfile +++ b/gemfiles/rails_5_1.gemfile @@ -13,6 +13,8 @@ if defined?(JRUBY_VERSION) gem 'activerecord-jdbc-adapter', '~> 51.0' gem 'activerecord-jdbcpostgresql-adapter', '~> 51.0' gem 'activerecord-jdbcmysql-adapter', '~> 51.0' +else + gem "pg", "< 1.0.0" end gemspec path: "../" From a092cd55df487719f76654bcad4f6ba8dd930e2f Mon Sep 17 00:00:00 2001 From: meganemura Date: Mon, 26 Feb 2018 23:25:45 +0900 Subject: [PATCH 64/91] Remove Rails 4.0, 4.1 from Appraisals --- Appraisals | 8 -------- 1 file changed, 8 deletions(-) diff --git a/Appraisals b/Appraisals index a5a1765a..7f40c052 100644 --- a/Appraisals +++ b/Appraisals @@ -1,11 +1,3 @@ -appraise "rails-4-0" do - gem "rails", "~> 4.0.0" -end - -appraise "rails-4-1" do - gem "rails", "~> 4.1.0" -end - appraise "rails-4-2" do gem "rails", "~> 4.2.0" end From da1bc3685253a69bce7b9b619af1ff3acac64764 Mon Sep 17 00:00:00 2001 From: meganemura Date: Mon, 26 Feb 2018 23:35:42 +0900 Subject: [PATCH 65/91] .gitignore: Ignore gemfiles/vendor --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 5588c1d0..01c3930e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ .bundle *.lock gemfiles/*.lock +gemfiles/vendor pkg/* *.log .idea From 804ab53bcf779c346fba1bb5f97200c990df51a0 Mon Sep 17 00:00:00 2001 From: meganemura Date: Mon, 26 Feb 2018 23:35:11 +0900 Subject: [PATCH 66/91] Update Appraisals --- Appraisals | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/Appraisals b/Appraisals index 7f40c052..1915e0f6 100644 --- a/Appraisals +++ b/Appraisals @@ -1,15 +1,44 @@ appraise "rails-4-2" do gem "rails", "~> 4.2.0" + platforms :ruby do + gem "pg", "< 1.0.0" + end + platforms :jruby do + gem 'activerecord-jdbc-adapter', '~> 1.3' + gem 'activerecord-jdbcpostgresql-adapter', '~> 1.3' + gem 'activerecord-jdbcmysql-adapter', '~> 1.3' + end end appraise "rails-5-0" do gem "rails", "~> 5.0.0" + platforms :ruby do + gem "pg", "< 1.0.0" + end + platforms :jruby do + gem 'activerecord-jdbc-adapter', '~> 50.0' + gem 'activerecord-jdbcpostgresql-adapter', '~> 50.0' + gem 'activerecord-jdbcmysql-adapter', '~> 50.0' + end end appraise "rails-5-1" do gem "rails", "5.1.1" + platforms :ruby do + gem "pg", "< 1.0.0" + end + platforms :jruby do + gem 'activerecord-jdbc-adapter', '~> 51.0' + gem 'activerecord-jdbcpostgresql-adapter', '~> 51.0' + gem 'activerecord-jdbcmysql-adapter', '~> 51.0' + end end appraise "rails-master" do gem "rails", git: 'https://github.com/rails/rails.git' + platforms :jruby do + gem 'activerecord-jdbc-adapter', '~> 51.0' + gem 'activerecord-jdbcpostgresql-adapter', '~> 51.0' + gem 'activerecord-jdbcmysql-adapter', '~> 51.0' + end end From 777c36629f97cbccb7063b87817bf6a3d3170336 Mon Sep 17 00:00:00 2001 From: meganemura Date: Mon, 26 Feb 2018 23:36:00 +0900 Subject: [PATCH 67/91] $ appraisal install --- gemfiles/rails_4_2.gemfile | 12 +++++++----- gemfiles/rails_5_0.gemfile | 12 +++++++----- gemfiles/rails_5_1.gemfile | 12 +++++++----- gemfiles/rails_master.gemfile | 8 ++++---- 4 files changed, 25 insertions(+), 19 deletions(-) diff --git a/gemfiles/rails_4_2.gemfile b/gemfiles/rails_4_2.gemfile index 9c0545fe..45c663b6 100644 --- a/gemfiles/rails_4_2.gemfile +++ b/gemfiles/rails_4_2.gemfile @@ -9,12 +9,14 @@ group :local do gem "guard-rspec", "~> 4.2" end -if defined?(JRUBY_VERSION) - gem 'activerecord-jdbc-adapter', '~> 1.3' - gem 'activerecord-jdbcpostgresql-adapter', '~> 1.3' - gem 'activerecord-jdbcmysql-adapter', '~> 1.3' -else +platforms :ruby do gem "pg", "< 1.0.0" end +platforms :jruby do + gem "activerecord-jdbc-adapter", "~> 1.3" + gem "activerecord-jdbcpostgresql-adapter", "~> 1.3" + gem "activerecord-jdbcmysql-adapter", "~> 1.3" +end + gemspec path: "../" diff --git a/gemfiles/rails_5_0.gemfile b/gemfiles/rails_5_0.gemfile index 4d5b1659..07d99a14 100644 --- a/gemfiles/rails_5_0.gemfile +++ b/gemfiles/rails_5_0.gemfile @@ -9,12 +9,14 @@ group :local do gem "guard-rspec", "~> 4.2" end -if defined?(JRUBY_VERSION) - gem 'activerecord-jdbc-adapter', '~> 50.0' - gem 'activerecord-jdbcpostgresql-adapter', '~> 50.0' - gem 'activerecord-jdbcmysql-adapter', '~> 50.0' -else +platforms :ruby do gem "pg", "< 1.0.0" end +platforms :jruby do + gem "activerecord-jdbc-adapter", "~> 50.0" + gem "activerecord-jdbcpostgresql-adapter", "~> 50.0" + gem "activerecord-jdbcmysql-adapter", "~> 50.0" +end + gemspec path: "../" diff --git a/gemfiles/rails_5_1.gemfile b/gemfiles/rails_5_1.gemfile index 956f5199..9036c5b0 100644 --- a/gemfiles/rails_5_1.gemfile +++ b/gemfiles/rails_5_1.gemfile @@ -9,12 +9,14 @@ group :local do gem "guard-rspec", "~> 4.2" end -if defined?(JRUBY_VERSION) - gem 'activerecord-jdbc-adapter', '~> 51.0' - gem 'activerecord-jdbcpostgresql-adapter', '~> 51.0' - gem 'activerecord-jdbcmysql-adapter', '~> 51.0' -else +platforms :ruby do gem "pg", "< 1.0.0" end +platforms :jruby do + gem "activerecord-jdbc-adapter", "~> 51.0" + gem "activerecord-jdbcpostgresql-adapter", "~> 51.0" + gem "activerecord-jdbcmysql-adapter", "~> 51.0" +end + gemspec path: "../" diff --git a/gemfiles/rails_master.gemfile b/gemfiles/rails_master.gemfile index 9ab4b629..e5978830 100644 --- a/gemfiles/rails_master.gemfile +++ b/gemfiles/rails_master.gemfile @@ -9,10 +9,10 @@ group :local do gem "guard-rspec", "~> 4.2" end -if defined?(JRUBY_VERSION) - gem 'activerecord-jdbc-adapter', '~> 51.0' - gem 'activerecord-jdbcpostgresql-adapter', '~> 51.0' - gem 'activerecord-jdbcmysql-adapter', '~> 51.0' +platforms :jruby do + gem "activerecord-jdbc-adapter", "~> 51.0" + gem "activerecord-jdbcpostgresql-adapter", "~> 51.0" + gem "activerecord-jdbcmysql-adapter", "~> 51.0" end gemspec path: "../" From e4ad593adbb0cd710637b775445c12d4db6ee868 Mon Sep 17 00:00:00 2001 From: meganemura Date: Tue, 27 Feb 2018 00:20:25 +0900 Subject: [PATCH 68/91] Appraisals: Add rails-5-2 --- Appraisals | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Appraisals b/Appraisals index 1915e0f6..7d2486a7 100644 --- a/Appraisals +++ b/Appraisals @@ -34,6 +34,15 @@ appraise "rails-5-1" do end end +appraise "rails-5-2" do + gem "rails", "~> 5.2.0.rc1" + platforms :jruby do + gem 'activerecord-jdbc-adapter', '~> 51.0' + gem 'activerecord-jdbcpostgresql-adapter', '~> 51.0' + gem 'activerecord-jdbcmysql-adapter', '~> 51.0' + end +end + appraise "rails-master" do gem "rails", git: 'https://github.com/rails/rails.git' platforms :jruby do From f1558a9a7244ac7a6bd3c709cf2c11a3e91cf8bd Mon Sep 17 00:00:00 2001 From: meganemura Date: Tue, 27 Feb 2018 00:21:02 +0900 Subject: [PATCH 69/91] $ appraisal install --- gemfiles/rails_5_2.gemfile | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 gemfiles/rails_5_2.gemfile diff --git a/gemfiles/rails_5_2.gemfile b/gemfiles/rails_5_2.gemfile new file mode 100644 index 00000000..3b8a7e75 --- /dev/null +++ b/gemfiles/rails_5_2.gemfile @@ -0,0 +1,18 @@ +# This file was generated by Appraisal + +source "http://rubygems.org" + +gem "rails", "~> 5.2.0.rc1" + +group :local do + gem "pry" + gem "guard-rspec", "~> 4.2" +end + +platforms :jruby do + gem "activerecord-jdbc-adapter", "~> 51.0" + gem "activerecord-jdbcpostgresql-adapter", "~> 51.0" + gem "activerecord-jdbcmysql-adapter", "~> 51.0" +end + +gemspec path: "../" From c4c6198384cc0490d1badcee80f10797165412a3 Mon Sep 17 00:00:00 2001 From: meganemura Date: Tue, 27 Feb 2018 00:22:16 +0900 Subject: [PATCH 70/91] Test against Rails 5.2 --- .travis.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.travis.yml b/.travis.yml index 7e59ba1a..831c3418 100644 --- a/.travis.yml +++ b/.travis.yml @@ -11,6 +11,7 @@ gemfile: - gemfiles/rails_4_2.gemfile - gemfiles/rails_5_0.gemfile - gemfiles/rails_5_1.gemfile + - gemfiles/rails_5_2.gemfile - gemfiles/rails_master.gemfile bundler_args: --without local before_install: @@ -27,10 +28,14 @@ matrix: gemfile: gemfiles/rails_5_0.gemfile - rvm: 2.1.9 gemfile: gemfiles/rails_5_1.gemfile + - rvm: 2.1.9 + gemfile: gemfiles/rails_5_2.gemfile - rvm: 2.1.9 gemfile: gemfiles/rails_master.gemfile - rvm: jruby-9.1.15.0 gemfile: gemfiles/rails_5_1.gemfile + - rvm: jruby-9.1.15.0 + gemfile: gemfiles/rails_5_2.gemfile - rvm: jruby-9.1.15.0 gemfile: gemfiles/rails_master.gemfile fast_finish: true From 1c185841e3d98e8b9290be2a02373670ea606bd3 Mon Sep 17 00:00:00 2001 From: meganemura Date: Tue, 27 Feb 2018 01:18:40 +0900 Subject: [PATCH 71/91] Skip tests for compatibility --- spec/unit/migrator_spec.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spec/unit/migrator_spec.rb b/spec/unit/migrator_spec.rb index 1e67131d..e4f1ca92 100644 --- a/spec/unit/migrator_spec.rb +++ b/spec/unit/migrator_spec.rb @@ -8,7 +8,7 @@ # Don't need a real switch here, just testing behaviour before { allow(Apartment::Tenant.adapter).to receive(:connect_to_new) } - context "with ActiveRecord below 5.2.0" do + context "with ActiveRecord below 5.2.0", skip: ActiveRecord.version >= Gem::Version.new("5.2.0") do before do allow(ActiveRecord::Migrator).to receive(:migrations_paths) { %w(spec/dummy/db/migrate) } allow(Apartment::Migrator).to receive(:activerecord_below_5_2?) { true } @@ -42,7 +42,7 @@ end end - context "with ActiveRecord abowe or equal to 5.2.0" do + context "with ActiveRecord abowe or equal to 5.2.0", skip: ActiveRecord.version < Gem::Version.new("5.2.0") do before do allow(Apartment::Migrator).to receive(:activerecord_below_5_2?) { false } end From eb200cc24b50cfb5aa344cfe4f3caec4790bcc35 Mon Sep 17 00:00:00 2001 From: meganemura Date: Tue, 27 Feb 2018 01:35:58 +0900 Subject: [PATCH 72/91] Remove redundant parens --- Rakefile | 2 +- lib/apartment/migrator.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Rakefile b/Rakefile index 947b8868..df67edc6 100644 --- a/Rakefile +++ b/Rakefile @@ -116,7 +116,7 @@ def my_config end def activerecord_below_5_2? - ActiveRecord.version.release() < Gem::Version.new('5.2.0') + ActiveRecord.version.release < Gem::Version.new('5.2.0') end def migrate diff --git a/lib/apartment/migrator.rb b/lib/apartment/migrator.rb index d8a04047..c99e9a3b 100644 --- a/lib/apartment/migrator.rb +++ b/lib/apartment/migrator.rb @@ -45,7 +45,7 @@ def rollback(database, step = 1) private def activerecord_below_5_2? - ActiveRecord.version.release() < Gem::Version.new('5.2.0') + ActiveRecord.version.release < Gem::Version.new('5.2.0') end end end From ed7cd94a0b65cc6ef6b3b4eee8c49001b33df39c Mon Sep 17 00:00:00 2001 From: meganemura Date: Tue, 27 Feb 2018 01:38:15 +0900 Subject: [PATCH 73/91] Fix typo --- spec/unit/migrator_spec.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spec/unit/migrator_spec.rb b/spec/unit/migrator_spec.rb index e4f1ca92..242b6b97 100644 --- a/spec/unit/migrator_spec.rb +++ b/spec/unit/migrator_spec.rb @@ -42,7 +42,7 @@ end end - context "with ActiveRecord abowe or equal to 5.2.0", skip: ActiveRecord.version < Gem::Version.new("5.2.0") do + context "with ActiveRecord above or equal to 5.2.0", skip: ActiveRecord.version < Gem::Version.new("5.2.0") do before do allow(Apartment::Migrator).to receive(:activerecord_below_5_2?) { false } end From e8f2cee84569743d8797373d093805794995dc69 Mon Sep 17 00:00:00 2001 From: Stuart Terrett Date: Mon, 19 Mar 2018 12:41:20 -0400 Subject: [PATCH 74/91] Substitute tenant name in pg dump --- lib/apartment.rb | 8 +++++-- lib/apartment/adapters/postgresql_adapter.rb | 22 ++++++++++++++------ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/apartment.rb b/lib/apartment.rb index e888cf17..b20a7728 100644 --- a/lib/apartment.rb +++ b/lib/apartment.rb @@ -10,8 +10,8 @@ class << self extend Forwardable - ACCESSOR_METHODS = [:use_schemas, :use_sql, :seed_after_create, :prepend_environment, :append_environment, :with_multi_server_setup ] - WRITER_METHODS = [:tenant_names, :database_schema_file, :excluded_models, :default_schema, :persistent_schemas, :connection_class, :tld_length, :db_migrate_tenants, :seed_data_file, :parallel_migration_threads] + ACCESSOR_METHODS = [:use_schemas, :use_sql, :seed_after_create, :prepend_environment, :append_environment, :with_multi_server_setup] + WRITER_METHODS = [:tenant_names, :database_schema_file, :excluded_models, :default_schema, :persistent_schemas, :connection_class, :tld_length, :db_migrate_tenants, :seed_data_file, :parallel_migration_threads, :pg_excluded_names] attr_accessor(*ACCESSOR_METHODS) attr_writer(*WRITER_METHODS) @@ -78,6 +78,10 @@ def seed_data_file @seed_data_file = "#{Rails.root}/db/seeds.rb" end + def pg_excluded_names + @pg_excluded_names || [] + end + # Reset all the config for Apartment def reset (ACCESSOR_METHODS + WRITER_METHODS).each{|method| remove_instance_variable(:"@#{method}") if instance_variable_defined?(:"@#{method}") } diff --git a/lib/apartment/adapters/postgresql_adapter.rb b/lib/apartment/adapters/postgresql_adapter.rb index f428cb8f..bb35421e 100644 --- a/lib/apartment/adapters/postgresql_adapter.rb +++ b/lib/apartment/adapters/postgresql_adapter.rb @@ -155,7 +155,7 @@ def pg_dump_schema # @return {String} raw SQL contaning inserts with data from schema_migrations # def pg_dump_schema_migrations_data - with_pg_env { `pg_dump -a --inserts -t schema_migrations -t ar_internal_metadata -n #{default_tenant} #{dbname}` } + with_pg_env { `pg_dump -a --inserts -t schema_migrations -n #{default_tenant} #{dbname}` } end # Temporary set Postgresql related environment variables if there are in @config @@ -180,11 +180,21 @@ def with_pg_env(&block) def patch_search_path(sql) search_path = "SET search_path = \"#{current}\", #{default_tenant};" - sql - .split("\n") - .select {|line| check_input_against_regexps(line, PSQL_DUMP_BLACKLISTED_STATEMENTS).empty?} - .prepend(search_path) - .join("\n") + swap_schema_qualifier(sql) + .split("\n") + .select {|line| check_input_against_regexps(line, PSQL_DUMP_BLACKLISTED_STATEMENTS).empty?} + .prepend(search_path) + .join("\n") + end + + def swap_schema_qualifier(sql) + sql.gsub(/#{default_tenant}\.\w*/) do |match| + if Apartment.pg_excluded_names.any? { |name| match.include? name } + match + else + match.gsub(default_tenant, current) + end + end end # Checks if any of regexps matches against input From 478d7795b8e73f36558cf99511c9b6f6c2e1c720 Mon Sep 17 00:00:00 2001 From: Stuart Terrett Date: Tue, 27 Mar 2018 08:32:26 -0400 Subject: [PATCH 75/91] Add note to initializer template --- .../apartment/install/templates/apartment.rb | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/lib/generators/apartment/install/templates/apartment.rb b/lib/generators/apartment/install/templates/apartment.rb index 5e573d2f..6c7c52fb 100644 --- a/lib/generators/apartment/install/templates/apartment.rb +++ b/lib/generators/apartment/install/templates/apartment.rb @@ -85,6 +85,16 @@ # Uncomment the line below if you want to disable this behaviour in production. # # config.prepend_environment = !Rails.env.production? + + # When using PostgreSQL schemas, the database dump will be namespaced, and + # apartment will substitute the default namespace (usually public) with the + # name of the new tenant when creating a new tenant. Some items must maintain + # a reference to the default namespace (ie public) - for instance, a default + # uuid generation. Uncomment the line below to create a list of namespaced + # items in the schema dump that should *not* have their namespace replaced by + # the new tenant + # + # config.pg_excluded_names = ["uuid_generate_v4"] end # Setup a custom Tenant switching middleware. The Proc should return the name of the Tenant that From 944d00e6a689d2270349b17e105d5feb4617b6cb Mon Sep 17 00:00:00 2001 From: meganemura Date: Tue, 27 Mar 2018 22:40:58 +0900 Subject: [PATCH 76/91] Pin mysql2 version in gemfiles mysql2 gem has a breaking change. https://github.com/brianmario/mysql2/releases/tag/0.5.0 --- gemfiles/rails_4_2.gemfile | 1 + gemfiles/rails_5_0.gemfile | 4 ++++ gemfiles/rails_5_1.gemfile | 4 ++++ gemfiles/rails_5_2.gemfile | 6 ++++++ gemfiles/rails_master.gemfile | 6 ++++++ 5 files changed, 21 insertions(+) diff --git a/gemfiles/rails_4_2.gemfile b/gemfiles/rails_4_2.gemfile index 45c663b6..ef31a500 100644 --- a/gemfiles/rails_4_2.gemfile +++ b/gemfiles/rails_4_2.gemfile @@ -11,6 +11,7 @@ end platforms :ruby do gem "pg", "< 1.0.0" + gem "mysql2", "~> 0.4.0" end platforms :jruby do diff --git a/gemfiles/rails_5_0.gemfile b/gemfiles/rails_5_0.gemfile index 07d99a14..af23462b 100644 --- a/gemfiles/rails_5_0.gemfile +++ b/gemfiles/rails_5_0.gemfile @@ -11,6 +11,10 @@ end platforms :ruby do gem "pg", "< 1.0.0" + + # TODO: Remove pinning if rails can use mysql2 0.5.0 + # https://github.com/brianmario/mysql2/releases/tag/0.5.0 + gem "mysql2", "~> 0.4.0" end platforms :jruby do diff --git a/gemfiles/rails_5_1.gemfile b/gemfiles/rails_5_1.gemfile index 9036c5b0..25f73271 100644 --- a/gemfiles/rails_5_1.gemfile +++ b/gemfiles/rails_5_1.gemfile @@ -11,6 +11,10 @@ end platforms :ruby do gem "pg", "< 1.0.0" + + # TODO: Remove pinning if rails can use mysql2 0.5.0 + # https://github.com/brianmario/mysql2/releases/tag/0.5.0 + gem "mysql2", "~> 0.4.0" end platforms :jruby do diff --git a/gemfiles/rails_5_2.gemfile b/gemfiles/rails_5_2.gemfile index 3b8a7e75..60165402 100644 --- a/gemfiles/rails_5_2.gemfile +++ b/gemfiles/rails_5_2.gemfile @@ -9,6 +9,12 @@ group :local do gem "guard-rspec", "~> 4.2" end +platforms :ruby do + # TODO: Remove pinning if rails can use mysql2 0.5.0 + # https://github.com/brianmario/mysql2/releases/tag/0.5.0 + gem "mysql2", "~> 0.4.0" +end + platforms :jruby do gem "activerecord-jdbc-adapter", "~> 51.0" gem "activerecord-jdbcpostgresql-adapter", "~> 51.0" diff --git a/gemfiles/rails_master.gemfile b/gemfiles/rails_master.gemfile index e5978830..98ab8e7a 100644 --- a/gemfiles/rails_master.gemfile +++ b/gemfiles/rails_master.gemfile @@ -9,6 +9,12 @@ group :local do gem "guard-rspec", "~> 4.2" end +platforms :ruby do + # TODO: Remove pinning if rails can use mysql2 0.5.0 + # https://github.com/brianmario/mysql2/releases/tag/0.5.0 + gem "mysql2", "~> 0.4.0" +end + platforms :jruby do gem "activerecord-jdbc-adapter", "~> 51.0" gem "activerecord-jdbcpostgresql-adapter", "~> 51.0" From fc1765f950260f8d68c23160ef758848f998c233 Mon Sep 17 00:00:00 2001 From: meganemura Date: Tue, 27 Mar 2018 23:35:33 +0900 Subject: [PATCH 77/91] gemfiles/* should be generated by `appraisal install` --- Appraisals | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/Appraisals b/Appraisals index 7d2486a7..cf0f9c3a 100644 --- a/Appraisals +++ b/Appraisals @@ -2,6 +2,7 @@ appraise "rails-4-2" do gem "rails", "~> 4.2.0" platforms :ruby do gem "pg", "< 1.0.0" + gem "mysql2", "~> 0.4.0" end platforms :jruby do gem 'activerecord-jdbc-adapter', '~> 1.3' @@ -14,6 +15,9 @@ appraise "rails-5-0" do gem "rails", "~> 5.0.0" platforms :ruby do gem "pg", "< 1.0.0" + # TODO: Remove pinning if rails can use mysql2 0.5.0 + # https://github.com/brianmario/mysql2/releases/tag/0.5.0 + gem "mysql2", "~> 0.4.0" end platforms :jruby do gem 'activerecord-jdbc-adapter', '~> 50.0' @@ -26,6 +30,9 @@ appraise "rails-5-1" do gem "rails", "5.1.1" platforms :ruby do gem "pg", "< 1.0.0" + # TODO: Remove pinning if rails can use mysql2 0.5.0 + # https://github.com/brianmario/mysql2/releases/tag/0.5.0 + gem "mysql2", "~> 0.4.0" end platforms :jruby do gem 'activerecord-jdbc-adapter', '~> 51.0' @@ -36,6 +43,11 @@ end appraise "rails-5-2" do gem "rails", "~> 5.2.0.rc1" + platforms :ruby do + # TODO: Remove pinning if rails can use mysql2 0.5.0 + # https://github.com/brianmario/mysql2/releases/tag/0.5.0 + gem "mysql2", "~> 0.4.0" + end platforms :jruby do gem 'activerecord-jdbc-adapter', '~> 51.0' gem 'activerecord-jdbcpostgresql-adapter', '~> 51.0' @@ -45,6 +57,11 @@ end appraise "rails-master" do gem "rails", git: 'https://github.com/rails/rails.git' + platforms :ruby do + # TODO: Remove pinning if rails can use mysql2 0.5.0 + # https://github.com/brianmario/mysql2/releases/tag/0.5.0 + gem "mysql2", "~> 0.4.0" + end platforms :jruby do gem 'activerecord-jdbc-adapter', '~> 51.0' gem 'activerecord-jdbcpostgresql-adapter', '~> 51.0' From 964fc0c9ab275419cb669db1d4d6466c988ef9a4 Mon Sep 17 00:00:00 2001 From: meganemura Date: Tue, 27 Mar 2018 23:36:22 +0900 Subject: [PATCH 78/91] $ appraisal install --- gemfiles/rails_5_0.gemfile | 3 --- gemfiles/rails_5_1.gemfile | 3 --- gemfiles/rails_5_2.gemfile | 2 -- gemfiles/rails_master.gemfile | 2 -- 4 files changed, 10 deletions(-) diff --git a/gemfiles/rails_5_0.gemfile b/gemfiles/rails_5_0.gemfile index af23462b..5dc01061 100644 --- a/gemfiles/rails_5_0.gemfile +++ b/gemfiles/rails_5_0.gemfile @@ -11,9 +11,6 @@ end platforms :ruby do gem "pg", "< 1.0.0" - - # TODO: Remove pinning if rails can use mysql2 0.5.0 - # https://github.com/brianmario/mysql2/releases/tag/0.5.0 gem "mysql2", "~> 0.4.0" end diff --git a/gemfiles/rails_5_1.gemfile b/gemfiles/rails_5_1.gemfile index 25f73271..8195b636 100644 --- a/gemfiles/rails_5_1.gemfile +++ b/gemfiles/rails_5_1.gemfile @@ -11,9 +11,6 @@ end platforms :ruby do gem "pg", "< 1.0.0" - - # TODO: Remove pinning if rails can use mysql2 0.5.0 - # https://github.com/brianmario/mysql2/releases/tag/0.5.0 gem "mysql2", "~> 0.4.0" end diff --git a/gemfiles/rails_5_2.gemfile b/gemfiles/rails_5_2.gemfile index 60165402..35eb4dc9 100644 --- a/gemfiles/rails_5_2.gemfile +++ b/gemfiles/rails_5_2.gemfile @@ -10,8 +10,6 @@ group :local do end platforms :ruby do - # TODO: Remove pinning if rails can use mysql2 0.5.0 - # https://github.com/brianmario/mysql2/releases/tag/0.5.0 gem "mysql2", "~> 0.4.0" end diff --git a/gemfiles/rails_master.gemfile b/gemfiles/rails_master.gemfile index 98ab8e7a..25410421 100644 --- a/gemfiles/rails_master.gemfile +++ b/gemfiles/rails_master.gemfile @@ -10,8 +10,6 @@ group :local do end platforms :ruby do - # TODO: Remove pinning if rails can use mysql2 0.5.0 - # https://github.com/brianmario/mysql2/releases/tag/0.5.0 gem "mysql2", "~> 0.4.0" end From e0f5fd7ec857e9121ac8dfa49015fcff5429bfbe Mon Sep 17 00:00:00 2001 From: Stuart Terrett Date: Tue, 27 Mar 2018 14:19:00 -0400 Subject: [PATCH 79/91] Preserve search path during pg tenant creation --- lib/apartment/adapters/postgresql_adapter.rb | 28 ++++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/lib/apartment/adapters/postgresql_adapter.rb b/lib/apartment/adapters/postgresql_adapter.rb index bb35421e..42ce4839 100644 --- a/lib/apartment/adapters/postgresql_adapter.rb +++ b/lib/apartment/adapters/postgresql_adapter.rb @@ -113,12 +113,24 @@ class PostgresqlSchemaFromSqlAdapter < PostgresqlSchemaAdapter ] def import_database_schema - clone_pg_schema - copy_schema_migrations + preserving_search_path do + clone_pg_schema + copy_schema_migrations + end end private + # Re-set search path after the schema is imported. + # Postgres now sets search path to empty before dumping the schema + # and it mut be reset + # + def preserving_search_path + search_path = Apartment.connection.execute("show search_path").first["search_path"] + yield + Apartment.connection.execute("set search_path = #{search_path}") + end + # Clone default schema into new schema named after current tenant # def clone_pg_schema @@ -180,11 +192,11 @@ def with_pg_env(&block) def patch_search_path(sql) search_path = "SET search_path = \"#{current}\", #{default_tenant};" - swap_schema_qualifier(sql) - .split("\n") - .select {|line| check_input_against_regexps(line, PSQL_DUMP_BLACKLISTED_STATEMENTS).empty?} - .prepend(search_path) - .join("\n") + swap_schema_qualifier(sql) + .split("\n") + .select {|line| check_input_against_regexps(line, PSQL_DUMP_BLACKLISTED_STATEMENTS).empty?} + .prepend(search_path) + .join("\n") end def swap_schema_qualifier(sql) @@ -192,7 +204,7 @@ def swap_schema_qualifier(sql) if Apartment.pg_excluded_names.any? { |name| match.include? name } match else - match.gsub(default_tenant, current) + match.gsub(default_tenant, %{"#{current}"}) end end end From 5a49053d87ae9fb4dc4edb61992c164073ab8453 Mon Sep 17 00:00:00 2001 From: Stuart Terrett Date: Tue, 27 Mar 2018 14:19:30 -0400 Subject: [PATCH 80/91] Bump postgres version in Jenkins --- docker-compose.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docker-compose.yml b/docker-compose.yml index 937e2b3b..23b556f0 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ version: '2' services: postgresql: - image: postgres:9.2 + image: postgres:10.3 environment: POSTGRES_PASSWORD: "" ports: From 528df25f4701952038de85eb474d1600004add49 Mon Sep 17 00:00:00 2001 From: Stuart Terrett Date: Tue, 27 Mar 2018 14:49:40 -0400 Subject: [PATCH 81/91] Properly namespace ar_internal_metadata dump --- lib/apartment/adapters/postgresql_adapter.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/apartment/adapters/postgresql_adapter.rb b/lib/apartment/adapters/postgresql_adapter.rb index 42ce4839..cac82c58 100644 --- a/lib/apartment/adapters/postgresql_adapter.rb +++ b/lib/apartment/adapters/postgresql_adapter.rb @@ -167,7 +167,7 @@ def pg_dump_schema # @return {String} raw SQL contaning inserts with data from schema_migrations # def pg_dump_schema_migrations_data - with_pg_env { `pg_dump -a --inserts -t schema_migrations -n #{default_tenant} #{dbname}` } + with_pg_env { `pg_dump -a --inserts -t #{default_tenant}.schema_migrations -t #{default_tenant}.ar_internal_metadata #{dbname}` } end # Temporary set Postgresql related environment variables if there are in @config From 803d510342740cf4d0136fa3cc5aa968bc45e6e0 Mon Sep 17 00:00:00 2001 From: meganemura Date: Fri, 13 Apr 2018 18:10:54 +0900 Subject: [PATCH 82/91] Drop support for JRuby + Rails 5.0 --- .travis.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.travis.yml b/.travis.yml index 831c3418..299033e2 100644 --- a/.travis.yml +++ b/.travis.yml @@ -23,6 +23,8 @@ matrix: allow_failures: - rvm: ruby-head - gemfile: gemfiles/rails_master.gemfile + - rvm: jruby-9.1.15.0 + gemfile: gemfiles/rails_5_0.gemfile exclude: - rvm: 2.1.9 gemfile: gemfiles/rails_5_0.gemfile From 010cd789ac49dc7750680bb1094a919907dda3c0 Mon Sep 17 00:00:00 2001 From: meganemura Date: Fri, 13 Apr 2018 15:42:41 +0900 Subject: [PATCH 83/91] Appraisals: Use rails 5.2.0 --- Appraisals | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Appraisals b/Appraisals index cf0f9c3a..fc4a99b8 100644 --- a/Appraisals +++ b/Appraisals @@ -42,7 +42,7 @@ appraise "rails-5-1" do end appraise "rails-5-2" do - gem "rails", "~> 5.2.0.rc1" + gem "rails", "~> 5.2.0" platforms :ruby do # TODO: Remove pinning if rails can use mysql2 0.5.0 # https://github.com/brianmario/mysql2/releases/tag/0.5.0 From b4c1aeae085087443123af8da5d0386cd5445ff1 Mon Sep 17 00:00:00 2001 From: meganemura Date: Fri, 13 Apr 2018 15:44:25 +0900 Subject: [PATCH 84/91] $ appraisal install --- gemfiles/rails_5_2.gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gemfiles/rails_5_2.gemfile b/gemfiles/rails_5_2.gemfile index 35eb4dc9..7ece66a9 100644 --- a/gemfiles/rails_5_2.gemfile +++ b/gemfiles/rails_5_2.gemfile @@ -2,7 +2,7 @@ source "http://rubygems.org" -gem "rails", "~> 5.2.0.rc1" +gem "rails", "~> 5.2.0" group :local do gem "pry" From 7e08174d36aa3d082dd1597fc8531a65d661d8f8 Mon Sep 17 00:00:00 2001 From: meganemura Date: Sat, 14 Apr 2018 01:05:46 +0900 Subject: [PATCH 85/91] Use guard clause --- lib/apartment/railtie.rb | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/apartment/railtie.rb b/lib/apartment/railtie.rb index 09dade09..fa3be67e 100644 --- a/lib/apartment/railtie.rb +++ b/lib/apartment/railtie.rb @@ -27,11 +27,11 @@ class Railtie < Rails::Railtie # See the middleware/console declarations below to help with this. Hope to fix that soon. # config.to_prepare do + next if ARGV.any? { |arg| arg =~ /\Aassets:(?:precompile|clean)\z/ } + begin - unless ARGV.any? { |arg| arg =~ /\Aassets:(?:precompile|clean)\z/ } - Apartment.connection_class.connection_pool.with_connection do - Apartment::Tenant.init - end + Apartment.connection_class.connection_pool.with_connection do + Apartment::Tenant.init end rescue ::ActiveRecord::NoDatabaseError => e puts e.message From 5211eb43185c176d99a1c05167fa2e8cf3587c27 Mon Sep 17 00:00:00 2001 From: meganemura Date: Sat, 14 Apr 2018 01:10:16 +0900 Subject: [PATCH 86/91] Swallow the AR::NoDatabaseError Printing an Error message is a little bit annoying until invoke db:create. --- lib/apartment/railtie.rb | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/apartment/railtie.rb b/lib/apartment/railtie.rb index fa3be67e..6a18833f 100644 --- a/lib/apartment/railtie.rb +++ b/lib/apartment/railtie.rb @@ -33,8 +33,9 @@ class Railtie < Rails::Railtie Apartment.connection_class.connection_pool.with_connection do Apartment::Tenant.init end - rescue ::ActiveRecord::NoDatabaseError => e - puts e.message + rescue ::ActiveRecord::NoDatabaseError + # Since `db:create` and other tasks invoke this block from Rails 5.2.0, + # we need to swallow the error to execute `db:create` properly. end end From 6e52aa3a9f342407bcad873ed3669021ad17a543 Mon Sep 17 00:00:00 2001 From: meganemura Date: Sat, 14 Apr 2018 02:03:46 +0900 Subject: [PATCH 87/91] Appraisals: rails ~> 5.1.0 --- Appraisals | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Appraisals b/Appraisals index fc4a99b8..22bb12c1 100644 --- a/Appraisals +++ b/Appraisals @@ -27,7 +27,7 @@ appraise "rails-5-0" do end appraise "rails-5-1" do - gem "rails", "5.1.1" + gem "rails", "~> 5.1.0" platforms :ruby do gem "pg", "< 1.0.0" # TODO: Remove pinning if rails can use mysql2 0.5.0 From 12058a1e1295a9ad38cdeadbc9fbed692972ff60 Mon Sep 17 00:00:00 2001 From: meganemura Date: Sat, 14 Apr 2018 01:52:29 +0900 Subject: [PATCH 88/91] Remove pinning for mysql2 --- Appraisals | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/Appraisals b/Appraisals index 22bb12c1..ebe71c75 100644 --- a/Appraisals +++ b/Appraisals @@ -15,9 +15,6 @@ appraise "rails-5-0" do gem "rails", "~> 5.0.0" platforms :ruby do gem "pg", "< 1.0.0" - # TODO: Remove pinning if rails can use mysql2 0.5.0 - # https://github.com/brianmario/mysql2/releases/tag/0.5.0 - gem "mysql2", "~> 0.4.0" end platforms :jruby do gem 'activerecord-jdbc-adapter', '~> 50.0' @@ -30,9 +27,6 @@ appraise "rails-5-1" do gem "rails", "~> 5.1.0" platforms :ruby do gem "pg", "< 1.0.0" - # TODO: Remove pinning if rails can use mysql2 0.5.0 - # https://github.com/brianmario/mysql2/releases/tag/0.5.0 - gem "mysql2", "~> 0.4.0" end platforms :jruby do gem 'activerecord-jdbc-adapter', '~> 51.0' @@ -43,11 +37,6 @@ end appraise "rails-5-2" do gem "rails", "~> 5.2.0" - platforms :ruby do - # TODO: Remove pinning if rails can use mysql2 0.5.0 - # https://github.com/brianmario/mysql2/releases/tag/0.5.0 - gem "mysql2", "~> 0.4.0" - end platforms :jruby do gem 'activerecord-jdbc-adapter', '~> 51.0' gem 'activerecord-jdbcpostgresql-adapter', '~> 51.0' @@ -57,11 +46,6 @@ end appraise "rails-master" do gem "rails", git: 'https://github.com/rails/rails.git' - platforms :ruby do - # TODO: Remove pinning if rails can use mysql2 0.5.0 - # https://github.com/brianmario/mysql2/releases/tag/0.5.0 - gem "mysql2", "~> 0.4.0" - end platforms :jruby do gem 'activerecord-jdbc-adapter', '~> 51.0' gem 'activerecord-jdbcpostgresql-adapter', '~> 51.0' From 750379f453d2e66b5da46eeff125a54ff1a2c760 Mon Sep 17 00:00:00 2001 From: meganemura Date: Sat, 14 Apr 2018 01:53:03 +0900 Subject: [PATCH 89/91] $ appraisal install --- gemfiles/rails_5_0.gemfile | 1 - gemfiles/rails_5_1.gemfile | 3 +-- gemfiles/rails_5_2.gemfile | 4 ---- gemfiles/rails_master.gemfile | 4 ---- 4 files changed, 1 insertion(+), 11 deletions(-) diff --git a/gemfiles/rails_5_0.gemfile b/gemfiles/rails_5_0.gemfile index 5dc01061..07d99a14 100644 --- a/gemfiles/rails_5_0.gemfile +++ b/gemfiles/rails_5_0.gemfile @@ -11,7 +11,6 @@ end platforms :ruby do gem "pg", "< 1.0.0" - gem "mysql2", "~> 0.4.0" end platforms :jruby do diff --git a/gemfiles/rails_5_1.gemfile b/gemfiles/rails_5_1.gemfile index 8195b636..28353198 100644 --- a/gemfiles/rails_5_1.gemfile +++ b/gemfiles/rails_5_1.gemfile @@ -2,7 +2,7 @@ source "http://rubygems.org" -gem "rails", "5.1.1" +gem "rails", "~> 5.1.0" group :local do gem "pry" @@ -11,7 +11,6 @@ end platforms :ruby do gem "pg", "< 1.0.0" - gem "mysql2", "~> 0.4.0" end platforms :jruby do diff --git a/gemfiles/rails_5_2.gemfile b/gemfiles/rails_5_2.gemfile index 7ece66a9..d6c51894 100644 --- a/gemfiles/rails_5_2.gemfile +++ b/gemfiles/rails_5_2.gemfile @@ -9,10 +9,6 @@ group :local do gem "guard-rspec", "~> 4.2" end -platforms :ruby do - gem "mysql2", "~> 0.4.0" -end - platforms :jruby do gem "activerecord-jdbc-adapter", "~> 51.0" gem "activerecord-jdbcpostgresql-adapter", "~> 51.0" diff --git a/gemfiles/rails_master.gemfile b/gemfiles/rails_master.gemfile index 25410421..e5978830 100644 --- a/gemfiles/rails_master.gemfile +++ b/gemfiles/rails_master.gemfile @@ -9,10 +9,6 @@ group :local do gem "guard-rspec", "~> 4.2" end -platforms :ruby do - gem "mysql2", "~> 0.4.0" -end - platforms :jruby do gem "activerecord-jdbc-adapter", "~> 51.0" gem "activerecord-jdbcpostgresql-adapter", "~> 51.0" From 68825d4dacdbf60abb4b0601ae5169b8f1e0f591 Mon Sep 17 00:00:00 2001 From: meganemura Date: Tue, 27 Feb 2018 01:50:23 +0900 Subject: [PATCH 90/91] Prepare release for v2.2.0 --- HISTORY.md | 20 ++++++++++++++++++++ lib/apartment/version.rb | 2 +- 2 files changed, 21 insertions(+), 1 deletion(-) diff --git a/HISTORY.md b/HISTORY.md index 77b30b13..162403fc 100644 --- a/HISTORY.md +++ b/HISTORY.md @@ -1,3 +1,23 @@ +# 2.2.0 + * April 14, 2018 + +## Added + - #523: Add Rails 5.2 support [IngusSkaistkalns] + - #504: Test against Ruby 2.5.0 [ahorek] + - #528: Test against Rails 5.2 [meganemura] + +## Removed + - #504: Remove Rails 4.0/4.1 support [ahorek] + - #545: Stop supporting for JRuby + Rails 5.0 [meganemura] + +## Fixed + - #537: Fix PostgresqlSchemaFromSqlAdapter for newer PostgreSQL [shterrett] + - #532: Issue is reported by [aldrinmartoq] + - #519: Fix exception when main database doesn't exist [mayeco] + +## Changed + - #514: Fix typo [menorval] + # 2.1.0 * December 15, 2017 diff --git a/lib/apartment/version.rb b/lib/apartment/version.rb index bd56fa54..0b599e94 100644 --- a/lib/apartment/version.rb +++ b/lib/apartment/version.rb @@ -1,3 +1,3 @@ module Apartment - VERSION = "2.1.0" + VERSION = "2.2.0" end From 78273c5731deed1607c1b9e8b272be4f032bded9 Mon Sep 17 00:00:00 2001 From: Brent Odell Date: Thu, 14 Feb 2019 09:03:22 -0600 Subject: [PATCH 91/91] Update .ruby-version to 2.5.3 --- .ruby-version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.ruby-version b/.ruby-version index 81af5fe5..aedc15bb 100644 --- a/.ruby-version +++ b/.ruby-version @@ -1 +1 @@ -ruby-2.3 +2.5.3