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,47 @@
|
|
1
|
+
require 'low_card_tables'
|
2
|
+
|
3
|
+
describe LowCardTables::ActiveRecord::Relation do
|
4
|
+
before :each do
|
5
|
+
module ArRelationBaseSpecModule
|
6
|
+
def where(*args)
|
7
|
+
where_calls << args
|
8
|
+
:where_called
|
9
|
+
end
|
10
|
+
|
11
|
+
def where_calls
|
12
|
+
@where_calls ||= [ ]
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
@obj = Object.new
|
17
|
+
class << @obj
|
18
|
+
include ArRelationBaseSpecModule
|
19
|
+
include LowCardTables::ActiveRecord::Relation
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should pass through if has_any_low_card_tables? is false" do
|
24
|
+
expect(@obj).to receive(:has_any_low_card_tables?).once.and_return(false)
|
25
|
+
@obj.where(:a, :b, :c).should == :where_called
|
26
|
+
@obj.where_calls.should == [ [ :a, :b, :c ] ]
|
27
|
+
end
|
28
|
+
|
29
|
+
context "with has_any_low_card_tables? == true" do
|
30
|
+
before :each do
|
31
|
+
expect(@obj).to receive(:has_any_low_card_tables?).once.and_return(true)
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should pass through if passed :_low_card_direct => true" do
|
35
|
+
@obj.where(:a => :b, :c => :d, :_low_card_direct => true).should == :where_called
|
36
|
+
@obj.where_calls.should == [ [ { :a => :b, :c => :d } ] ]
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should delegate otherwise" do
|
40
|
+
dmm = double('_low_card_dynamic_method_manager')
|
41
|
+
expect(@obj).to receive(:_low_card_dynamic_method_manager).once.and_return(dmm)
|
42
|
+
expect(dmm).to receive(:scope_from_query).once.with(@obj, :foo => :bar, :bar => :baz).and_return :yo_ho_ho
|
43
|
+
@obj.where(:foo => :bar, :bar => :baz).should == :yo_ho_ho
|
44
|
+
@obj.where_calls.length.should == 0
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'low_card_tables'
|
2
|
+
require 'active_support'
|
3
|
+
|
4
|
+
describe LowCardTables::ActiveRecord::Scoping do
|
5
|
+
before :each do
|
6
|
+
module ArScopingBaseSpecModule
|
7
|
+
extend ActiveSupport::Concern
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
def scope(*args, &block)
|
11
|
+
scope_calls << { :args => args, :block => block }
|
12
|
+
:scope_called
|
13
|
+
end
|
14
|
+
|
15
|
+
def scope_calls
|
16
|
+
@scope_calls ||= [ ]
|
17
|
+
end
|
18
|
+
|
19
|
+
def reset!
|
20
|
+
@scope_calls = [ ]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class ArScopingTestClass
|
26
|
+
include ArScopingBaseSpecModule
|
27
|
+
include LowCardTables::ActiveRecord::Scoping
|
28
|
+
end
|
29
|
+
|
30
|
+
ArScopingTestClass.reset!
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should pass through if given a block" do
|
34
|
+
proc = lambda { }
|
35
|
+
ArScopingTestClass.scope(:foo, :bar => :baz, &proc).should == :scope_called
|
36
|
+
ArScopingTestClass.scope_calls.should == [ { :args => [ :foo, { :bar => :baz } ], :block => proc } ]
|
37
|
+
end
|
38
|
+
|
39
|
+
it "should pass through if there are no low-card tables" do
|
40
|
+
expect(ArScopingTestClass).to receive(:has_any_low_card_tables?).and_return(false)
|
41
|
+
ArScopingTestClass.scope(:foo, :bar => :baz).should == :scope_called
|
42
|
+
ArScopingTestClass.scope_calls.should == [ { :args => [ :foo, { :bar => :baz } ], :block => nil } ]
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should pass through if it isn't handed a Relation" do
|
46
|
+
expect(ArScopingTestClass).to receive(:has_any_low_card_tables?).and_return(true)
|
47
|
+
ArScopingTestClass.scope(:foo, :bar => :baz).should == :scope_called
|
48
|
+
ArScopingTestClass.scope_calls.should == [ { :args => [ :foo, { :bar => :baz } ], :block => nil } ]
|
49
|
+
end
|
50
|
+
|
51
|
+
context "with low-card tables and a Relation" do
|
52
|
+
before :each do
|
53
|
+
expect(ArScopingTestClass).to receive(:has_any_low_card_tables?).and_return(true)
|
54
|
+
@relation = Object.new
|
55
|
+
expect(@relation).to receive(:kind_of?).with(::ActiveRecord::Relation).and_return(true)
|
56
|
+
|
57
|
+
@associations = [ Object.new, Object.new ]
|
58
|
+
expect(@associations[0]).to receive(:foreign_key_column_name).at_least(:once).and_return("fk1")
|
59
|
+
expect(@associations[1]).to receive(:foreign_key_column_name).at_least(:once).and_return("fk2")
|
60
|
+
|
61
|
+
@lcam = double('low_card_associations_manager')
|
62
|
+
expect(ArScopingTestClass).to receive(:_low_card_associations_manager).at_least(:once).and_return(@lcam)
|
63
|
+
expect(@lcam).to receive(:associations).at_least(:once).and_return(@associations)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should be fine as long as you don't use any of the foreign keys" do
|
67
|
+
where_values = [ "foo=a", "bar=b" ]
|
68
|
+
expect(@relation).to receive(:where_values).and_return(where_values)
|
69
|
+
|
70
|
+
ArScopingTestClass.scope(:foo, @relation).should == :scope_called
|
71
|
+
ArScopingTestClass.scope_calls.should == [ { :args => [ :foo, @relation ], :block => nil } ]
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should blow up if you do use any of the foreign keys" do
|
75
|
+
where_values = [ "foo=a", "fk2=b" ]
|
76
|
+
expect(@relation).to receive(:where_values).and_return(where_values)
|
77
|
+
|
78
|
+
lambda { ArScopingTestClass.scope(:foo, @relation) }.should raise_error(LowCardTables::Errors::LowCardStaticScopeError, /fk2/i)
|
79
|
+
ArScopingTestClass.scope_calls.should == [ { :args => [ :foo, @relation ], :block => nil } ]
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should blow up if you do use any of the foreign keys, with #to_sql necessary" do
|
83
|
+
wv2 = Object.new
|
84
|
+
expect(wv2).to receive(:to_sql).at_least(:once).and_return("fk2=b")
|
85
|
+
where_values = [ "foo=a", wv2 ]
|
86
|
+
expect(@relation).to receive(:where_values).and_return(where_values)
|
87
|
+
|
88
|
+
lambda { ArScopingTestClass.scope(:foo, @relation) }.should raise_error(LowCardTables::Errors::LowCardStaticScopeError, /fk2/i)
|
89
|
+
ArScopingTestClass.scope_calls.should == [ { :args => [ :foo, @relation ], :block => nil } ]
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should skip where values that aren't a String and don't respond to :to_sql" do
|
93
|
+
wv2 = Object.new
|
94
|
+
where_values = [ "foo=a", wv2 ]
|
95
|
+
expect(@relation).to receive(:where_values).and_return(where_values)
|
96
|
+
|
97
|
+
ArScopingTestClass.scope(:foo, @relation).should == :scope_called
|
98
|
+
ArScopingTestClass.scope_calls.should == [ { :args => [ :foo, @relation ], :block => nil } ]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'low_card_tables'
|
2
|
+
|
3
|
+
describe LowCardTables::HasLowCardTable::Base do
|
4
|
+
before :each do
|
5
|
+
@spec_class = Class.new
|
6
|
+
@spec_class.class_eval do
|
7
|
+
include LowCardTables::HasLowCardTable::Base
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
it "should always has_any_low_card_tables?" do
|
12
|
+
@spec_class.has_any_low_card_tables?.should == true
|
13
|
+
end
|
14
|
+
|
15
|
+
context "with a low-card associations manager" do
|
16
|
+
before :each do
|
17
|
+
@lcam = double("low_card_associations_manager")
|
18
|
+
expect(LowCardTables::HasLowCardTable::LowCardAssociationsManager).to receive(:new).once.with(@spec_class).and_return(@lcam)
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should create and maintain one low-card associations manager" do
|
22
|
+
@spec_class._low_card_associations_manager.should be(@lcam)
|
23
|
+
@spec_class._low_card_associations_manager.should be(@lcam)
|
24
|
+
end
|
25
|
+
|
26
|
+
%w{has_low_card_table _low_card_association _low_card_update_collapsed_rows low_card_value_collapsing_update_scheme}.each do |method_name|
|
27
|
+
it "should forward ##{method_name} to the LCAM" do
|
28
|
+
args = [ :foo, { :bar => :baz } ]
|
29
|
+
rv = Object.new
|
30
|
+
expect(@lcam).to receive(method_name).once.with(*args).and_return(rv)
|
31
|
+
|
32
|
+
@spec_class.send(method_name, *args).should be(rv)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should forward low_card_update_foreign_keys! to the LCAM" do
|
37
|
+
instance = @spec_class.new
|
38
|
+
rv = Object.new
|
39
|
+
|
40
|
+
expect(@lcam).to receive(:low_card_update_foreign_keys!).once.with(instance).and_return(rv)
|
41
|
+
|
42
|
+
instance.low_card_update_foreign_keys!.should be(rv)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should create and maintain one low-card dynamic methods manager" do
|
47
|
+
@lcdmm = double("low_card_dynamic_methods_manager")
|
48
|
+
expect(LowCardTables::HasLowCardTable::LowCardDynamicMethodManager).to receive(:new).once.with(@spec_class).and_return(@lcdmm)
|
49
|
+
|
50
|
+
@spec_class._low_card_dynamic_method_manager.should be(@lcdmm)
|
51
|
+
@spec_class._low_card_dynamic_method_manager.should be(@lcdmm)
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should create and maintain one low-card dynamic methods module" do
|
55
|
+
class HasLowCardTableBaseDynamicMethodsModuleTest
|
56
|
+
include LowCardTables::HasLowCardTable::Base
|
57
|
+
end
|
58
|
+
|
59
|
+
HasLowCardTableBaseDynamicMethodsModuleTest.ancestors.detect { |a| a.name =~ /LowCardDynamicMethods/i }.should_not be
|
60
|
+
|
61
|
+
mod = HasLowCardTableBaseDynamicMethodsModuleTest._low_card_dynamic_methods_module
|
62
|
+
mod.class.should == ::Module
|
63
|
+
HasLowCardTableBaseDynamicMethodsModuleTest.ancestors.include?(mod).should be
|
64
|
+
HasLowCardTableBaseDynamicMethodsModuleTest.ancestors.detect { |a| a.name =~ /LowCardDynamicMethods/i }.should be
|
65
|
+
|
66
|
+
HasLowCardTableBaseDynamicMethodsModuleTest.const_get(:LowCardDynamicMethods).should be(mod)
|
67
|
+
|
68
|
+
HasLowCardTableBaseDynamicMethodsModuleTest._low_card_dynamic_methods_module.should be(mod)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should create and maintain one low-card objects manager" do
|
72
|
+
lcom = double("low_card_objects_manager")
|
73
|
+
obj = @spec_class.new
|
74
|
+
expect(LowCardTables::HasLowCardTable::LowCardObjectsManager).to receive(:new).once.with(obj).and_return(lcom)
|
75
|
+
|
76
|
+
obj._low_card_objects_manager.should be(lcom)
|
77
|
+
obj._low_card_objects_manager.should be(lcom)
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,287 @@
|
|
1
|
+
require 'low_card_tables'
|
2
|
+
|
3
|
+
describe LowCardTables::HasLowCardTable::LowCardAssociation do
|
4
|
+
class ::ModelClassNameAscName; end
|
5
|
+
|
6
|
+
before :each do
|
7
|
+
@model_class = Class.new
|
8
|
+
allow(@model_class).to receive(:name).and_return('model_class_name')
|
9
|
+
|
10
|
+
col1 = double("column_1")
|
11
|
+
allow(col1).to receive(:name).and_return("id")
|
12
|
+
col2 = double("column_2")
|
13
|
+
allow(col2).to receive(:name).and_return("name")
|
14
|
+
@col3 = double("column_3")
|
15
|
+
allow(@col3).to receive(:name).and_return("model_class_name_asc_name_id")
|
16
|
+
|
17
|
+
allow(@model_class).to receive(:columns).and_return([ col1, col2, @col3 ])
|
18
|
+
|
19
|
+
allow(ModelClassNameAscName).to receive(:is_low_card_table?).and_return(true)
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "instantiation, #foreign_key_column_name, #association_name, and #low_card_class" do
|
23
|
+
context "with a referred-to class" do
|
24
|
+
before :each do
|
25
|
+
expect(ModelClassNameAscName).to receive(:low_card_referred_to_by).once.with(@model_class)
|
26
|
+
end
|
27
|
+
|
28
|
+
it "should create a new, simple instance correctly, and tell the referred-to class" do
|
29
|
+
association = LowCardTables::HasLowCardTable::LowCardAssociation.new(@model_class, :asc_name, { })
|
30
|
+
|
31
|
+
association.association_name.should == 'asc_name'
|
32
|
+
association.foreign_key_column_name.should == 'model_class_name_asc_name_id'
|
33
|
+
association.low_card_class.should be(::ModelClassNameAscName)
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "options" do
|
37
|
+
{
|
38
|
+
::ModelClassNameAscName => 'Class object',
|
39
|
+
'ModelClassNameAscName' => 'String',
|
40
|
+
:ModelClassNameAscName => 'Symbol',
|
41
|
+
:model_class_name_asc_name => 'Symbol (underscored)',
|
42
|
+
'model_class_name_asc_name' => 'String (underscored)'
|
43
|
+
}.each do |input, description|
|
44
|
+
it "should allow setting the referred-to class name by #{description}" do
|
45
|
+
allow(@col3).to receive(:name).and_return("model_class_name_foobar_id")
|
46
|
+
association = LowCardTables::HasLowCardTable::LowCardAssociation.new(@model_class, :foobar, { :class => input })
|
47
|
+
|
48
|
+
association.association_name.should == 'foobar'
|
49
|
+
association.foreign_key_column_name.should == 'model_class_name_foobar_id'
|
50
|
+
association.low_card_class.should be(::ModelClassNameAscName)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should allow setting the foreign key" do
|
55
|
+
association = LowCardTables::HasLowCardTable::LowCardAssociation.new(@model_class, :foobar, { :class => ModelClassNameAscName, :foreign_key => :model_class_name_asc_name_id })
|
56
|
+
|
57
|
+
association.association_name.should == 'foobar'
|
58
|
+
association.foreign_key_column_name.should == 'model_class_name_asc_name_id'
|
59
|
+
association.low_card_class.should be(::ModelClassNameAscName)
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should fail instantiation if the foreign key specified isn't a column" do
|
65
|
+
lambda do
|
66
|
+
LowCardTables::HasLowCardTable::LowCardAssociation.new(@model_class, :foobar,
|
67
|
+
{ :class => ModelClassNameAscName, :foreign_key => :bogus_id })
|
68
|
+
end.should raise_error(ArgumentError, /bogus_id/i)
|
69
|
+
end
|
70
|
+
|
71
|
+
it "should fail instantiation if the foreign key inferred isn't a column" do
|
72
|
+
allow(@col3).to receive(:name).and_return("whatever")
|
73
|
+
lambda do
|
74
|
+
LowCardTables::HasLowCardTable::LowCardAssociation.new(@model_class, :foobar,
|
75
|
+
{ :class => ModelClassNameAscName })
|
76
|
+
end.should raise_error(ArgumentError, /model_class_name_foobar_id/i)
|
77
|
+
end
|
78
|
+
|
79
|
+
it "should fail instantiation if the class inferred can't be found" do
|
80
|
+
allow(@col3).to receive(:name).and_return("model_class_name_yohoho_id")
|
81
|
+
lambda do
|
82
|
+
LowCardTables::HasLowCardTable::LowCardAssociation.new(@model_class, :yohoho, { })
|
83
|
+
end.should raise_error(ArgumentError, /ModelClassNameYohoho/i)
|
84
|
+
end
|
85
|
+
|
86
|
+
it "should fail instantiation if the class specified can't be found" do
|
87
|
+
allow(@col3).to receive(:name).and_return("model_class_name_yohoho_id")
|
88
|
+
lambda do
|
89
|
+
LowCardTables::HasLowCardTable::LowCardAssociation.new(@model_class, :yohoho, { :class => :FooBar })
|
90
|
+
end.should raise_error(ArgumentError, /FooBar/i)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should fail instantiation if the class specified isn't a Class" do
|
94
|
+
::Object.const_set(:FooBar1, "hi")
|
95
|
+
|
96
|
+
allow(@col3).to receive(:name).and_return("model_class_name_yohoho_id")
|
97
|
+
lambda do
|
98
|
+
LowCardTables::HasLowCardTable::LowCardAssociation.new(@model_class, :yohoho, { :class => :FooBar1 })
|
99
|
+
end.should raise_error(ArgumentError, /\"hi\"/i)
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should fail instantiation if the class specified doesn't respond to is_low_card_table" do
|
103
|
+
klass = Class.new
|
104
|
+
|
105
|
+
allow(@col3).to receive(:name).and_return("model_class_name_yohoho_id")
|
106
|
+
lambda do
|
107
|
+
LowCardTables::HasLowCardTable::LowCardAssociation.new(@model_class, :yohoho, { :class => klass })
|
108
|
+
end.should raise_error(ArgumentError, /is_low_card_table/i)
|
109
|
+
end
|
110
|
+
|
111
|
+
it "should fail instantiation if the class specified isn't a low-card table Class" do
|
112
|
+
klass = Class.new
|
113
|
+
expect(klass).to receive(:is_low_card_table?).and_return(false)
|
114
|
+
|
115
|
+
allow(@col3).to receive(:name).and_return("model_class_name_yohoho_id")
|
116
|
+
lambda do
|
117
|
+
LowCardTables::HasLowCardTable::LowCardAssociation.new(@model_class, :yohoho, { :class => klass })
|
118
|
+
end.should raise_error(ArgumentError, /is_low_card_table/i)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
describe "#delegated_method_names" do
|
123
|
+
def check_delegated_method_names(options, expected_results)
|
124
|
+
expect(ModelClassNameAscName).to receive(:low_card_referred_to_by).once.with(@model_class)
|
125
|
+
expect(ModelClassNameAscName).to receive(:low_card_value_column_names).and_return(%w{foo bar baz})
|
126
|
+
association = LowCardTables::HasLowCardTable::LowCardAssociation.new(@model_class, :asc_name, options)
|
127
|
+
association.delegated_method_names.sort.should == expected_results.sort
|
128
|
+
end
|
129
|
+
|
130
|
+
it "should contain all methods by default" do
|
131
|
+
check_delegated_method_names({ }, %w{foo bar baz})
|
132
|
+
end
|
133
|
+
|
134
|
+
it "should contain no methods if :delegate => false" do
|
135
|
+
check_delegated_method_names({ :delegate => false }, [ ])
|
136
|
+
end
|
137
|
+
|
138
|
+
it "should contain only specified methods if specified" do
|
139
|
+
check_delegated_method_names({ :delegate => %w{bar baz} }, %w{bar baz})
|
140
|
+
end
|
141
|
+
|
142
|
+
it "should contain only a single method if specified (Symbol)" do
|
143
|
+
check_delegated_method_names({ :delegate => :bar }, %w{bar})
|
144
|
+
end
|
145
|
+
|
146
|
+
it "should contain only a single method if specified (String)" do
|
147
|
+
check_delegated_method_names({ :delegate => 'bar' }, %w{bar})
|
148
|
+
end
|
149
|
+
|
150
|
+
it "should exclude certain methods if specified" do
|
151
|
+
check_delegated_method_names({ :delegate => { :except => %w{bar baz} } }, %w{foo})
|
152
|
+
end
|
153
|
+
|
154
|
+
it "should blow up if passed anything else" do
|
155
|
+
lambda do
|
156
|
+
check_delegated_method_names({ :delegate => { :a => :b } }, %w{foo})
|
157
|
+
end.should raise_error(ArgumentError)
|
158
|
+
|
159
|
+
lambda do
|
160
|
+
check_delegated_method_names({ :delegate => 123 }, %w{foo})
|
161
|
+
end.should raise_error(ArgumentError)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
describe "#class_method_name_to_low_card_method_name_map" do
|
166
|
+
def check_class_method_name_to_low_card_method_name_map(options, expected_results)
|
167
|
+
expect(ModelClassNameAscName).to receive(:low_card_referred_to_by).once.with(@model_class)
|
168
|
+
expect(ModelClassNameAscName).to receive(:low_card_value_column_names).and_return(%w{foo bar baz})
|
169
|
+
association = LowCardTables::HasLowCardTable::LowCardAssociation.new(@model_class, :asc_name, options)
|
170
|
+
|
171
|
+
association.class_method_name_to_low_card_method_name_map.should == expected_results
|
172
|
+
end
|
173
|
+
|
174
|
+
it "should map all methods to their same names by default" do
|
175
|
+
check_class_method_name_to_low_card_method_name_map({ }, {
|
176
|
+
'foo' => 'foo', 'foo=' => 'foo=', 'bar' => 'bar', 'bar=' => 'bar=', 'baz' => 'baz', 'baz=' => 'baz=' })
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should exclude methods if requested" do
|
180
|
+
check_class_method_name_to_low_card_method_name_map({ :delegate => %w{foo bar} }, {
|
181
|
+
'foo' => 'foo', 'foo=' => 'foo=', 'bar' => 'bar', 'bar=' => 'bar=' })
|
182
|
+
end
|
183
|
+
|
184
|
+
it "should prefix methods if requested" do
|
185
|
+
check_class_method_name_to_low_card_method_name_map({ :prefix => true }, {
|
186
|
+
'asc_name_foo' => 'foo', 'asc_name_foo=' => 'foo=', 'asc_name_bar' => 'bar', 'asc_name_bar=' => 'bar=',
|
187
|
+
'asc_name_baz' => 'baz', 'asc_name_baz=' => 'baz=' })
|
188
|
+
end
|
189
|
+
|
190
|
+
it "should prefix methods with a given string if requested" do
|
191
|
+
check_class_method_name_to_low_card_method_name_map({ :prefix => :quux }, {
|
192
|
+
'quux_foo' => 'foo', 'quux_foo=' => 'foo=', 'quux_bar' => 'bar', 'quux_bar=' => 'bar=',
|
193
|
+
'quux_baz' => 'baz', 'quux_baz=' => 'baz=' })
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
context "with a valid instance" do
|
198
|
+
before :each do
|
199
|
+
expect(ModelClassNameAscName).to receive(:low_card_referred_to_by).once.with(@model_class)
|
200
|
+
@association = LowCardTables::HasLowCardTable::LowCardAssociation.new(@model_class, :asc_name, { })
|
201
|
+
end
|
202
|
+
|
203
|
+
describe "#create_low_card_object_for" do
|
204
|
+
it "should fail if passed something of the wrong class" do
|
205
|
+
lambda { @association.create_low_card_object_for("foo") }.should raise_error(/foo/i)
|
206
|
+
end
|
207
|
+
|
208
|
+
it "should return an empty object if there's no ID" do
|
209
|
+
instance = @model_class.new
|
210
|
+
expect(instance).to receive(:[]).with('model_class_name_asc_name_id').and_return(nil)
|
211
|
+
|
212
|
+
rv = Object.new
|
213
|
+
expect(ModelClassNameAscName).to receive(:new).once.and_return(rv)
|
214
|
+
|
215
|
+
obj = @association.create_low_card_object_for(instance)
|
216
|
+
obj.should be(rv)
|
217
|
+
end
|
218
|
+
|
219
|
+
it "should return a duplicated object if there is an ID" do
|
220
|
+
instance = @model_class.new
|
221
|
+
expect(instance).to receive(:[]).with('model_class_name_asc_name_id').and_return(12345)
|
222
|
+
|
223
|
+
template = Object.new
|
224
|
+
expect(ModelClassNameAscName).to receive(:low_card_row_for_id).once.with(12345).and_return(template)
|
225
|
+
|
226
|
+
rv = Object.new
|
227
|
+
expect(template).to receive(:dup).once.and_return(rv)
|
228
|
+
expect(rv).to receive(:id=).once.with(nil)
|
229
|
+
|
230
|
+
obj = @association.create_low_card_object_for(instance)
|
231
|
+
obj.should be(rv)
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
235
|
+
describe "#update_collapsed_rows" do
|
236
|
+
before :each do
|
237
|
+
@map = { :foo => [ :bar, :baz ] }
|
238
|
+
end
|
239
|
+
|
240
|
+
it "should just call it if it's callable" do
|
241
|
+
scheme = Object.new
|
242
|
+
expect(scheme).to receive(:call).once.with(@map)
|
243
|
+
|
244
|
+
@association.update_collapsed_rows(@map, scheme)
|
245
|
+
end
|
246
|
+
|
247
|
+
it "should do nothing if it's :none" do
|
248
|
+
@association.update_collapsed_rows(@map, :none)
|
249
|
+
end
|
250
|
+
|
251
|
+
# We deliberately don't spec, here, the case where we update in batches; we leave it to the system tests.
|
252
|
+
# A unit test would just test some insanely specific and long combination of ActiveRecord calls, and it wouldn't
|
253
|
+
# be a useful test anyway, since you're vastly more likely to make errors in figuring out what the DB needs --
|
254
|
+
# and you'd just spec those errors as well.
|
255
|
+
end
|
256
|
+
|
257
|
+
describe "#update_foreign_key!" do
|
258
|
+
before :each do
|
259
|
+
@model_instance = double("model_instance")
|
260
|
+
lcom = double("low_card_objects_manager")
|
261
|
+
low_card_object = double("low_card_object")
|
262
|
+
|
263
|
+
expect(@model_instance).to receive(:_low_card_objects_manager).and_return(lcom)
|
264
|
+
expect(lcom).to receive(:object_for).with(@association).and_return(low_card_object)
|
265
|
+
|
266
|
+
expect(ModelClassNameAscName).to receive(:low_card_value_column_names).and_return(%w{foo bar baz})
|
267
|
+
allow(low_card_object).to receive(:[]) { |name| "foo#{name}" }
|
268
|
+
|
269
|
+
expect(ModelClassNameAscName).to receive(:low_card_find_or_create_ids_for).with({
|
270
|
+
'foo' => 'foofoo', 'bar' => 'foobar', 'baz' => 'foobaz' }).and_return(12345)
|
271
|
+
end
|
272
|
+
|
273
|
+
it "should change the ID when necessary" do
|
274
|
+
expect(@model_instance).to receive(:[]).with("model_class_name_asc_name_id").and_return(345)
|
275
|
+
expect(@model_instance).to receive(:[]=).with("model_class_name_asc_name_id", 12345)
|
276
|
+
|
277
|
+
@association.update_foreign_key!(@model_instance)
|
278
|
+
end
|
279
|
+
|
280
|
+
it "should not change the ID if it's not necessary" do
|
281
|
+
expect(@model_instance).to receive(:[]).with("model_class_name_asc_name_id").and_return(12345)
|
282
|
+
|
283
|
+
@association.update_foreign_key!(@model_instance)
|
284
|
+
end
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|