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,190 @@
|
|
1
|
+
require 'low_card_tables'
|
2
|
+
|
3
|
+
describe LowCardTables::HasLowCardTable::LowCardAssociationsManager do
|
4
|
+
describe "instantiation" do
|
5
|
+
it "should require a Class that descends from ActiveRecord::Base" do
|
6
|
+
klass = Class.new(String)
|
7
|
+
|
8
|
+
lambda do
|
9
|
+
LowCardTables::HasLowCardTable::LowCardAssociationsManager.new(klass)
|
10
|
+
end.should raise_error(ArgumentError)
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should require a Class that is not itself a low-card class" do
|
14
|
+
klass = Class.new(::ActiveRecord::Base)
|
15
|
+
allow(klass).to receive(:is_low_card_table?).and_return(true)
|
16
|
+
|
17
|
+
lambda do
|
18
|
+
LowCardTables::HasLowCardTable::LowCardAssociationsManager.new(klass)
|
19
|
+
end.should raise_error(ArgumentError)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
context "with a normal model class" do
|
24
|
+
before :each do
|
25
|
+
@model_class = Class.new
|
26
|
+
allow(@model_class).to receive(:superclass).and_return(::ActiveRecord::Base)
|
27
|
+
allow(@model_class).to receive(:is_low_card_table?).and_return(false)
|
28
|
+
expect(@model_class).to receive(:before_save).once.with(:low_card_update_foreign_keys!)
|
29
|
+
|
30
|
+
@manager = LowCardTables::HasLowCardTable::LowCardAssociationsManager.new(@model_class)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should have no associations by default" do
|
34
|
+
@manager.associations.should == [ ]
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should have a default :low_card_value_collapsing_update_scheme" do
|
38
|
+
@manager.low_card_value_collapsing_update_scheme.should == :default
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "#low_card_value_collapsing_update_scheme" do
|
42
|
+
it "should return :default by default" do
|
43
|
+
@manager.low_card_value_collapsing_update_scheme.should == :default
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should be settable to :none or :default" do
|
47
|
+
@manager.low_card_value_collapsing_update_scheme :none
|
48
|
+
@manager.low_card_value_collapsing_update_scheme.should == :none
|
49
|
+
@manager.low_card_value_collapsing_update_scheme :default
|
50
|
+
@manager.low_card_value_collapsing_update_scheme.should == :default
|
51
|
+
end
|
52
|
+
|
53
|
+
it "should be settable to a positive integer" do
|
54
|
+
@manager.low_card_value_collapsing_update_scheme 1
|
55
|
+
@manager.low_card_value_collapsing_update_scheme.should == 1
|
56
|
+
@manager.low_card_value_collapsing_update_scheme 345
|
57
|
+
@manager.low_card_value_collapsing_update_scheme.should == 345
|
58
|
+
|
59
|
+
lambda { @manager.low_card_value_collapsing_update_scheme 0 }.should raise_error(ArgumentError)
|
60
|
+
lambda { @manager.low_card_value_collapsing_update_scheme -27 }.should raise_error(ArgumentError)
|
61
|
+
end
|
62
|
+
|
63
|
+
it "should be settable to something that responds to :call" do
|
64
|
+
callable = double("callable")
|
65
|
+
allow(callable).to receive(:call)
|
66
|
+
|
67
|
+
@manager.low_card_value_collapsing_update_scheme callable
|
68
|
+
@manager.low_card_value_collapsing_update_scheme.should be(callable)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should not be settable to anything else" do
|
72
|
+
lambda { @manager.low_card_value_collapsing_update_scheme "foo" }.should raise_error(ArgumentError)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "#has_low_card_table" do
|
77
|
+
it "should require a non-blank association name" do
|
78
|
+
lambda { @manager.has_low_card_table(nil) }.should raise_error(ArgumentError)
|
79
|
+
lambda { @manager.has_low_card_table("") }.should raise_error(ArgumentError)
|
80
|
+
lambda { @manager.has_low_card_table(" ") }.should raise_error(ArgumentError)
|
81
|
+
end
|
82
|
+
|
83
|
+
context "with one association" do
|
84
|
+
before :each do
|
85
|
+
options = { :a => :b, :c => :d }
|
86
|
+
|
87
|
+
@association = double("low_card_association")
|
88
|
+
expect(LowCardTables::HasLowCardTable::LowCardAssociation).to receive(:new).once.with(@model_class, 'foo', options).and_return(@association)
|
89
|
+
|
90
|
+
lcdmm = double("low_card_dynamic_method_manager")
|
91
|
+
expect(@model_class).to receive(:_low_card_dynamic_method_manager).at_least(:once).and_return(lcdmm)
|
92
|
+
|
93
|
+
expect(lcdmm).to receive(:sync_methods!).at_least(:once)
|
94
|
+
|
95
|
+
@manager.has_low_card_table(:foo, options)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should create a new association and add it to the list" do
|
99
|
+
@manager.associations.should == [ @association ]
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should remove any previous associations with the same name" do
|
103
|
+
new_options = { :x => :y, :z => :a }
|
104
|
+
allow(@association).to receive(:association_name).and_return("foo")
|
105
|
+
|
106
|
+
association2 = double("association2")
|
107
|
+
|
108
|
+
expect(LowCardTables::HasLowCardTable::LowCardAssociation).to receive(:new).once.with(@model_class, 'foo', new_options).and_return(association2)
|
109
|
+
|
110
|
+
@manager.has_low_card_table('foo', new_options)
|
111
|
+
@manager.associations.should == [ association2 ]
|
112
|
+
end
|
113
|
+
|
114
|
+
context "and another association" do
|
115
|
+
before :each do
|
116
|
+
new_options = { :x => :y, :z => :a }
|
117
|
+
allow(@association).to receive(:association_name).and_return("foo")
|
118
|
+
|
119
|
+
@association2 = double("association2")
|
120
|
+
allow(@association2).to receive(:association_name).and_return("bar")
|
121
|
+
|
122
|
+
expect(LowCardTables::HasLowCardTable::LowCardAssociation).to receive(:new).once.with(@model_class, 'bar', new_options).and_return(@association2)
|
123
|
+
|
124
|
+
@manager.has_low_card_table('bar', new_options)
|
125
|
+
@manager.associations.should == [ @association, @association2 ]
|
126
|
+
end
|
127
|
+
|
128
|
+
it "should retrieve them by name" do
|
129
|
+
@manager._low_card_association("foo").should == @association
|
130
|
+
@manager._low_card_association("bar").should == @association2
|
131
|
+
@manager.maybe_low_card_association("foo").should == @association
|
132
|
+
@manager.maybe_low_card_association("bar").should == @association2
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should do the right thing when they're not found" do
|
136
|
+
@manager.maybe_low_card_association("baz").should_not be
|
137
|
+
lambda { @manager._low_card_association("baz") }.should raise_error(LowCardTables::Errors::LowCardAssociationNotFoundError, /bar[\s,]+foo/)
|
138
|
+
end
|
139
|
+
|
140
|
+
it "should update all foreign keys on #low_card_update_foreign_keys!" do
|
141
|
+
model_instance = double("model_instance")
|
142
|
+
allow(model_instance).to receive(:kind_of?).with(@model_class).and_return(true)
|
143
|
+
|
144
|
+
@association.should receive(:update_foreign_key!).once.with(model_instance)
|
145
|
+
@association2.should receive(:update_foreign_key!).once.with(model_instance)
|
146
|
+
|
147
|
+
@manager.low_card_update_foreign_keys!(model_instance)
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should blow up if passed something that isn't of the correct class to #low_card_update_foreign_keys!" do
|
151
|
+
model_instance = double("model_instance")
|
152
|
+
allow(model_instance).to receive(:kind_of?).with(@model_class).and_return(false)
|
153
|
+
|
154
|
+
lambda { @manager.low_card_update_foreign_keys!(model_instance) }.should raise_error(ArgumentError)
|
155
|
+
end
|
156
|
+
|
157
|
+
describe "_low_card_update_collapsed_rows" do
|
158
|
+
it "should call #update_collapsed_rows on associations that match the low-card model class passed" do
|
159
|
+
@manager.low_card_value_collapsing_update_scheme 345
|
160
|
+
|
161
|
+
low_card_class = double("low_card_class")
|
162
|
+
other_low_card_class = double("other_low_card_class")
|
163
|
+
|
164
|
+
collapse_map = double("collapse_map")
|
165
|
+
|
166
|
+
allow(@association).to receive(:low_card_class).and_return(other_low_card_class)
|
167
|
+
allow(@association2).to receive(:low_card_class).and_return(low_card_class)
|
168
|
+
|
169
|
+
expect(@association2).to receive(:update_collapsed_rows).with(collapse_map, 345).once
|
170
|
+
|
171
|
+
@manager._low_card_update_collapsed_rows(low_card_class, collapse_map)
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
describe "#low_card_column_information_reset!" do
|
179
|
+
it "should call through to the model class" do
|
180
|
+
low_card_model = double("low_card_model")
|
181
|
+
|
182
|
+
lcdmm = double("low_card_dynamic_method_manager")
|
183
|
+
expect(@model_class).to receive(:_low_card_dynamic_method_manager).and_return(lcdmm)
|
184
|
+
expect(lcdmm).to receive(:sync_methods!).once
|
185
|
+
|
186
|
+
@manager.low_card_column_information_reset!(low_card_model)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
@@ -0,0 +1,234 @@
|
|
1
|
+
require 'low_card_tables'
|
2
|
+
|
3
|
+
describe LowCardTables::HasLowCardTable::LowCardDynamicMethodManager do
|
4
|
+
before :each do
|
5
|
+
@model_class = double("model_class")
|
6
|
+
@lcam = double("low_card_associations_manager")
|
7
|
+
allow(@model_class).to receive(:_low_card_associations_manager).and_return(@lcam)
|
8
|
+
|
9
|
+
@methods_module = Module.new
|
10
|
+
allow(@model_class).to receive(:_low_card_dynamic_methods_module).and_return(@methods_module)
|
11
|
+
|
12
|
+
@manager = LowCardTables::HasLowCardTable::LowCardDynamicMethodManager.new(@model_class)
|
13
|
+
end
|
14
|
+
|
15
|
+
context "with two associations and installed methods" do
|
16
|
+
before :each do
|
17
|
+
@association1 = double("association1")
|
18
|
+
allow(@association1).to receive(:association_name).and_return("foo")
|
19
|
+
allow(@association1).to receive(:foreign_key_column_name).and_return("a1fk")
|
20
|
+
allow(@association1).to receive(:class_method_name_to_low_card_method_name_map).and_return({
|
21
|
+
'cm1' => 'lc1m1', 'cm2' => 'lc1m2' })
|
22
|
+
|
23
|
+
@association2 = double("association2")
|
24
|
+
allow(@association2).to receive(:association_name).and_return("bar")
|
25
|
+
allow(@association2).to receive(:foreign_key_column_name).and_return("a2fk")
|
26
|
+
allow(@association2).to receive(:class_method_name_to_low_card_method_name_map).and_return({
|
27
|
+
'cm2' => 'lc2m1', 'cm3' => 'lc2m2' })
|
28
|
+
|
29
|
+
allow(@lcam).to receive(:associations).and_return([ @association1, @association2 ])
|
30
|
+
|
31
|
+
@manager.sync_methods!
|
32
|
+
end
|
33
|
+
|
34
|
+
describe "#scope_from_query" do
|
35
|
+
before :each do
|
36
|
+
@base_scope = double("base_scope")
|
37
|
+
@end_scope = double("end_scope")
|
38
|
+
end
|
39
|
+
|
40
|
+
it "should pass through non-low-card constraints" do
|
41
|
+
expect(@base_scope).to receive(:where).once.with({ :name => 'bonk', :_low_card_direct => true }).and_return(@end_scope)
|
42
|
+
@manager.scope_from_query(@base_scope, { :name => 'bonk' }).should be(@end_scope)
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should apply low-card constraints" do
|
46
|
+
low_card_class_1 = double("low_card_class_1")
|
47
|
+
allow(@association1).to receive(:low_card_class).and_return(low_card_class_1)
|
48
|
+
expect(low_card_class_1).to receive(:low_card_ids_matching).with({ 'lc1m1' => false }).and_return([ 3, 9, 12 ])
|
49
|
+
|
50
|
+
expect(@base_scope).to receive(:where).once.with("a1fk IN (:ids)", { :ids => [ 3, 9, 12 ] }).and_return(@end_scope)
|
51
|
+
@manager.scope_from_query(@base_scope, { :cm1 => false }).should be(@end_scope)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should apply multiple low-card constraints combined with non-low-card constraints" do
|
55
|
+
low_card_class_1 = double("low_card_class_1")
|
56
|
+
allow(@association1).to receive(:low_card_class).and_return(low_card_class_1)
|
57
|
+
expect(low_card_class_1).to receive(:low_card_ids_matching).with({ 'lc1m1' => false }).and_return([ 3, 9, 12 ])
|
58
|
+
|
59
|
+
low_card_class_2 = double("low_card_class_2")
|
60
|
+
allow(@association2).to receive(:low_card_class).and_return(low_card_class_2)
|
61
|
+
expect(low_card_class_2).to receive(:low_card_ids_matching).with({ 'lc2m2' => [ :a, :b ], 'lc2m1' => 'yohoho' }).and_return([ 4, 6, 8 ])
|
62
|
+
|
63
|
+
mid_scope = double("mid_scope")
|
64
|
+
|
65
|
+
# expect(@base_scope).to receive(:where).once.with("a1fk IN (:ids)", { :ids => [ 3, 9, 12 ] }).and_return(mid_scope)
|
66
|
+
# expect(mid_scope).to receive(:where).once.with("a2fk IN (:ids)", { :ids => [ 4, 6, 8 ] }).and_return(@end_scope)
|
67
|
+
|
68
|
+
base_args = mid_args = nil
|
69
|
+
|
70
|
+
expect(@base_scope).to receive(:where).once { |*args| base_args = args; mid_scope }
|
71
|
+
expect(mid_scope).to receive(:where).once { |*args| mid_args = args; @end_scope }
|
72
|
+
|
73
|
+
@manager.scope_from_query(@base_scope, { :cm1 => false, :cm3 => [ :a, :b ], :bar => { 'lc2m1' => "yohoho" } }).should be(@end_scope)
|
74
|
+
|
75
|
+
# Perfectly valid for this to happen in either order
|
76
|
+
if base_args[0] =~ /^a1fk/
|
77
|
+
base_args.should == [ 'a1fk IN (:ids)', { :ids => [ 3, 9, 12 ]}]
|
78
|
+
mid_args.should == [ 'a2fk IN (:ids)', { :ids => [ 4, 6, 8 ]}]
|
79
|
+
else
|
80
|
+
base_args.should == [ 'a2fk IN (:ids)', { :ids => [ 4, 6, 8 ]}]
|
81
|
+
mid_args.should == [ 'a1fk IN (:ids)', { :ids => [ 3, 9, 12 ]}]
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should apply low-card constraints in combination with direct foreign-key constraints" do
|
86
|
+
low_card_class_1 = double("low_card_class_1")
|
87
|
+
allow(@association1).to receive(:low_card_class).and_return(low_card_class_1)
|
88
|
+
expect(low_card_class_1).to receive(:low_card_ids_matching).with({ 'lc1m1' => false }).and_return([ 3, 9, 12 ])
|
89
|
+
|
90
|
+
mid_scope = double("mid_scope")
|
91
|
+
|
92
|
+
expect(@base_scope).to receive(:where).once.with({ :a2fk => [ 1, 3, 12 ], :_low_card_direct => true }).and_return(mid_scope)
|
93
|
+
expect(mid_scope).to receive(:where).once.with("a1fk IN (:ids)", { :ids => [ 3, 9, 12 ] }).and_return(@end_scope)
|
94
|
+
|
95
|
+
@manager.scope_from_query(@base_scope, { :cm1 => false, :a2fk => [ 1, 3, 12] }).should be(@end_scope)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe "method delegation and invocation" do
|
100
|
+
before :each do
|
101
|
+
@invoked_object = double("invoked_object")
|
102
|
+
@low_card_object = double("low_card_object")
|
103
|
+
@args = double("args")
|
104
|
+
@rv = double("rv")
|
105
|
+
|
106
|
+
allow(@invoked_object).to receive(:kind_of?).with(@model_class).and_return(true)
|
107
|
+
end
|
108
|
+
|
109
|
+
def check_invocation(method_name, association_name, low_card_method_name)
|
110
|
+
expect(@invoked_object).to receive(association_name).and_return(@low_card_object)
|
111
|
+
expect(@low_card_object).to receive(low_card_method_name).with(@args).and_return(@rv)
|
112
|
+
|
113
|
+
@methods_module.instance_methods.map(&:to_s).include?(method_name.to_s).should be
|
114
|
+
|
115
|
+
@manager.run_low_card_method(@invoked_object, method_name, @args).should be(@rv)
|
116
|
+
end
|
117
|
+
|
118
|
+
context "after changing associations" do
|
119
|
+
before :each do
|
120
|
+
@manager.sync_methods!
|
121
|
+
|
122
|
+
@association3 = double("association3")
|
123
|
+
allow(@association3).to receive(:association_name).and_return("baz")
|
124
|
+
allow(@association3).to receive(:foreign_key_column_name).and_return("a3fk")
|
125
|
+
allow(@association3).to receive(:class_method_name_to_low_card_method_name_map).and_return({
|
126
|
+
'cm2' => 'lc3m1', 'cm4' => 'lc3m2' })
|
127
|
+
|
128
|
+
allow(@lcam).to receive(:associations).and_return([ @association1, @association3 ])
|
129
|
+
|
130
|
+
@manager.sync_methods!
|
131
|
+
end
|
132
|
+
|
133
|
+
it "should run the right method for cm1" do
|
134
|
+
check_invocation("cm1", "foo", "lc1m1")
|
135
|
+
end
|
136
|
+
|
137
|
+
it "should run the right method for cm2" do
|
138
|
+
check_invocation("cm2", "baz", "lc3m1")
|
139
|
+
end
|
140
|
+
|
141
|
+
it "should run the right method for cm3" do
|
142
|
+
@methods_module.instance_methods.map(&:to_s).include?("cm3").should_not be
|
143
|
+
|
144
|
+
lambda { @manager.run_low_card_method(@invoked_object, "cm3", @args) }.should raise_error(NameError, /cm3/)
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should run the right method for cm4" do
|
148
|
+
check_invocation("cm4", "baz", "lc3m2")
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
it "should run the right method for cm1" do
|
153
|
+
check_invocation("cm1", "foo", "lc1m1")
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should run the right method for cm2" do
|
157
|
+
check_invocation("cm2", "bar", "lc2m1")
|
158
|
+
end
|
159
|
+
|
160
|
+
it "should run the right method for cm3" do
|
161
|
+
check_invocation("cm3", "bar", "lc2m2")
|
162
|
+
end
|
163
|
+
|
164
|
+
def check_association(method_name, association)
|
165
|
+
lcom = double("low_card_objects_manager")
|
166
|
+
expect(@invoked_object).to receive(:_low_card_objects_manager).and_return(lcom)
|
167
|
+
expect(lcom).to receive(:object_for).with(association).and_return(@low_card_object)
|
168
|
+
|
169
|
+
@methods_module.instance_methods.map(&:to_s).include?(method_name).should be
|
170
|
+
|
171
|
+
@manager.run_low_card_method(@invoked_object, method_name, [ ]).should be(@low_card_object)
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should return the right association for foo" do
|
175
|
+
check_association("foo", @association1)
|
176
|
+
end
|
177
|
+
|
178
|
+
it "should return the right association for bar" do
|
179
|
+
check_association("bar", @association2)
|
180
|
+
end
|
181
|
+
|
182
|
+
def check_foreign_key_get(method_name, association)
|
183
|
+
lcom = double("low_card_objects_manager")
|
184
|
+
expect(@invoked_object).to receive(:_low_card_objects_manager).and_return(lcom)
|
185
|
+
expect(lcom).to receive(:foreign_key_for).with(association).and_return(12345)
|
186
|
+
|
187
|
+
@methods_module.instance_methods.map(&:to_s).include?(method_name).should be
|
188
|
+
|
189
|
+
@manager.run_low_card_method(@invoked_object, method_name, [ ]).should == 12345
|
190
|
+
end
|
191
|
+
|
192
|
+
it "should return the right foreign key for a1fk" do
|
193
|
+
check_foreign_key_get("a1fk", @association1)
|
194
|
+
end
|
195
|
+
|
196
|
+
it "should return the right foreign key for a2fk" do
|
197
|
+
check_foreign_key_get("a2fk", @association2)
|
198
|
+
end
|
199
|
+
|
200
|
+
def check_foreign_key_set(method_name, association)
|
201
|
+
args = double("args")
|
202
|
+
lcom = double("low_card_objects_manager")
|
203
|
+
expect(@invoked_object).to receive(:_low_card_objects_manager).and_return(lcom)
|
204
|
+
expect(lcom).to receive(:set_foreign_key_for).with(association, args)
|
205
|
+
|
206
|
+
@methods_module.instance_methods.map(&:to_s).include?(method_name).should be
|
207
|
+
|
208
|
+
@manager.run_low_card_method(@invoked_object, method_name, args)
|
209
|
+
end
|
210
|
+
|
211
|
+
it "should set the right foreign key for a1fk" do
|
212
|
+
check_foreign_key_set("a1fk=", @association1)
|
213
|
+
end
|
214
|
+
|
215
|
+
it "should set the right foreign key for a2fk" do
|
216
|
+
check_foreign_key_set("a2fk=", @association2)
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should blow up if asked to invoke a method that doesn't exist" do
|
220
|
+
lambda do
|
221
|
+
@manager.run_low_card_method(@invoked_object, "quux", [ ])
|
222
|
+
end.should raise_error(/quux/i)
|
223
|
+
end
|
224
|
+
|
225
|
+
it "should blow up if given an object of the wrong class" do
|
226
|
+
allow(@invoked_object).to receive(:kind_of?).with(@model_class).and_return(false)
|
227
|
+
|
228
|
+
lambda do
|
229
|
+
@manager.run_low_card_method(@invoked_object, "foo", [ ])
|
230
|
+
end.should raise_error(ArgumentError)
|
231
|
+
end
|
232
|
+
end
|
233
|
+
end
|
234
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'low_card_tables'
|
2
|
+
|
3
|
+
describe LowCardTables::HasLowCardTable::LowCardObjectsManager do
|
4
|
+
before :each do
|
5
|
+
@model_instance = double("model_instance")
|
6
|
+
@manager = LowCardTables::HasLowCardTable::LowCardObjectsManager.new(@model_instance)
|
7
|
+
|
8
|
+
@model_class = double("model_class")
|
9
|
+
allow(@model_instance).to receive(:class).and_return(@model_class)
|
10
|
+
|
11
|
+
@lcam = double("low_card_associations_manager")
|
12
|
+
allow(@model_class).to receive(:_low_card_associations_manager).and_return(@lcam)
|
13
|
+
|
14
|
+
@association1 = double("LowCardAssociation")
|
15
|
+
allow(@association1).to receive(:association_name).and_return("foo")
|
16
|
+
allow(@association1).to receive(:foreign_key_column_name).and_return("blahblah_id")
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#object_for" do
|
20
|
+
it "should call through to the association to create the object, and only once" do
|
21
|
+
associated_object = double("associated_object")
|
22
|
+
|
23
|
+
expect(@lcam).to receive(:_low_card_association).with("foo").and_return(@association1)
|
24
|
+
expect(@association1).to receive(:create_low_card_object_for).once.with(@model_instance).and_return(associated_object)
|
25
|
+
|
26
|
+
@manager.object_for(@association1).should be(associated_object)
|
27
|
+
@manager.object_for(@association1).should be(associated_object)
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should maintain multiple associcated objects separately" do
|
31
|
+
associated_object_1 = double("associated_object_1")
|
32
|
+
associated_object_2 = double("associated_object_2")
|
33
|
+
|
34
|
+
@association2 = double("LowCardAssociation2")
|
35
|
+
allow(@association2).to receive(:association_name).and_return("bar")
|
36
|
+
|
37
|
+
expect(@lcam).to receive(:_low_card_association).with("foo").and_return(@association1)
|
38
|
+
expect(@association1).to receive(:create_low_card_object_for).once.with(@model_instance).and_return(associated_object_1)
|
39
|
+
|
40
|
+
expect(@lcam).to receive(:_low_card_association).with("bar").and_return(@association2)
|
41
|
+
expect(@association2).to receive(:create_low_card_object_for).once.with(@model_instance).and_return(associated_object_2)
|
42
|
+
|
43
|
+
@manager.object_for(@association1).should be(associated_object_1)
|
44
|
+
@manager.object_for(@association2).should be(associated_object_2)
|
45
|
+
@manager.object_for(@association1).should be(associated_object_1)
|
46
|
+
@manager.object_for(@association2).should be(associated_object_2)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
describe "foreign-key support" do
|
51
|
+
it "should call through to the model instance on get" do
|
52
|
+
expect(@model_instance).to receive(:[]).with("blahblah_id").and_return(12345)
|
53
|
+
@manager.foreign_key_for(@association1).should == 12345
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should call through to the model instance on set, invalidate the object, and return the new value" do
|
57
|
+
associated_object_1 = double("associated_object_1")
|
58
|
+
expect(@lcam).to receive(:_low_card_association).with("foo").at_least(:once).and_return(@association1)
|
59
|
+
expect(@association1).to receive(:create_low_card_object_for).once.with(@model_instance).and_return(associated_object_1)
|
60
|
+
@manager.object_for(@association1).should be(associated_object_1)
|
61
|
+
|
62
|
+
expect(@model_instance).to receive(:[]=).with("blahblah_id", 23456)
|
63
|
+
@manager.set_foreign_key_for(@association1, 23456).should == 23456
|
64
|
+
|
65
|
+
associated_object_2 = double("associated_object_2")
|
66
|
+
expect(@association1).to receive(:create_low_card_object_for).once.with(@model_instance).and_return(associated_object_2)
|
67
|
+
@manager.object_for(@association1).should be(associated_object_2)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|