cubicle 0.1.2 → 0.1.3

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.
Files changed (37) hide show
  1. data/CHANGELOG.rdoc +14 -0
  2. data/README.rdoc +188 -174
  3. data/cubicle.gemspec +26 -10
  4. data/lib/cubicle.rb +47 -422
  5. data/lib/cubicle/aggregation.rb +58 -7
  6. data/lib/cubicle/aggregation/ad_hoc.rb +12 -0
  7. data/lib/cubicle/aggregation/aggregation_manager.rb +212 -0
  8. data/lib/cubicle/aggregation/dsl.rb +108 -0
  9. data/lib/cubicle/aggregation/map_reduce_helper.rb +55 -0
  10. data/lib/cubicle/data.rb +29 -84
  11. data/lib/cubicle/data/hierarchy.rb +55 -0
  12. data/lib/cubicle/data/level.rb +62 -0
  13. data/lib/cubicle/data/member.rb +28 -0
  14. data/lib/cubicle/data/table.rb +56 -0
  15. data/lib/cubicle/measure.rb +30 -20
  16. data/lib/cubicle/mongo_mapper/aggregate_plugin.rb +1 -1
  17. data/lib/cubicle/ordered_hash_with_indifferent_access.rb +27 -0
  18. data/lib/cubicle/query.rb +21 -194
  19. data/lib/cubicle/query/dsl.rb +118 -0
  20. data/lib/cubicle/query/dsl/time_intelligence.rb +89 -0
  21. data/lib/cubicle/ratio.rb +28 -12
  22. data/lib/cubicle/version.rb +2 -2
  23. data/test/cubicle/aggregation/ad_hoc_test.rb +21 -0
  24. data/test/cubicle/cubicle_aggregation_test.rb +84 -20
  25. data/test/cubicle/cubicle_query_test.rb +36 -0
  26. data/test/cubicle/data/data_test.rb +30 -0
  27. data/test/cubicle/data/level_test.rb +42 -0
  28. data/test/cubicle/data/member_test.rb +40 -0
  29. data/test/cubicle/{cubicle_data_test.rb → data/table_test.rb} +50 -50
  30. data/test/cubicle/duration_test.rb +46 -48
  31. data/test/cubicle/ordered_hash_with_indifferent_access_test.rb +19 -0
  32. data/test/cubicles/defect_cubicle.rb +31 -31
  33. data/test/log/test.log +102066 -0
  34. metadata +26 -10
  35. data/lib/cubicle/data_level.rb +0 -60
  36. data/test/cubicle/cubicle_data_level_test.rb +0 -58
  37. data/test/cubicle/cubicle_test.rb +0 -85
