cubicle 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,12 @@
1
+ module Cubicle
2
+ class Ratio < CalculatedMeasure
3
+
4
+ attr_reader :numerator, :denominator
5
+ def initialize(member_name,numerator,denominator,opts={})
6
+ @numerator, @denominator = numerator, denominator
7
+ opts[:expression]="(value.#{denominator} > 0 && value.#{numerator} ? value.#{numerator}/value.#{denominator} : 0)"
8
+ super(member_name,opts)
9
+ end
10
+
11
+ end
12
+ end
@@ -0,0 +1,46 @@
1
+ class Time
2
+ include Cubicle::DateTime
3
+
4
+ #support date ranges that can iterate by day, month, etc.
5
+ #iteration
6
+ def step_by
7
+ @step_by ||= :second
8
+ end
9
+
10
+ def step_by=(by)
11
+ @step_by = by
12
+ end
13
+
14
+ def succ
15
+ next_date = advance(step_by.to_s.pluralize.to_sym=>1)
16
+ next_date.step_by = self.step_by
17
+ next_date
18
+ end
19
+ end
20
+
21
+ class Date
22
+ include Cubicle::DateTime
23
+
24
+ #support date ranges that can iterate by day, month, etc.
25
+ #iteration
26
+ def step_by
27
+ @step_by ||= :day
28
+ end
29
+
30
+ def step_by=(by)
31
+ @step_by = by
32
+ end
33
+
34
+ def succ
35
+ next_date = advance(step_by.to_s.pluralize.to_sym=>1)
36
+ next_date.step_by = self.step_by
37
+ next_date
38
+ end
39
+ end
40
+
41
+ class Range
42
+ def by!(step_by)
43
+ first.step_by = step_by if first.respond_to?(:step_by=)
44
+ self
45
+ end
46
+ end
@@ -0,0 +1,3 @@
1
+ module Cubicle
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,15 @@
1
+ development: &global_settings
2
+ database: cubicle_dev
3
+ host: 127.0.0.1
4
+ port: 27017
5
+
6
+ test:
7
+ database: cubicle_test
8
+ <<: *global_settings
9
+
10
+ production:
11
+ database: cubicle
12
+ #username: username
13
+ #password: password
14
+ <<: *global_settings
15
+
@@ -0,0 +1,21 @@
1
+ require "test_helper"
2
+
3
+ class AggregationTest < ActiveSupport::TestCase
4
+ context "Executing an ad hoc query via an aggregation" do
5
+ setup do
6
+ Defect.create_test_data
7
+ @results = Cubicle::Aggregation.new("defects") do
8
+ dimension :product, :field_name=>"product.name"
9
+ count :total, :field_name=>"defect_id"
10
+ end.query
11
+ end
12
+ should "return appropriately aggregated data" do
13
+ assert_equal "Brush Fire Bottle Rockets", @results[0]["product"]
14
+ assert_equal 1, @results[0]["total"]
15
+ assert_equal "Evil's Pickling Spice", @results[1]["product"]
16
+ assert_equal 1, @results[1]["total"]
17
+ assert_equal "Sad Day Moonshine", @results[2]["product"]
18
+ assert_equal 3, @results[2]["total"]
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,58 @@
1
+ require "test_helper"
2
+
3
+ class CubicleDataLevelTest < ActiveSupport::TestCase
4
+ context "Cubicle::CubicleDataLevel#new" do
5
+ should "succeed with no arguments" do
6
+ assert_nothing_raised do
7
+ Cubicle::DataLevel.new
8
+ end
9
+ end
10
+ should "Choose a default name if none is provided" do
11
+ assert_equal "Unknown Level", Cubicle::DataLevel.new.name
12
+ end
13
+ should "Set the name using the first argument" do
14
+ assert_equal :me, Cubicle::DataLevel.new(:me).name
15
+ end
16
+ end
17
+ context "Indexing into a Cubicle::CubicleDataLevel" do
18
+ should "provide indifferent access" do
19
+ level = Cubicle::DataLevel.new
20
+ level[:hello] = "hi"
21
+ assert_equal "hi", level["hello"]
22
+ level["hi"] = :hello
23
+ assert_equal :hello, level[:hi]
24
+ end
25
+ should "Provide an empty array as a default value for missing keys" do
26
+ assert_equal [], Cubicle::DataLevel.new["hello there"]
27
+ l = Cubicle::DataLevel.new
28
+ l[:hi] << 1 << 2 << 3
29
+ assert_equal [1,2,3], l[:hi]
30
+ end
31
+ should "allow method missing to index into hash" do
32
+ l = Cubicle::DataLevel.new
33
+ l.hello = "goodbye"
34
+ assert_equal "goodbye", l[:hello]
35
+ assert_equal "goodbye", l.hello
36
+ end
37
+ end
38
+ context "CubeDataLevel.leaf_level?" do
39
+ should "Correctly identify a leaf level" do
40
+ l = Cubicle::DataLevel.new(:happy)
41
+ assert l.leaf_level?
42
+ l[:a] = []
43
+ assert l.leaf_level?
44
+ end
45
+ should "Corrently identify non-leaf level" do
46
+ l = Cubicle::DataLevel.new(:happy)
47
+ l[:a] = Cubicle::DataLevel.new(:sad)
48
+ assert_not_equal true, l.leaf_level?
49
+ end
50
+ end
51
+ context "CubeDataLevel.flatten" do
52
+ should "Flatten using a provided member name" do
53
+ l = Cubicle::DataLevel.new(:happy,{:a=>[{:a=>3,:b=>2}],:b=>[{:a=>4,:b=>1}]})
54
+ assert_equal [2,1], l.flatten(:b)
55
+ end
56
+ end
57
+
58
+ end
@@ -0,0 +1,51 @@
1
+ require "test_helper"
2
+
3
+ class CubicleDataTest < ActiveSupport::TestCase
4
+ context "Given a map-reduce query result" do
5
+ setup do
6
+ @raw_data = [{"_id"=>{"company_id"=>'c1', "month"=>"2009-11"}, "value"=>{"requests"=>1.0, "transports"=>1.0}},
7
+ {"_id"=>{"company_id"=>'c1', "month"=>"2009-11"}, "value"=>{"requests"=>1.0, "controllable_declines"=>1.0}},
8
+ {"_id"=>{"company_id"=>'c1', "month"=>"2010-01"}, "value"=>{"requests"=>2.0, "transports"=>2.0}}]
9
+ @query = Class.new do
10
+ extend Cubicle
11
+ dimensions :company_id, :month
12
+ measures :requests, :transports, :controllable_declines
13
+ end
14
+ @data = Cubicle::Data.new(@query,@raw_data)
15
+ end
16
+ context "CubicleData#initialize" do
17
+ should "merge dimension and measure hashes for each row when initialized" do
18
+ assert_equal 3, @data.length
19
+ assert_equal 4, @data[0].keys.length
20
+ assert_nil @data[0]["_id"]
21
+ assert_nil @data[0]["value"]
22
+ assert_equal ["company_id","month","requests","transports"].sort, @data[0].keys.sort
23
+ end
24
+ end
25
+ context "CubicleData.hierarchize" do
26
+ should "hierarchize according to the dimensions of the original query when called without arguments" do
27
+ hierarchy = @data.hierarchize
28
+ assert_equal 1, hierarchy.length
29
+ assert_equal :company_id, hierarchy.name
30
+ assert_equal 2, hierarchy.c1.length
31
+ assert_equal 2, hierarchy.c1["2009-11"].length
32
+ assert_equal 2.0, hierarchy.c1["2010-01"][0].transports
33
+ end
34
+ should "hierarchize according to the order of dimensions provided" do
35
+ hierarchy = @data.hierarchize :month, :company_id
36
+ assert_equal 2, hierarchy.length
37
+ assert_equal :month, hierarchy.name
38
+ assert_equal 1, hierarchy["2009-11"].length
39
+ assert_equal 2, hierarchy["2009-11"].c1.length
40
+ assert_equal 1.0, hierarchy["2009-11"].c1[0].transports
41
+ end
42
+ should "hierarchize only the provided dimensions" do
43
+ hierarchy = @data.hierarchize :month
44
+ assert_equal 2, hierarchy.length
45
+ assert hierarchy.leaf_level?
46
+ assert_equal 1.0, hierarchy["2009-11"][0].transports
47
+ assert_equal "c1", hierarchy["2009-11"][0].company_id
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,326 @@
1
+ require "test_helper"
2
+
3
+ class CubicleQueryTest < ActiveSupport::TestCase
4
+ context "CubicleQuery#select" do
5
+ setup do
6
+ Defect.create_test_data
7
+ end
8
+ should "raise an exception when given a non-existent member" do
9
+ assert_raise RuntimeError do
10
+ DefectCubicle.query do
11
+ select :does_not_exist
12
+ end
13
+ end
14
+ end
15
+ should "query the underlying data source of cubicle rather than the persistent cache when :transient=>true" do
16
+ query = DefectCubicle.query :defer=>true do
17
+ transient!
18
+ select :product, :total_defects
19
+ end
20
+ query.execute()
21
+ assert_equal "defects", query.source_collection_name
22
+ end
23
+ should "query the persistent cache when transient=>false" do
24
+ query = DefectCubicle.query :defer=>true do
25
+ select :product, :total_defects
26
+ end
27
+ query.execute()
28
+ assert_equal "defect_cubicles_cubicle", query.source_collection_name
29
+ end
30
+ should "Select dimensions in the by clause" do
31
+ query_results = DefectCubicle.query do
32
+ select :all_measures
33
+ by :product
34
+ end
35
+ assert_equal :product, query_results.name
36
+ assert_equal "Brush Fire Bottle Rockets", query_results.member_names[0]
37
+ end
38
+ should "Select aliased dimensions in the by clause" do
39
+ query_results = DefectCubicle.query do
40
+ select :all_measures
41
+ by :date
42
+ end
43
+ assert_equal :manufacture_date, query_results.name
44
+ assert_equal "2009-12-09", query_results.member_names[0]
45
+ end
46
+ context "when specifying a dimension" do
47
+ setup do
48
+ @results = DefectCubicle.query(:product, :all_measures)
49
+ end
50
+ should "return the specified subset of data, including all measures" do
51
+ assert_equal 3, @results.length
52
+
53
+ assert_equal "Brush Fire Bottle Rockets", @results[0]["product"]
54
+ assert_equal 1, @results[0]["total_defects"]
55
+ assert_equal 0, @results[0]["preventable_defects"]
56
+ assert_equal 0.43, @results[0]["total_cost"]
57
+ assert_equal 0.43, @results[0]["avg_cost"]
58
+ assert_equal 0, @results[0]["preventable_pct"]
59
+ end
60
+
61
+ end
62
+ context "when specifying a dimension using an alias" do
63
+ setup do
64
+ @results = DefectCubicle.query(:date, :all_measures)
65
+ end
66
+ should "return the specified subset of data, including all measures" do
67
+ assert_equal 4, @results.length
68
+ assert_equal "2009-12-09", @results[0]["manufacture_date"]
69
+ assert_equal 1, @results[0]["total_defects"]
70
+ assert_equal 0, @results[0]["preventable_defects"]
71
+ assert_equal 0.43, @results[0]["total_cost"]
72
+ assert_equal 0.43, @results[0]["avg_cost"]
73
+ assert_equal 0, @results[0]["preventable_pct"]
74
+ end
75
+ end
76
+ context "when specifying a dimension from a transient query" do
77
+ setup do
78
+ #DefectCubicle.transient!
79
+ @results = DefectCubicle.query do |q|
80
+ q.transient!
81
+ q.select :product, :all_measures
82
+ end
83
+ end
84
+ should "return the specified subset of data, including all measures" do
85
+ assert_equal 3, @results.length
86
+
87
+ assert_equal "Brush Fire Bottle Rockets", @results[0]["product"]
88
+ assert_equal 1, @results[0]["total_defects"]
89
+ assert_equal 0, @results[0]["preventable_defects"]
90
+ assert_equal 0.43, @results[0]["total_cost"]
91
+ assert_equal 0.43, @results[0]["avg_cost"]
92
+ assert_equal 0, @results[0]["preventable_pct"]
93
+ end
94
+
95
+ end
96
+ context "when specifying a dimensional filter on a transient query" do
97
+ setup do
98
+ @results = DefectCubicle.query do
99
+ transient!
100
+ select :product, :all_measures
101
+ where :product=>"Sad Day Moonshine"
102
+ end
103
+ end
104
+ should "return a filtered subset of data" do
105
+ assert_equal 1, @results.length
106
+ assert_equal "Sad Day Moonshine", @results[0]["product"]
107
+ assert_equal 3, @results[0]["total_defects"]
108
+ assert_equal 2, @results[0]["preventable_defects"]
109
+ assert_equal 15.91, @results[0]["total_cost"]
110
+ assert_equal 15.91/3, @results[0]["avg_cost"]
111
+ assert_equal 2/3.0, @results[0]["preventable_pct"]
112
+ end
113
+
114
+ end
115
+ context "when specifying a dimensional filter on a transient query using an alias" do
116
+ setup do
117
+ #DefectCubicle.transient!
118
+ @results = DefectCubicle.query do
119
+ transient!
120
+ select :manufacture_date, :all_measures
121
+ where :date=>"2009-12-09"
122
+ end
123
+ end
124
+ should "return a filtered subset of data" do
125
+ assert_equal 1, @results.length
126
+ assert_equal "2009-12-09", @results[0]["manufacture_date"]
127
+ assert_equal 1, @results[0]["total_defects"]
128
+ assert_equal 0, @results[0]["preventable_defects"]
129
+ assert_equal 0.43, @results[0]["total_cost"]
130
+ assert_equal 0.43, @results[0]["avg_cost"]
131
+ assert_equal 0, @results[0]["preventable_pct"]
132
+ end
133
+
134
+ end
135
+ context "when specifying a dimensional filter on a non-transient query" do
136
+ setup do
137
+ @results = DefectCubicle.query do
138
+ select :product, :all_measures
139
+ where :product=>"Sad Day Moonshine"
140
+ end
141
+ end
142
+ should "return a filtered subset of data" do
143
+ assert_equal 1, @results.length
144
+ assert_equal "Sad Day Moonshine", @results[0]["product"]
145
+ assert_equal 3, @results[0]["total_defects"]
146
+ assert_equal 2, @results[0]["preventable_defects"]
147
+ assert_in_delta 15.91, @results[0]["total_cost"],0.0001
148
+ assert_in_delta 15.91/3, @results[0]["avg_cost"],0.0001
149
+ assert_in_delta 2/3.0, @results[0]["preventable_pct"],0.0001
150
+ end
151
+
152
+ end
153
+ context "when specifying a dimensional filter on a non-transient query using an alias" do
154
+ setup do
155
+ @results = DefectCubicle.query do
156
+ select :date, :all_measures
157
+ where :date=>"2009-12-09"
158
+ end
159
+ end
160
+ should "return a filtered subset of data" do
161
+ assert_equal 1, @results.length
162
+ assert_equal "2009-12-09", @results[0]["manufacture_date"]
163
+ assert_equal 1, @results[0]["total_defects"]
164
+ assert_equal 0, @results[0]["preventable_defects"]
165
+ assert_equal 0.43, @results[0]["total_cost"]
166
+ assert_equal 0.43, @results[0]["avg_cost"]
167
+ assert_equal 0, @results[0]["preventable_pct"]
168
+ end
169
+
170
+ end
171
+ context "when specifying a dimensional filter on a non-transient query using $where" do
172
+ setup do
173
+ @results = DefectCubicle.query do
174
+ select :product, :all_measures
175
+ where "$where"=>"this._id.product=='Sad Day Moonshine'"
176
+ end
177
+ end
178
+ should "return a filtered subset of data" do
179
+ assert_equal 1, @results.length
180
+ assert_equal "Sad Day Moonshine", @results[0]["product"]
181
+ assert_equal 3, @results[0]["total_defects"]
182
+ assert_equal 2, @results[0]["preventable_defects"]
183
+ assert_in_delta 15.91, @results[0]["total_cost"],0.0001
184
+ assert_in_delta 15.91/3, @results[0]["avg_cost"],0.0001
185
+ assert_equal 2/3.0, @results[0]["preventable_pct"]
186
+ end
187
+
188
+ end
189
+ context "when specifying a dimensional filter on a transient query using $where" do
190
+ setup do
191
+ @results = DefectCubicle.query do
192
+ transient!
193
+ select :product, :all_measures
194
+ where "$where"=>"this.product.name=='Sad Day Moonshine'"
195
+ end
196
+ end
197
+ should "return a filtered subset of data" do
198
+ assert_equal 1, @results.length
199
+ assert_equal "Sad Day Moonshine", @results[0]["product"]
200
+ assert_equal 3, @results[0]["total_defects"]
201
+ assert_equal 2, @results[0]["preventable_defects"]
202
+ assert_equal 15.91, @results[0]["total_cost"]
203
+ assert_equal 15.91/3, @results[0]["avg_cost"]
204
+ assert_equal 2/3.0, @results[0]["preventable_pct"]
205
+ end
206
+
207
+ end
208
+ context "when specifying a dimensional filter for an expression based dimension on a transient query" do
209
+ setup do
210
+ @results = DefectCubicle.query do
211
+ transient!
212
+ select :product,:all_measures
213
+ where :month=>"2010-01"
214
+ end
215
+ end
216
+ should "return a filtered subset of data" do
217
+ assert_equal 2, @results.length
218
+ assert_equal "Evil's Pickling Spice", @results[0]["product"]
219
+ assert_equal "Sad Day Moonshine", @results[1]["product"]
220
+ end
221
+
222
+ end
223
+ context "when specifying a sort order on a transient query" do
224
+ setup do
225
+ @results = DefectCubicle.query do
226
+ transient!
227
+ select :product,:all_measures
228
+ order_by [:product, :desc]
229
+ end
230
+ end
231
+ should "return sorted data" do
232
+ assert_equal 3, @results.length
233
+ assert_equal "Sad Day Moonshine", @results[0]["product"]
234
+ assert_equal "Evil's Pickling Spice", @results[1]["product"]
235
+ assert_equal "Brush Fire Bottle Rockets", @results[2]["product"]
236
+ end
237
+
238
+ end
239
+ context "when specifying a sort order on a non transient query" do
240
+ setup do
241
+ @results = DefectCubicle.query do
242
+ select :product,:all_measures
243
+ order_by [:product, :desc]
244
+ end
245
+ end
246
+ should "return sorted data" do
247
+ assert_equal 3, @results.length
248
+ assert_equal "Sad Day Moonshine", @results[0]["product"]
249
+ assert_equal "Evil's Pickling Spice", @results[1]["product"]
250
+ assert_equal "Brush Fire Bottle Rockets", @results[2]["product"]
251
+ end
252
+
253
+ end
254
+ context "when requesting YTD" do
255
+ setup do
256
+ Time.now = "2010-01-04"
257
+ @results = DefectCubicle.query do
258
+ select :date, :all_measures
259
+ year_to_date
260
+ end
261
+ end
262
+ should "present YTD data based on Time.now" do
263
+ assert_equal 1, @results.length
264
+ assert_in_delta 12.97, @results[0]["total_cost"],0.0001
265
+ end
266
+ end
267
+ context "when requesting MTD in a non-transient query do" do
268
+ setup do
269
+ Time.now = "2010-01-05"
270
+ @results = DefectCubicle.query do
271
+ select :month, :all_measures
272
+ month_to_date
273
+ end
274
+ end
275
+ should "present MTD data based on Time.now" do
276
+ assert_equal 1, @results.length
277
+ assert_in_delta 12.99, @results[0]["total_cost"],0.0001
278
+ end
279
+ end
280
+ context "when requesting MTD in a transient query do" do
281
+ setup do
282
+ Time.now = "2010-01-05"
283
+ @results = DefectCubicle.query do
284
+ transient!
285
+ select :month, :all_measures
286
+ month_to_date
287
+ end
288
+ end
289
+ should "present MTD data based on Time.now" do
290
+ assert_equal 1, @results.length
291
+ assert_in_delta 12.99, @results[0]["total_cost"],0.0001
292
+ end
293
+ end
294
+ context "when requesting for_the_last_complete 1.months" do
295
+ setup do
296
+ Time.now = "2010-01-30"
297
+ @results = DefectCubicle.query do
298
+ select :month, :all_measures
299
+ for_the_last_complete :month
300
+ end
301
+ end
302
+ should "present data for the previous month" do
303
+ assert_equal 1, @results.length
304
+ assert_equal 0.43, @results[0]["total_cost"]
305
+ end
306
+ end
307
+ context "Date filters against native Time types" do
308
+ setup do
309
+ Time.now = "2010-01-30"
310
+ Cubicle::DateTime.db_time_format = :native
311
+ @results = DefectCubicle.query do
312
+ time_dimension :manufacture_time
313
+ select :month, :all_measures
314
+ for_the_last_complete :month
315
+ end
316
+ end
317
+ should "dance like a butterfly and sting like a bee" do
318
+ assert_equal 1, @results.length
319
+ assert_equal 0.43, @results[0]["total_cost"]
320
+ end
321
+ teardown do
322
+ Cubicle::DateTime.db_time_format = :iso8601
323
+ end
324
+ end
325
+ end
326
+ end