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.
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,207 @@
1
+ require 'low_card_tables'
2
+
3
+ describe LowCardTables::LowCardTable::Base do
4
+ before :each do
5
+ @test_class = Class.new
6
+ @test_class.send(:include, LowCardTables::LowCardTable::Base)
7
+ end
8
+
9
+ it "should make its recipient inherit the low-card policy from LowCardTables" do
10
+ LowCardTables.low_card_cache_expiration 20
11
+ @test_class.low_card_cache_expiration.should == 20
12
+
13
+ LowCardTables.low_card_cache_expiration 45
14
+ @test_class.low_card_cache_expiration.should == 45
15
+ end
16
+
17
+ context "with an instance" do
18
+ before :each do
19
+ @instance = @test_class.new
20
+ end
21
+
22
+ describe "row-matching" do
23
+ it "should match columns by hash-indexing, by default" do
24
+ expect(@instance).to receive(:[]).with('foo').twice.and_return(:bar)
25
+
26
+ @instance._low_card_column_matches?(:foo, :bar).should be
27
+ @instance._low_card_column_matches?(:foo, :baz).should_not be
28
+ end
29
+
30
+ it "should match a hash one-by-one, by calling through to _low_card_column_matches?" do
31
+ expect(@instance).to receive(:_low_card_column_matches?).with(:foo, :bar).and_return(true)
32
+ expect(@instance).to receive(:_low_card_column_matches?).with(:baz, :quux).and_return(true)
33
+
34
+ @instance._low_card_row_matches_hash?(:foo => :bar, :baz => :quux).should be
35
+ end
36
+
37
+ it "should not match a different hash, by calling through to _low_card_column_matches?" do
38
+ expect(@instance).to receive(:_low_card_column_matches?).with(:foo, :bar).and_return(false)
39
+
40
+ @instance._low_card_row_matches_hash?(:foo => :bar).should_not be
41
+ end
42
+
43
+ it "should always match if the hash contains nothing" do
44
+ @instance._low_card_row_matches_hash?({ }).should be
45
+ end
46
+
47
+ it "should match a set of hashes, by looking for one that matches, by calling through to _low_card_row_matches_hash?" do
48
+ h1 = { :foo => :bar, :bar => :baz }
49
+ h2 = { :a => :b, 'c' => 12345 }
50
+
51
+ expect(@instance).to receive(:_low_card_row_matches_hash?).with(h1).and_return(false)
52
+ expect(@instance).to receive(:_low_card_row_matches_hash?).with(h2).and_return(true)
53
+
54
+ @instance._low_card_row_matches_any_hash?([ h1, h2 ]).should be
55
+ end
56
+
57
+ it "should fail to match a set of hashes, by looking for one that matches, by calling through to _low_card_row_matches_hash?" do
58
+ h1 = { :foo => :bar, :bar => :baz }
59
+ h2 = { :a => :b, 'c' => 12345 }
60
+
61
+ expect(@instance).to receive(:_low_card_row_matches_hash?).with(h1).and_return(false)
62
+ expect(@instance).to receive(:_low_card_row_matches_hash?).with(h2).and_return(false)
63
+
64
+ @instance._low_card_row_matches_any_hash?([ h1, h2 ]).should_not be
65
+ end
66
+
67
+ it "should match blocks by calling them" do
68
+ block = double("block")
69
+
70
+ expect(block).to receive(:call).once.with(@instance).and_return(true)
71
+ @instance._low_card_row_matches_block?(block).should be
72
+
73
+ expect(block).to receive(:call).once.with(@instance).and_return(false)
74
+ @instance._low_card_row_matches_block?(block).should_not be
75
+ end
76
+ end
77
+ end
78
+
79
+ it "should save and return options properly" do
80
+ @test_class.is_low_card_table(:foo => :bar, :baz => :quux)
81
+ @test_class.low_card_options.should == { :foo => :bar, :baz => :quux }
82
+
83
+ @test_class.low_card_options = { :a => :b, :c => :d }
84
+ @test_class.low_card_options.should == { :a => :b, :c => :d }
85
+ end
86
+
87
+ it "should declare that it's a low-card table" do
88
+ @test_class.is_low_card_table?.should be
89
+ end
90
+
91
+ describe "row manager" do
92
+ it "should create a row manager of the right class, by default" do
93
+ rm = @test_class._low_card_row_manager
94
+ @test_class._low_card_row_manager.should be(rm)
95
+
96
+ rm.class.should == LowCardTables::LowCardTable::RowManager
97
+ rm.low_card_model.should be(@test_class)
98
+ end
99
+
100
+ it "should call through to the row manager on #reset_column_information" do
101
+ mod = Module.new
102
+ mod.module_eval do
103
+ def reset_column_information
104
+ @_reset_column_information_calls ||= 0
105
+ @_reset_column_information_calls += 1
106
+ :reset_column_information_return_value
107
+ end
108
+
109
+ def reset_column_information_calls
110
+ @_reset_column_information_calls
111
+ end
112
+ end
113
+
114
+ test_class = Class.new
115
+ test_class.send(:extend, mod)
116
+ test_class.send(:include, LowCardTables::LowCardTable::Base)
117
+
118
+ rm = double("row_manager")
119
+ allow(test_class).to receive(:_low_card_row_manager).and_return(rm)
120
+
121
+ expect(rm).to receive(:column_information_reset!).once
122
+
123
+ test_class.reset_column_information.should == :reset_column_information_return_value
124
+ test_class.reset_column_information_calls.should == 1
125
+ end
126
+
127
+ context "with a mock row manager" do
128
+ before :each do
129
+ @rm = double("row_manager")
130
+ allow(@test_class).to receive(:_low_card_row_manager).and_return(@rm)
131
+ end
132
+
133
+ %w{all_rows row_for_id rows_for_ids rows_matching ids_matching find_ids_for find_or_create_ids_for
134
+ find_rows_for find_or_create_rows_for flush_cache! referring_models collapse_rows_and_update_referrers!
135
+ value_column_names referred_to_by
136
+ ensure_has_unique_index! remove_unique_index!}.each do |method_name|
137
+ it "should delegate to the row manager for #{method_name}" do
138
+ expect(@rm).to receive(method_name).once.with(:foo, :bar).and_return(:baz)
139
+ @test_class.send("low_card_#{method_name}", :foo, :bar).should == :baz
140
+ end
141
+ end
142
+ end
143
+ end
144
+
145
+ context "saving" do
146
+ before :each do
147
+ @save_mod = Module.new
148
+ @save_mod.module_eval do
149
+ def save(*args)
150
+ _saves_called << [ :save, args ]
151
+ :save_return_value
152
+ end
153
+
154
+ def save!(*args)
155
+ _saves_called << [ :save!, args ]
156
+ :save_return_value!
157
+ end
158
+
159
+ def _saves_called
160
+ @_saves_called ||= [ ]
161
+ end
162
+ end
163
+
164
+ # We need a new class -- because we need to make sure our module gets in there first
165
+ @test_class = Class.new
166
+ @test_class.send(:include, @save_mod)
167
+ @test_class.send(:include, LowCardTables::LowCardTable::Base)
168
+
169
+ @test_class.is_low_card_table
170
+ @test_class.is_low_card_table # ensure that calling it twice doesn't mess anything up
171
+
172
+ @instance = @test_class.new
173
+ end
174
+
175
+ it "should refuse to save, by default" do
176
+ lambda { @instance.save }.should raise_error(LowCardTables::Errors::LowCardCannotSaveAssociatedLowCardObjectsError)
177
+ @instance._saves_called.length.should == 0
178
+
179
+ lambda { @instance.save! }.should raise_error(LowCardTables::Errors::LowCardCannotSaveAssociatedLowCardObjectsError)
180
+ @instance._saves_called.length.should == 0
181
+ end
182
+
183
+ it "should save, if invoked via #save_low_card_row" do
184
+ @instance.save_low_card_row(:foo, :bar).should == :save_return_value
185
+ @instance._saves_called.should == [ [ :save, [ :foo, :bar ] ] ]
186
+ end
187
+
188
+ it "should save, if invoked via #save_low_card_row!" do
189
+ @instance.save_low_card_row!(:foo, :bar).should == :save_return_value!
190
+ @instance._saves_called.should == [ [ :save!, [ :foo, :bar ] ] ]
191
+ end
192
+
193
+ it "should not accidentally continue allowing saves if save! blows up" do
194
+ @save_mod.module_eval do
195
+ def save!(*args)
196
+ _saves_called << [ :save!, args ]
197
+ raise "kaboom"
198
+ end
199
+ end
200
+
201
+ lambda { @instance.save_low_card_row!(:foo, :bar) }.should raise_error(/kaboom/i)
202
+ @instance._saves_called.should == [ [ :save!, [ :foo, :bar ] ] ]
203
+
204
+ lambda { @instance.save! }.should raise_error(LowCardTables::Errors::LowCardCannotSaveAssociatedLowCardObjectsError)
205
+ end
206
+ end
207
+ end
@@ -0,0 +1,128 @@
1
+ require 'low_card_tables'
2
+ require 'active_support/time'
3
+
4
+ describe LowCardTables::LowCardTable::CacheExpiration::ExponentialCacheExpirationPolicy do
5
+ def klass
6
+ LowCardTables::LowCardTable::CacheExpiration::ExponentialCacheExpirationPolicy
7
+ end
8
+
9
+ it "should create itself with default values" do
10
+ instance = klass.new({ :start_time => Time.now })
11
+
12
+ instance.zero_floor.should == 3.minutes
13
+ instance.min_time.should == 10.seconds
14
+ instance.exponent.should == 2.0
15
+ instance.max_time.should == 1.hour
16
+ end
17
+
18
+ it "should prevent specifying invalid values" do
19
+ lambda { klass.new(:start_time => Time.now, :zero_floor_time => 'foo') }.should raise_error(ArgumentError, /zero_floor_time/i)
20
+ lambda { klass.new(:start_time => Time.now, :zero_floor_time => -1.0) }.should raise_error(ArgumentError, /zero_floor_time/i)
21
+
22
+ lambda { klass.new(:start_time => Time.now, :min_time => 'foo') }.should raise_error(ArgumentError, /min_time/i)
23
+ lambda { klass.new(:start_time => Time.now, :min_time => -1.0) }.should raise_error(ArgumentError, /min_time/i)
24
+ lambda { klass.new(:start_time => Time.now, :min_time => 0.0) }.should raise_error(ArgumentError, /min_time/i)
25
+ lambda { klass.new(:start_time => Time.now, :min_time => 1.0) }.should raise_error(ArgumentError, /min_time/i)
26
+
27
+ lambda { klass.new(:start_time => Time.now, :exponent => 'foo') }.should raise_error(ArgumentError, /exponent/i)
28
+ lambda { klass.new(:start_time => Time.now, :exponent => -1.0) }.should raise_error(ArgumentError, /exponent/i)
29
+ lambda { klass.new(:start_time => Time.now, :exponent => 0.0) }.should raise_error(ArgumentError, /exponent/i)
30
+ lambda { klass.new(:start_time => Time.now, :exponent => 1.0) }.should raise_error(ArgumentError, /exponent/i)
31
+
32
+ lambda { klass.new(:start_time => Time.now, :max_time => 'foo') }.should raise_error(ArgumentError, /max_time/i)
33
+ lambda { klass.new(:start_time => Time.now, :max_time => 0.0) }.should raise_error(ArgumentError, /max_time/i)
34
+ lambda { klass.new(:start_time => Time.now, :max_time => -1.0) }.should raise_error(ArgumentError, /max_time/i)
35
+ lambda { klass.new(:start_time => Time.now, :min_time => 3.0, :max_time => 2.0) }.should raise_error(ArgumentError, /max_time/i)
36
+ end
37
+
38
+ def with_instance(options = { })
39
+ @start_time = Time.now
40
+ @cache_read_at = @start_time
41
+ @instance = klass.new(options.merge(:start_time => @start_time))
42
+
43
+ yield
44
+ end
45
+
46
+ def refill!(time)
47
+ @cache_read_at = @start_time + time
48
+ end
49
+
50
+ def should_be_stale!(time)
51
+ @instance.stale?(@cache_read_at, @start_time + time).should be
52
+ end
53
+
54
+ def should_not_be_stale!(time)
55
+ @instance.stale?(@cache_read_at, @start_time + time).should_not be
56
+ end
57
+
58
+ it "should have a zero floor that's adjustable" do
59
+ with_instance(:zero_floor_time => 1.0) do
60
+ should_be_stale!(0.0)
61
+ should_be_stale!(0.5)
62
+ should_be_stale!(0.9)
63
+
64
+ refill!(1.0)
65
+ should_not_be_stale!(1.1)
66
+ end
67
+
68
+ with_instance(:zero_floor_time => 0.0) do
69
+ should_not_be_stale!(0.0)
70
+
71
+ refill!(0.5)
72
+ should_not_be_stale!(0.6)
73
+ end
74
+ end
75
+
76
+ it "should start an initial period of min_time seconds" do
77
+ with_instance(:zero_floor_time => 1.0, :min_time => 3.0) do
78
+ should_be_stale!(1.0)
79
+ refill!(1.0)
80
+
81
+ should_not_be_stale!(1.5)
82
+ should_not_be_stale!(3.0)
83
+ should_not_be_stale!(3.9)
84
+ should_be_stale!(4.1)
85
+ end
86
+ end
87
+
88
+ it "should exponentially increase according to the exponent" do
89
+ with_instance(:zero_floor_time => 1.0, :min_time => 3.0, :exponent => 1.5) do
90
+ # period 1: 1.0 - 4.0 seconds (3.0 duration)
91
+ refill!(1.0)
92
+ should_not_be_stale!(3.9)
93
+
94
+ # period 2: 4.0 - 8.5 seconds (4.5 duration)
95
+ refill!(4.0)
96
+ should_not_be_stale!(4.0)
97
+ should_not_be_stale!(7.0)
98
+ should_not_be_stale!(8.4)
99
+ should_be_stale!(8.5)
100
+
101
+ # period 3: 8.5 - 15.25 seconds (6.75 duration)
102
+ refill!(8.5)
103
+ should_not_be_stale!(8.5)
104
+ should_not_be_stale!(12.0)
105
+ should_not_be_stale!(15.0)
106
+ should_be_stale!(15.25)
107
+ end
108
+ end
109
+
110
+ it "should cap out at the max_time" do
111
+ with_instance(:zero_floor_time => 1.0, :min_time => 5.0, :exponent => 100.0, :max_time => 10.0) do
112
+ # zero floor: 0.0-1.0
113
+ # period 1: 1.0-6.0
114
+ # period 2: 6.0-16.0
115
+ refill!(6.0)
116
+
117
+ should_not_be_stale!(6.0)
118
+ should_not_be_stale!(15.9)
119
+ should_be_stale!(16.0)
120
+
121
+ # period 3: 16.0-26.0
122
+ refill!(16.0)
123
+ should_not_be_stale!(16.0)
124
+ should_not_be_stale!(25.9)
125
+ should_be_stale!(26.0)
126
+ end
127
+ end
128
+ end
@@ -0,0 +1,25 @@
1
+ require 'low_card_tables'
2
+
3
+ describe LowCardTables::LowCardTable::CacheExpiration::FixedCacheExpirationPolicy do
4
+ def klass
5
+ LowCardTables::LowCardTable::CacheExpiration::FixedCacheExpirationPolicy
6
+ end
7
+
8
+ it "should require a non-negative expiration time" do
9
+ lambda { klass.new("foo") }.should raise_error(ArgumentError)
10
+ lambda { klass.new(-1.0) }.should raise_error(ArgumentError)
11
+ end
12
+
13
+ it "should expire at that time" do
14
+ i = klass.new(1.0)
15
+
16
+ i.expiration_time.should == 1.0
17
+
18
+ the_time = Time.now - rand(100_000)
19
+ i.stale?(the_time, the_time).should_not be
20
+ i.stale?(the_time, the_time + 0.5).should_not be
21
+ i.stale?(the_time, the_time + 0.99).should_not be
22
+ i.stale?(the_time, the_time + 1.0).should be
23
+ i.stale?(the_time, the_time + 15.0).should be
24
+ end
25
+ end
@@ -0,0 +1,100 @@
1
+ require 'low_card_tables'
2
+ require 'active_support/time'
3
+
4
+ describe LowCardTables::LowCardTable::CacheExpiration::HasCacheExpiration do
5
+ before :each do
6
+ @test_class = Class.new
7
+ @test_class.send(:include, LowCardTables::LowCardTable::CacheExpiration::HasCacheExpiration)
8
+ end
9
+
10
+ it "should default to no policy" do
11
+ @test_class.low_card_cache_expiration.should be_nil
12
+ end
13
+
14
+ it "should allow setting to a no-caching policy" do
15
+ @test_class.low_card_cache_expiration 0
16
+ @test_class.low_card_cache_expiration.should == 0
17
+ @test_class.low_card_cache_expiration_policy_object.class.should == LowCardTables::LowCardTable::CacheExpiration::NoCachingExpirationPolicy
18
+ end
19
+
20
+ it "should allow setting to a fixed policy" do
21
+ @test_class.low_card_cache_expiration 15
22
+ @test_class.low_card_cache_expiration.should == 15
23
+ @test_class.low_card_cache_expiration_policy_object.class.should == LowCardTables::LowCardTable::CacheExpiration::FixedCacheExpirationPolicy
24
+ @test_class.low_card_cache_expiration_policy_object.expiration_time.should == 15
25
+ end
26
+
27
+ it "should allow setting to an unlimited policy" do
28
+ @test_class.low_card_cache_expiration :unlimited
29
+ @test_class.low_card_cache_expiration.should == :unlimited
30
+ @test_class.low_card_cache_expiration_policy_object.class.should == LowCardTables::LowCardTable::CacheExpiration::UnlimitedCacheExpirationPolicy
31
+ end
32
+
33
+ it "should allow setting to an exponential policy, with defaults" do
34
+ @test_class.low_card_cache_expiration :exponential
35
+ @test_class.low_card_cache_expiration.should == :exponential
36
+ @test_class.low_card_cache_expiration_policy_object.class.should == LowCardTables::LowCardTable::CacheExpiration::ExponentialCacheExpirationPolicy
37
+ @test_class.low_card_cache_expiration_policy_object.zero_floor.should == 3.minutes
38
+ @test_class.low_card_cache_expiration_policy_object.min_time.should == 10.seconds
39
+ @test_class.low_card_cache_expiration_policy_object.exponent.should == 2.0
40
+ @test_class.low_card_cache_expiration_policy_object.max_time.should == 1.hour
41
+ end
42
+
43
+ it "should allow setting to an exponential policy, with overrides" do
44
+ @test_class.low_card_cache_expiration :exponential, :zero_floor_time => 2.0, :min_time => 3.0, :exponent => 4.0, :max_time => 5.0
45
+ @test_class.low_card_cache_expiration.should == [ :exponential, { :zero_floor_time => 2.0, :min_time => 3.0, :exponent => 4.0, :max_time => 5.0 } ]
46
+ @test_class.low_card_cache_expiration_policy_object.class.should == LowCardTables::LowCardTable::CacheExpiration::ExponentialCacheExpirationPolicy
47
+ @test_class.low_card_cache_expiration_policy_object.zero_floor.should == 2.0
48
+ @test_class.low_card_cache_expiration_policy_object.min_time.should == 3.0
49
+ @test_class.low_card_cache_expiration_policy_object.exponent.should == 4.0
50
+ @test_class.low_card_cache_expiration_policy_object.max_time.should == 5.0
51
+ end
52
+
53
+ it "should allow changing the policy" do
54
+ @test_class.low_card_cache_expiration 15
55
+ @test_class.low_card_cache_expiration.should == 15
56
+ @test_class.low_card_cache_expiration :unlimited
57
+ @test_class.low_card_cache_expiration.should == :unlimited
58
+ end
59
+
60
+ it "should not change the policy if there's an exception" do
61
+ @test_class.low_card_cache_expiration 15
62
+ @test_class.low_card_cache_expiration.should == 15
63
+
64
+ lambda { @test_class.low_card_cache_expiration :exponential, :zero_floor_time => -10.0 }.should raise_error(ArgumentError)
65
+
66
+ @test_class.low_card_cache_expiration.should == 15
67
+ end
68
+
69
+ it "should fall back to the inherited class, if there is one" do
70
+ @parent_class = Class.new
71
+ @parent_class.send(:include, LowCardTables::LowCardTable::CacheExpiration::HasCacheExpiration)
72
+
73
+ @test_class.low_card_cache_policy_inherits_from(@parent_class)
74
+
75
+ @test_class.low_card_cache_expiration.should be_nil
76
+ @test_class.low_card_cache_expiration_policy_object.should be_nil
77
+
78
+ @parent_class.low_card_cache_expiration 15
79
+ @parent_class.low_card_cache_expiration.should == 15
80
+ @parent_class.low_card_cache_expiration_policy_object.class.should == LowCardTables::LowCardTable::CacheExpiration::FixedCacheExpirationPolicy
81
+ @parent_class.low_card_cache_expiration_policy_object.expiration_time.should == 15
82
+ @test_class.low_card_cache_expiration.should == 15
83
+ @test_class.low_card_cache_expiration_policy_object.class.should == LowCardTables::LowCardTable::CacheExpiration::FixedCacheExpirationPolicy
84
+ @test_class.low_card_cache_expiration_policy_object.expiration_time.should == 15
85
+
86
+ @parent_class.low_card_cache_expiration :exponential
87
+ @test_class.low_card_cache_expiration.should == :exponential
88
+ @test_class.low_card_cache_expiration_policy_object.class.should == LowCardTables::LowCardTable::CacheExpiration::ExponentialCacheExpirationPolicy
89
+
90
+ @test_class.low_card_cache_expiration 27
91
+ @test_class.low_card_cache_expiration.should == 27
92
+ @test_class.low_card_cache_expiration_policy_object.class.should == LowCardTables::LowCardTable::CacheExpiration::FixedCacheExpirationPolicy
93
+ @test_class.low_card_cache_expiration_policy_object.expiration_time.should == 27
94
+
95
+ @parent_class.low_card_cache_expiration 105
96
+ @test_class.low_card_cache_expiration.should == 27
97
+ @test_class.low_card_cache_expiration_policy_object.class.should == LowCardTables::LowCardTable::CacheExpiration::FixedCacheExpirationPolicy
98
+ @test_class.low_card_cache_expiration_policy_object.expiration_time.should == 27
99
+ end
100
+ end