data/lib/cubicle/data.rb CHANGED
@@ -1,84 +1,29 @@
1
- module Cubicle
2
- class Data < Array
3
-
4
- attr_reader :dimension_names, :measure_names, :total_count
5
-
6
- def initialize(query,query_results,total_count = nil)
7
- @dimension_names = query.dimensions.map{|d|d.name}
8
- @measure_names = query.measures.map{|m|m.name}
9
- @time_dimension_name = query.time_dimension.name if query.respond_to?(:time_dimension) && query.time_dimension
10
- @time_period = query.time_period if query.respond_to?(:time_period)
11
- @time_range = query.time_range if query.respond_to?(:time_range)
12
- extract_data(query_results)
13
- @total_count = total_count if total_count
14
- end
15
-
16
- def hierarchize(*args)
17
- args = [@time_dimension_name || @dimension_names].flatten if args.blank?
18
- extract_dimensions args, self
19
- end
20
- alias hierarchize_by hierarchize
21
- alias by hierarchize
22
-
23
- def records_per_page=(records_per_page)
24
- @records_per_page=records_per_page
25
- end
26
-
27
- def total_pages
28
- if (!defined?(@total_count))
29
- raise "Cannot find the total number of pages without setting the total count"
30
- end
31
-
32
- if (!defined?(@records_per_page))
33
- raise "Cannot find the total number of pages without setting the number of records per page"
34
- end
35
-
36
- (@total_count.to_f / @records_per_page.to_f).ceil
37
- end
38
-
39
- private
40
-
41
- def extract_dimensions(dimension_names, data)
42
- data, dimension_names = data.dup, dimension_names.dup
43
-
44
- return data.map{|measures|Cubicle::DataLevel.new(:measures,measures)} if dimension_names.blank?
45
-
46
- dim_name = dimension_names.shift
47
-
48
- result = Cubicle::DataLevel.new(dim_name)
49
- data.each do |tuple|
50
- member_name = tuple.delete(dim_name.to_s) || "Unknown"
51
- result[member_name] << tuple
52
- end
53
-
54
- result.each do |key,value|
55
- result[key] = extract_dimensions(dimension_names,value)
56
- end
57
-
58
- expand_time_dimension_if_required(result)
59
-
60
- result
61
- end
62
-
63
- def extract_data(data)
64
- data.each do |result|
65
- new = result.dup
66
- self << new.delete("_id").merge(new.delete("value"))
67
- end
68
- end
69
-
70
- def expand_time_dimension_if_required(data_level)
71
- return unless data_level.leaf_level? && @time_dimension_name && @time_dimension_name.to_s == data_level.name.to_s &&
72
- @time_range && @time_period
73
-
74
- @time_range.by!(@time_period)
75
-
76
- @time_range.each do |date|
77
- formatted_date = date.to_cubicle(@time_period)
78
- data_level[formatted_date] = [Cubicle::DataLevel.new(:measures,{})] unless data_level.include?(formatted_date)
79
- end
80
- data_level.keys.sort!
81
- end
82
- end
83
- end
84
-
1
+ module Cubicle
2
+ module Data
3
+
4
+ def self.aggregate(data,measures)
5
+ aggregated = OrderedHashWithIndifferentAccess.new {|hash,key|hash[key]=[]}
6
+ #in step one, we will gather our values into columns to give to the measure
7
+ #definitions to aggregation.
8
+ data.each do |row|
9
+ measures.each do |measure|
10
+ if (row.include?(measure.name))
11
+ val = row[measure.name]
12
+ aggregated[measure.name] << val if val.kind_of?(Numeric)
13
+ end
14
+ end
15
+ end
16
+ #in step two, we will let the measures reduce the columns of values to a single number, preferably using
17
+ #black magic or human sacrifice
18
+ measures.each do |measure|
19
+ aggregated[measure.name] = measure.aggregate(aggregated[measure.name])
20
+ end
21
+
22
+ #give each measure a final shot to operate on the results. This is useful for measures that
23
+ #act on the results of other aggregations, like Ratio does.
24
+ measures.each {|measure|measure.finalize_aggregation(aggregated)}
25
+ aggregated
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,55 @@
1
+ module Cubicle
2
+ module Data
3
+ class Hierarchy < Cubicle::Data::Level
4
+ include Member
5
+
6
+ attr_reader :measures
7
+ def initialize(root_dimension,measures)
8
+ super(root_dimension)
9
+ @measures = measures
10
+ @member_name = name
11
+ end
12
+
13
+ def self.hierarchize_table(table, dimension_names=nil)
14
+ dimension_names = [table.time_dimension_name || table.dimension_names].flatten if dimension_names.blank?
15
+ Cubicle::Data::Hierarchy.extract_dimensions(dimension_names,table,table.dup)
16
+ end
17
+ private
18
+
19
+ def self.extract_dimensions(dimension_names, data, table,parent_level=nil)
20
+ data, dimension_names = data.dup, dimension_names.dup
21
+
22
+ return data if dimension_names.blank?
23
+
24
+ dim_name = dimension_names.shift
25
+ dim = table.dimensions.find{|d|d.name==dim_name}
26
+ level = parent_level ? Cubicle::Data::Level.new(dim,parent_level) : Cubicle::Data::Hierarchy.new(dim,data.measures)
27
+ data.each do |tuple|
28
+ member_name = tuple.delete(dim_name.to_s) || "Unknown"
29
+ level[member_name] << tuple
30
+ end
31
+
32
+ level.each do |key,value|
33
+ level[key] = Cubicle::Data::Hierarchy.extract_dimensions(dimension_names,value,table,level)
34
+ end
35
+
36
+ Cubicle::Data::Hierarchy.expand_time_dimension_if_required(level,table)
37
+
38
+ level
39
+ end
40
+
41
+ def self.expand_time_dimension_if_required(data_level,table)
42
+ return unless data_level.leaf_level? && table.time_dimension_name && table.time_dimension_name.to_s == data_level.name.to_s &&
43
+ table.time_range && table.time_period
44
+
45
+ table.time_range.by!(table.time_period)
46
+
47
+ table.time_range.each do |date|
48
+ formatted_date = date.to_cubicle(table.time_period)
49
+ data_level[formatted_date] = [OrderedHashWithIndifferentAccess.new] unless data_level.include?(formatted_date)
50
+ end
51
+ data_level.keys.sort!
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,62 @@
1
+ module Cubicle
2
+ module Data
3
+ class Level < OrderedHashWithIndifferentAccess
4
+
5
+ def initialize(dimension,parent_level=nil)
6
+ @dimension = dimension
7
+ @parent_level = parent_level
8
+ super() {|hash,key|hash[key]=[]}#Always have an array freshly baked when strangers call
9
+ end
10
+
11
+ attr_reader :dimension, :parent_level
12
+ attr_accessor :missing_member_default
13
+
14
+ alias member_names keys
15
+ alias members values
16
+
17
+ def name
18
+ @dimension.name
19
+ end
20
+
21
+ def flatten(member_name = nil, opts={}, &block)
22
+
23
+ default_val = opts[:default] || @missing_member_default || 0
24
+
25
+ self.values.inject([]) do |output, data|
26
+ data.inject(output) do |flattened, value|
27
+ value.missing_member_default = default_val if value.respond_to?(:missing_member_default)
28
+
29
+ if block_given?
30
+ flat_val = block.arity == 1 ? (yield value) : (value.instance_eval(&block))
31
+ end
32
+ flat_val ||= value[member_name] if member_name && value.include?(member_name)
33
+ flat_val ||= default_val
34
+ flattened << flat_val
35
+ end
36
+ end
37
+ end
38
+
39
+ def leaf_level?
40
+ return self.length < 1 ||
41
+ !self[self.keys[0]].is_a?(Cubicle::Data::Level)
42
+ end
43
+
44
+ def []=(key,val)
45
+ prepare_level_member(val,key,self)
46
+ super(key.to_s,val)
47
+ end
48
+
49
+ def hierarchy
50
+ parent_level || self
51
+ end
52
+
53
+ private
54
+ def prepare_level_member(member,member_name,parent_level)
55
+ member.class_eval("include Cubicle::Data::Member")
56
+ member.member_name = member_name
57
+ member.parent_level = parent_level
58
+ end
59
+
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,28 @@
1
+ module Cubicle
2
+ module Data
3
+ module Member
4
+ attr_accessor :member_name, :parent_level
5
+
6
+ def measure_values
7
+ @measure_values ||= aggregate_children()
8
+ end
9
+
10
+ def leaf_member?
11
+ !self.kind_of?(Cubicle::Data::Level)
12
+ end
13
+
14
+ def measures
15
+ parent_level.hierarchy.measures
16
+ end
17
+
18
+ def measure_data
19
+ leaf_member? ? self : members.map{|member|member.aggregate_children}
20
+ end
21
+
22
+ def aggregate_children()
23
+ Cubicle::Data.aggregate(measure_data,measures)
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,56 @@
1
+ module Cubicle
2
+ module Data
3
+ class Table < Array
4
+ attr_reader :total_count, :dimensions, :measures, :time_dimension_name, :time_range, :time_period
5
+
6
+ def initialize(query,query_results,total_count = nil)
7
+ @dimensions = Marshal.load(Marshal.dump(query.dimensions))
8
+ @measures = Marshal.load(Marshal.dump(query.measures))
9
+ @time_dimension_name = query.time_dimension.name if query.respond_to?(:time_dimension) && query.time_dimension
10
+ @time_period = query.time_period if query.respond_to?(:time_period)
11
+ @time_range = query.time_range if query.respond_to?(:time_range)
12
+ extract_data(query_results)
13
+ @total_count = total_count if total_count
14
+ end
15
+
16
+ def dimension_names
17
+ @dimensions.map{|d|d.name}
18
+ end
19
+
20
+ def measure_names
21
+ @measures.map{|m|m.name}
22
+ end
23
+
24
+ def hierarchize(*args)
25
+ Cubicle::Data::Hierarchy.hierarchize_table(self,args)
26
+ end
27
+ alias hierarchize_by hierarchize
28
+ alias by hierarchize
29
+
30
+ def records_per_page=(records_per_page)
31
+ @records_per_page=records_per_page
32
+ end
33
+
34
+ def total_pages
35
+ if (!defined?(@total_count))
36
+ raise "Cannot find the total number of pages without setting the total count"
37
+ end
38
+
39
+ if (!defined?(@records_per_page))
40
+ raise "Cannot find the total number of pages without setting the number of records per page"
41
+ end
42
+
43
+ (@total_count.to_f / @records_per_page.to_f).ceil
44
+ end
45
+
46
+ private
47
+
48
+ def extract_data(data)
49
+ data.each do |result|
50
+ new = result.dup
51
+ self << OrderedHashWithIndifferentAccess.new(new.delete("_id").merge(new.delete("value")))
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -1,20 +1,30 @@
1
- module Cubicle
2
- class Measure < Member
3
-
4
- def initialize(*args)
5
- super
6
- @aggregation_method = self.options.delete(:aggregation_method) || default_aggregation_method
7
- end
8
-
9
- attr_accessor :aggregation_method #can be :sum, :average, :count
10
-
11
- def to_js_value
12
- return super unless aggregation_method == :count
13
- "((#{super}) ? 1 : 0)"
14
- end
15
-
16
- def default_aggregation_method
17
- :count
18
- end
19
- end
20
- end
1
+ module Cubicle
2
+ class Measure < Member
3
+
4
+ def initialize(*args)
5
+ super
6
+ @aggregation_method = self.options.delete(:aggregation_method) || default_aggregation_method
7
+ end
8
+
9
+ attr_accessor :aggregation_method #can be :sum, :average, :count
10
+
11
+ def to_js_value
12
+ return super unless aggregation_method == :count
13
+ "((#{super}) ? 1 : 0)"
14
+ end
15
+
16
+ def default_aggregation_method
17
+ :count
18
+ end
19
+
20
+ def aggregate(values)
21
+ return nil if values.blank?
22
+ sum = values.inject(0){|total,val|total+val}
23
+ aggregation_method == :average ? sum/values.length : sum
24
+ end
25
+
26
+ def finalize_aggregation(aggregation)
27
+ aggregation
28
+ end
29
+ end
30
+ end
@@ -3,7 +3,7 @@ module Cubicle
3
3
  module AggregatePlugin
