apartment 0.23.2 → 0.24.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/HISTORY.md +12 -1
- data/README.md +16 -13
- data/Rakefile +6 -1
- data/TODO.md +9 -14
- data/lib/apartment.rb +17 -3
- data/lib/apartment/adapters/abstract_adapter.rb +53 -44
- data/lib/apartment/adapters/jdbc_postgresql_adapter.rb +9 -9
- data/lib/apartment/adapters/mysql2_adapter.rb +15 -15
- data/lib/apartment/adapters/postgresql_adapter.rb +26 -18
- data/lib/apartment/adapters/sqlite3_adapter.rb +13 -13
- data/lib/apartment/database.rb +2 -2
- data/lib/apartment/elevators/host_hash.rb +3 -3
- data/lib/apartment/elevators/subdomain.rb +2 -3
- data/lib/apartment/migrator.rb +3 -3
- data/lib/apartment/railtie.rb +2 -1
- data/lib/apartment/tasks/enhancements.rb +26 -0
- data/lib/apartment/version.rb +1 -1
- data/lib/generators/apartment/install/templates/apartment.rb +13 -8
- data/lib/tasks/apartment.rake +34 -38
- data/spec/adapters/jdbc_mysql_adapter_spec.rb +2 -4
- data/spec/adapters/jdbc_postgresql_adapter_spec.rb +6 -6
- data/spec/adapters/mysql2_adapter_spec.rb +5 -5
- data/spec/adapters/postgresql_adapter_spec.rb +6 -6
- data/spec/adapters/sqlite3_adapter_spec.rb +2 -2
- data/spec/database_spec.rb +2 -2
- data/spec/dummy/config/initializers/apartment.rb +2 -2
- data/spec/examples/connection_adapter_examples.rb +2 -2
- data/spec/examples/generic_adapter_examples.rb +15 -15
- data/spec/examples/schema_adapter_examples.rb +6 -6
- data/spec/integration/apartment_rake_integration_spec.rb +4 -4
- data/spec/integration/query_caching_spec.rb +2 -2
- data/spec/support/requirements.rb +2 -5
- data/spec/tasks/apartment_rake_spec.rb +8 -9
- data/spec/unit/config_spec.rb +17 -10
- data/spec/unit/elevators/subdomain_spec.rb +26 -6
- metadata +3 -2
@@ -7,15 +7,13 @@ if defined?(JRUBY_VERSION)
|
|
7
7
|
|
8
8
|
subject { Apartment::Database.jdbc_mysql_adapter config.symbolize_keys }
|
9
9
|
|
10
|
-
def
|
10
|
+
def tenant_names
|
11
11
|
ActiveRecord::Base.connection.execute("SELECT schema_name FROM information_schema.schemata").collect { |row| row['schema_name'] }
|
12
12
|
end
|
13
13
|
|
14
|
-
let(:
|
14
|
+
let(:default_tenant) { subject.process { ActiveRecord::Base.connection.current_database } }
|
15
15
|
|
16
16
|
it_should_behave_like "a generic apartment adapter"
|
17
17
|
it_should_behave_like "a connection based apartment adapter"
|
18
|
-
|
19
18
|
end
|
20
|
-
|
21
19
|
end
|
@@ -11,12 +11,12 @@ if defined?(JRUBY_VERSION)
|
|
11
11
|
|
12
12
|
before { Apartment.use_schemas = true }
|
13
13
|
|
14
|
-
# Not sure why, but somehow using let(:
|
15
|
-
def
|
14
|
+
# Not sure why, but somehow using let(:tenant_names) memoizes for the whole example group, not just each test
|
15
|
+
def tenant_names
|
16
16
|
ActiveRecord::Base.connection.execute("SELECT nspname FROM pg_namespace;").collect { |row| row['nspname'] }
|
17
17
|
end
|
18
18
|
|
19
|
-
let(:
|
19
|
+
let(:default_tenant) { subject.process { ActiveRecord::Base.connection.schema_search_path.gsub('"', '') } }
|
20
20
|
|
21
21
|
it_should_behave_like "a generic apartment adapter"
|
22
22
|
it_should_behave_like "a schema based apartment adapter"
|
@@ -26,12 +26,12 @@ if defined?(JRUBY_VERSION)
|
|
26
26
|
|
27
27
|
before { Apartment.use_schemas = false }
|
28
28
|
|
29
|
-
# Not sure why, but somehow using let(:
|
30
|
-
def
|
29
|
+
# Not sure why, but somehow using let(:tenant_names) memoizes for the whole example group, not just each test
|
30
|
+
def tenant_names
|
31
31
|
connection.execute("select datname from pg_database;").collect { |row| row['datname'] }
|
32
32
|
end
|
33
33
|
|
34
|
-
let(:
|
34
|
+
let(:default_tenant) { subject.process { ActiveRecord::Base.connection.current_database } }
|
35
35
|
|
36
36
|
it_should_behave_like "a generic apartment adapter"
|
37
37
|
it_should_behave_like "a connection based apartment adapter"
|
@@ -6,19 +6,19 @@ describe Apartment::Adapters::Mysql2Adapter, database: :mysql do
|
|
6
6
|
|
7
7
|
subject(:adapter){ Apartment::Database.mysql2_adapter config }
|
8
8
|
|
9
|
-
def
|
9
|
+
def tenant_names
|
10
10
|
ActiveRecord::Base.connection.execute("SELECT schema_name FROM information_schema.schemata").collect { |row| row[0] }
|
11
11
|
end
|
12
12
|
|
13
|
-
let(:
|
13
|
+
let(:default_tenant) { subject.process { ActiveRecord::Base.connection.current_database } }
|
14
14
|
|
15
15
|
context "using - the equivalent of - schemas" do
|
16
16
|
before { Apartment.use_schemas = true }
|
17
17
|
|
18
18
|
it_should_behave_like "a generic apartment adapter"
|
19
19
|
|
20
|
-
describe "#
|
21
|
-
its(:
|
20
|
+
describe "#default_tenant" do
|
21
|
+
its(:default_tenant){ should == config[:database] }
|
22
22
|
end
|
23
23
|
|
24
24
|
describe "#init" do
|
@@ -33,7 +33,7 @@ describe Apartment::Adapters::Mysql2Adapter, database: :mysql do
|
|
33
33
|
it "should process model exclusions" do
|
34
34
|
Apartment::Database.init
|
35
35
|
|
36
|
-
Company.table_name.should == "#{
|
36
|
+
Company.table_name.should == "#{default_tenant}.companies"
|
37
37
|
end
|
38
38
|
end
|
39
39
|
end
|
@@ -10,12 +10,12 @@ describe Apartment::Adapters::PostgresqlAdapter, database: :postgresql do
|
|
10
10
|
|
11
11
|
before{ Apartment.use_schemas = true }
|
12
12
|
|
13
|
-
# Not sure why, but somehow using let(:
|
14
|
-
def
|
13
|
+
# Not sure why, but somehow using let(:tenant_names) memoizes for the whole example group, not just each test
|
14
|
+
def tenant_names
|
15
15
|
ActiveRecord::Base.connection.execute("SELECT nspname FROM pg_namespace;").collect { |row| row['nspname'] }
|
16
16
|
end
|
17
17
|
|
18
|
-
let(:
|
18
|
+
let(:default_tenant) { subject.process { ActiveRecord::Base.connection.schema_search_path.gsub('"', '') } }
|
19
19
|
|
20
20
|
it_should_behave_like "a generic apartment adapter"
|
21
21
|
it_should_behave_like "a schema based apartment adapter"
|
@@ -25,12 +25,12 @@ describe Apartment::Adapters::PostgresqlAdapter, database: :postgresql do
|
|
25
25
|
|
26
26
|
before{ Apartment.use_schemas = false }
|
27
27
|
|
28
|
-
# Not sure why, but somehow using let(:
|
29
|
-
def
|
28
|
+
# Not sure why, but somehow using let(:tenant_names) memoizes for the whole example group, not just each test
|
29
|
+
def tenant_names
|
30
30
|
connection.execute("select datname from pg_database;").collect { |row| row['datname'] }
|
31
31
|
end
|
32
32
|
|
33
|
-
let(:
|
33
|
+
let(:default_tenant) { subject.process { ActiveRecord::Base.connection.current_database } }
|
34
34
|
|
35
35
|
it_should_behave_like "a generic apartment adapter"
|
36
36
|
it_should_behave_like "a connection based apartment adapter"
|
@@ -7,12 +7,12 @@ describe Apartment::Adapters::Sqlite3Adapter, database: :sqlite do
|
|
7
7
|
subject{ Apartment::Database.sqlite3_adapter config }
|
8
8
|
|
9
9
|
context "using connections" do
|
10
|
-
def
|
10
|
+
def tenant_names
|
11
11
|
db_dir = File.expand_path("../../dummy/db", __FILE__)
|
12
12
|
Dir.glob("#{db_dir}/*.sqlite3").map { |file| File.basename(file, '.sqlite3') }
|
13
13
|
end
|
14
14
|
|
15
|
-
let(:
|
15
|
+
let(:default_tenant) do
|
16
16
|
subject.process { File.basename(Apartment::Test.config['connections']['sqlite']['database'], '.sqlite3') }
|
17
17
|
end
|
18
18
|
|
data/spec/database_spec.rb
CHANGED
@@ -87,9 +87,9 @@ describe Apartment::Database do
|
|
87
87
|
|
88
88
|
it 'has a threadsafe adapter' do
|
89
89
|
subject.switch(db1)
|
90
|
-
thread = Thread.new { subject.
|
90
|
+
thread = Thread.new { subject.current_tenant.should == Apartment.default_schema }
|
91
91
|
thread.join
|
92
|
-
subject.
|
92
|
+
subject.current_tenant.should == db1
|
93
93
|
end
|
94
94
|
end
|
95
95
|
end
|
@@ -3,7 +3,7 @@ require 'spec_helper'
|
|
3
3
|
shared_examples_for "a connection based apartment adapter" do
|
4
4
|
include Apartment::Spec::AdapterRequirements
|
5
5
|
|
6
|
-
let(:
|
6
|
+
let(:default_tenant){ subject.process{ ActiveRecord::Base.connection.current_database } }
|
7
7
|
|
8
8
|
describe "#init" do
|
9
9
|
it "should process model exclusions" do
|
@@ -31,4 +31,4 @@ shared_examples_for "a connection based apartment adapter" do
|
|
31
31
|
}.to raise_error(Apartment::DatabaseNotFound)
|
32
32
|
end
|
33
33
|
end
|
34
|
-
end
|
34
|
+
end
|
@@ -14,8 +14,8 @@ shared_examples_for "a generic apartment adapter" do
|
|
14
14
|
describe "#create" do
|
15
15
|
|
16
16
|
it "should create the new databases" do
|
17
|
-
|
18
|
-
|
17
|
+
tenant_names.should include(db1)
|
18
|
+
tenant_names.should include(db2)
|
19
19
|
end
|
20
20
|
|
21
21
|
it "should load schema.rb to new schema" do
|
@@ -31,11 +31,11 @@ shared_examples_for "a generic apartment adapter" do
|
|
31
31
|
|
32
32
|
subject.create(db2) do
|
33
33
|
@count = User.count
|
34
|
-
subject.
|
34
|
+
subject.current_tenant.should == db2
|
35
35
|
User.create
|
36
36
|
end
|
37
37
|
|
38
|
-
subject.
|
38
|
+
subject.current_tenant.should_not == db2
|
39
39
|
|
40
40
|
subject.process(db2){ User.count.should == @count + 1 }
|
41
41
|
end
|
@@ -44,25 +44,25 @@ shared_examples_for "a generic apartment adapter" do
|
|
44
44
|
describe "#drop" do
|
45
45
|
it "should remove the db" do
|
46
46
|
subject.drop db1
|
47
|
-
|
47
|
+
tenant_names.should_not include(db1)
|
48
48
|
end
|
49
49
|
end
|
50
50
|
|
51
51
|
describe "#process" do
|
52
52
|
it "should connect" do
|
53
53
|
subject.process(db1) do
|
54
|
-
subject.
|
54
|
+
subject.current_tenant.should == db1
|
55
55
|
end
|
56
56
|
end
|
57
57
|
|
58
58
|
it "should reset" do
|
59
59
|
subject.process(db1)
|
60
|
-
subject.
|
60
|
+
subject.current_tenant.should == default_tenant
|
61
61
|
end
|
62
62
|
|
63
|
-
# We're often finding when using Apartment in tests, the `
|
63
|
+
# We're often finding when using Apartment in tests, the `current_tenant` (ie the previously connect to db)
|
64
64
|
# gets dropped, but process will try to return to that db in a test. We should just reset if it doesn't exist
|
65
|
-
it "should not throw exception if
|
65
|
+
it "should not throw exception if current_tenant is no longer accessible" do
|
66
66
|
subject.switch(db2)
|
67
67
|
|
68
68
|
expect {
|
@@ -75,19 +75,19 @@ shared_examples_for "a generic apartment adapter" do
|
|
75
75
|
it "should reset connection" do
|
76
76
|
subject.switch(db1)
|
77
77
|
subject.reset
|
78
|
-
subject.
|
78
|
+
subject.current_tenant.should == default_tenant
|
79
79
|
end
|
80
80
|
end
|
81
81
|
|
82
82
|
describe "#switch" do
|
83
83
|
it "should connect to new db" do
|
84
84
|
subject.switch(db1)
|
85
|
-
subject.
|
85
|
+
subject.current_tenant.should == db1
|
86
86
|
end
|
87
87
|
|
88
88
|
it "should reset connection if database is nil" do
|
89
89
|
subject.switch
|
90
|
-
subject.
|
90
|
+
subject.current_tenant.should == default_tenant
|
91
91
|
end
|
92
92
|
|
93
93
|
it "should raise an error if database is invalid" do
|
@@ -97,11 +97,11 @@ shared_examples_for "a generic apartment adapter" do
|
|
97
97
|
end
|
98
98
|
end
|
99
99
|
|
100
|
-
describe "#
|
100
|
+
describe "#current_tenant" do
|
101
101
|
it "should return the current db name" do
|
102
102
|
subject.switch(db1)
|
103
|
-
subject.
|
103
|
+
subject.current_tenant.should == db1
|
104
104
|
subject.current.should == db1
|
105
105
|
end
|
106
106
|
end
|
107
|
-
end
|
107
|
+
end
|
@@ -5,7 +5,7 @@ shared_examples_for "a schema based apartment adapter" do
|
|
5
5
|
|
6
6
|
let(:schema1){ db1 }
|
7
7
|
let(:schema2){ db2 }
|
8
|
-
let(:public_schema){
|
8
|
+
let(:public_schema){ default_tenant }
|
9
9
|
|
10
10
|
describe "#init" do
|
11
11
|
|
@@ -76,7 +76,7 @@ shared_examples_for "a schema based apartment adapter" do
|
|
76
76
|
expect {
|
77
77
|
subject.create(db)
|
78
78
|
}.to_not raise_error
|
79
|
-
|
79
|
+
tenant_names.should include(db.to_s)
|
80
80
|
end
|
81
81
|
|
82
82
|
after{ subject.drop(db) }
|
@@ -99,7 +99,7 @@ shared_examples_for "a schema based apartment adapter" do
|
|
99
99
|
expect {
|
100
100
|
subject.drop(db)
|
101
101
|
}.to_not raise_error
|
102
|
-
|
102
|
+
tenant_names.should_not include(db.to_s)
|
103
103
|
end
|
104
104
|
|
105
105
|
after { subject.drop(db) rescue nil }
|
@@ -213,17 +213,17 @@ shared_examples_for "a schema based apartment adapter" do
|
|
213
213
|
end
|
214
214
|
end
|
215
215
|
|
216
|
-
describe "#
|
216
|
+
describe "#current_tenant" do
|
217
217
|
it "should return the current schema name" do
|
218
218
|
subject.switch(schema1)
|
219
|
-
subject.
|
219
|
+
subject.current_tenant.should == schema1
|
220
220
|
subject.current.should == schema1
|
221
221
|
end
|
222
222
|
|
223
223
|
context "persistent_schemas", :persistent_schemas => true do
|
224
224
|
it "should exlude persistent_schemas" do
|
225
225
|
subject.switch(schema1)
|
226
|
-
subject.
|
226
|
+
subject.current_tenant.should == schema1
|
227
227
|
subject.current.should == schema1
|
228
228
|
end
|
229
229
|
end
|
@@ -19,7 +19,7 @@ describe "apartment rake tasks", database: :postgresql do
|
|
19
19
|
Apartment.configure do |config|
|
20
20
|
config.use_schemas = true
|
21
21
|
config.excluded_models = ["Company"]
|
22
|
-
config.
|
22
|
+
config.tenant_names = lambda{ Company.pluck(:database) }
|
23
23
|
end
|
24
24
|
Apartment::Database.reload!(config)
|
25
25
|
|
@@ -49,7 +49,7 @@ describe "apartment rake tasks", database: :postgresql do
|
|
49
49
|
|
50
50
|
describe "#migrate" do
|
51
51
|
it "should migrate all databases" do
|
52
|
-
ActiveRecord::Migrator.should_receive(:migrate).exactly(company_count
|
52
|
+
ActiveRecord::Migrator.should_receive(:migrate).exactly(company_count).times
|
53
53
|
|
54
54
|
@rake['apartment:migrate'].invoke
|
55
55
|
end
|
@@ -57,7 +57,7 @@ describe "apartment rake tasks", database: :postgresql do
|
|
57
57
|
|
58
58
|
describe "#rollback" do
|
59
59
|
it "should rollback all dbs" do
|
60
|
-
ActiveRecord::Migrator.should_receive(:rollback).exactly(company_count
|
60
|
+
ActiveRecord::Migrator.should_receive(:rollback).exactly(company_count).times
|
61
61
|
|
62
62
|
@rake['apartment:rollback'].invoke
|
63
63
|
end
|
@@ -71,4 +71,4 @@ describe "apartment rake tasks", database: :postgresql do
|
|
71
71
|
end
|
72
72
|
end
|
73
73
|
end
|
74
|
-
end
|
74
|
+
end
|
@@ -6,7 +6,7 @@ describe 'query caching' do
|
|
6
6
|
before do
|
7
7
|
Apartment.configure do |config|
|
8
8
|
config.excluded_models = ["Company"]
|
9
|
-
config.
|
9
|
+
config.tenant_names = lambda{ Company.pluck(:database) }
|
10
10
|
config.use_schemas = true
|
11
11
|
end
|
12
12
|
|
@@ -38,4 +38,4 @@ describe 'query caching' do
|
|
38
38
|
Apartment::Database.switch db_names.last
|
39
39
|
User.find_by_name(db_names.first).should be_nil
|
40
40
|
end
|
41
|
-
end
|
41
|
+
end
|
@@ -7,11 +7,9 @@ module Apartment
|
|
7
7
|
#
|
8
8
|
#
|
9
9
|
module AdapterRequirements
|
10
|
-
|
11
10
|
extend ActiveSupport::Concern
|
12
11
|
|
13
12
|
included do
|
14
|
-
|
15
13
|
before do
|
16
14
|
subject.create(db1)
|
17
15
|
subject.create(db2)
|
@@ -27,12 +25,11 @@ module Apartment
|
|
27
25
|
end
|
28
26
|
end
|
29
27
|
|
30
|
-
%w{subject
|
28
|
+
%w{subject tenant_names default_tenant}.each do |method|
|
31
29
|
define_method method do
|
32
30
|
raise "You must define a `#{method}` method in your host group"
|
33
31
|
end unless defined?(method)
|
34
32
|
end
|
35
|
-
|
36
33
|
end
|
37
34
|
end
|
38
|
-
end
|
35
|
+
end
|
@@ -30,11 +30,11 @@ describe "apartment rake tasks" do
|
|
30
30
|
|
31
31
|
context 'database migration' do
|
32
32
|
|
33
|
-
let(:
|
34
|
-
let(:
|
33
|
+
let(:tenant_names){ 3.times.map{ Apartment::Test.next_db } }
|
34
|
+
let(:tenant_count){ tenant_names.length }
|
35
35
|
|
36
36
|
before do
|
37
|
-
Apartment.stub(:
|
37
|
+
Apartment.stub(:tenant_names).and_return tenant_names
|
38
38
|
end
|
39
39
|
|
40
40
|
describe "apartment:migrate" do
|
@@ -43,7 +43,7 @@ describe "apartment rake tasks" do
|
|
43
43
|
end
|
44
44
|
|
45
45
|
it "should migrate public and all multi-tenant dbs" do
|
46
|
-
Apartment::Migrator.should_receive(:migrate).exactly(
|
46
|
+
Apartment::Migrator.should_receive(:migrate).exactly(tenant_count).times
|
47
47
|
@rake['apartment:migrate'].invoke
|
48
48
|
end
|
49
49
|
end
|
@@ -69,7 +69,7 @@ describe "apartment rake tasks" do
|
|
69
69
|
end
|
70
70
|
|
71
71
|
it "migrates up to a specific version" do
|
72
|
-
Apartment::Migrator.should_receive(:run).with(:up, anything, version.to_i).exactly(
|
72
|
+
Apartment::Migrator.should_receive(:run).with(:up, anything, version.to_i).exactly(tenant_count).times
|
73
73
|
@rake['apartment:migrate:up'].invoke
|
74
74
|
end
|
75
75
|
end
|
@@ -96,23 +96,22 @@ describe "apartment rake tasks" do
|
|
96
96
|
end
|
97
97
|
|
98
98
|
it "migrates up to a specific version" do
|
99
|
-
Apartment::Migrator.should_receive(:run).with(:down, anything, version.to_i).exactly(
|
99
|
+
Apartment::Migrator.should_receive(:run).with(:down, anything, version.to_i).exactly(tenant_count).times
|
100
100
|
@rake['apartment:migrate:down'].invoke
|
101
101
|
end
|
102
102
|
end
|
103
103
|
end
|
104
104
|
|
105
105
|
describe "apartment:rollback" do
|
106
|
-
|
107
106
|
let(:step){ '3' }
|
108
107
|
|
109
108
|
it "should rollback dbs" do
|
110
|
-
Apartment::Migrator.should_receive(:rollback).exactly(
|
109
|
+
Apartment::Migrator.should_receive(:rollback).exactly(tenant_count).times
|
111
110
|
@rake['apartment:rollback'].invoke
|
112
111
|
end
|
113
112
|
|
114
113
|
it "should rollback dbs STEP amt" do
|
115
|
-
Apartment::Migrator.should_receive(:rollback).with(anything, step.to_i).exactly(
|
114
|
+
Apartment::Migrator.should_receive(:rollback).with(anything, step.to_i).exactly(tenant_count).times
|
116
115
|
ENV['STEP'] = step
|
117
116
|
@rake['apartment:rollback'].invoke
|
118
117
|
end
|
data/spec/unit/config_spec.rb
CHANGED
@@ -36,26 +36,33 @@ describe Apartment do
|
|
36
36
|
Apartment.seed_after_create.should be_true
|
37
37
|
end
|
38
38
|
|
39
|
+
it "should set tld_length" do
|
40
|
+
Apartment.configure do |config|
|
41
|
+
config.tld_length = 2
|
42
|
+
end
|
43
|
+
Apartment.tld_length.should == 2
|
44
|
+
end
|
45
|
+
|
39
46
|
context "databases" do
|
40
47
|
it "should return object if it doesnt respond_to call" do
|
41
|
-
|
48
|
+
tenant_names = ['users', 'companies']
|
42
49
|
|
43
50
|
Apartment.configure do |config|
|
44
51
|
config.excluded_models = []
|
45
|
-
config.
|
52
|
+
config.tenant_names = tenant_names
|
46
53
|
end
|
47
|
-
Apartment.
|
54
|
+
Apartment.tenant_names.should == tenant_names
|
48
55
|
end
|
49
56
|
|
50
57
|
it "should invoke the proc if appropriate" do
|
51
|
-
|
52
|
-
|
58
|
+
tenant_names = lambda{ ['users', 'users'] }
|
59
|
+
tenant_names.should_receive(:call)
|
53
60
|
|
54
61
|
Apartment.configure do |config|
|
55
62
|
config.excluded_models = []
|
56
|
-
config.
|
63
|
+
config.tenant_names = tenant_names
|
57
64
|
end
|
58
|
-
Apartment.
|
65
|
+
Apartment.tenant_names
|
59
66
|
end
|
60
67
|
|
61
68
|
it "should return the invoked proc if appropriate" do
|
@@ -63,12 +70,12 @@ describe Apartment do
|
|
63
70
|
|
64
71
|
Apartment.configure do |config|
|
65
72
|
config.excluded_models = []
|
66
|
-
config.
|
73
|
+
config.tenant_names = dbs
|
67
74
|
end
|
68
75
|
|
69
|
-
Apartment.
|
76
|
+
Apartment.tenant_names.should == Company.all
|
70
77
|
end
|
71
78
|
end
|
72
79
|
|
73
80
|
end
|
74
|
-
end
|
81
|
+
end
|