rails_multitenant 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 20d2743f52770c7de4e81ae6936148c6c1f474f2
4
- data.tar.gz: a533e376642c62f6beb78570ef94eaa99d57f6d8
3
+ metadata.gz: 7fa50760b463e1f9b31abc6a19d13e863dc5604a
4
+ data.tar.gz: 7af84280e893555c5c40a4e55cfad57bba8d4252
5
5
  SHA512:
6
- metadata.gz: 213daa229aaf72402c6964eeddb045b5f33f2da1dbd2eecf01eeb568f04f97336ddad5af5c3fee90de13531b4090511432438796746dd2694d4af454f081e5f5
7
- data.tar.gz: 00ab17d047dc6117014bbb86b93ad7fcc731a067347c6d8437fc575350b8ea314096ba89a76dfbc6a8e0ca413be2db2108492c85e8771d00016072d3919cd0b8
6
+ metadata.gz: b7aca78e340f93da77d625dae54d1c372053fe732032ec7f4acf2b651a436d75fa502895ef212df9a37318a080bf683fa0d4ed785301138bc79ff70d20d06be3
7
+ data.tar.gz: 91f20b900cd76a6e8afae4ebd8d239b456c74ea1cc0170d660e05109156ac2b28c459fd330c54d56169de7efda6ee935489f1b64fc8697d3e69ed86c33181558
data/CHANGELOG.md ADDED
@@ -0,0 +1,6 @@
1
+ # Changelog
2
+
3
+ ### 0.2.0 (unreleased)
4
+ * Merged [PR 2](https://github.com/salsify/rails-multitenant/pull/2) which adds support for
5
+ multi-tenancy based on a foreign key to an external model. As part of this the `multitenant_model_on`
6
+ method was renamed to `multitenant_on_model`.
data/Rakefile CHANGED
@@ -1 +1,8 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler/gem_tasks'
2
+
3
+ require 'rspec/core/rake_task'
4
+ RSpec::Core::RakeTask.new(:spec) do |task|
5
+ task.verbose = false
6
+ end
7
+
8
+ task default: :spec
@@ -39,7 +39,7 @@ module RailsMultitenant
39
39
 
40
40
  def current_registry_obj
41
41
  key_class = respond_to?(:base_class) ? base_class : self
42
- "#{key_class.name.downcase}_obj".to_sym
42
+ "#{key_class.name.underscore}_obj".to_sym
43
43
  end
44
44
  end
45
45
 
@@ -103,12 +103,12 @@ module RailsMultitenant
103
103
 
104
104
  def current_instance_registry_id
105
105
  key_class = respond_to?(:base_class) ? base_class : self
106
- "#{key_class.name.downcase}_id".to_sym
106
+ "#{key_class.name.underscore}_id".to_sym
107
107
  end
108
108
 
109
109
  def current_instance_registry_obj
110
110
  key_class = respond_to?(:base_class) ? base_class : self
111
- "#{key_class.name.downcase}_obj".to_sym
111
+ "#{key_class.name.underscore}_obj".to_sym
112
112
  end
113
113
 
114
114
  include RegistryDependentOn
@@ -132,6 +132,7 @@ module RailsMultitenant
132
132
  def set(symbol, value)
133
133
  globals[symbol] = value
134
134
  end
135
+ alias_method :[]=, :set
135
136
 
136
137
  # delete this global
137
138
  def delete(symbol)
@@ -152,6 +153,7 @@ module RailsMultitenant
152
153
  def get(symbol)
153
154
  globals[symbol]
154
155
  end
156
+ alias_method :[], :get
155
157
 
156
158
  # Duplicate the registry
157
159
  def duplicate_registry
@@ -160,10 +162,10 @@ module RailsMultitenant
160
162
  end
161
163
  end
162
164
 
163
- # Run a block of code with an empty registry
164
- def with_isolated_registry
165
+ # Run a block of code with an the given registry
166
+ def with_isolated_registry(registry = {})
165
167
  prior_globals = globals
166
- self.globals = {}
168
+ self.globals = registry
167
169
  yield
168
170
  ensure
169
171
  self.globals = prior_globals
@@ -1,14 +1,6 @@
1
1
  module RailsMultitenant
2
2
  module MultitenantModel
3
3
  extend ActiveSupport::Concern
4
- =begin
5
- included do
6
- belongs_to :organization
7
- validates_presence_of :organization_id
8
- scope :from_current_org, -> { where(organization_id: Organization.current_id) }
9
- default_scope { from_current_org }
10
- end
11
- =end
12
4
 
13
5
  included do
14
6
  class << self
@@ -18,19 +10,29 @@ module RailsMultitenant
18
10
 
19
11
  module ClassMethods
20
12
 
21
- def multitenant_model_on(context_entity)
22
- @context_entity_id_field = "#{context_entity}_id"
13
+ def multitenant_on(context_entity_id_field)
14
+ self.context_entity_id_field = context_entity_id_field
15
+ validates_presence_of context_entity_id_field
16
+
17
+ context_entity = context_entity_id_field.to_s.gsub(/_id$/, '')
23
18
  scope_sym = "from_current_#{context_entity}".to_sym
24
19
 
25
- belongs_to context_entity
26
- validates_presence_of context_entity_id_field
27
- scope scope_sym, -> { where(context_entity_id_field => context_entity.to_s.classify.constantize.current_id) }
20
+ scope scope_sym, -> do
21
+ where(context_entity_id_field => GlobalContextRegistry[context_entity_id_field])
22
+ end
23
+
28
24
  default_scope { send(scope_sym) }
25
+
29
26
  define_method "strip_#{context_entity}_scope" do
30
27
  unscope(where: context_entity_id_field)
31
28
  end
32
29
  end
33
30
 
31
+ def multitenant_on_model(context_entity)
32
+ multitenant_on("#{context_entity}_id".to_sym)
33
+ belongs_to context_entity
34
+ end
35
+
34
36
  def validates_multitenant_uniqueness_of(*attr_names)
35
37
  options = attr_names.extract_options!.symbolize_keys
36
38
  existing_scope = Array.wrap(options.delete(:scope))
@@ -0,0 +1,5 @@
1
+ RSpec::Matchers.define(:be_multitenant_on) do |expected|
2
+ match do |actual|
3
+ actual.context_entity_id_field == expected
4
+ end
5
+ end
@@ -1,3 +1,3 @@
1
1
  module RailsMultitenant
2
- VERSION = "0.1.0"
2
+ VERSION = '0.2.0'
3
3
  end
@@ -3,3 +3,5 @@ require 'active_record'
3
3
 
4
4
  require "rails_multitenant/global_context_registry"
5
5
  require "rails_multitenant/multitenant_model"
6
+
7
+ # rails_multitenant/rspec has to be explicitly included by clients who want to use it
@@ -0,0 +1,12 @@
1
+ require 'spec_helper'
2
+ require 'rails_multitenant/rspec'
3
+
4
+ describe "be_multitenant_on matcher" do
5
+ it "accepts a valid context field id" do
6
+ expect(ExternalItem).to be_multitenant_on(:external_organization_id)
7
+ end
8
+
9
+ it "rejects an invalid context field id" do
10
+ expect(ExternalItem).not_to be_multitenant_on(:other_field)
11
+ end
12
+ end
data/spec/db/schema.rb CHANGED
@@ -1,16 +1,16 @@
1
1
  # encoding: UTF-8
2
2
 
3
- ActiveRecord::Schema.define(:version => 0) do
3
+ ActiveRecord::Schema.define(version: 0) do
4
4
 
5
- create_table(:organizations, force: true) do |t|
6
- t.string :name
7
- end
5
+ create_table(:organizations, force: true)
8
6
 
9
7
  create_table(:items, force: true) do |t|
10
- t.string :name
11
8
  t.integer :organization_id
12
9
  end
13
10
 
11
+ create_table(:external_items, force: true) do |t|
12
+ t.integer :external_organization_id
13
+ end
14
14
  end
15
15
 
16
16
  class Organization < ActiveRecord::Base
@@ -19,5 +19,11 @@ end
19
19
 
20
20
  class Item < ActiveRecord::Base
21
21
  include RailsMultitenant::MultitenantModel
22
- multitenant_model_on :organization
22
+ multitenant_on_model :organization
23
+ end
24
+
25
+ class ExternalItem < ActiveRecord::Base
26
+ include RailsMultitenant::MultitenantModel
27
+ multitenant_on :external_organization_id
23
28
  end
29
+
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+ include RailsMultitenant
3
+
4
+ describe ExternalItem do
5
+
6
+ let!(:external_item1) { as_external_org(1) { ExternalItem.create! } }
7
+
8
+ let!(:external_item2) { as_external_org(2) { ExternalItem.create! } }
9
+ let!(:external_item3) { as_external_org(2) { ExternalItem.create! } }
10
+
11
+ specify 'org1 has the correct external items' do
12
+ as_external_org(1) do
13
+ expect(ExternalItem.all).to eq [external_item1]
14
+ end
15
+ end
16
+
17
+ specify 'org2 has the correct external items' do
18
+ as_external_org(2) do
19
+ expect(ExternalItem.all).to match_array [external_item2, external_item3]
20
+ end
21
+ end
22
+
23
+ it 'does not return external items from other orgs' do
24
+ as_external_org(2) do
25
+ expect(ExternalItem.where(id: external_item1.id)).to eq []
26
+ end
27
+ end
28
+
29
+ def as_external_org(id, &block)
30
+ GlobalContextRegistry.with_isolated_registry(external_organization_id: id, &block)
31
+ end
32
+
33
+ end
@@ -15,6 +15,16 @@ describe GlobalContextRegistry do
15
15
  end
16
16
  end
17
17
 
18
+ describe '.[] and .[]=' do
19
+ before do
20
+ GlobalContextRegistry[:boo] = 'baz'
21
+ end
22
+
23
+ specify do
24
+ expect(GlobalContextRegistry[:boo]).to eq 'baz'
25
+ end
26
+ end
27
+
18
28
  describe '.delete' do
19
29
  specify do
20
30
  expect(GlobalContextRegistry.delete(:foo)).to eq 'bar'
@@ -28,6 +38,12 @@ describe GlobalContextRegistry do
28
38
  expect(GlobalContextRegistry.get(:foo)).to be_nil
29
39
  end
30
40
  end
41
+
42
+ specify do
43
+ GlobalContextRegistry.with_isolated_registry(foo: 'updated') do
44
+ expect(GlobalContextRegistry.get(:foo)).to eq 'updated'
45
+ end
46
+ end
31
47
  end
32
48
 
33
49
  describe '.replace_registry and .new_registry' do
data/spec/item_spec.rb CHANGED
@@ -6,11 +6,11 @@ require 'spec_helper'
6
6
 
7
7
  describe Item do
8
8
 
9
- let!(:item1) { Item.create(name: 'item1') }
9
+ let!(:item1) { Item.create! }
10
10
 
11
- let!(:org2) { Organization.create(name: 'org2') }
12
- let!(:item2) { org2.as_current { Item.create(name: 'item2') } }
13
- let!(:item3) { org2.as_current { Item.create(name: 'item3') } }
11
+ let!(:org2) { Organization.create! }
12
+ let!(:item2) { org2.as_current { Item.create! } }
13
+ let!(:item3) { org2.as_current { Item.create! } }
14
14
 
15
15
  specify 'default org should have one item' do
16
16
  expect(Item.all).to eq [ item1 ]
data/spec/spec_helper.rb CHANGED
@@ -30,8 +30,6 @@ require 'db/schema'
30
30
  RSpec.configure do |config|
31
31
  config.order = 'random'
32
32
 
33
- config.add_setting :test_org_id
34
-
35
33
  config.before(:suite) do
36
34
  DatabaseCleaner.clean_with(:truncation)
37
35
  end
@@ -40,9 +38,7 @@ RSpec.configure do |config|
40
38
  DatabaseCleaner.strategy = :transaction
41
39
  DatabaseCleaner.start
42
40
  RailsMultitenant::GlobalContextRegistry.new_registry
43
- org = Organization.create(name: 'test org')
44
- RSpec.configuration.test_org_id = org.id
45
- Organization.current_id = RSpec.configuration.test_org_id
41
+ Organization.current_id = Organization.create!.id
46
42
  end
47
43
 
48
44
  config.after(:each) do
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_multitenant
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pat Breault
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-12-23 00:00:00.000000000 Z
11
+ date: 2016-01-15 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -158,6 +158,7 @@ files:
158
158
  - ".gitignore"
159
159
  - ".ruby-version"
160
160
  - ".travis.yml"
161
+ - CHANGELOG.md
161
162
  - CODE_OF_CONDUCT.md
162
163
  - Gemfile
163
164
  - README.md
@@ -167,11 +168,13 @@ files:
167
168
  - lib/rails_multitenant.rb
168
169
  - lib/rails_multitenant/global_context_registry.rb
169
170
  - lib/rails_multitenant/multitenant_model.rb
171
+ - lib/rails_multitenant/rspec.rb
170
172
  - lib/rails_multitenant/version.rb
171
- - log/test.log
172
173
  - rails_multitenant.gemspec
174
+ - spec/be_multitenant_on_matcher_spec.rb
173
175
  - spec/db/database.yml
174
176
  - spec/db/schema.rb
177
+ - spec/external_item_spec.rb
175
178
  - spec/global_context_registry_spec.rb
176
179
  - spec/item_spec.rb
177
180
  - spec/spec_helper.rb
@@ -200,8 +203,10 @@ signing_key:
200
203
  specification_version: 4
201
204
  summary: Automatically configures multiple tenants in a Rails environment
202
205
  test_files:
206
+ - spec/be_multitenant_on_matcher_spec.rb
203
207
  - spec/db/database.yml
204
208
  - spec/db/schema.rb
209
+ - spec/external_item_spec.rb
205
210
  - spec/global_context_registry_spec.rb
206
211
  - spec/item_spec.rb
207
212
  - spec/spec_helper.rb