4
4
  module ClassMethods
5
5
  def aggregate(&block)
6
- return Cubicle::Aggregation.new(self.collection_name,&block)
6
+ return Cubicle::Aggregation::AdHoc.new(self.collection_name,&block)
7
7
  end
8
8
  end
9
9
 
@@ -0,0 +1,27 @@
1
+ class OrderedHashWithIndifferentAccess < OrderedHash
2
+ def initialize(initial_data={},&block)
3
+ merge!(initial_data.stringify_keys)
4
+ super(&block) if block
5
+ end
6
+
7
+
8
+ def [](key)
9
+ key = key.to_s
10
+ #self[key] = [] unless self.keys.include?(key)
11
+ super(key)
12
+ end
13
+
14
+ def []=(key,val)
15
+ super(key.to_s,val)
16
+ end
17
+
18
+ def include?(key)
19
+ super(key.to_s)
20
+ end
21
+
22
+ def method_missing(sym,*args,&block)
23
+ return self[sym.to_s[0..-2]] = args[0] if sym.to_s =~ /.*=$/
24
+ return self[sym] if self.keys.include?(sym.to_s)
25
+ missing_member_default
26
+ end
27
+ end
data/lib/cubicle/query.rb CHANGED
@@ -1,14 +1,16 @@
1
1
  module Cubicle
