low_card_tables 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.travis.yml +59 -0
  4. data/Gemfile +17 -0
  5. data/LICENSE +21 -0
  6. data/README.md +75 -0
  7. data/Rakefile +6 -0
  8. data/lib/low_card_tables.rb +72 -0
  9. data/lib/low_card_tables/active_record/base.rb +55 -0
  10. data/lib/low_card_tables/active_record/migrations.rb +223 -0
  11. data/lib/low_card_tables/active_record/relation.rb +35 -0
  12. data/lib/low_card_tables/active_record/scoping.rb +87 -0
  13. data/lib/low_card_tables/errors.rb +74 -0
  14. data/lib/low_card_tables/has_low_card_table/base.rb +114 -0
  15. data/lib/low_card_tables/has_low_card_table/low_card_association.rb +273 -0
  16. data/lib/low_card_tables/has_low_card_table/low_card_associations_manager.rb +143 -0
  17. data/lib/low_card_tables/has_low_card_table/low_card_dynamic_method_manager.rb +224 -0
  18. data/lib/low_card_tables/has_low_card_table/low_card_objects_manager.rb +80 -0
  19. data/lib/low_card_tables/low_card_table/base.rb +184 -0
  20. data/lib/low_card_tables/low_card_table/cache.rb +214 -0
  21. data/lib/low_card_tables/low_card_table/cache_expiration/exponential_cache_expiration_policy.rb +151 -0
  22. data/lib/low_card_tables/low_card_table/cache_expiration/fixed_cache_expiration_policy.rb +23 -0
  23. data/lib/low_card_tables/low_card_table/cache_expiration/has_cache_expiration.rb +100 -0
  24. data/lib/low_card_tables/low_card_table/cache_expiration/no_caching_expiration_policy.rb +13 -0
  25. data/lib/low_card_tables/low_card_table/cache_expiration/unlimited_cache_expiration_policy.rb +13 -0
  26. data/lib/low_card_tables/low_card_table/row_collapser.rb +175 -0
  27. data/lib/low_card_tables/low_card_table/row_manager.rb +681 -0
  28. data/lib/low_card_tables/low_card_table/table_unique_index.rb +134 -0
  29. data/lib/low_card_tables/version.rb +4 -0
  30. data/lib/low_card_tables/version_support.rb +52 -0
  31. data/low_card_tables.gemspec +69 -0
  32. data/spec/low_card_tables/helpers/database_helper.rb +148 -0
  33. data/spec/low_card_tables/helpers/query_spy_helper.rb +47 -0
  34. data/spec/low_card_tables/helpers/system_helpers.rb +63 -0
  35. data/spec/low_card_tables/system/basic_system_spec.rb +254 -0
  36. data/spec/low_card_tables/system/bulk_system_spec.rb +334 -0
  37. data/spec/low_card_tables/system/caching_system_spec.rb +531 -0
  38. data/spec/low_card_tables/system/migrations_system_spec.rb +747 -0
  39. data/spec/low_card_tables/system/options_system_spec.rb +581 -0
  40. data/spec/low_card_tables/system/queries_system_spec.rb +142 -0
  41. data/spec/low_card_tables/system/validations_system_spec.rb +88 -0
  42. data/spec/low_card_tables/unit/active_record/base_spec.rb +53 -0
  43. data/spec/low_card_tables/unit/active_record/migrations_spec.rb +207 -0
  44. data/spec/low_card_tables/unit/active_record/relation_spec.rb +47 -0
  45. data/spec/low_card_tables/unit/active_record/scoping_spec.rb +101 -0
  46. data/spec/low_card_tables/unit/has_low_card_table/base_spec.rb +79 -0
  47. data/spec/low_card_tables/unit/has_low_card_table/low_card_association_spec.rb +287 -0
  48. data/spec/low_card_tables/unit/has_low_card_table/low_card_associations_manager_spec.rb +190 -0
  49. data/spec/low_card_tables/unit/has_low_card_table/low_card_dynamic_method_manager_spec.rb +234 -0
  50. data/spec/low_card_tables/unit/has_low_card_table/low_card_objects_manager_spec.rb +70 -0
  51. data/spec/low_card_tables/unit/low_card_table/base_spec.rb +207 -0
  52. data/spec/low_card_tables/unit/low_card_table/cache_expiration/exponential_cache_expiration_policy_spec.rb +128 -0
  53. data/spec/low_card_tables/unit/low_card_table/cache_expiration/fixed_cache_expiration_policy_spec.rb +25 -0
  54. data/spec/low_card_tables/unit/low_card_table/cache_expiration/has_cache_expiration_policy_spec.rb +100 -0
  55. data/spec/low_card_tables/unit/low_card_table/cache_expiration/no_caching_expiration_policy_spec.rb +14 -0
  56. data/spec/low_card_tables/unit/low_card_table/cache_expiration/unlimited_cache_expiration_policy_spec.rb +14 -0
  57. data/spec/low_card_tables/unit/low_card_table/cache_spec.rb +282 -0
  58. data/spec/low_card_tables/unit/low_card_table/row_collapser_spec.rb +109 -0
  59. data/spec/low_card_tables/unit/low_card_table/row_manager_spec.rb +918 -0
  60. data/spec/low_card_tables/unit/low_card_table/table_unique_index_spec.rb +117 -0
  61. 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