low_card_tables 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|