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,63 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'active_record/migration'
|
3
|
+
|
4
|
+
module LowCardTables
|
5
|
+
module Helpers
|
6
|
+
module SystemHelpers
|
7
|
+
def migrate(&block)
|
8
|
+
migration_class = Class.new(::ActiveRecord::Migration)
|
9
|
+
metaclass = migration_class.class_eval { class << self; self; end }
|
10
|
+
metaclass.instance_eval { define_method(:up, &block) }
|
11
|
+
|
12
|
+
::ActiveRecord::Migration.suppress_messages do
|
13
|
+
migration_class.migrate(:up)
|
14
|
+
end
|
15
|
+
|
16
|
+
LowCardTables::VersionSupport.clear_schema_cache!(::ActiveRecord::Base)
|
17
|
+
end
|
18
|
+
|
19
|
+
def define_model_class(name, table_name, &block)
|
20
|
+
model_class = Class.new(::ActiveRecord::Base)
|
21
|
+
::Object.send(:remove_const, name) if ::Object.const_defined?(name)
|
22
|
+
::Object.const_set(name, model_class)
|
23
|
+
model_class.table_name = table_name
|
24
|
+
model_class.class_eval(&block)
|
25
|
+
end
|
26
|
+
|
27
|
+
def create_standard_system_spec_tables!
|
28
|
+
migrate do
|
29
|
+
drop_table :lctables_spec_user_statuses rescue nil
|
30
|
+
create_table :lctables_spec_user_statuses do |t|
|
31
|
+
t.boolean :deleted, :null => false
|
32
|
+
t.boolean :deceased
|
33
|
+
t.string :gender, :null => false
|
34
|
+
t.integer :donation_level
|
35
|
+
end
|
36
|
+
|
37
|
+
add_index :lctables_spec_user_statuses, [ :deleted, :deceased, :gender, :donation_level ], :unique => true, :name => 'index_lctables_spec_user_statuses_on_all'
|
38
|
+
|
39
|
+
drop_table :lctables_spec_users rescue nil
|
40
|
+
create_table :lctables_spec_users do |t|
|
41
|
+
t.string :name, :null => false
|
42
|
+
t.integer :user_status_id, :null => false, :limit => 2
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def create_standard_system_spec_models!
|
48
|
+
define_model_class(:UserStatus, 'lctables_spec_user_statuses') { is_low_card_table }
|
49
|
+
define_model_class(:User, 'lctables_spec_users') { has_low_card_table :status }
|
50
|
+
define_model_class(:UserStatusBackdoor, 'lctables_spec_user_statuses') { }
|
51
|
+
|
52
|
+
::UserStatus.low_card_cache_expiration :unlimited
|
53
|
+
end
|
54
|
+
|
55
|
+
def drop_standard_system_spec_tables!
|
56
|
+
migrate do
|
57
|
+
drop_table :lctables_spec_user_statuses rescue nil
|
58
|
+
drop_table :lctables_spec_users rescue nil
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -0,0 +1,254 @@
|
|
1
|
+
require 'low_card_tables'
|
2
|
+
require 'low_card_tables/helpers/database_helper'
|
3
|
+
require 'low_card_tables/helpers/system_helpers'
|
4
|
+
|
5
|
+
describe "LowCardTables basic operations" do
|
6
|
+
include LowCardTables::Helpers::SystemHelpers
|
7
|
+
|
8
|
+
before :each do
|
9
|
+
@dh = LowCardTables::Helpers::DatabaseHelper.new
|
10
|
+
@dh.setup_activerecord!
|
11
|
+
end
|
12
|
+
|
13
|
+
context "with standard setup" do
|
14
|
+
before :each do
|
15
|
+
create_standard_system_spec_tables!
|
16
|
+
create_standard_system_spec_models!
|
17
|
+
|
18
|
+
@user1 = ::User.new
|
19
|
+
@user1.name = 'User1'
|
20
|
+
@user1.deleted = false
|
21
|
+
@user1.deceased = false
|
22
|
+
@user1.gender = 'female'
|
23
|
+
@user1.donation_level = 3
|
24
|
+
@user1.save!
|
25
|
+
end
|
26
|
+
|
27
|
+
after :each do
|
28
|
+
drop_standard_system_spec_tables!
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should say #is_low_card_table? appropriately" do
|
32
|
+
::UserStatus.is_low_card_table?.should be
|
33
|
+
::User.is_low_card_table?.should_not be
|
34
|
+
|
35
|
+
::UserStatus.low_card_options.should == { }
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should blow up if you say #has_low_card_table for something that isn't" do
|
39
|
+
define_model_class(:UserStatusReferencingError, :lctables_spec_user_statuses) { }
|
40
|
+
|
41
|
+
lambda do
|
42
|
+
define_model_class(:UserReferencingError, :lctables_spec_users) do
|
43
|
+
has_low_card_table :status, :class => :UserStatusReferencingError, :foreign_key => :user_status_id
|
44
|
+
end
|
45
|
+
end.should raise_error(ArgumentError, /UserStatusReferencingError/i)
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should allow setting all options, and create an appropriate row" do
|
49
|
+
@user1.should be
|
50
|
+
|
51
|
+
rows = ::UserStatusBackdoor.all
|
52
|
+
rows.length.should == 1
|
53
|
+
row = rows[0]
|
54
|
+
row.id.should == @user1.user_status_id
|
55
|
+
row.deleted.should == false
|
56
|
+
row.deceased.should == false
|
57
|
+
row.gender.should == 'female'
|
58
|
+
row.donation_level.should == 3
|
59
|
+
|
60
|
+
user1_v2 = User.where(:name => 'User1').first
|
61
|
+
user1_v2.deleted.should == false
|
62
|
+
user1_v2.deceased.should == false
|
63
|
+
user1_v2.gender.should == 'female'
|
64
|
+
user1_v2.donation_level.should == 3
|
65
|
+
end
|
66
|
+
|
67
|
+
it "should expose a low-card row, but not with an ID, when read in from the DB" do
|
68
|
+
@user1.status.should be
|
69
|
+
@user1.status.id.should_not be
|
70
|
+
|
71
|
+
user1_v2 = User.where(:name => 'User1').first
|
72
|
+
user1_v2.should be
|
73
|
+
user1_v2.status.should be
|
74
|
+
user1_v2.status.id.should_not be
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should not allow re-saving the status to the DB, with or without changes" do
|
78
|
+
lambda { @user1.status.save! }.should raise_error
|
79
|
+
@user1.deleted = true
|
80
|
+
lambda { @user1.status.save! }.should raise_error
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should allow changing a property, and create another row, but only for the final set" do
|
84
|
+
previous_status_id = @user1.user_status_id
|
85
|
+
|
86
|
+
@user1.gender = 'unknown'
|
87
|
+
@user1.gender = 'male'
|
88
|
+
@user1.donation_level = 1
|
89
|
+
@user1.save!
|
90
|
+
|
91
|
+
rows = ::UserStatusBackdoor.all
|
92
|
+
rows.length.should == 2
|
93
|
+
new_row = rows.detect { |r| r.id != previous_status_id }
|
94
|
+
new_row.id.should == @user1.user_status_id
|
95
|
+
new_row.deleted.should == false
|
96
|
+
new_row.deceased.should == false
|
97
|
+
new_row.gender.should == 'male'
|
98
|
+
new_row.donation_level.should == 1
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should allow creating another associated row, and they should be independent, even if they start with the same low-card ID" do
|
102
|
+
user2 = ::User.new
|
103
|
+
user2.name = 'User2'
|
104
|
+
user2.deleted = false
|
105
|
+
user2.deceased = false
|
106
|
+
user2.gender = 'female'
|
107
|
+
user2.donation_level = 3
|
108
|
+
user2.save!
|
109
|
+
|
110
|
+
@user1.user_status_id.should == user2.user_status_id
|
111
|
+
|
112
|
+
user2.deleted = true
|
113
|
+
user2.save!
|
114
|
+
|
115
|
+
@user1.user_status_id.should_not == user2.user_status_id
|
116
|
+
@user1.deleted.should == false
|
117
|
+
user2.deleted.should == true
|
118
|
+
|
119
|
+
user1_v2 = ::User.where(:name => 'User1').first
|
120
|
+
user2_v2 = ::User.where(:name => 'User2').first
|
121
|
+
|
122
|
+
user1_v2.deleted.should == false
|
123
|
+
user2_v2.deleted.should == true
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should have an explicit 'assign the low-card ID now' call" do
|
127
|
+
user2 = ::User.new
|
128
|
+
user2.name = 'User2'
|
129
|
+
user2.deleted = false
|
130
|
+
user2.deceased = false
|
131
|
+
user2.gender = 'female'
|
132
|
+
user2.donation_level = 3
|
133
|
+
|
134
|
+
user2.low_card_update_foreign_keys!
|
135
|
+
user2.user_status_id.should be
|
136
|
+
user2.user_status_id.should > 0
|
137
|
+
user2.user_status_id.should == @user1.user_status_id
|
138
|
+
|
139
|
+
::UserStatusBackdoor.count.should == 1
|
140
|
+
|
141
|
+
user2 = ::User.new
|
142
|
+
user2.name = 'User2'
|
143
|
+
user2.deleted = false
|
144
|
+
user2.deceased = false
|
145
|
+
user2.gender = 'female'
|
146
|
+
user2.donation_level = 9
|
147
|
+
|
148
|
+
user2.low_card_update_foreign_keys!
|
149
|
+
user2.user_status_id.should be
|
150
|
+
user2.user_status_id.should > 0
|
151
|
+
user2.user_status_id.should_not == @user1.user_status_id
|
152
|
+
|
153
|
+
::UserStatusBackdoor.count.should == 2
|
154
|
+
end
|
155
|
+
|
156
|
+
it "should let you assign the low-card ID directly, and remap the associated object when that happens" do
|
157
|
+
previous_status_id = @user1.user_status_id
|
158
|
+
|
159
|
+
@user1.deleted = true
|
160
|
+
@user1.save!
|
161
|
+
|
162
|
+
new_status_id = @user1.user_status_id
|
163
|
+
new_status_id.should_not == previous_status_id
|
164
|
+
|
165
|
+
@user1.deceased = true
|
166
|
+
|
167
|
+
@user1.deleted.should == true
|
168
|
+
@user1.deceased.should == true
|
169
|
+
|
170
|
+
@user1.user_status_id = previous_status_id
|
171
|
+
|
172
|
+
@user1.user_status_id.should == previous_status_id
|
173
|
+
@user1.user_status_id.should_not == new_status_id
|
174
|
+
|
175
|
+
@user1.deleted.should == false
|
176
|
+
@user1.deceased.should == false
|
177
|
+
@user1.gender.should == 'female'
|
178
|
+
@user1.donation_level.should == 3
|
179
|
+
end
|
180
|
+
|
181
|
+
it "should blow up if there are too many rows in the low-card table" do
|
182
|
+
class ::UserStatus
|
183
|
+
is_low_card_table :max_row_count => 10
|
184
|
+
end
|
185
|
+
|
186
|
+
15.times do |i|
|
187
|
+
bd = ::UserStatusBackdoor.new
|
188
|
+
bd.deleted = false
|
189
|
+
bd.deceased = false
|
190
|
+
bd.gender = 'female'
|
191
|
+
bd.donation_level = i + 10
|
192
|
+
bd.save!
|
193
|
+
end
|
194
|
+
|
195
|
+
lambda do
|
196
|
+
::UserStatus.low_card_flush_cache!
|
197
|
+
::UserStatus.low_card_all_rows
|
198
|
+
end.should raise_error(LowCardTables::Errors::LowCardTooManyRowsError, /11/)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
it "should handle column default values in exactly the same way as ActiveRecord" do
|
203
|
+
migrate do
|
204
|
+
drop_table :lctables_spec_user_statuses rescue nil
|
205
|
+
create_table :lctables_spec_user_statuses do |t|
|
206
|
+
t.boolean :deleted, :null => false
|
207
|
+
t.boolean :deceased, :default => false
|
208
|
+
t.string :gender, :default => 'unknown'
|
209
|
+
t.integer :donation_level, :default => 5
|
210
|
+
end
|
211
|
+
|
212
|
+
add_index :lctables_spec_user_statuses, [ :deleted, :deceased, :gender, :donation_level ], :unique => true, :name => 'index_lctables_spec_user_statuses_on_all'
|
213
|
+
|
214
|
+
drop_table :lctables_spec_users rescue nil
|
215
|
+
create_table :lctables_spec_users do |t|
|
216
|
+
t.string :name, :null => false
|
217
|
+
t.integer :user_status_id, :null => false, :limit => 2
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
create_standard_system_spec_models!
|
222
|
+
|
223
|
+
user = ::User.new
|
224
|
+
user.name = 'Name1'
|
225
|
+
user.deleted.should == nil
|
226
|
+
user.deceased.should == false
|
227
|
+
user.gender.should == 'unknown'
|
228
|
+
user.donation_level.should == 5
|
229
|
+
|
230
|
+
lambda { user.save! }.should raise_error(LowCardTables::Errors::LowCardInvalidLowCardRowsError)
|
231
|
+
user.deleted = false
|
232
|
+
user.save!
|
233
|
+
|
234
|
+
user.deleted.should == false
|
235
|
+
user.deceased.should == false
|
236
|
+
user.gender.should == 'unknown'
|
237
|
+
user.donation_level.should == 5
|
238
|
+
|
239
|
+
user2 = ::User.find(user.id)
|
240
|
+
user2.name.should == 'Name1'
|
241
|
+
user2.deleted.should == false
|
242
|
+
user2.deceased.should == false
|
243
|
+
user2.gender.should == 'unknown'
|
244
|
+
user2.donation_level.should == 5
|
245
|
+
|
246
|
+
rows = ::UserStatusBackdoor.all
|
247
|
+
rows.length.should == 1
|
248
|
+
row = rows[0]
|
249
|
+
row.deleted.should == false
|
250
|
+
row.deceased.should == false
|
251
|
+
row.gender.should == 'unknown'
|
252
|
+
row.donation_level.should == 5
|
253
|
+
end
|
254
|
+
end
|
@@ -0,0 +1,334 @@
|
|
1
|
+
require 'low_card_tables'
|
2
|
+
require 'low_card_tables/helpers/database_helper'
|
3
|
+
require 'low_card_tables/helpers/system_helpers'
|
4
|
+
require 'low_card_tables/helpers/query_spy_helper'
|
5
|
+
|
6
|
+
describe "LowCardTables bulk operations" do
|
7
|
+
include LowCardTables::Helpers::SystemHelpers
|
8
|
+
|
9
|
+
before :each do
|
10
|
+
@dh = LowCardTables::Helpers::DatabaseHelper.new
|
11
|
+
@dh.setup_activerecord!
|
12
|
+
end
|
13
|
+
|
14
|
+
context "with standard setup" do
|
15
|
+
before :each do
|
16
|
+
create_standard_system_spec_tables!
|
17
|
+
create_standard_system_spec_models!
|
18
|
+
|
19
|
+
# create several low-card rows in our table
|
20
|
+
user = User.new
|
21
|
+
user.name = 'User1'
|
22
|
+
user.deleted = false
|
23
|
+
user.deceased = false
|
24
|
+
user.gender = 'male'
|
25
|
+
user.donation_level = 5
|
26
|
+
user.save!
|
27
|
+
|
28
|
+
@hash1_id = user.user_status_id
|
29
|
+
|
30
|
+
user.deleted = true
|
31
|
+
user.save!
|
32
|
+
|
33
|
+
@hash2_id = user.user_status_id
|
34
|
+
|
35
|
+
user.deleted = false
|
36
|
+
user.gender = 'female'
|
37
|
+
user.donation_level = 9
|
38
|
+
user.save!
|
39
|
+
|
40
|
+
@hash3_id = user.user_status_id
|
41
|
+
|
42
|
+
@hash1 = { :deleted => false, :deceased => false, :gender => 'male', :donation_level => 5 }.with_indifferent_access
|
43
|
+
@hash2 = { :deleted => true, :deceased => false, :gender => 'male', :donation_level => 5 }.with_indifferent_access
|
44
|
+
@hash3 = { :deleted => false, :deceased => false, :gender => 'female', :donation_level => 9 }.with_indifferent_access
|
45
|
+
@hash4 = { :deleted => false, :deceased => true, :gender => 'female', :donation_level => 3 }.with_indifferent_access
|
46
|
+
@hash5 = { :deleted => false, :deceased => true, :gender => 'male', :donation_level => 2 }.with_indifferent_access
|
47
|
+
end
|
48
|
+
|
49
|
+
after :each do
|
50
|
+
drop_standard_system_spec_tables!
|
51
|
+
end
|
52
|
+
|
53
|
+
def verify_row(row, deleted, deceased, gender, donation_level)
|
54
|
+
row.deleted.should == deleted
|
55
|
+
row.deceased.should == deceased
|
56
|
+
row.gender.should == gender
|
57
|
+
row.donation_level.should == donation_level
|
58
|
+
end
|
59
|
+
|
60
|
+
def verify_by_id(rows, id, deleted, deceased, gender, donation_level)
|
61
|
+
row = rows.detect { |r| r.id == id }
|
62
|
+
row.should be
|
63
|
+
verify_row(row, deleted, deceased, gender, donation_level)
|
64
|
+
end
|
65
|
+
|
66
|
+
def ensure_zero_database_calls(&block)
|
67
|
+
LowCardTables::Helpers::QuerySpyHelper.with_query_spy('lctables_spec_user_statuses') do |spy|
|
68
|
+
::UserStatus.low_card_all_rows
|
69
|
+
|
70
|
+
pre_count = spy.call_count
|
71
|
+
block.call
|
72
|
+
post_count = spy.call_count
|
73
|
+
post_count.should == pre_count
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should allow for bulk retrieval of subsets of rows" do
|
78
|
+
ensure_zero_database_calls do
|
79
|
+
hash_selector_1 = { :deleted => false, :deceased => false }
|
80
|
+
hash_selector_2 = { :deceased => false, :donation_level => 5 }
|
81
|
+
results = ::UserStatus.low_card_rows_matching([ hash_selector_1, hash_selector_2 ])
|
82
|
+
|
83
|
+
results.size.should == 2
|
84
|
+
|
85
|
+
results[hash_selector_1].should be
|
86
|
+
results[hash_selector_1].length.should == 2
|
87
|
+
results[hash_selector_1].map(&:id).sort.should == [ @hash1_id, @hash3_id ].sort
|
88
|
+
|
89
|
+
verify_by_id(results[hash_selector_1], @hash1_id, false, false, 'male', 5)
|
90
|
+
verify_by_id(results[hash_selector_1], @hash3_id, false, false, 'female', 9)
|
91
|
+
|
92
|
+
results[hash_selector_2].should be
|
93
|
+
results[hash_selector_2].length.should == 2
|
94
|
+
results[hash_selector_2].map(&:id).sort.should == [ @hash1_id, @hash2_id ].sort
|
95
|
+
|
96
|
+
verify_by_id(results[hash_selector_2], @hash1_id, false, false, 'male', 5)
|
97
|
+
verify_by_id(results[hash_selector_2], @hash2_id, true, false, 'male', 5)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
it "should allow for bulk retrieval of subsets of IDs" do
|
102
|
+
ensure_zero_database_calls do
|
103
|
+
hash_selector_1 = { :deleted => false, :deceased => false }
|
104
|
+
hash_selector_2 = { :deceased => false, :donation_level => 5 }
|
105
|
+
results = ::UserStatus.low_card_ids_matching([ hash_selector_1, hash_selector_2 ])
|
106
|
+
|
107
|
+
results.size.should == 2
|
108
|
+
|
109
|
+
results[hash_selector_1].should be
|
110
|
+
results[hash_selector_1].length.should == 2
|
111
|
+
results[hash_selector_1].sort.should == [ @hash1_id, @hash3_id ].sort
|
112
|
+
|
113
|
+
results[hash_selector_2].should be
|
114
|
+
results[hash_selector_2].length.should == 2
|
115
|
+
results[hash_selector_2].sort.should == [ @hash1_id, @hash2_id ].sort
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
it "should raise an exception if passed invalid values in the hashes" do
|
120
|
+
ensure_zero_database_calls do
|
121
|
+
lambda { ::UserStatus.low_card_rows_matching([ { :deleted => false, :foo => 1 }]) }.should raise_error(LowCardTables::Errors::LowCardColumnNotPresentError)
|
122
|
+
lambda { ::UserStatus.low_card_ids_matching([ { :deleted => false, :foo => 1 }]) }.should raise_error(LowCardTables::Errors::LowCardColumnNotPresentError)
|
123
|
+
end
|
124
|
+
end
|
125
|
+
|
126
|
+
it "should raise an exception if there are missing values in the hashes" do
|
127
|
+
ensure_zero_database_calls do
|
128
|
+
lambda { ::UserStatus.low_card_find_rows_for([ { :deleted => false, :gender => 'male', :donation_level => 5 }]) }.should raise_error(LowCardTables::Errors::LowCardColumnNotSpecifiedError)
|
129
|
+
lambda { ::UserStatus.low_card_find_ids_for([ { :deleted => false, :gender => 'male', :donation_level => 5 }]) }.should raise_error(LowCardTables::Errors::LowCardColumnNotSpecifiedError)
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
context "with a table with defaults" do
|
134
|
+
before :each do
|
135
|
+
migrate do
|
136
|
+
drop_table :lctables_spec_bulk_defaults rescue nil
|
137
|
+
create_table :lctables_spec_bulk_defaults do |t|
|
138
|
+
t.boolean :deleted, :null => false
|
139
|
+
t.string :gender, :null => false, :default => 'female'
|
140
|
+
t.integer :donation_level, :default => 10
|
141
|
+
end
|
142
|
+
|
143
|
+
add_index :lctables_spec_bulk_defaults, [ :deleted, :gender, :donation_level ], :unique => true, :name => 'index_lctables_spec_bulk_defaults_on_all'
|
144
|
+
|
145
|
+
drop_table :lctables_spec_users_defaults rescue nil
|
146
|
+
create_table :lctables_spec_users_defaults do |t|
|
147
|
+
t.string :name, :null => false
|
148
|
+
t.integer :user_status_id, :null => false, :limit => 2
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
define_model_class(:UserStatusBulkDefaults, :lctables_spec_bulk_defaults) { is_low_card_table }
|
153
|
+
define_model_class(:UserBulkDefaults, :lctables_spec_users_defaults) { has_low_card_table :status, :class => 'UserStatusBulkDefaults', :foreign_key => :user_status_id }
|
154
|
+
end
|
155
|
+
|
156
|
+
after :each do
|
157
|
+
migrate do
|
158
|
+
drop_table :lctables_spec_bulk_defaults rescue nil
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
it "should fill in missing values in the hashes with defaults when finding rows" do
|
163
|
+
u1 = ::UserBulkDefaults.new
|
164
|
+
u1.name = 'User 1'
|
165
|
+
u1.deleted = false
|
166
|
+
u1.save!
|
167
|
+
|
168
|
+
u1.name.should == 'User 1'
|
169
|
+
u1.deleted.should == false
|
170
|
+
u1.gender.should == 'female'
|
171
|
+
u1.donation_level.should == 10
|
172
|
+
|
173
|
+
u2 = ::UserBulkDefaults.new
|
174
|
+
u2.name = 'User 2'
|
175
|
+
u2.deleted = false
|
176
|
+
u2.gender = 'female'
|
177
|
+
u2.donation_level = 8
|
178
|
+
u2.save!
|
179
|
+
|
180
|
+
status_id_1 = u1.user_status_id
|
181
|
+
status_id_1.should be
|
182
|
+
status_row_1 = ::UserStatusBulkDefaults.find(status_id_1)
|
183
|
+
|
184
|
+
status_id_2 = u2.user_status_id
|
185
|
+
status_id_2.should be
|
186
|
+
status_row_2 = ::UserStatusBulkDefaults.find(status_id_2)
|
187
|
+
|
188
|
+
lambda { ::UserStatusBulkDefaults.low_card_find_rows_for({ :gender => 'female' }) }.should raise_error(LowCardTables::Errors::LowCardColumnNotSpecifiedError)
|
189
|
+
::UserStatusBulkDefaults.low_card_find_rows_for({ :deleted => false }).should == status_row_1
|
190
|
+
::UserStatusBulkDefaults.low_card_find_rows_for({ :deleted => false, :donation_level => 8 }).should == status_row_2
|
191
|
+
end
|
192
|
+
|
193
|
+
it "should fill in missing values in the hashes with defaults when creating rows" do
|
194
|
+
row = ::UserStatusBulkDefaults.low_card_find_or_create_rows_for({ :deleted => false })
|
195
|
+
row.should be
|
196
|
+
row.id.should be
|
197
|
+
row.id.should > 0
|
198
|
+
|
199
|
+
row.deleted.should == false
|
200
|
+
row.gender.should == 'female'
|
201
|
+
row.donation_level.should == 10
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
it "should allow for bulk retrieval of rows by IDs" do
|
206
|
+
ensure_zero_database_calls do
|
207
|
+
results = ::UserStatus.low_card_rows_for_ids([ @hash1_id, @hash3_id ])
|
208
|
+
results.size.should == 2
|
209
|
+
verify_row(results[@hash1_id], false, false, 'male', 5)
|
210
|
+
verify_row(results[@hash3_id], false, false, 'female', 9)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
it "should raise if asked for an ID that's not present" do
|
215
|
+
random_id = 1_000_000 + rand(1_000_000)
|
216
|
+
|
217
|
+
e = nil
|
218
|
+
begin
|
219
|
+
::UserStatus.low_card_rows_for_ids([ @hash1_id, @hash3_id, random_id ])
|
220
|
+
rescue => x
|
221
|
+
e = x
|
222
|
+
end
|
223
|
+
|
224
|
+
e.should be
|
225
|
+
e.class.should == LowCardTables::Errors::LowCardIdNotFoundError
|
226
|
+
e.ids.should == [ random_id ]
|
227
|
+
end
|
228
|
+
|
229
|
+
it "should allow for retrieving all rows" do
|
230
|
+
ensure_zero_database_calls do
|
231
|
+
results = ::UserStatus.low_card_all_rows
|
232
|
+
results.size.should == 3
|
233
|
+
verify_by_id(results, @hash1_id, false, false, 'male', 5)
|
234
|
+
verify_by_id(results, @hash2_id, true, false, 'male', 5)
|
235
|
+
verify_by_id(results, @hash3_id, false, false, 'female', 9)
|
236
|
+
end
|
237
|
+
end
|
238
|
+
|
239
|
+
it "should allow retrieving an individual row directly" do
|
240
|
+
ensure_zero_database_calls do
|
241
|
+
row = ::UserStatus.low_card_row_for_id(@hash2_id)
|
242
|
+
verify_row(row, true, false, 'male', 5)
|
243
|
+
end
|
244
|
+
end
|
245
|
+
|
246
|
+
def row_from_hash(h)
|
247
|
+
::UserStatus.new(h)
|
248
|
+
end
|
249
|
+
|
250
|
+
%w{hash object}.each do |input_type|
|
251
|
+
def to_desired_input_type(hashes, type)
|
252
|
+
case type
|
253
|
+
when 'hash' then hashes
|
254
|
+
when 'object' then hashes.map { |h| ::UserStatus.new(h) }
|
255
|
+
else raise "Unknown input_type: #{input_type.inspect}"
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
it "should allow for bulk retrieval-and-creation of rows by #{input_type}" do
|
260
|
+
input = to_desired_input_type([ @hash1, @hash3, @hash4, @hash5 ], input_type)
|
261
|
+
|
262
|
+
result = ::UserStatus.low_card_find_or_create_rows_for(input)
|
263
|
+
result.size.should == 4
|
264
|
+
|
265
|
+
result[input[0]].id.should == @hash1_id
|
266
|
+
result[@hash2].should be_nil
|
267
|
+
result[input[1]].id.should == @hash3_id
|
268
|
+
|
269
|
+
known_ids = [ @hash1_id, @hash2_id, @hash3_id ]
|
270
|
+
known_ids.include?(result[input[2]].id).should_not be
|
271
|
+
known_ids.include?(result[input[3]].id).should_not be
|
272
|
+
|
273
|
+
verify_row(result[input[2]], false, true, 'female', 3)
|
274
|
+
verify_row(result[input[3]], false, true, 'male', 2)
|
275
|
+
|
276
|
+
::UserStatusBackdoor.count.should == 5
|
277
|
+
verify_row(::UserStatusBackdoor.find(result[input[0]].id), false, false, 'male', 5)
|
278
|
+
verify_row(::UserStatusBackdoor.find(result[input[1]].id), false, false, 'female', 9)
|
279
|
+
verify_row(::UserStatusBackdoor.find(result[input[2]].id), false, true, 'female', 3)
|
280
|
+
verify_row(::UserStatusBackdoor.find(result[input[3]].id), false, true, 'male', 2)
|
281
|
+
end
|
282
|
+
|
283
|
+
it "should allow for bulk retrieval-and-creation of IDs by #{input_type}" do
|
284
|
+
input = to_desired_input_type([ @hash1, @hash3, @hash4, @hash5 ], input_type)
|
285
|
+
|
286
|
+
result = ::UserStatus.low_card_find_or_create_ids_for(input)
|
287
|
+
result.size.should == 4
|
288
|
+
|
289
|
+
result[input[0]].should == @hash1_id
|
290
|
+
result[@hash2].should be_nil
|
291
|
+
result[input[1]].should == @hash3_id
|
292
|
+
|
293
|
+
known_ids = [ @hash1_id, @hash2_id, @hash3_id ]
|
294
|
+
known_ids.include?(result[input[2]]).should_not be
|
295
|
+
known_ids.include?(result[input[3]]).should_not be
|
296
|
+
|
297
|
+
::UserStatusBackdoor.count.should == 5
|
298
|
+
verify_row(::UserStatusBackdoor.find(result[input[0]]), false, false, 'male', 5)
|
299
|
+
verify_row(::UserStatusBackdoor.find(result[input[1]]), false, false, 'female', 9)
|
300
|
+
verify_row(::UserStatusBackdoor.find(result[input[2]]), false, true, 'female', 3)
|
301
|
+
verify_row(::UserStatusBackdoor.find(result[input[3]]), false, true, 'male', 2)
|
302
|
+
end
|
303
|
+
|
304
|
+
it "should allow for bulk retrieval of rows with exact matches by #{input_type}" do
|
305
|
+
ensure_zero_database_calls do
|
306
|
+
results = ::UserStatus.low_card_find_rows_for([ @hash1, @hash2, @hash3, @hash4, @hash5 ])
|
307
|
+
results.size.should == 5
|
308
|
+
|
309
|
+
verify_row(results[@hash1], false, false, 'male', 5)
|
310
|
+
verify_row(results[@hash2], true, false, 'male', 5)
|
311
|
+
verify_row(results[@hash3], false, false, 'female', 9)
|
312
|
+
|
313
|
+
results[@hash4].should be_nil
|
314
|
+
results[@hash5].should be_nil
|
315
|
+
end
|
316
|
+
end
|
317
|
+
|
318
|
+
it "should allow for bulk retrieval of IDs with exact matches by #{input_type}" do
|
319
|
+
ensure_zero_database_calls do
|
320
|
+
input = to_desired_input_type([ @hash1, @hash2, @hash3, @hash4, @hash5 ], input_type)
|
321
|
+
results = ::UserStatus.low_card_find_ids_for(input)
|
322
|
+
results.size.should == 5
|
323
|
+
|
324
|
+
results[input[0]].should == @hash1_id
|
325
|
+
results[input[1]].should == @hash2_id
|
326
|
+
results[input[2]].should == @hash3_id
|
327
|
+
|
328
|
+
results[input[3]].should be_nil
|
329
|
+
results[input[4]].should be_nil
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|