2
2
  class Query
3
+ include Dsl
3
4
 
4
- attr_reader :time_period, :transient
5
+ attr_reader :time_period, :transient, :aggregation
5
6
  attr_accessor :source_collection_name
6
- def initialize(cubicle)
7
- @cubicle = cubicle
7
+
8
+ def initialize(aggregation)
9
+ @aggregation = aggregation
8
10
 
9
11
  @dimensions = Cubicle::MemberList.new
10
12
  @measures = Cubicle::MemberList.new
11
- @source_collection_name = @cubicle.target_collection_name
13
+ @source_collection_name = @aggregation.target_collection_name
12
14
  @where, @from_date, @to_date, @date_dimension, @time_period, @limit, @offset = nil
13
15
  @all_dimensions, @all_measures = false, false
14
16
  @transient = false
@@ -16,31 +18,24 @@ module Cubicle
16
18
  @order_by=[]
17
19
  @from_date_filter = "$gte"
18
20
  @to_date_filter = "$lte"
21
+ @query_aliases=HashWithIndifferentAccess.new
19
22
  end
20
23
 
21
24
  def clone
22
25
  Marshal.load(Marshal.dump(self))
23
26
  end
24
27
 
25
- def select_all
26
- select :all_dimensions, :all_measures
27
- end
28
-
29
28
  def selected?(member = nil)
