cubicle 0.1.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.
@@ -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