Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support has many through #1

Open
wants to merge 35 commits into
base: support_has_many_through
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
01e1c61
Update to latest rubygems in CI runs
excid3 Feb 16, 2023
944e499
Test against Ruby 3.2 (#305)
excid3 Feb 16, 2023
5542e7b
Honor the scope passed to associations when performing validations
adrian-gomez May 6, 2022
efb05b2
Ignore tenant scope when finding tenant on sidekiq
santiagoherrero May 20, 2022
d8ec254
Only validate associations if those are changing
adrian-gomez Aug 3, 2022
1e2c820
not to run sidekiq_spec unless sidekiq is installed
nunommc Jun 13, 2023
5c0599b
self pulls Sidekiq middleware if the gem has been loaded
nunommc Jun 13, 2023
7fce32f
Refactor test dependencies (#315)
excid3 Jun 15, 2023
c2e6a2a
Merge branch 'master' into sidekiq-autoload
excid3 Jun 15, 2023
34fd07e
Defer Sidekiq extension to after ActiveRecord loads
excid3 Jun 15, 2023
44a50e8
Merge branch 'master' of github.com:hinthealth/acts_as_tenant into hi…
excid3 Jun 19, 2023
23daba5
Add job_scope configuration
excid3 Jun 19, 2023
7146e9e
Merge branch 'hinthealth-master'
excid3 Jun 19, 2023
0dd17c4
Standardize
excid3 Jun 19, 2023
8752973
Mention acts_as_tenant-delayed_job in readme
excid3 Jun 21, 2023
d29185f
Update README.md with small dev console QoL suggestion (#322)
tvongaza Oct 5, 2023
0497b29
Replace RequestStore dependency with CurrentAttributes (#313)
excid3 Dec 6, 2023
69d2162
Disable legacy_connection_handling on Rails 6.1+ (#308)
HashNotAdam Dec 6, 2023
6ecf5d2
Add a new ActiveJobExtensions which hooks into the serialization and …
tvongaza Dec 7, 2023
02be9be
Revert legacy connection handling config for dummy app
excid3 Dec 7, 2023
6fd5edb
Update appraisals
excid3 Dec 7, 2023
44a5b7c
Drop Rails 5.2 support
excid3 Dec 7, 2023
1f5431e
Update gemfiles
excid3 Dec 7, 2023
4f936e2
Version bump
excid3 Dec 7, 2023
0c4130e
Update changelog
excid3 Dec 7, 2023
572469d
Add mailer preview example
excid3 Dec 7, 2023
5ef6d28
Serialize tenant to string instead of GlobalID (#326)
ziadsawalha Dec 13, 2023
8e8f9de
Version bump
excid3 Dec 14, 2023
599d8af
Add a tenant change hook (#333)
Winslett Jan 11, 2024
7e3bd8a
Update CHANGELOG.md
excid3 Jan 11, 2024
4228c9d
Add Mac m2 platform to gemfiles
trevorrjohn Jan 19, 2024
55a71aa
Support "has_many through" relationships
HashNotAdam Feb 15, 2023
782a692
Support manual creation of HABTM record
HashNotAdam Mar 29, 2023
90f40a2
Test with Ruby 3.3
trevorrjohn Jan 19, 2024
3d95733
Merge branch 'support_has_many_through' into support_has_many_through
trevorrjohn Jan 20, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 8 additions & 15 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,39 +14,32 @@ jobs:
name: ruby-${{ matrix.ruby }} ${{ matrix.gemfile }}
strategy:
matrix:
ruby: [ '2.6', '2.7', '3.0', '3.1']
ruby: ['3.0', '3.1', '3.2', '3.3']
gemfile:
- rails_5
- rails_6
- rails_6_1
- rails_7
- rails_7_1
- rails_main
- sidekiq_6
- sidekiq_7
exclude:
- ruby: '3.1'
gemfile: rails_5
- ruby: '3.0'
gemfile: rails_5
# Rails 7 requires Ruby 2.7 or higher
- ruby: '2.6'
gemfile: rails_7
- ruby: '2.6'
gemfile: rails_main
- ruby: '2.6'
gemfile: sidekiq_7
- ruby: '3.2'
gemfile: rails_6

env:
BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile
BUNDLE_PATH_RELATIVE_TO_CWD: true

steps:
- uses: actions/checkout@v3
- uses: actions/checkout@v4
- name: Set up Ruby
uses: ruby/setup-ruby@v1
uses: ruby/setup-ruby@v2
with:
ruby-version: ${{ matrix.ruby }}
bundler: default
bundler-cache: true
rubygems: latest
- name: StandardRb check
run: bundle exec standardrb
- name: Run tests
Expand Down
18 changes: 11 additions & 7 deletions Appraisals
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
appraise "rails-5" do
gem "rails", "~> 5.2", ">= 5.2.6"
end

appraise "rails-6" do
gem "rails", "~> 6.0", ">= 6.0.4.1"
gem "rails", "~> 6.0.0"
end

appraise "rails-6-1" do
gem "rails", "~> 6.1", ">= 6.1.4.1"
gem "rails", "~> 6.1.0"
end

appraise "rails-7" do
gem "rails", "~> 7.0", ">= 7.0.0"
gem "rails", "~> 7.0.0"
end

appraise "rails-7-1" do
gem "rails", "~> 7.1.0"
end

appraise "rails-main" do
Expand All @@ -21,6 +21,10 @@ appraise "rails-main" do
end
end

appraise "sidekiq-6" do
gem "sidekiq", "~> 6.0"
end

appraise "sidekiq-7" do
gem "sidekiq", "~> 7.0"
end
37 changes: 37 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,43 @@
Unreleased
----------

* Add `config.tenant_change_hook` callback when a tenant changes. [#333](https://github.com/ErwinM/acts_as_tenant/pull/333)

This can be used to implement Postgres's row-level security for example

```ruby
ActsAsTenant.configure do |config|
config.tenant_change_hook = lambda do |tenant|
if tenant.present?
ActiveRecord::Base.connection.execute(ActiveRecord::Base.sanitize_sql_array(["SET rls.account_id = ?;", tenant.id]))
Rails.logger.info "Changed tenant to " + [tenant.id, tenant.name].to_json
end
end
end
```

1.0.1
-----

* Cast GID to string for job args #326

1.0.0
-----

* [Breaking] Drop Rails 5.2 support
* Set current_tenant with ActiveJob automatically #319
* Replace RequestStore dependency with CurrentAttributes. #313 - @excid3
* Add `scope` support to `acts_as_tenant :account, ->{ with_deleted }` #282 - @adrian-gomez
The scope will be forwarded to `belongs_to`.
* Add `job_scope` configuration to customize how tenants are loaded in background jobs - @excid3
This is helpful for situations like soft delete:

```ruby
ActsAsTenant.configure do |config|
config.job_scope = ->{ with_deleted }
end
```

0.6.1
-----

Expand Down
14 changes: 7 additions & 7 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,12 @@ gemspec
# Git. Remember to move these dependencies to your gemspec before releasing
# your gem to rubygems.org.

gem "rspec", ">=3.0"
gem "rspec-rails"
gem "sqlite3"
gem "standard"
gem "sidekiq", "~> 7.0"
gem "appraisal", github: "thoughtbot/appraisal"

# To use a debugger
gem "byebug", group: [:development, :test]

# Ruby 3.1+ no longer includes these by default
group :development, :test do
gem "net-imap"
gem "net-pop"
gem "net-smtp"
end
73 changes: 56 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ Want to see how it works? Check out [the ActsAsTenant walkthrough video](https:/

Installation
------------
acts_as_tenant will only work on Rails 5.2 and up. This is due to changes made to the handling of `default_scope`, an essential pillar of the gem.

To use it, add it to your Gemfile:

Expand All @@ -45,20 +44,23 @@ gem 'acts_as_tenant'

Getting started
===============

There are two steps in adding multi-tenancy to your app with acts_as_tenant:

1. setting the current tenant and
2. scoping your models.

Setting the current tenant
--------------------------

There are three ways to set the current tenant:

1. by using the subdomain to lookup the current tenant,
2. by setting the current tenant in the controller, and
3. by setting the current tenant for a block.

### Looking Up Tenants

#### By Subdomain to lookup the current tenant

```ruby
Expand Down Expand Up @@ -115,7 +117,7 @@ end

This allows the tenant to be set before any other code runs so everything is within the current tenant.

### Setting the current tenant for a block ###
### Setting the current tenant for a block

```ruby
ActsAsTenant.with_tenant(current_account) do
Expand All @@ -128,16 +130,17 @@ any code in this block will be scoped to the current tenant. All methods that se

**Note:** If the current tenant is not set by one of these methods, Acts_as_tenant will be unable to apply the proper scope to your models. So make sure you use one of the two methods to tell acts_as_tenant about the current tenant.

### Disabling tenant checking for a block ###
### Disabling tenant checking for a block

```ruby
ActsAsTenant.without_tenant do
# Tenant checking is disabled for all code in this block
end
```

This is useful in shared routes such as admin panels or internal dashboards when `require_tenant` option is enabled throughout the app.

### Allowing tenant updating for a block ###
### Allowing tenant updating for a block

```ruby
ActsAsTenant.with_mutable_tenant do
Expand All @@ -147,7 +150,7 @@ end

This will allow you to change the tenant of a model. This feature is useful for admin screens, where it is ok to allow certain users to change the tenant on existing models in specific cases.

### Require tenant to be set always ###
### Require tenant to be set always

If you want to require the tenant to be set at all times, you can configure acts_as_tenant to raise an error when a query is made without a tenant available. See below under configuration options.

Expand Down Expand Up @@ -194,7 +197,7 @@ Project.tasks.all # => all tasks with account_id => 3

Acts_as_tenant uses Rails' `default_scope` method to scope models. Rails 3.1 changed the way `default_scope` works in a good way. A user defined `default_scope` should integrate seamlessly with the one added by `acts_as_tenant`.

### Validating attribute uniqueness ###
### Validating attribute uniqueness

If you need to validate for uniqueness, chances are that you want to scope this validation to a tenant. You can do so by using:

Expand All @@ -204,24 +207,23 @@ validates_uniqueness_to_tenant :name, :email

All options available to Rails' own `validates_uniqueness_of` are also available to this method.

### Custom foreign_key ###
### Custom foreign_key

You can explicitly specifiy a foreign_key for AaT to use should the key differ from the default:

```ruby
acts_as_tenant(:account, :foreign_key => 'accountID') # by default AaT expects account_id
```

### Custom primary_key ###
### Custom primary_key

You can also explicitly specifiy a primary_key for AaT to use should the key differ from the default:

```ruby
acts_as_tenant(:account, :primary_key => 'primaryID') # by default AaT expects id
```


### Has and belongs to many ###
### Has and belongs to many

You can scope a model that is part of a HABTM relationship by using the `through` option.

Expand All @@ -244,12 +246,16 @@ end

Configuration options
---------------------

An initializer can be created to control (currently one) option in ActsAsTenant. Defaults
are shown below with sample overrides following. In `config/initializers/acts_as_tenant.rb`:

```ruby
ActsAsTenant.configure do |config|
config.require_tenant = false # true

# Customize the query for loading the tenant in background jobs
config.job_scope = ->{ all }
end
```

Expand All @@ -269,8 +275,34 @@ end

`ActsAsTenant.should_require_tenant?` is used to determine if a tenant is required in the current context, either by evaluating the lambda provided, or by returning the boolean value assigned to `config.require_tenant`.

When using `config.require_tenant` alongside the `rails console`, a nice quality of life tweak is to set the tenant in the console session in your initializer script. For example in `config/initializers/acts_as_tenant.rb`:

```ruby
SET_TENANT_PROC = lambda do
if defined?(Rails::Console)
puts "> ActsAsTenant.current_tenant = Account.first"
ActsAsTenant.current_tenant = Account.first
end
end

Rails.application.configure do
if Rails.env.development?
# Set the tenant to the first account in development on load
config.after_initialize do
SET_TENANT_PROC.call
end

# Reset the tenant after calling 'reload!' in the console
ActiveSupport::Reloader.to_complete do
SET_TENANT_PROC.call
end
end
end
```

belongs_to options
---------------------
------------------

`acts_as_tenant :account` includes the belongs_to relationship.
So when using acts_as_tenant on a model, do not add `belongs_to :account` alongside `acts_as_tenant :account`:

Expand All @@ -286,16 +318,22 @@ You can add the following `belongs_to` options to `acts_as_tenant`:

Example: `acts_as_tenant(:account, counter_cache: true)`

Sidekiq support
---------------
Background Processing libraries
-------------------------------

ActsAsTenant supports

ActsAsTenant supports [Sidekiq](http://sidekiq.org/). A background processing library.
Add the following code to your `config/initializers/acts_as_tenant.rb`:
- ActiveJob - ActsAsTenant will automatically save the current tenant in ActiveJob arguments and set it when the job runs.

- [Sidekiq](//sidekiq.org/)
Add the following code to `config/initializers/acts_as_tenant.rb`:

```ruby
require 'acts_as_tenant/sidekiq'
```

- DelayedJob - [acts_as_tenant-delayed_job](https://github.com/nunommc/acts_as_tenant-delayed_job)

Testing
---------------

Expand All @@ -311,7 +349,6 @@ Rails.application.configure do
config.middleware.use ActsAsTenant::TestTenantMiddleware
end
```

```ruby
# spec_helper.rb
config.before(:suite) do |example|
Expand All @@ -335,9 +372,9 @@ config.after(:each) do |example|
ActsAsTenant.test_tenant = nil
end
```

Bug reports & suggested improvements
------------------------------------

If you have found a bug or want to suggest an improvement, please use our issue tracked at:

[github.com/ErwinM/acts_as_tenant/issues](http://github.com/ErwinM/acts_as_tenant/issues)
Expand All @@ -357,10 +394,12 @@ We use the Appraisal gem to run tests against supported versions of Rails to tes

Author & Credits
----------------

acts_as_tenant is written by Erwin Matthijssen & Chris Oliver.

This gem was inspired by Ryan Sonnek's [Multitenant](https://github.com/wireframe/multitenant) gem and its use of default_scope.

License
-------

Copyright (c) 2011 Erwin Matthijssen, released under the MIT license
10 changes: 1 addition & 9 deletions acts_as_tenant.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,5 @@ Gem::Specification.new do |spec|

spec.require_paths = ["lib"]

spec.add_dependency "request_store", ">= 1.0.5"
spec.add_dependency "rails", ">= 5.2"

spec.add_development_dependency "rspec", ">=3.0"
spec.add_development_dependency "rspec-rails"
spec.add_development_dependency "sqlite3"
spec.add_development_dependency "sidekiq", "~> 6.1", ">= 6.1.2"
spec.add_development_dependency "standard"
spec.add_development_dependency "appraisal"
spec.add_dependency "rails", ">= 6.0"
end
14 changes: 0 additions & 14 deletions gemfiles/rails_5.gemfile

This file was deleted.

Loading