low_card_tables 1.0.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 +7 -0
- data/.gitignore +18 -0
- data/.travis.yml +59 -0
- data/Gemfile +17 -0
- data/LICENSE +21 -0
- data/README.md +75 -0
- data/Rakefile +6 -0
- data/lib/low_card_tables.rb +72 -0
- data/lib/low_card_tables/active_record/base.rb +55 -0
- data/lib/low_card_tables/active_record/migrations.rb +223 -0
- data/lib/low_card_tables/active_record/relation.rb +35 -0
- data/lib/low_card_tables/active_record/scoping.rb +87 -0
- data/lib/low_card_tables/errors.rb +74 -0
- data/lib/low_card_tables/has_low_card_table/base.rb +114 -0
- data/lib/low_card_tables/has_low_card_table/low_card_association.rb +273 -0
- data/lib/low_card_tables/has_low_card_table/low_card_associations_manager.rb +143 -0
- data/lib/low_card_tables/has_low_card_table/low_card_dynamic_method_manager.rb +224 -0
- data/lib/low_card_tables/has_low_card_table/low_card_objects_manager.rb +80 -0
- data/lib/low_card_tables/low_card_table/base.rb +184 -0
- data/lib/low_card_tables/low_card_table/cache.rb +214 -0
- data/lib/low_card_tables/low_card_table/cache_expiration/exponential_cache_expiration_policy.rb +151 -0
- data/lib/low_card_tables/low_card_table/cache_expiration/fixed_cache_expiration_policy.rb +23 -0
- data/lib/low_card_tables/low_card_table/cache_expiration/has_cache_expiration.rb +100 -0
- data/lib/low_card_tables/low_card_table/cache_expiration/no_caching_expiration_policy.rb +13 -0
- data/lib/low_card_tables/low_card_table/cache_expiration/unlimited_cache_expiration_policy.rb +13 -0
- data/lib/low_card_tables/low_card_table/row_collapser.rb +175 -0
- data/lib/low_card_tables/low_card_table/row_manager.rb +681 -0
- data/lib/low_card_tables/low_card_table/table_unique_index.rb +134 -0
- data/lib/low_card_tables/version.rb +4 -0
- data/lib/low_card_tables/version_support.rb +52 -0
- data/low_card_tables.gemspec +69 -0
- data/spec/low_card_tables/helpers/database_helper.rb +148 -0
- data/spec/low_card_tables/helpers/query_spy_helper.rb +47 -0
- data/spec/low_card_tables/helpers/system_helpers.rb +63 -0
- data/spec/low_card_tables/system/basic_system_spec.rb +254 -0
- data/spec/low_card_tables/system/bulk_system_spec.rb +334 -0
- data/spec/low_card_tables/system/caching_system_spec.rb +531 -0
- data/spec/low_card_tables/system/migrations_system_spec.rb +747 -0
- data/spec/low_card_tables/system/options_system_spec.rb +581 -0
- data/spec/low_card_tables/system/queries_system_spec.rb +142 -0
- data/spec/low_card_tables/system/validations_system_spec.rb +88 -0
- data/spec/low_card_tables/unit/active_record/base_spec.rb +53 -0
- data/spec/low_card_tables/unit/active_record/migrations_spec.rb +207 -0
- data/spec/low_card_tables/unit/active_record/relation_spec.rb +47 -0
- data/spec/low_card_tables/unit/active_record/scoping_spec.rb +101 -0
- data/spec/low_card_tables/unit/has_low_card_table/base_spec.rb +79 -0
- data/spec/low_card_tables/unit/has_low_card_table/low_card_association_spec.rb +287 -0
- data/spec/low_card_tables/unit/has_low_card_table/low_card_associations_manager_spec.rb +190 -0
- data/spec/low_card_tables/unit/has_low_card_table/low_card_dynamic_method_manager_spec.rb +234 -0
- data/spec/low_card_tables/unit/has_low_card_table/low_card_objects_manager_spec.rb +70 -0
- data/spec/low_card_tables/unit/low_card_table/base_spec.rb +207 -0
- data/spec/low_card_tables/unit/low_card_table/cache_expiration/exponential_cache_expiration_policy_spec.rb +128 -0
- data/spec/low_card_tables/unit/low_card_table/cache_expiration/fixed_cache_expiration_policy_spec.rb +25 -0
- data/spec/low_card_tables/unit/low_card_table/cache_expiration/has_cache_expiration_policy_spec.rb +100 -0
- data/spec/low_card_tables/unit/low_card_table/cache_expiration/no_caching_expiration_policy_spec.rb +14 -0
- data/spec/low_card_tables/unit/low_card_table/cache_expiration/unlimited_cache_expiration_policy_spec.rb +14 -0
- data/spec/low_card_tables/unit/low_card_table/cache_spec.rb +282 -0
- data/spec/low_card_tables/unit/low_card_table/row_collapser_spec.rb +109 -0
- data/spec/low_card_tables/unit/low_card_table/row_manager_spec.rb +918 -0
- data/spec/low_card_tables/unit/low_card_table/table_unique_index_spec.rb +117 -0
- metadata +206 -0
@@ -0,0 +1,142 @@
|
|
1
|
+
require 'low_card_tables'
|
2
|
+
require 'low_card_tables/helpers/database_helper'
|
3
|
+
require 'low_card_tables/helpers/system_helpers'
|
4
|
+
|
5
|
+
describe "LowCardTables query support" do
|
6
|
+
include LowCardTables::Helpers::SystemHelpers
|
7
|
+
|
8
|
+
def create_user!(name, deleted, deceased, gender, donation_level)
|
9
|
+
out = ::User.new
|
10
|
+
out.name = name
|
11
|
+
out.deleted = deleted
|
12
|
+
out.deceased = deceased
|
13
|
+
out.gender = gender
|
14
|
+
out.donation_level = donation_level
|
15
|
+
out.save!
|
16
|
+
out
|
17
|
+
end
|
18
|
+
|
19
|
+
before :each do
|
20
|
+
@dh = LowCardTables::Helpers::DatabaseHelper.new
|
21
|
+
@dh.setup_activerecord!
|
22
|
+
|
23
|
+
create_standard_system_spec_tables!
|
24
|
+
create_standard_system_spec_models!
|
25
|
+
|
26
|
+
@user1 = create_user!('User1', false, false, 'female', 10)
|
27
|
+
@user2 = create_user!('User2', true, false, 'female', 10)
|
28
|
+
@user3 = create_user!('User3', false, true, 'female', 10)
|
29
|
+
@user4 = create_user!('User4', false, false, 'male', 10)
|
30
|
+
@user5 = create_user!('User5', false, false, 'female', 8)
|
31
|
+
end
|
32
|
+
|
33
|
+
after :each do
|
34
|
+
drop_standard_system_spec_tables!
|
35
|
+
end
|
36
|
+
|
37
|
+
def check_user_ids(users, expected_users)
|
38
|
+
users.to_a.map(&:id).sort.should == expected_users.to_a.map(&:id).sort
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should allow 'where' clauses that use the association name" do
|
42
|
+
check_user_ids(::User.where(:status => { :deleted => false }), [ @user1, @user3, @user4, @user5 ])
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should allow 'where' clauses that use the foreign-key name" do
|
46
|
+
check_user_ids(::User.where(:user_status_id => ::UserStatus.low_card_ids_matching({ :deleted => false })), [ @user1, @user3, @user4, @user5 ])
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should allow 'where' clauses that use delegated properties directly" do
|
50
|
+
check_user_ids(::User.where(:deleted => false), [ @user1, @user3, @user4, @user5 ])
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should allow 'where' clauses that combine low-card and non-low-card properties" do
|
54
|
+
check_user_ids(::User.where(:deleted => false, :name => 'User1'), [ @user1 ])
|
55
|
+
check_user_ids(::User.where(:deleted => false, :name => 'User2'), [ ])
|
56
|
+
end
|
57
|
+
|
58
|
+
it "should allow 'where' clauses that use delegated properties with differently-prefixed names directly" do
|
59
|
+
define_model_class(:User2, :lctables_spec_users) { has_low_card_table :status, :prefix => :foo, :class => ::UserStatus, :foreign_key => :user_status_id }
|
60
|
+
|
61
|
+
lambda { check_user_ids(::User2.where(:deleted => false), [ ]) }.should raise_error(::ActiveRecord::StatementInvalid)
|
62
|
+
check_user_ids(::User2.where(:foo_deleted => false), [ @user1, @user3, @user4, @user5 ])
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should not allow 'where' clauses that use non-delegated properties" do
|
66
|
+
define_model_class(:User3, :lctables_spec_users) { has_low_card_table :status, :delegate => [ :deceased ], :class => ::UserStatus, :foreign_key => :user_status_id }
|
67
|
+
|
68
|
+
lambda { check_user_ids(::User3.where(:deleted => false), [ ]) }.should raise_error(::ActiveRecord::StatementInvalid)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should compose 'where' clauses correctly" do
|
72
|
+
check_user_ids(::User.where(:deleted => false).where(:gender => 'male'), [ @user4 ])
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should compose delegated and specified clauses correctly" do
|
76
|
+
check_user_ids(::User.where(:deleted => false, :status => { :gender => 'male'}), [ @user4 ])
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should allow using low-card properties in the default scope" do
|
80
|
+
LowCardTables::VersionSupport.define_default_scope(::User, :deleted => false)
|
81
|
+
check_user_ids(::User.all, [ @user1, @user3, @user4, @user5 ])
|
82
|
+
end
|
83
|
+
|
84
|
+
it "should allow using low-card properties in arbitrary scopes and the default scope" do
|
85
|
+
LowCardTables::VersionSupport.define_default_scope(::User, :deleted => false)
|
86
|
+
class ::User < ::ActiveRecord::Base
|
87
|
+
scope :foo, lambda { where(:gender => 'female') }
|
88
|
+
scope :bar, lambda { where(:status => { :deceased => false }) }
|
89
|
+
end
|
90
|
+
|
91
|
+
check_user_ids(::User.all, [ @user1, @user3, @user4, @user5 ])
|
92
|
+
check_user_ids(::User.foo, [ @user1, @user3, @user5 ])
|
93
|
+
check_user_ids(::User.bar, [ @user1, @user4, @user5 ])
|
94
|
+
check_user_ids(::User.foo.bar, [ @user1, @user5 ])
|
95
|
+
check_user_ids(::User.bar.foo, [ @user1, @user5 ])
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should pick up new low-card rows when using a low-card property in a scope" do
|
99
|
+
LowCardTables::VersionSupport.define_default_scope(::User, nil)
|
100
|
+
class ::User < ::ActiveRecord::Base
|
101
|
+
scope :foo, lambda { where(:deceased => false) }
|
102
|
+
scope :bar, lambda { where(:gender => 'female') }
|
103
|
+
end
|
104
|
+
|
105
|
+
check_user_ids(::User.all, [ @user1, @user2, @user3, @user4, @user5 ])
|
106
|
+
check_user_ids(::User.foo, [ @user1, @user2, @user4, @user5 ])
|
107
|
+
check_user_ids(::User.bar, [ @user1, @user2, @user3, @user5 ])
|
108
|
+
|
109
|
+
@user6 = create_user!('User6', false, false, 'female', 7)
|
110
|
+
[ @user1, @user2, @user3, @user4, @user5 ].map(&:user_status_id).include?(@user6.user_status_id).should_not be
|
111
|
+
|
112
|
+
check_user_ids(::User.all, [ @user1, @user2, @user3, @user4, @user5, @user6 ])
|
113
|
+
check_user_ids(::User.foo, [ @user1, @user2, @user4, @user5, @user6 ])
|
114
|
+
check_user_ids(::User.bar, [ @user1, @user2, @user3, @user5, @user6 ])
|
115
|
+
end
|
116
|
+
|
117
|
+
it "should blow up if you constrain on low-card foreign keys in a static scope" do
|
118
|
+
lambda do
|
119
|
+
LowCardTables::VersionSupport.define_default_scope(::User, nil)
|
120
|
+
class ::User < ::ActiveRecord::Base
|
121
|
+
scope :foo, where(:deleted => false)
|
122
|
+
end
|
123
|
+
end.should raise_error(LowCardTables::Errors::LowCardStaticScopeError, /user_status_id/mi)
|
124
|
+
|
125
|
+
lambda do
|
126
|
+
LowCardTables::VersionSupport.define_default_scope(::User, nil)
|
127
|
+
class ::User < ::ActiveRecord::Base
|
128
|
+
scope :foo, where(:user_status_id => ::UserStatus.low_card_ids_matching(:deleted => false))
|
129
|
+
end
|
130
|
+
end.should raise_error(LowCardTables::Errors::LowCardStaticScopeError, /user_status_id/mi)
|
131
|
+
end
|
132
|
+
|
133
|
+
|
134
|
+
it "should not blow up if you constrain on other things in a static scope" do
|
135
|
+
LowCardTables::VersionSupport.define_default_scope(::User, nil)
|
136
|
+
class ::User < ::ActiveRecord::Base
|
137
|
+
scope :foo, where(:name => %w{foo bar})
|
138
|
+
end
|
139
|
+
|
140
|
+
::User.first.should be
|
141
|
+
end
|
142
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
require 'low_card_tables'
|
2
|
+
require 'low_card_tables/helpers/database_helper'
|
3
|
+
require 'low_card_tables/helpers/system_helpers'
|
4
|
+
|
5
|
+
describe "LowCardTables validation support" do
|
6
|
+
include LowCardTables::Helpers::SystemHelpers
|
7
|
+
|
8
|
+
before :each do
|
9
|
+
@dh = LowCardTables::Helpers::DatabaseHelper.new
|
10
|
+
@dh.setup_activerecord!
|
11
|
+
|
12
|
+
create_standard_system_spec_tables!
|
13
|
+
create_standard_system_spec_models!
|
14
|
+
|
15
|
+
class ::UserStatus
|
16
|
+
validates :gender, :inclusion => { :in => %w{male female other} }
|
17
|
+
end
|
18
|
+
|
19
|
+
class ::User
|
20
|
+
validates :donation_level, :numericality => { :greater_than_or_equal_to => 0, :less_than_or_equal_to => 10 }
|
21
|
+
end
|
22
|
+
|
23
|
+
@user1 = ::User.new
|
24
|
+
@user1.name = 'User1'
|
25
|
+
@user1.deleted = false
|
26
|
+
@user1.deceased = false
|
27
|
+
@user1.gender = 'female'
|
28
|
+
@user1.donation_level = 3
|
29
|
+
@user1.save!
|
30
|
+
end
|
31
|
+
|
32
|
+
after :each do
|
33
|
+
drop_standard_system_spec_tables!
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should allow validations on the low-card table that are enforced" do
|
37
|
+
@user1.gender = 'amazing'
|
38
|
+
e = nil
|
39
|
+
|
40
|
+
begin
|
41
|
+
@user1.save!
|
42
|
+
rescue => x
|
43
|
+
e = x
|
44
|
+
end
|
45
|
+
|
46
|
+
e.should be
|
47
|
+
e.class.should == LowCardTables::Errors::LowCardInvalidLowCardRowsError
|
48
|
+
e.message.should match(/lctables_spec_user_statuses/mi)
|
49
|
+
e.message.should match(/validation/mi)
|
50
|
+
e.message.should match(/gender/mi)
|
51
|
+
e.message.should match(/gender is not included in the list/mi)
|
52
|
+
e.message.should match(/amazing/mi)
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should allow the associated table to validate low-card data" do
|
56
|
+
@user1.donation_level = 40
|
57
|
+
e = nil
|
58
|
+
|
59
|
+
begin
|
60
|
+
@user1.save!
|
61
|
+
rescue => x
|
62
|
+
e = x
|
63
|
+
end
|
64
|
+
|
65
|
+
e.should be
|
66
|
+
e.class.should == ::ActiveRecord::RecordInvalid
|
67
|
+
e.message.should match(/donation level/mi)
|
68
|
+
e.message.should match(/less than or equal to 10/mi)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should gracefully handle database-level rejection of a new low-card row" do
|
72
|
+
@user1.deleted = nil
|
73
|
+
e = nil
|
74
|
+
|
75
|
+
begin
|
76
|
+
@user1.save!
|
77
|
+
rescue => x
|
78
|
+
e = x
|
79
|
+
end
|
80
|
+
|
81
|
+
e.should be
|
82
|
+
e.class.should == LowCardTables::Errors::LowCardInvalidLowCardRowsError
|
83
|
+
e.message.should match(/lctables_spec_user_statuses/mi)
|
84
|
+
e.message.should match(/gender/mi)
|
85
|
+
e.message.should match(/nil/mi)
|
86
|
+
e.message.should match(/ActiveRecord::StatementInvalid/mi)
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
require 'low_card_tables'
|
2
|
+
|
3
|
+
describe LowCardTables::ActiveRecord::Base do
|
4
|
+
before :each do
|
5
|
+
@klass = Class.new
|
6
|
+
@klass.send(:include, LowCardTables::ActiveRecord::Base)
|
7
|
+
end
|
8
|
+
|
9
|
+
it "should include LowCardTables::LowCardTable::Base appropriately and respond to #is_low_card_table? appropriately" do
|
10
|
+
@klass.ancestors.include?(LowCardTables::LowCardTable::Base).should_not be
|
11
|
+
@klass.is_low_card_table?.should_not be
|
12
|
+
|
13
|
+
opts = Hash.new
|
14
|
+
|
15
|
+
@klass.is_low_card_table(opts)
|
16
|
+
@klass.is_low_card_table?.should be
|
17
|
+
@klass.ancestors.include?(LowCardTables::LowCardTable::Base).should be
|
18
|
+
@klass.low_card_options.should be(opts)
|
19
|
+
|
20
|
+
# ...and again:
|
21
|
+
opts = Hash.new
|
22
|
+
|
23
|
+
@klass.is_low_card_table(opts)
|
24
|
+
@klass.is_low_card_table?.should be
|
25
|
+
@klass.ancestors.include?(LowCardTables::LowCardTable::Base).should be
|
26
|
+
@klass.low_card_options.should be(opts)
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should include LowCardTables::HasLowCardTable::Base appropriately and respond to #has_any_low_card_tables? appropriately" do
|
30
|
+
@klass.ancestors.include?(LowCardTables::HasLowCardTable::Base).should_not be
|
31
|
+
@klass.has_any_low_card_tables?.should_not be
|
32
|
+
|
33
|
+
name = :foo
|
34
|
+
opts = Hash.new
|
35
|
+
|
36
|
+
mock_associations_manager = double('LowCardAssociationsManager')
|
37
|
+
expect(mock_associations_manager).to receive(:has_low_card_table).with(name, opts).once
|
38
|
+
expect(LowCardTables::HasLowCardTable::LowCardAssociationsManager).to receive(:new).and_return(mock_associations_manager)
|
39
|
+
|
40
|
+
@klass.has_low_card_table(name, opts)
|
41
|
+
@klass.has_any_low_card_tables?.should be
|
42
|
+
@klass.ancestors.include?(LowCardTables::HasLowCardTable::Base).should be
|
43
|
+
|
44
|
+
name = :bar
|
45
|
+
opts = Hash.new
|
46
|
+
|
47
|
+
expect(mock_associations_manager).to receive(:has_low_card_table).with(name, opts).once
|
48
|
+
|
49
|
+
@klass.has_low_card_table(name, opts)
|
50
|
+
@klass.has_any_low_card_tables?.should be
|
51
|
+
@klass.ancestors.include?(LowCardTables::HasLowCardTable::Base).should be
|
52
|
+
end
|
53
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
require 'low_card_tables'
|
2
|
+
|
3
|
+
describe LowCardTables::ActiveRecord::Migrations do
|
4
|
+
class MockMigrationClass
|
5
|
+
attr_reader :calls
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@calls = [ ]
|
9
|
+
end
|
10
|
+
|
11
|
+
%w{add_column remove_column}.each do |method_name|
|
12
|
+
class_eval %{
|
13
|
+
def #{method_name}(*args)
|
14
|
+
record_call(:#{method_name}, args)
|
15
|
+
end}
|
16
|
+
end
|
17
|
+
|
18
|
+
%w{create_table change_table}.each do |method_name|
|
19
|
+
class_eval %{
|
20
|
+
def #{method_name}(*args, &block)
|
21
|
+
record_call(:#{method_name}, args, &block)
|
22
|
+
instance_eval(&block)
|
23
|
+
end}
|
24
|
+
end
|
25
|
+
|
26
|
+
private
|
27
|
+
def record_call(name, args, &block)
|
28
|
+
@calls << { :name => name, :args => args, :block => block }
|
29
|
+
end
|
30
|
+
|
31
|
+
include LowCardTables::ActiveRecord::Migrations
|
32
|
+
end
|
33
|
+
|
34
|
+
before :each do
|
35
|
+
@migration = MockMigrationClass.new
|
36
|
+
@opts = { }
|
37
|
+
@proc = lambda { |*args| }
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should pass through correctly for :create_table" do
|
41
|
+
@migration.create_table(:foo, @opts, &@proc)
|
42
|
+
@migration.calls.should == [ { :name => :create_table, :args => [ :foo, @opts ], :block => @proc } ]
|
43
|
+
end
|
44
|
+
|
45
|
+
context "with mock ::Rails" do
|
46
|
+
before :each do
|
47
|
+
rails_class = Object.new
|
48
|
+
Object.send(:remove_const, :Rails) if Object.const_defined?(:Rails)
|
49
|
+
Object.const_set(:Rails, rails_class)
|
50
|
+
|
51
|
+
application = Object.new
|
52
|
+
|
53
|
+
expect(rails_class).to receive(:application).at_least(:once).and_return(application)
|
54
|
+
expect(application).to receive(:eager_load!).once
|
55
|
+
end
|
56
|
+
|
57
|
+
context "without mock low-card model" do
|
58
|
+
it "should create a temporary low-card model if :low_card => true" do
|
59
|
+
@opts[:low_card] = true
|
60
|
+
|
61
|
+
temp_class = Class.new
|
62
|
+
expect(Class).to receive(:new).once.with(::ActiveRecord::Base).and_return(temp_class).ordered
|
63
|
+
expect(temp_class).to receive(:table_name=).once.with(:foo).ordered
|
64
|
+
expect(temp_class).to receive(:is_low_card_table).once.with().ordered
|
65
|
+
expect(temp_class).to receive(:reset_column_information).once.ordered
|
66
|
+
|
67
|
+
expect(temp_class).to receive(:low_card_remove_unique_index!).once.ordered
|
68
|
+
|
69
|
+
expect(temp_class).to receive(:reset_column_information).at_least(2).times.ordered
|
70
|
+
expect(temp_class).to receive(:low_card_value_column_names).twice.ordered.and_return([ 'x', 'y' ])
|
71
|
+
|
72
|
+
expect(LowCardTables::VersionSupport).to receive(:clear_schema_cache!).once.ordered.with(temp_class)
|
73
|
+
|
74
|
+
expect(temp_class).to receive(:low_card_ensure_has_unique_index!).once.with(true).ordered
|
75
|
+
|
76
|
+
@migration.create_table(:foo, @opts, &@proc)
|
77
|
+
@migration.calls.should == [ { :name => :create_table, :args => [ :foo, { } ], :block => @proc } ]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
context "with mock low-card model" do
|
82
|
+
before :each do
|
83
|
+
non_low_card_class = Object.new
|
84
|
+
@low_card_class = Object.new
|
85
|
+
|
86
|
+
expect(non_low_card_class).to receive(:table_name).and_return('bar')
|
87
|
+
expect(@low_card_class).to receive(:table_name).and_return('foo')
|
88
|
+
|
89
|
+
expect(@low_card_class).to receive(:is_low_card_table?).and_return(true)
|
90
|
+
expect(@low_card_class).to receive(:name).at_least(:once).and_return('Whatever')
|
91
|
+
|
92
|
+
expect(::ActiveRecord::Base).to receive(:descendants).and_return([ non_low_card_class, @low_card_class ])
|
93
|
+
end
|
94
|
+
|
95
|
+
%w{add_column remove_column create_table change_table}.each do |method_name|
|
96
|
+
context "#{method_name}" do
|
97
|
+
before :each do
|
98
|
+
@method = method_name.to_sym
|
99
|
+
@args = case @method
|
100
|
+
when :create_table then [ :foo, { :bar => :baz } ]
|
101
|
+
when :change_table then [ :foo ]
|
102
|
+
when :add_column then [ :foo, :bar, :integer, { :null => false } ]
|
103
|
+
when :remove_column then [ :foo, :bar ]
|
104
|
+
else raise "unknown method_name #{method_name.inspect}"
|
105
|
+
end
|
106
|
+
|
107
|
+
@proc = case @method
|
108
|
+
when :create_table, :change_table then lambda { |*args| }
|
109
|
+
else nil
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def add_option(args, hash)
|
114
|
+
if args[-1].kind_of?(Hash)
|
115
|
+
args[-1].merge!(hash)
|
116
|
+
else
|
117
|
+
args << hash
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
def remove_low_card_options(args)
|
122
|
+
out = args.dup
|
123
|
+
if out[-1].kind_of?(Hash)
|
124
|
+
out[-1].delete_if { |k,v| k.to_s =~ /^low_card/ }
|
125
|
+
out.pop if out[-1].size == 0
|
126
|
+
end
|
127
|
+
out
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should call #eager_load, pick up an AR descendant properly, and enforce the index" do
|
131
|
+
expect(@low_card_class).to receive(:low_card_remove_unique_index!).once.ordered
|
132
|
+
|
133
|
+
expect(@low_card_class).to receive(:reset_column_information).at_least(2).times.ordered
|
134
|
+
expect(@low_card_class).to receive(:low_card_value_column_names).twice.ordered.and_return([ 'x', 'y' ])
|
135
|
+
|
136
|
+
expect(LowCardTables::VersionSupport).to receive(:clear_schema_cache!).once.ordered.with(@low_card_class)
|
137
|
+
|
138
|
+
expect(@low_card_class).to receive(:low_card_ensure_has_unique_index!).once.with(true).ordered
|
139
|
+
|
140
|
+
@migration.send(@method, *@args, &@proc)
|
141
|
+
@migration.calls.should == [ { :name => @method, :args => @args, :block => @proc } ]
|
142
|
+
end
|
143
|
+
|
144
|
+
it "should not reinstitute the index if :low_card_collapse_rows => true" do
|
145
|
+
add_option(@args, :low_card_collapse_rows => false)
|
146
|
+
|
147
|
+
expect(@low_card_class).to receive(:low_card_remove_unique_index!).once.ordered
|
148
|
+
|
149
|
+
expect(@low_card_class).to receive(:reset_column_information).at_least(2).times.ordered
|
150
|
+
expect(@low_card_class).to receive(:low_card_value_column_names).twice.ordered.and_return([ 'x', 'y' ])
|
151
|
+
|
152
|
+
expect(LowCardTables::VersionSupport).to receive(:clear_schema_cache!).once.ordered.with(@low_card_class)
|
153
|
+
|
154
|
+
@migration.send(@method, *@args, &@proc)
|
155
|
+
|
156
|
+
expected_args = remove_low_card_options(@args)
|
157
|
+
@migration.calls.should == [ { :name => @method, :args => expected_args, :block => @proc } ]
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should detect removed columns" do
|
161
|
+
add_option(@args, :low_card_foo => :bar)
|
162
|
+
|
163
|
+
expect(@low_card_class).to receive(:low_card_remove_unique_index!).once.ordered
|
164
|
+
|
165
|
+
expect(@low_card_class).to receive(:reset_column_information).at_least(2).times.ordered
|
166
|
+
expect(@low_card_class).to receive(:low_card_value_column_names).once.ordered.and_return([ 'x', 'y' ])
|
167
|
+
|
168
|
+
expect(LowCardTables::VersionSupport).to receive(:clear_schema_cache!).once.ordered.with(@low_card_class)
|
169
|
+
|
170
|
+
expect(@low_card_class).to receive(:low_card_value_column_names).once.ordered.and_return([ 'y' ])
|
171
|
+
expect(@low_card_class).to receive(:low_card_collapse_rows_and_update_referrers!).once.ordered.with(:low_card_foo => :bar)
|
172
|
+
|
173
|
+
expect(@low_card_class).to receive(:low_card_ensure_has_unique_index!).once.with(true).ordered
|
174
|
+
|
175
|
+
@migration.send(@method, *@args, &@proc)
|
176
|
+
expected_args = remove_low_card_options(@args)
|
177
|
+
@migration.calls.should == [ { :name => @method, :args => expected_args, :block => @proc } ]
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
it "should not do anything twice if calls are nested" do
|
183
|
+
@opts[:foo] = :bar
|
184
|
+
|
185
|
+
expect(@low_card_class).to receive(:low_card_remove_unique_index!).once.ordered
|
186
|
+
|
187
|
+
expect(@low_card_class).to receive(:reset_column_information).at_least(2).times.ordered
|
188
|
+
expect(@low_card_class).to receive(:low_card_value_column_names).twice.ordered.and_return([ 'x', 'y' ])
|
189
|
+
|
190
|
+
expect(LowCardTables::VersionSupport).to receive(:clear_schema_cache!).once.ordered.with(@low_card_class)
|
191
|
+
|
192
|
+
expect(@low_card_class).to receive(:low_card_ensure_has_unique_index!).once.with(true).ordered
|
193
|
+
|
194
|
+
inner_opts = { :a => :b, :low_card_foo => :bar }
|
195
|
+
@proc = lambda do |*args|
|
196
|
+
remove_column :bar, :baz, inner_opts
|
197
|
+
end
|
198
|
+
|
199
|
+
@migration.create_table(:foo, @opts, &@proc)
|
200
|
+
@migration.calls.should == [
|
201
|
+
{ :name => :create_table, :args => [ :foo, { :foo => :bar } ], :block => @proc },
|
202
|
+
{ :name => :remove_column, :args => [ :bar, :baz, { :a => :b } ], :block => nil }
|
203
|
+
]
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|