cubicle 0.1.4 → 0.1.5
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.rdoc +7 -0
- data/cubicle.gemspec +2 -2
- data/lib/cubicle/aggregation/dsl.rb +116 -107
- data/lib/cubicle/duration.rb +1 -1
- data/lib/cubicle/version.rb +1 -1
- data/test/cubicle/duration_test.rb +68 -46
- data/test/cubicles/defect_cubicle.rb +33 -31
- data/test/log/test.log +5821 -0
- metadata +3 -3
data/CHANGELOG.rdoc
CHANGED
@@ -1,3 +1,10 @@
|
|
1
|
+
==0.1.5
|
2
|
+
* Added duration_since (aliased as elapsed, age_since) to calculate the time elapsed since any
|
3
|
+
given timestamp and the current execution time of the query.
|
4
|
+
|
5
|
+
== 0.1.4
|
6
|
+
* Cached aggregations were being stored on disk, but not properly utilized. This is now fixed.
|
7
|
+
|
1
8
|
== 0.1.3
|
2
9
|
* Formalized flat (Cubicle::Data::Table) and hierarchical (Cubicle::Data::Hierarchy) data results from cubicle queries,
|
3
10
|
and added client side support for rolling up measure values in hierarchical data so that no matter how a given
|
data/cubicle.gemspec
CHANGED
@@ -5,11 +5,11 @@
|
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{cubicle}
|
8
|
-
s.version = "0.1.
|
8
|
+
s.version = "0.1.5"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Nathan Stults"]
|
12
|
-
s.date = %q{2010-03-
|
12
|
+
s.date = %q{2010-03-29}
|
13
13
|
s.description = %q{Cubicle provides a dsl and aggregation caching framework for automating the generation, execution and caching of map reduce queries when using MongoDB in Ruby. Cubicle also includes a MongoMapper plugin for quickly performing ad-hoc, multi-level group-by queries against a MongoMapper model.}
|
14
14
|
s.email = %q{hereiam@sonic.net}
|
15
15
|
s.extra_rdoc_files = [
|
@@ -1,108 +1,117 @@
|
|
1
|
-
module Cubicle
|
2
|
-
module Aggregation
|
3
|
-
module Dsl
|
4
|
-
|
5
|
-
def source_collection_name(collection_name = nil)
|
6
|
-
return @source_collection = collection_name if collection_name
|
7
|
-
@source_collection ||= name.chomp("Cubicle").chomp("Cube").chomp("Aggregation").underscore.pluralize
|
8
|
-
end
|
9
|
-
alias source_collection_name= source_collection_name
|
10
|
-
|
11
|
-
def target_collection_name(collection_name = nil)
|
12
|
-
return nil if transient?
|
13
|
-
return @target_name = collection_name if collection_name
|
14
|
-
@target_name ||= "#{name.blank? ? source_collection_name : name.underscore.pluralize}_cubicle"
|
15
|
-
end
|
16
|
-
alias target_collection_name= target_collection_name
|
17
|
-
|
18
|
-
def dimension(*args)
|
19
|
-
dimensions << Cubicle::Dimension.new(*args)
|
20
|
-
dimensions[-1]
|
21
|
-
end
|
22
|
-
|
23
|
-
def dimensions(*args)
|
24
|
-
return (@dimensions ||= Cubicle::MemberList.new) if args.length < 1
|
25
|
-
args = args[0] if args.length == 1 && args[0].is_a?(Array)
|
26
|
-
args.each {|dim| dimension dim }
|
27
|
-
@dimensions
|
28
|
-
end
|
29
|
-
|
30
|
-
def measure(*args)
|
31
|
-
measures << Measure.new(*args)
|
32
|
-
measures[-1]
|
33
|
-
end
|
34
|
-
|
35
|
-
def measures(*args)
|
36
|
-
return (@measures ||= Cubicle::MemberList.new) if args.length < 1
|
37
|
-
args = args[0] if args.length == 1 && args[0].is_a?(Array)
|
38
|
-
args.each {|m| measure m}
|
39
|
-
@measures
|
40
|
-
end
|
41
|
-
|
42
|
-
def count(*args)
|
43
|
-
options = args.extract_options!
|
44
|
-
options[:aggregation_method] = :count
|
45
|
-
measure(*(args << options))
|
46
|
-
end
|
47
|
-
|
48
|
-
def average(*args)
|
49
|
-
options = args.extract_options!
|
50
|
-
options[:aggregation_method] = :average
|
51
|
-
measure(*(args << options))
|
52
|
-
#Averaged fields need a count of non-null values to properly calculate the average
|
53
|
-
args[0] = "#{args[0]}_count".to_sym
|
54
|
-
count *args
|
55
|
-
end
|
56
|
-
alias avg average
|
57
|
-
|
58
|
-
def sum(*args)
|
59
|
-
options = args.extract_options!
|
60
|
-
options[:aggregation_method] = :sum
|
61
|
-
measure(*(args << options))
|
62
|
-
end
|
63
|
-
|
64
|
-
def duration(*args)
|
65
|
-
options = args.extract_options!
|
66
|
-
options[:in] ||= durations_in
|
67
|
-
args << options
|
68
|
-
measures << (dur = Duration.new(*args))
|
69
|
-
count("#{dur.name}_count".to_sym, :expression=>dur.expression) if dur.aggregation_method == :average
|
70
|
-
end
|
71
|
-
|
72
|
-
def average_duration(*args)
|
73
|
-
duration(*args)
|
74
|
-
end
|
75
|
-
alias avg_duration average_duration
|
76
|
-
|
77
|
-
def total_duration(*args)
|
78
|
-
options = args.extract_options!
|
79
|
-
options[:aggregation_method] = :sum
|
80
|
-
duration(*(args<<options))
|
81
|
-
end
|
82
|
-
|
83
|
-
def
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
def
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
1
|
+
module Cubicle
|
2
|
+
module Aggregation
|
3
|
+
module Dsl
|
4
|
+
|
5
|
+
def source_collection_name(collection_name = nil)
|
6
|
+
return @source_collection = collection_name if collection_name
|
7
|
+
@source_collection ||= name.chomp("Cubicle").chomp("Cube").chomp("Aggregation").underscore.pluralize
|
8
|
+
end
|
9
|
+
alias source_collection_name= source_collection_name
|
10
|
+
|
11
|
+
def target_collection_name(collection_name = nil)
|
12
|
+
return nil if transient?
|
13
|
+
return @target_name = collection_name if collection_name
|
14
|
+
@target_name ||= "#{name.blank? ? source_collection_name : name.underscore.pluralize}_cubicle"
|
15
|
+
end
|
16
|
+
alias target_collection_name= target_collection_name
|
17
|
+
|
18
|
+
def dimension(*args)
|
19
|
+
dimensions << Cubicle::Dimension.new(*args)
|
20
|
+
dimensions[-1]
|
21
|
+
end
|
22
|
+
|
23
|
+
def dimensions(*args)
|
24
|
+
return (@dimensions ||= Cubicle::MemberList.new) if args.length < 1
|
25
|
+
args = args[0] if args.length == 1 && args[0].is_a?(Array)
|
26
|
+
args.each {|dim| dimension dim }
|
27
|
+
@dimensions
|
28
|
+
end
|
29
|
+
|
30
|
+
def measure(*args)
|
31
|
+
measures << Measure.new(*args)
|
32
|
+
measures[-1]
|
33
|
+
end
|
34
|
+
|
35
|
+
def measures(*args)
|
36
|
+
return (@measures ||= Cubicle::MemberList.new) if args.length < 1
|
37
|
+
args = args[0] if args.length == 1 && args[0].is_a?(Array)
|
38
|
+
args.each {|m| measure m}
|
39
|
+
@measures
|
40
|
+
end
|
41
|
+
|
42
|
+
def count(*args)
|
43
|
+
options = args.extract_options!
|
44
|
+
options[:aggregation_method] = :count
|
45
|
+
measure(*(args << options))
|
46
|
+
end
|
47
|
+
|
48
|
+
def average(*args)
|
49
|
+
options = args.extract_options!
|
50
|
+
options[:aggregation_method] = :average
|
51
|
+
measure(*(args << options))
|
52
|
+
#Averaged fields need a count of non-null values to properly calculate the average
|
53
|
+
args[0] = "#{args[0]}_count".to_sym
|
54
|
+
count *args
|
55
|
+
end
|
56
|
+
alias avg average
|
57
|
+
|
58
|
+
def sum(*args)
|
59
|
+
options = args.extract_options!
|
60
|
+
options[:aggregation_method] = :sum
|
61
|
+
measure(*(args << options))
|
62
|
+
end
|
63
|
+
|
64
|
+
def duration(*args)
|
65
|
+
options = args.extract_options!
|
66
|
+
options[:in] ||= durations_in
|
67
|
+
args << options
|
68
|
+
measures << (dur = Duration.new(*args))
|
69
|
+
count("#{dur.name}_count".to_sym, :expression=>dur.expression) if dur.aggregation_method == :average
|
70
|
+
end
|
71
|
+
|
72
|
+
def average_duration(*args)
|
73
|
+
duration(*args)
|
74
|
+
end
|
75
|
+
alias avg_duration average_duration
|
76
|
+
|
77
|
+
def total_duration(*args)
|
78
|
+
options = args.extract_options!
|
79
|
+
options[:aggregation_method] = :sum
|
80
|
+
duration(*(args<<options))
|
81
|
+
end
|
82
|
+
|
83
|
+
def duration_since(*args)
|
84
|
+
options = args.extract_options!
|
85
|
+
ms1 = args.length > 1 ? args.delete_at(1) : args.shift
|
86
|
+
options[ms1] = :now
|
87
|
+
duration(*(args<<options))
|
88
|
+
end
|
89
|
+
alias age_since duration_since
|
90
|
+
alias elapsed duration_since
|
91
|
+
|
92
|
+
def durations_in(unit_of_time = nil)
|
93
|
+
return (@duration_unit ||= :seconds) unless unit_of_time
|
94
|
+
@duration_unit = unit_of_time.to_s.pluralize.to_sym
|
95
|
+
end
|
96
|
+
alias :duration_unit :durations_in
|
97
|
+
|
98
|
+
|
99
|
+
def ratio(member_name, numerator, denominator)
|
100
|
+
measures << Ratio.new(member_name, numerator, denominator)
|
101
|
+
end
|
102
|
+
|
103
|
+
def aggregation(*member_list)
|
104
|
+
member_list = member_list[0] if member_list[0].is_a?(Array)
|
105
|
+
aggregations << member_list
|
106
|
+
end
|
107
|
+
|
108
|
+
def time_dimension(*args)
|
109
|
+
return (@time_dimension ||= nil) unless args.length > 0
|
110
|
+
@time_dimension = dimension(*args)
|
111
|
+
end
|
112
|
+
alias time_dimension= time_dimension
|
113
|
+
alias date time_dimension
|
114
|
+
alias time time_dimension
|
115
|
+
end
|
116
|
+
end
|
108
117
|
end
|
data/lib/cubicle/duration.rb
CHANGED
@@ -23,7 +23,7 @@ module Cubicle
|
|
23
23
|
cond = " && (#{self.condition})" unless self.condition.blank?
|
24
24
|
#prefix these names for the expression
|
25
25
|
prefix = "#{self.timestamp_prefix}#{self.timestamp_prefix.blank? ? '' : '.'}"
|
26
|
-
ms1,ms2 = [self.begin_milestone,self.end_milestone].map{|ms|"this.#{prefix}#{ms}"}
|
26
|
+
ms1,ms2 = [self.begin_milestone,self.end_milestone].map{|ms|ms.to_s=='now' ? "new Date(#{Time.now.to_i*1000})" : "this.#{prefix}#{ms}"}
|
27
27
|
@expression = "(#{ms1} && #{ms2}#{cond}) ? (#{ms2}-#{ms1})/#{denominator} : null"
|
28
28
|
end
|
29
29
|
|
data/lib/cubicle/version.rb
CHANGED
@@ -1,47 +1,69 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
class DurationTest < ActiveSupport::TestCase
|
4
|
-
context "Querying a cubicle with durations" do
|
5
|
-
setup do
|
6
|
-
#record 1, ms1 = 1/1/2000, ms2 = 1/2/2000, ms3=1/4/2000
|
7
|
-
#record 2, ms1 = 1/1/2000, ms2 = 1/3/2000, ms3=1/5/2000
|
8
|
-
Defect.create_duration_test_data
|
9
|
-
end
|
10
|
-
should "correctly calculate durations for each activity" do
|
11
|
-
results = DefectCubicle.query do
|
12
|
-
select :all
|
13
|
-
by :operator
|
14
|
-
end
|
15
|
-
a1 = results["a"][0]
|
16
|
-
a2 = results["b"][0]
|
17
|
-
|
18
|
-
assert_equal 1 * 60 * 60 * 24, a1["ms1_to_ms2_average"]
|
19
|
-
assert_equal 2 * 60 * 60 * 24, a1["ms2_to_ms3_sum"]
|
20
|
-
assert_equal 3, a1["total_duration"]
|
21
|
-
|
22
|
-
assert_equal 2 * 60 * 60 * 24, a2["ms1_to_ms2_average"]
|
23
|
-
assert_equal 2 * 60 * 60 * 24, a2["ms2_to_ms3_sum"]
|
24
|
-
assert_equal 4, a2["total_duration"]
|
25
|
-
end
|
26
|
-
|
27
|
-
results = DefectCubicle.query do
|
28
|
-
select :all_measures, :product
|
29
|
-
end
|
30
|
-
results = results[0]
|
31
|
-
|
32
|
-
assert_equal 1.5 * 60 * 60 * 24, results["ms1_to_ms2_average"]
|
33
|
-
assert_equal 4 * 60 * 60 * 24, results["ms2_to_ms3_sum"]
|
34
|
-
assert_equal 3.5, results["total_duration"]
|
35
|
-
|
36
|
-
|
37
|
-
should "respect the condition argument" do
|
38
|
-
results = DefectCubicle.query do
|
39
|
-
select :all_measures, :product
|
40
|
-
end
|
41
|
-
results = results[0]
|
42
|
-
|
43
|
-
assert_equal 3, results["conditional_duration"]
|
44
|
-
end
|
45
|
-
|
46
|
-
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class DurationTest < ActiveSupport::TestCase
|
4
|
+
context "Querying a cubicle with durations" do
|
5
|
+
setup do
|
6
|
+
#record 1, ms1 = 1/1/2000, ms2 = 1/2/2000, ms3=1/4/2000
|
7
|
+
#record 2, ms1 = 1/1/2000, ms2 = 1/3/2000, ms3=1/5/2000
|
8
|
+
Defect.create_duration_test_data
|
9
|
+
end
|
10
|
+
should "correctly calculate durations for each activity" do
|
11
|
+
results = DefectCubicle.query do
|
12
|
+
select :all
|
13
|
+
by :operator
|
14
|
+
end
|
15
|
+
a1 = results["a"][0]
|
16
|
+
a2 = results["b"][0]
|
17
|
+
|
18
|
+
assert_equal 1 * 60 * 60 * 24, a1["ms1_to_ms2_average"]
|
19
|
+
assert_equal 2 * 60 * 60 * 24, a1["ms2_to_ms3_sum"]
|
20
|
+
assert_equal 3, a1["total_duration"]
|
21
|
+
|
22
|
+
assert_equal 2 * 60 * 60 * 24, a2["ms1_to_ms2_average"]
|
23
|
+
assert_equal 2 * 60 * 60 * 24, a2["ms2_to_ms3_sum"]
|
24
|
+
assert_equal 4, a2["total_duration"]
|
25
|
+
end
|
26
|
+
should "correctly aggregate durations" do
|
27
|
+
results = DefectCubicle.query do
|
28
|
+
select :all_measures, :product
|
29
|
+
end
|
30
|
+
results = results[0]
|
31
|
+
|
32
|
+
assert_equal 1.5 * 60 * 60 * 24, results["ms1_to_ms2_average"]
|
33
|
+
assert_equal 4 * 60 * 60 * 24, results["ms2_to_ms3_sum"]
|
34
|
+
assert_equal 3.5, results["total_duration"]
|
35
|
+
|
36
|
+
end
|
37
|
+
should "respect the condition argument" do
|
38
|
+
results = DefectCubicle.query do
|
39
|
+
select :all_measures, :product
|
40
|
+
end
|
41
|
+
results = results[0]
|
42
|
+
|
43
|
+
assert_equal 3, results["conditional_duration"]
|
44
|
+
end
|
45
|
+
should "calculate duration_since via elapsed" do
|
46
|
+
Time.now = "1/10/2000"
|
47
|
+
results = DefectCubicle.query do
|
48
|
+
select :all_measures, :product
|
49
|
+
end
|
50
|
+
puts results.inspect
|
51
|
+
results = results[0]
|
52
|
+
|
53
|
+
assert_equal((6+5)/2.0, results["ms3_to_now_average"])
|
54
|
+
|
55
|
+
end
|
56
|
+
should "calculate named duration_since via age_since" do
|
57
|
+
Time.now = "1/10/2000"
|
58
|
+
results = DefectCubicle.query do
|
59
|
+
select :all_measures, :product
|
60
|
+
end
|
61
|
+
puts results.inspect
|
62
|
+
results = results[0]
|
63
|
+
|
64
|
+
assert_equal((6+5)/2.0, results["avg_time_since_ms3"])
|
65
|
+
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
47
69
|
end
|
@@ -1,32 +1,34 @@
|
|
1
|
-
class DefectCubicle
|
2
|
-
extend Cubicle::Aggregation
|
3
|
-
|
4
|
-
date :manufacture_date, :field_name=>'manufacture_date', :alias=>:date
|
5
|
-
dimension :month, :expression=>'this.manufacture_date.substring(0,7)'
|
6
|
-
dimension :year, :expression=>'this.manufacture_date.substring(0,4)'
|
7
|
-
|
8
|
-
dimension :manufacture_time
|
9
|
-
|
10
|
-
dimension :product, :field_name=>'product.name'
|
11
|
-
dimension :region, :field_name=>'plant.address.region'
|
12
|
-
|
13
|
-
dimensions :operator, :outcome
|
14
|
-
|
15
|
-
count :total_defects, :field_name=>'defect_id'
|
16
|
-
count :preventable_defects, :expression=>'this.root_cause != "act_of_god"'
|
17
|
-
sum :total_cost, :field_name=>'cost'
|
18
|
-
avg :avg_cost, :field_name=>'cost'
|
19
|
-
|
20
|
-
#calculated fields
|
21
|
-
ratio :preventable_pct, :preventable_defects, :total_defects
|
22
|
-
|
23
|
-
#durations
|
24
|
-
average_duration :ms1 => :ms2
|
25
|
-
total_duration :ms2 => :ms3
|
26
|
-
duration
|
27
|
-
duration
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
1
|
+
class DefectCubicle
|
2
|
+
extend Cubicle::Aggregation
|
3
|
+
|
4
|
+
date :manufacture_date, :field_name=>'manufacture_date', :alias=>:date
|
5
|
+
dimension :month, :expression=>'this.manufacture_date.substring(0,7)'
|
6
|
+
dimension :year, :expression=>'this.manufacture_date.substring(0,4)'
|
7
|
+
|
8
|
+
dimension :manufacture_time
|
9
|
+
|
10
|
+
dimension :product, :field_name=>'product.name'
|
11
|
+
dimension :region, :field_name=>'plant.address.region'
|
12
|
+
|
13
|
+
dimensions :operator, :outcome
|
14
|
+
|
15
|
+
count :total_defects, :field_name=>'defect_id'
|
16
|
+
count :preventable_defects, :expression=>'this.root_cause != "act_of_god"'
|
17
|
+
sum :total_cost, :field_name=>'cost'
|
18
|
+
avg :avg_cost, :field_name=>'cost'
|
19
|
+
|
20
|
+
#calculated fields
|
21
|
+
ratio :preventable_pct, :preventable_defects, :total_defects
|
22
|
+
|
23
|
+
#durations
|
24
|
+
average_duration :ms1 => :ms2
|
25
|
+
total_duration :ms2 => :ms3
|
26
|
+
duration :total_duration, :ms1 => :ms3, :in=>:days
|
27
|
+
duration :conditional_duration, :ms1 => :ms3, :in=>:days, :condition=>"this.defect_id != 2"
|
28
|
+
elapsed :ms3, :in=>:days
|
29
|
+
age_since :avg_time_since_ms3, :ms3, :in=>:days
|
30
|
+
|
31
|
+
#pre-cached aggregations
|
32
|
+
aggregation :month, :year, :product
|
33
|
+
aggregation :month, :region
|
32
34
|
end
|