30
29
  return (@dimensions.length > 0 || @measures.length > 0) unless member
31
- member_name = member.kind_of?(Cubicle::Member) ? member.name : member.to_s
30
+ member_name = member.kind_of?(Cubicle::Member) ? member.name : unalias(member.to_s)
32
31
  return @dimensions[member_name] ||
33
32
  @measures[member_name]
34
33
  end
35
34
 
36
35
  def transient?
37
- @transient || @cubicle.transient?
36
+ @transient || @aggregation.transient?
38
37
  end
39
38
 
40
- def transient!
41
- @transient = true
42
- @source_collection_name = nil
43
- end
44
39
 
45
40
  def all_measures?
46
41
  @all_measures
@@ -48,183 +43,7 @@ module Cubicle
48
43
 
49
44
  def all_dimensions?
50
45
  @all_dimensions
51
- end
52
-
53
- def select(*args)
54
- args = args[0] if args[0].is_a?(Array)
55
-
56
- if (args.include?(:all))
57
- select_all
58
- return
59
- end
60
-
61
- if (args.include?(:all_measures))
62
- @all_measures = true
63
- @measures = Cubicle::MemberList.new
64
- end
65
- if (args.include?(:all_dimensions))
66
- @all_dimensions = true
67
- @dimensions = Cubicle::MemberList.new
68
- end
69
-
70
- return if args.length == 1 && selected?(args[0])
71
-
72
- found=[:all_measures,:all_dimensions]
73
-
74
- if args.length == 1 && !all_dimensions? && args[0].is_a?(Cubicle::Dimension)
75
- @dimensions << convert_dimension(args.pop)
76
- elsif args.length == 1 && !all_measures? && args[0].is_a?(Cubicle::Measure)
77
- @measures << convert_measure(args.pop)
78
- else
79
- #remove from the list any dimensions or measures that are already
80
- #selected. This allows select to be idempotent,
81
- #which is useful for ensuring certain members are selected
82
- #even though the user may already have selected them previously
83
- args.each do |member_name|
84
- if (member = @cubicle.dimensions[member_name])
85
- @dimensions << convert_dimension(member)
86
- elsif (member = @cubicle.measures[member_name])
87
- @measures << convert_measure(member)
88
- end
89
- found << member_name if member || selected?(member_name)
90
- end
91
- end
92
- args = args - found
93
- raise "You selected one or more members that do not exist in the underlying data source:#{args.inspect}" unless args.blank?
94
- self
95
- end
96
-
97
- def limit(in_limit = nil)
98
- return @limit unless in_limit
99
- @limit = in_limit
100
- return self
101
- end
102
-
103
- def offset(in_offset = nil)
104
- return @offset unless in_offset
105
- @offset = in_offset
106
- return self
107
- end
108
- alias skip offset
109
-
110
- def by(*args)
111
- return @by unless args.length > 0
112
-
113
- #We'll need these in the result set
114
- select *args
115
-
116
- #replace any alias names with actual member names
117
- @by = args.map{|member_name|@cubicle.find_member(member_name).name}
118
- return if @time_dimension #If a time dimension has been explicitly specified, the following isn't helpful.
119
-
120
- #Now let's see if we can find ourselves a time dimension
121
- if (@cubicle.time_dimension && time_dimension.included_in?(args))
122
- time_dimension(@cubicle.time_dimension)
123
- else
124
- args.each do |by_member|
125
- if (detected = detect_time_period by_member)
126
- time_dimension by_member
127
- @time_period = detected
128
- break
129
- end
130
- end
131
- end
132
- end
133
-
134
- def order_by(*args)
135
- return @order_by unless args.length > 0
136
- args.each do |order|
137
- @order_by << (order.is_a?(Array) ? order : [order,:asc])
138
- end
139
- end
140
-
141
- def time_range(date_range = nil)
142
- return nil unless date_range || @from_date || @to_date
143
- unless date_range
144
- start,stop = @from_date || Time.now, @to_date || Time.now
145
- return @to_date_filter=="$lte" ? start..stop : start...stop
146
- end
147
-
148
- @to_date_filter = date_range.exclude_end? ? "$lt" : "$lte"
149
- @from_date, @to_date = date_range.first, date_range.last if date_range
150
- end
151
-
152
- def time_dimension(dimension = nil)
153
- return (@time_dimension ||= @cubicle.time_dimension) unless dimension
154
- @time_dimension = dimension.is_a?(Cubicle::Dimension) ? dimension : @cubicle.dimensions[dimension]
155
- raise "No dimension matching the name #{dimension} could be found in the underlying data source" unless @time_dimension
156
- #select @time_dimension unless selected?(dimension)
157
- end
158
- alias date_dimension time_dimension
159
-
160
- def last(duration,as_of = Time.now)
161
- duration = 1.send(duration) if [:year,:month,:week,:day].include?(duration)
162
- period = duration.parts[0][0]
163
- @from_date = duration.ago(as_of).advance(period=>1)
164
- @to_date = as_of
165
- end
166
- alias for_the_last last
167
-
168
- def last_complete(duration,as_of = Time.now)
169
- duration = 1.send(duration) if [:year,:month,:week,:day].include?(duration)
170
- period = duration.parts[0][0]
171
- @to_date = as_of.beginning_of(period)
172
- @from_date = duration.ago(@to_date)
173
- @to_date_filter = "$lt"
174
- end
175
- alias for_the_last_complete last_complete
176
-
177
- def next(duration,as_of = Time.now)
178
- duration = 1.send(duration) if [:year,:month,:week,:day].include?(duration)
179
- period = duration.parts[0][0]
180
- @to_date = duration.from_now(as_of).advance(period=>-1)
181
- @from_date = as_of
182
- end
183
- alias for_the_next next
184
-
185
- def this(period,as_of = Time.now)
186
- @from_date = as_of.beginning_of(period)
187
- @to_date = as_of
188
- self
189
- end
190
-
191
- def from(time = nil)
192
- return @from_date unless time
193
- @from_date = if time.is_a?(Symbol)
194
- Time.send(time) if Time.respond_to?(time)
195
- Date.send(time).to_time if Date.respond_to?(time)
196
- else
197
- time.to_time
198
- end
199
- self
200
- end
201
-
202
- def until(time = nil)
203
- return @to_date unless time
204
- @to_date = if time.is_a?(Symbol)
205
- Time.send(time) if Time.respond_to?(time)
206
- Date.send(time).to_time if Date.respond_to?(time)
207
- else
208
- time.to_time
209
- end
210
- self
211
- end
212
-
213
- def ytd(as_of = Time.now)
214
- this :year, as_of
215
- end
216
- alias year_to_date ytd
217
-
218
- def mtd(as_of = Time.now)
219
- this :month, as_of
220
- end
221
- alias month_to_date mtd
222
-
223
- def where(filter = nil)
224
- return prepare_filter unless filter
225
- (@where ||= {}).merge!(filter)
226
- self
227
- end
46
+ end
228
47
 
