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,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
|