cohort_analysis 0.4.0 → 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.
- data/.yardopts +2 -0
- data/CHANGELOG +14 -2
- data/Gemfile +0 -9
- data/README.markdown +0 -64
- data/cohort_analysis.gemspec +20 -2
- data/lib/cohort_analysis/active_record_base_class_methods.rb +0 -4
- data/lib/cohort_analysis/active_record_relation_instance_methods.rb +34 -5
- data/lib/cohort_analysis/arel_select_manager_instance_methods.rb +8 -0
- data/lib/cohort_analysis/arel_table_instance_methods.rb +7 -0
- data/lib/cohort_analysis/strategy/big.rb +4 -5
- data/lib/cohort_analysis/strategy/strict.rb +8 -6
- data/lib/cohort_analysis/strategy.rb +98 -11
- data/lib/cohort_analysis/version.rb +1 -1
- data/lib/cohort_analysis.rb +15 -15
- data/test/helper.rb +54 -9
- data/test/test_cohort_analysis.rb +269 -181
- metadata +169 -65
- data/.document +0 -5
@@ -1,222 +1,310 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
c = ActiveRecord::Base.connection
|
4
|
-
c.create_table 'flights', :force => true do |t|
|
5
|
-
t.string 'origin'
|
6
|
-
t.string 'dest'
|
7
|
-
t.string 'airline'
|
8
|
-
t.string 'plane'
|
9
|
-
end
|
10
|
-
|
11
3
|
class Flight < ActiveRecord::Base
|
4
|
+
col :origin
|
5
|
+
col :dest
|
6
|
+
col :year, :type => :integer
|
7
|
+
col :airline
|
8
|
+
col :origin_city
|
9
|
+
col :dest_city
|
12
10
|
end
|
11
|
+
Flight.auto_upgrade!
|
13
12
|
|
14
13
|
FactoryGirl.define do
|
15
|
-
factory :lax, :class => Flight do
|
16
|
-
origin 'LAX'
|
17
|
-
end
|
18
14
|
factory :lax_sfo, :class => Flight do
|
19
15
|
origin 'LAX'
|
20
16
|
dest 'SFO'
|
21
17
|
end
|
22
|
-
factory :
|
18
|
+
factory :lax_ord, :class => Flight do
|
23
19
|
origin 'LAX'
|
24
|
-
dest '
|
25
|
-
airline 'Continental'
|
20
|
+
dest 'ORD'
|
26
21
|
end
|
27
|
-
factory :
|
28
|
-
origin '
|
22
|
+
factory :ord_sfo, :class => Flight do
|
23
|
+
origin 'ORD'
|
29
24
|
dest 'SFO'
|
30
|
-
plane 'A320'
|
31
|
-
end
|
32
|
-
factory :lax_sfo_aa_a320, :class => Flight do
|
33
|
-
origin 'LAX'
|
34
|
-
dest 'SFO'
|
35
|
-
airline 'American'
|
36
|
-
plane 'A320'
|
37
25
|
end
|
38
26
|
end
|
39
27
|
|
40
|
-
|
41
|
-
|
42
|
-
|
28
|
+
shared_examples_for 'an adapter the provides #cohort' do
|
29
|
+
def moot_condition
|
30
|
+
Arel.sql('9 = 9')
|
43
31
|
end
|
44
32
|
|
45
|
-
describe
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
33
|
+
describe :cohort do
|
34
|
+
it "finds the biggest set of records matching the characteristics" do
|
35
|
+
FactoryGirl.create(:lax_ord)
|
36
|
+
FactoryGirl.create(:lax_sfo)
|
37
|
+
assert_count 2, model.cohort(:origin => 'LAX')
|
38
|
+
assert_count 1, model.cohort(:dest => 'SFO')
|
39
|
+
assert_count 1, model.cohort(:origin => 'LAX', :dest => 'SFO')
|
40
|
+
assert_count 0, model.cohort(:dest => 'MSN')
|
41
|
+
end
|
52
42
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
43
|
+
it "handles arrays of values" do
|
44
|
+
FactoryGirl.create(:lax_ord)
|
45
|
+
FactoryGirl.create(:lax_sfo)
|
46
|
+
assert_count 2, model.cohort(:dest => ['ORD','SFO'])
|
47
|
+
assert_count 2, model.cohort(:origin => ['LAX'])
|
48
|
+
assert_count 1, model.cohort(:dest => ['SFO'])
|
49
|
+
assert_count 1, model.cohort(:origin => ['LAX'], :dest => ['SFO'])
|
50
|
+
assert_count 0, model.cohort(:dest => ['MSN'])
|
51
|
+
assert_count 1, model.cohort(:dest => ['MSN','SFO'])
|
52
|
+
end
|
58
53
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
54
|
+
it "matches everything if empty characteristics" do
|
55
|
+
FactoryGirl.create(:lax_ord)
|
56
|
+
FactoryGirl.create(:lax_sfo)
|
57
|
+
assert_count 2, model.cohort({})
|
58
|
+
end
|
59
|
+
|
60
|
+
it "discards characteristics to maximize size until the minimum size is met" do
|
61
|
+
a = FactoryGirl.create(:lax_ord)
|
62
|
+
b = FactoryGirl.create(:lax_sfo)
|
63
|
+
cohort = model.cohort({:origin => 'LAX', :dest => 'SFO'}, :minimum_size => 2)
|
64
|
+
assert_count 2, cohort
|
65
|
+
assert_members [a,b], cohort
|
66
|
+
end
|
67
|
+
|
68
|
+
it "returns an empty cohort (basically an impossible condition) unless the minimum size is set" do
|
69
|
+
FactoryGirl.create(:lax_ord)
|
70
|
+
cohort = model.cohort({:origin => 'LAX'}, :minimum_size => 2)
|
71
|
+
assert_count 0, cohort
|
72
|
+
end
|
73
|
+
|
74
|
+
it "discards characteristics in order until a minimum size is met" do
|
75
|
+
a = FactoryGirl.create(:lax_ord)
|
76
|
+
b = FactoryGirl.create(:lax_sfo)
|
77
|
+
cohort = model.cohort({:origin => 'LAX', :dest => 'MSN'}, :minimum_size => 2, :priority => [:origin, :dest])
|
78
|
+
assert_count 2, cohort
|
79
|
+
assert_members [a,b], cohort
|
80
|
+
end
|
81
|
+
|
82
|
+
it "returns an empty cohort if discarding characteristics in order has that effect" do
|
83
|
+
FactoryGirl.create(:lax_ord)
|
84
|
+
FactoryGirl.create(:lax_sfo)
|
85
|
+
cohort = model.cohort({:origin => 'LAX', :dest => 'MSN'}, :minimum_size => 2, :priority => [:dest, :origin])
|
86
|
+
assert_count 0, cohort
|
87
|
+
end
|
88
|
+
|
89
|
+
it "obeys conditions already added" do
|
90
|
+
FactoryGirl.create(:lax_ord, :year => 1900)
|
91
|
+
FactoryGirl.create(:lax_sfo, :year => 1900)
|
92
|
+
FactoryGirl.create(:lax_sfo, :year => 2009)
|
93
|
+
FactoryGirl.create(:ord_sfo, :year => 2009)
|
94
|
+
year_is_2009 = f_t[:year].eq(2009)
|
95
|
+
|
96
|
+
assert_count 1, model.where(year_is_2009).cohort(:origin => 'LAX').where(moot_condition)
|
97
|
+
assert_count 1, model.where(year_is_2009).cohort(:origin => 'LAX', :dest => 'MSN').where(moot_condition)
|
98
|
+
|
99
|
+
assert_count 1, model.where(year_is_2009).cohort({:origin => 'LAX', :dest => 'MSN'}, :priority => [:origin, :dest]).where(moot_condition)
|
100
|
+
assert_count 0, model.where(year_is_2009).cohort({:origin => 'LAX', :dest => 'MSN'}, :priority => [:dest, :origin]).where(moot_condition)
|
101
|
+
|
102
|
+
assert_count 0, model.where(year_is_2009).cohort({:origin => 'LAX'}, :minimum_size => 2).where(moot_condition)
|
103
|
+
assert_count 0, model.where(year_is_2009).cohort({:origin => 'LAX', :dest => 'MSN'}, :minimum_size => 2).where(moot_condition)
|
104
|
+
|
105
|
+
|
106
|
+
assert_count 1, model.where(year_is_2009).cohort(:origin => 'LAX').where(moot_condition)
|
107
|
+
assert_count 1, model.where(year_is_2009).cohort(:origin => 'LAX', :dest => 'SFO').where(moot_condition)
|
108
|
+
|
109
|
+
assert_count 1, model.where(year_is_2009).cohort({:origin => 'LAX', :dest => 'SFO'}, :priority => [:origin, :dest]).where(moot_condition)
|
110
|
+
assert_count 1, model.where(year_is_2009).cohort({:origin => 'LAX', :dest => 'SFO'}, :priority => [:dest, :origin]).where(moot_condition)
|
111
|
+
|
112
|
+
assert_count 0, model.where(year_is_2009).cohort({:origin => 'LAX'}, :minimum_size => 2).where(moot_condition)
|
113
|
+
assert_count 2, model.where(year_is_2009).cohort({:origin => 'LAX', :dest => 'SFO'}, :minimum_size => 2).where(moot_condition)
|
114
|
+
|
115
|
+
|
116
|
+
assert_count 1, model.where(year_is_2009).cohort(:origin => 'LAX').where(moot_condition)
|
117
|
+
assert_count 1, model.where(year_is_2009).cohort(:origin => 'LAX', :dest => 'ORD').where(moot_condition)
|
118
|
+
|
119
|
+
assert_count 1, model.where(year_is_2009).cohort({:origin => 'LAX', :dest => 'ORD'}, :priority => [:origin, :dest]).where(moot_condition)
|
120
|
+
assert_count 0, model.where(year_is_2009).cohort({:origin => 'LAX', :dest => 'ORD'}, :priority => [:dest, :origin]).where(moot_condition)
|
121
|
+
|
122
|
+
assert_count 0, model.where(year_is_2009).cohort({:origin => 'LAX'}, :minimum_size => 2).where(moot_condition)
|
123
|
+
assert_count 0, model.where(year_is_2009).cohort({:origin => 'LAX', :dest => 'ORD'}, :minimum_size => 2).where(moot_condition)
|
124
|
+
|
125
|
+
|
126
|
+
assert_count 1, model.where(year_is_2009).cohort(:origin => 'ORD').where(moot_condition)
|
127
|
+
assert_count 1, model.where(year_is_2009).cohort(:origin => 'ORD', :dest => 'MSN').where(moot_condition)
|
128
|
+
|
129
|
+
assert_count 1, model.where(year_is_2009).cohort({:origin => 'ORD', :dest => 'MSN'}, :priority => [:origin, :dest]).where(moot_condition)
|
130
|
+
assert_count 0, model.where(year_is_2009).cohort({:origin => 'ORD', :dest => 'MSN'}, :priority => [:dest, :origin]).where(moot_condition)
|
131
|
+
|
132
|
+
assert_count 0, model.where(year_is_2009).cohort({:origin => 'ORD'}, :minimum_size => 2).where(moot_condition)
|
133
|
+
assert_count 0, model.where(year_is_2009).cohort({:origin => 'ORD', :dest => 'MSN'}, :minimum_size => 2).where(moot_condition)
|
134
|
+
end
|
135
|
+
|
136
|
+
it "carries over into conditions added later" do
|
137
|
+
FactoryGirl.create(:lax_ord, :year => 1900)
|
138
|
+
FactoryGirl.create(:lax_sfo, :year => 1900)
|
139
|
+
FactoryGirl.create(:lax_sfo, :year => 2009)
|
140
|
+
FactoryGirl.create(:ord_sfo, :year => 2009)
|
141
|
+
year_is_2009 = f_t[:year].eq(2009)
|
142
|
+
|
143
|
+
assert_count 1, model.where(moot_condition).cohort(:origin => 'LAX').where(year_is_2009)
|
144
|
+
assert_count 1, model.where(moot_condition).cohort(:origin => 'LAX', :dest => 'MSN').where(year_is_2009)
|
66
145
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
146
|
+
assert_count 1, model.where(moot_condition).cohort({:origin => 'LAX', :dest => 'MSN'}, :priority => [:origin, :dest]).where(year_is_2009)
|
147
|
+
assert_count 0, model.where(moot_condition).cohort({:origin => 'LAX', :dest => 'MSN'}, :priority => [:dest, :origin]).where(year_is_2009)
|
148
|
+
|
149
|
+
assert_count 0, model.where(moot_condition).cohort({:origin => 'LAX'}, :minimum_size => 2).where(year_is_2009)
|
150
|
+
assert_count 0, model.where(moot_condition).cohort({:origin => 'LAX', :dest => 'MSN'}, :minimum_size => 2).where(year_is_2009)
|
151
|
+
|
152
|
+
|
153
|
+
assert_count 1, model.where(moot_condition).cohort(:origin => 'LAX').where(year_is_2009)
|
154
|
+
assert_count 1, model.where(moot_condition).cohort(:origin => 'LAX', :dest => 'SFO').where(year_is_2009)
|
155
|
+
|
156
|
+
assert_count 1, model.where(moot_condition).cohort({:origin => 'LAX', :dest => 'SFO'}, :priority => [:origin, :dest]).where(year_is_2009)
|
157
|
+
assert_count 1, model.where(moot_condition).cohort({:origin => 'LAX', :dest => 'SFO'}, :priority => [:dest, :origin]).where(year_is_2009)
|
158
|
+
|
159
|
+
assert_count 0, model.where(moot_condition).cohort({:origin => 'LAX'}, :minimum_size => 2).where(year_is_2009)
|
160
|
+
assert_count 2, model.where(moot_condition).cohort({:origin => 'LAX', :dest => 'SFO'}, :minimum_size => 2).where(year_is_2009)
|
161
|
+
|
162
|
+
|
163
|
+
assert_count 1, model.where(moot_condition).cohort(:origin => 'LAX').where(year_is_2009)
|
164
|
+
assert_count 1, model.where(moot_condition).cohort(:origin => 'LAX', :dest => 'ORD').where(year_is_2009)
|
165
|
+
|
166
|
+
assert_count 1, model.where(moot_condition).cohort({:origin => 'LAX', :dest => 'ORD'}, :priority => [:origin, :dest]).where(year_is_2009)
|
167
|
+
assert_count 0, model.where(moot_condition).cohort({:origin => 'LAX', :dest => 'ORD'}, :priority => [:dest, :origin]).where(year_is_2009)
|
168
|
+
|
169
|
+
assert_count 0, model.where(moot_condition).cohort({:origin => 'LAX'}, :minimum_size => 2).where(year_is_2009)
|
170
|
+
assert_count 0, model.where(moot_condition).cohort({:origin => 'LAX', :dest => 'ORD'}, :minimum_size => 2).where(year_is_2009)
|
171
|
+
|
172
|
+
|
173
|
+
assert_count 1, model.where(moot_condition).cohort(:origin => 'ORD').where(year_is_2009)
|
174
|
+
assert_count 1, model.where(moot_condition).cohort(:origin => 'ORD', :dest => 'MSN').where(year_is_2009)
|
175
|
+
|
176
|
+
assert_count 1, model.where(moot_condition).cohort({:origin => 'ORD', :dest => 'MSN'}, :priority => [:origin, :dest]).where(year_is_2009)
|
177
|
+
assert_count 0, model.where(moot_condition).cohort({:origin => 'ORD', :dest => 'MSN'}, :priority => [:dest, :origin]).where(year_is_2009)
|
178
|
+
|
179
|
+
assert_count 0, model.where(moot_condition).cohort({:origin => 'ORD'}, :minimum_size => 2).where(year_is_2009)
|
180
|
+
assert_count 0, model.where(moot_condition).cohort({:origin => 'ORD', :dest => 'MSN'}, :minimum_size => 2).where(year_is_2009)
|
181
|
+
end
|
182
|
+
|
183
|
+
it "can get where sql" do
|
184
|
+
FactoryGirl.create(:lax_ord)
|
185
|
+
FactoryGirl.create(:lax_sfo)
|
186
|
+
model.cohort(:origin => 'LAX').where_sql.must_equal %{WHERE ("flights"."origin" = 'LAX')}
|
187
|
+
end
|
188
|
+
|
189
|
+
it "will resolve independently from other cohorts" do
|
190
|
+
FactoryGirl.create(:lax_ord)
|
191
|
+
FactoryGirl.create(:lax_sfo)
|
192
|
+
assert_count 0, model.cohort(:dest => 'SFO').cohort(:dest => 'ORD')
|
193
|
+
end
|
194
|
+
|
195
|
+
it "will resolve independently from other cohorts (complex example)" do
|
196
|
+
FactoryGirl.create(:lax_ord)
|
197
|
+
FactoryGirl.create(:lax_ord, :origin_city => 'Los Angeles', :airline => 'Delta')
|
198
|
+
FactoryGirl.create(:lax_sfo)
|
199
|
+
FactoryGirl.create(:lax_ord, :origin_city => 'Los Angeles', :airline => 'Delta', :year => 2000)
|
200
|
+
FactoryGirl.create(:lax_sfo, :year => 2000)
|
201
|
+
year_condition = f_t[:year].eq(2000)
|
202
|
+
|
203
|
+
# sanity check
|
204
|
+
assert_count 2, model.where(year_condition)
|
205
|
+
assert_count 2, model.cohort(:origin => 'LAX', :dest => 'SFO')
|
206
|
+
assert_count 2, model.cohort(:origin_city => 'Los Angeles', :airline => 'Delta')
|
207
|
+
|
208
|
+
assert_count 1, model.cohort(:origin => 'LAX', :dest => 'SFO').where(year_condition)
|
209
|
+
assert_count 1, model.where(year_condition).cohort(:origin => 'LAX', :dest => 'SFO')
|
210
|
+
|
211
|
+
assert_count 2, model.cohort({:origin => 'LAX', :dest => 'SFO'}, :minimum_size => 2).where(year_condition)
|
212
|
+
assert_count 2, model.where(year_condition).cohort({:origin => 'LAX', :dest => 'SFO'}, :minimum_size => 2)
|
213
|
+
|
214
|
+
assert_count 1, model.cohort(:origin_city => 'Los Angeles', :airline => 'Delta').where(year_condition)
|
215
|
+
assert_count 1, model.where(year_condition).cohort(:origin_city => 'Los Angeles', :airline => 'Delta')
|
216
|
+
|
217
|
+
assert_count 1, model.cohort(:origin_city => 'Los Angeles', :airline => 'Delta').where(year_condition)
|
218
|
+
assert_count 1, model.where(year_condition).cohort(:origin_city => 'Los Angeles', :airline => 'Delta')
|
219
|
+
#--
|
220
|
+
|
221
|
+
assert_count 0, model.cohort(:origin => 'LAX', :dest => 'SFO').cohort(:origin_city => 'Los Angeles', :airline => 'Delta')
|
222
|
+
assert_count 0, model.cohort(:origin_city => 'Los Angeles', :airline => 'Delta').cohort(:origin => 'LAX', :dest => 'SFO')
|
223
|
+
end
|
224
|
+
|
225
|
+
describe "when used with UNION" do
|
226
|
+
before do
|
227
|
+
@ord = FactoryGirl.create(:lax_ord)
|
228
|
+
@sfo = FactoryGirl.create(:lax_sfo)
|
71
229
|
end
|
72
230
|
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
Flight.cohort({:dest => 'SFO', :origin => 'LAX'}, :strategy => :strict).count.must_equal 0
|
79
|
-
else
|
80
|
-
# activesupport provides ActiveSupport::OrderedHash
|
81
|
-
origin_important = ActiveSupport::OrderedHash.new
|
82
|
-
origin_important[:origin] = 'LAX'
|
83
|
-
origin_important[:dest] = 'SFO'
|
84
|
-
dest_important = ActiveSupport::OrderedHash.new
|
85
|
-
dest_important[:dest] = 'SFO'
|
86
|
-
dest_important[:origin] = 'LAX'
|
87
|
-
Flight.cohort(origin_important, :strategy => :strict).count.must_equal 1
|
88
|
-
Flight.cohort(dest_important, :strategy => :strict).count.must_equal 0
|
89
|
-
|
90
|
-
lambda {
|
91
|
-
Flight.cohort({:origin => 'LAX', :dest => 'SFO'}, :strategy => :strict).count
|
92
|
-
}.must_raise(ArgumentError, 'hash')
|
93
|
-
end
|
231
|
+
# sanity check!
|
232
|
+
it "has tests that use unions properly" do
|
233
|
+
ord = model.where(f_t[:dest].eq('ORD')).project(Arel.star)
|
234
|
+
sfo = model.where(f_t[:dest].eq('SFO')).project(Arel.star)
|
235
|
+
Flight.find_by_sql("SELECT * FROM #{ord.union(sfo).to_sql}").must_equal [@ord, @sfo]
|
94
236
|
end
|
237
|
+
|
238
|
+
it "builds successful cohorts" do
|
239
|
+
ord = model.cohort(:dest => 'ORD').project(Arel.star)
|
240
|
+
sfo = model.cohort(:dest => 'SFO').project(Arel.star)
|
241
|
+
Flight.find_by_sql("SELECT * FROM #{ord.union(sfo).to_sql}").must_equal [@ord, @sfo]
|
95
242
|
|
96
|
-
|
97
|
-
|
98
|
-
Flight.
|
99
|
-
Flight.cohort({:origin => 'LAX', :dest => 'SFO'}, :strategy => :strict, :priority => [:dest, :origin]).count.must_equal 0
|
100
|
-
Flight.cohort({:dest => 'SFO', :origin => 'LAX'}, :strategy => :strict, :priority => [:origin, :dest]).count.must_equal 1
|
101
|
-
Flight.cohort({:dest => 'SFO', :origin => 'LAX'}, :strategy => :strict, :priority => [:dest, :origin]).count.must_equal 0
|
243
|
+
msn = model.cohort(:origin => 'LAX', :dest => 'MSN').project(Arel.star)
|
244
|
+
lhr = model.cohort(:origin => 'LAX', :dest => 'LHR').project(Arel.star)
|
245
|
+
Flight.find_by_sql("SELECT * FROM #{msn.union(lhr).to_sql}").must_equal [@ord, @sfo]
|
102
246
|
end
|
103
247
|
|
104
|
-
it "
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
100.times { FactoryGirl.create(:lax_sfo) }
|
109
|
-
10.times { FactoryGirl.create(:lax_sfo_co) }
|
110
|
-
3.times { FactoryGirl.create(:lax_sfo_a320) }
|
111
|
-
1.times { FactoryGirl.create(:lax_sfo_aa_a320) }
|
112
|
-
end
|
113
|
-
Flight.count.must_equal 1_114 # sanity check
|
114
|
-
|
115
|
-
lax_sfo_aa_a320 = {:origin => 'LAX', :dest => 'SFO', :airline => 'American', :plane => 'A320'}
|
116
|
-
# don't discard anything
|
117
|
-
Flight.cohort(lax_sfo_aa_a320).count.must_equal 1
|
118
|
-
# discard airline
|
119
|
-
Flight.cohort(lax_sfo_aa_a320, :minimum_size => 2).count.must_equal 4
|
120
|
-
# discard plane and airline
|
121
|
-
Flight.cohort(lax_sfo_aa_a320, :minimum_size => 5).count.must_equal 114
|
122
|
-
# discard plane and airline and dest
|
123
|
-
Flight.cohort(lax_sfo_aa_a320, :minimum_size => 115).count.must_equal 1_114
|
124
|
-
|
125
|
-
lax_sfo_a320 = {:origin => 'LAX', :dest => 'SFO', :plane => 'A320'}
|
126
|
-
# don't discard anything
|
127
|
-
Flight.cohort(lax_sfo_a320).count.must_equal 4
|
128
|
-
# discard plane
|
129
|
-
Flight.cohort(lax_sfo_a320, :minimum_size => 5).count.must_equal 114
|
130
|
-
# discard plane and dest
|
131
|
-
Flight.cohort(lax_sfo_a320, :minimum_size => 115).count.must_equal 1_114
|
132
|
-
|
133
|
-
# off the rails here a bit
|
134
|
-
woah_lax_co_a320 = {:origin => 'LAX', :airline => 'Continental', :plane => 'A320'}
|
135
|
-
# discard plane
|
136
|
-
Flight.cohort(woah_lax_co_a320).count.must_equal 10
|
137
|
-
# discard plane and airline
|
138
|
-
Flight.cohort(woah_lax_co_a320, :minimum_size => 11).count.must_equal 1_114
|
248
|
+
it "doesn't somehow create unions with false positives" do
|
249
|
+
msn = model.cohort(:dest => 'MSN').project(Arel.star)
|
250
|
+
lhr = model.cohort(:dest => 'LHR').project(Arel.star)
|
251
|
+
ActiveRecord::Base.connection.select_value("SELECT COUNT(*) FROM #{msn.union(lhr).to_sql}").must_equal 0
|
139
252
|
end
|
140
253
|
|
141
|
-
it "
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
100.times { FactoryGirl.create(:lax_sfo) }
|
146
|
-
10.times { FactoryGirl.create(:lax_sfo_co) }
|
147
|
-
3.times { FactoryGirl.create(:lax_sfo_a320) }
|
148
|
-
1.times { FactoryGirl.create(:lax_sfo_aa_a320) }
|
149
|
-
end
|
150
|
-
|
151
|
-
lax_sfo_aa_a320 = {:origin => 'LAX', :dest => 'SFO', :airline => 'American', :plane => 'A320'}
|
152
|
-
priority = [:origin, :dest, :airline, :plane]
|
153
|
-
# discard nothing
|
154
|
-
Flight.cohort(lax_sfo_aa_a320, :strategy => :strict, :priority => priority).count.must_equal 1
|
155
|
-
# (force) discard plane, then (force) discard airline
|
156
|
-
Flight.cohort(lax_sfo_aa_a320, :strategy => :strict, :priority => priority, :minimum_size => 2).count.must_equal 114
|
157
|
-
# (force) discard plane, then (force) discard airline, then (force) discard dest
|
158
|
-
Flight.cohort(lax_sfo_aa_a320, :strategy => :strict, :priority => priority, :minimum_size => 115).count.must_equal 1_114
|
159
|
-
|
160
|
-
priority = [:plane, :airline, :dest, :origin]
|
161
|
-
# discard nothing
|
162
|
-
Flight.cohort(lax_sfo_aa_a320, :strategy => :strict, :priority => priority).count.must_equal 1
|
163
|
-
# (force) discard origin, then (force) discard dest, then (force) discard airline
|
164
|
-
Flight.cohort(lax_sfo_aa_a320, :strategy => :strict, :priority => priority, :minimum_size => 2).count.must_equal 4
|
165
|
-
# gives up!
|
166
|
-
Flight.cohort(lax_sfo_aa_a320, :strategy => :strict, :priority => priority, :minimum_size => 5).count.must_equal 0
|
254
|
+
it "builds unions where only one side has rows" do
|
255
|
+
msn = model.cohort(:dest => 'MSN').project(Arel.star)
|
256
|
+
ord = model.cohort(:dest => 'ORD').project(Arel.star)
|
257
|
+
Flight.find_by_sql("SELECT * FROM #{msn.union(ord).to_sql}").must_equal [@ord]
|
167
258
|
end
|
168
259
|
end
|
260
|
+
end
|
261
|
+
end
|
169
262
|
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
263
|
+
describe CohortAnalysis do
|
264
|
+
def assert_count(expected_count, relation)
|
265
|
+
relation = relation.clone
|
266
|
+
relation.projections = [Arel.sql('COUNT(*)')]
|
267
|
+
sql = relation.to_sql
|
268
|
+
ActiveRecord::Base.connection.select_value(sql).must_equal expected_count
|
269
|
+
end
|
176
270
|
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
Flight.where(origin_lax_constraint.and(dest_sfo_constraint)).count.must_equal 1
|
185
|
-
Flight.where(origin_lax_constraint.or(dest_sfo_constraint)).count.must_equal 2
|
186
|
-
end
|
271
|
+
def assert_members(expected_members, relation)
|
272
|
+
relation = relation.clone
|
273
|
+
table = relation.source.left
|
274
|
+
relation.projections = [Arel.star]
|
275
|
+
actual_members = Flight.find_by_sql relation.to_sql
|
276
|
+
actual_members.map(&:id).sort.must_equal expected_members.map(&:id).sort
|
277
|
+
end
|
187
278
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
end
|
279
|
+
def f_t
|
280
|
+
Arel::Table.new(:flights)
|
281
|
+
end
|
282
|
+
|
283
|
+
describe 'ArelSelectManagerInstanceMethods' do
|
284
|
+
it_behaves_like 'an adapter the provides #cohort'
|
285
|
+
def model
|
286
|
+
Arel::SelectManager.new(ActiveRecord::Base, Arel::Table.new(:flights))
|
197
287
|
end
|
198
288
|
end
|
199
289
|
|
200
|
-
describe '
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
FactoryGirl.create(:lax_sfo)
|
205
|
-
Flight.where(:dest => 'SFO').cohort(:origin => 'LAX').count.must_equal 1
|
206
|
-
Flight.where(:dest => 'SFO').cohort({:origin => 'LAX'}, :minimum_size => 2).count.must_equal 0
|
207
|
-
end
|
290
|
+
describe 'ArelTableInstanceMethods' do
|
291
|
+
it_behaves_like 'an adapter the provides #cohort'
|
292
|
+
def model
|
293
|
+
Arel::Table.new(:flights, ActiveRecord::Base)
|
208
294
|
end
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
295
|
+
end
|
296
|
+
|
297
|
+
describe 'ActiveRecordBaseClassMethods' do
|
298
|
+
it_behaves_like 'an adapter the provides #cohort'
|
299
|
+
def model
|
300
|
+
Flight
|
301
|
+
end
|
302
|
+
end
|
303
|
+
|
304
|
+
describe 'ActiveRecordRelationInstanceMethods' do
|
305
|
+
it_behaves_like 'an adapter the provides #cohort'
|
306
|
+
def model
|
307
|
+
Flight.scoped
|
220
308
|
end
|
221
309
|
end
|
222
310
|
end
|