229
48
  def dimension_names
230
49
  return dimensions.map{|dim|dim.name.to_s}
@@ -236,16 +55,16 @@ module Cubicle
236
55
 
237
56
  def dimensions
238
57
  return @dimensions unless all_dimensions?
239
- @cubicle.dimensions.collect{|dim|convert_dimension(dim)}
58
+ @aggregation.dimensions.collect{|dim|convert_dimension(dim)}
240
59
  end
241
60
 
242
61
  def measures
243
62
  return @measures unless all_measures?
244
- @cubicle.measures.collect{|measure|convert_measure(measure)}
63
+ @aggregation.measures.collect{|measure|convert_measure(measure)}
245
64
  end
246
65
 
247
66
  def execute(options={})
248
- @cubicle.execute_query(self,options)
67
+ @aggregation.execute_query(self,options)
249
68
  end
250
69
 
251
70
  private
@@ -315,5 +134,13 @@ module Cubicle
315
134
  Cubicle::Measure.new(measure.name, :expression=>expression,:aggregation_method=>aggregation)
316
135
  end
317
136
 
137
+ def unalias(*name_or_names)
138
+ return (@query_aliases[name_or_names[0]] || name_or_names[0]) unless
139
+ name_or_names.length > 1 || name_or_names[0].is_a?(Array)
140
+
141
+ name_or_names = name_or_names[0] if name_or_names[0].is_a?(Array)
142
+ name_or_names.map {|name|@query_aliases[name] || name}
143
+ end
144
+
318
145
  end
319
146
  end