mondrian-olap 1.2.0 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,21 +9,8 @@ module Mondrian
9
9
  end
10
10
 
11
11
  module ScriptElements
12
- def javascript(text)
13
- script text, :language => 'JavaScript'
14
- end
15
-
16
12
  private
17
13
 
18
- def coffeescript_function(arguments_string, text)
19
- # construct function to ensure that last expression is returned
20
- coffee_text = "#{arguments_string} ->\n" << text.gsub(/^/, ' ')
21
- javascript_text = CoffeeScript.compile(coffee_text, :bare => true)
22
- # remove function definition first and last lines
23
- javascript_text = javascript_text.strip.lines.to_a[1..-2].join
24
- javascript javascript_text
25
- end
26
-
27
14
  def ruby(*options, &block)
28
15
  udf_class_name = if options.include?(:shared)
29
16
  "#{name.capitalize}Udf"
@@ -74,64 +61,13 @@ module Mondrian
74
61
 
75
62
  class UserDefinedFunction < SchemaElement
76
63
  include ScriptElements
64
+
77
65
  attributes :name, # Name with which the user-defined function will be referenced in MDX expressions.
78
- # Name of the class which implemenets this user-defined function.
79
- # Must implement the mondrian.spi.UserDefinedFunction interface.
66
+ # Name of the class which implements this user-defined function.
67
+ # Must implement the mondrian.spi.UserDefinedFunction interface.
80
68
  :class_name
81
69
  elements :script
82
70
 
83
- def coffeescript(text)
84
- coffee_text = "__udf__ = {\n" << text << "}\n"
85
- javascript_text = CoffeeScript.compile(coffee_text, :bare => true)
86
- javascript_text << <<-JS
87
-
88
- __udf__.parameters || (__udf__.parameters = []);
89
- __udf__.returns || (__udf__.returns = "Scalar");
90
- var __scalarTypes__ = {"Numeric":true,"String":true,"Boolean":true,"DateTime":true,"Decimal":true,"Scalar":true};
91
- function __getType__(type) {
92
- if (__scalarTypes__[type]) {
93
- return new mondrian.olap.type[type+"Type"];
94
- } else if (type === "Member") {
95
- return mondrian.olap.type.MemberType.Unknown;
96
- } else {
97
- return null;
98
- }
99
- }
100
- function getParameterTypes() {
101
- var parameters = __udf__.parameters || [],
102
- types = [];
103
- for (var i = 0, len = parameters.length; i < len; i++) {
104
- types.push(__getType__(parameters[i]))
105
- }
106
- return types;
107
- }
108
- function getReturnType(parameterTypes) {
109
- var returns = __udf__.returns || "Scalar";
110
- return __getType__(returns);
111
- }
112
- if (__udf__.syntax) {
113
- function getSyntax() {
114
- return mondrian.olap.Syntax[__udf__.syntax];
115
- }
116
- }
117
- function execute(evaluator, args) {
118
- var parameters = __udf__.parameters || [],
119
- values = [],
120
- value;
121
- for (var i = 0, len = parameters.length; i < len; i++) {
122
- if (__scalarTypes__[parameters[i]]) {
123
- value = args[i].evaluateScalar(evaluator);
124
- } else {
125
- value = args[i].evaluate(evaluator);
126
- }
127
- values.push(value);
128
- }
129
- return __udf__.execute.apply(__udf__, values);
130
- }
131
- JS
132
- javascript javascript_text
133
- end
134
-
135
71
  class RubyUdfBase
136
72
  include Java::mondrian.spi.UserDefinedFunction
137
73
  def self.function_name=(name); @function_name = name; end
@@ -225,7 +161,7 @@ JS
225
161
  arguments_array_class = java.lang.Class.forName "[Lmondrian.spi.UserDefinedFunction$Argument;", true, class_loader
226
162
  add_method_signature("execute", [java.lang.Object, Java::mondrian.olap.Evaluator, arguments_array_class])
227
163
 
228
- # Override this metho if evaluator is needed
164
+ # Override this method if evaluator is needed
229
165
  def call_with_evaluator(evaluator, *values)
230
166
  call(*values)
231
167
  end
@@ -294,10 +230,6 @@ JS
294
230
  end
295
231
  end
296
232
 
297
- def coffeescript(text)
298
- coffeescript_function('(value)', text)
299
- end
300
-
301
233
  def ruby(*options, &block)
302
234
  ruby_formatter(options, Java::mondrian.spi.CellFormatter, 'formatCell', [java.lang.String, java.lang.Object], &block)
303
235
  end
@@ -308,12 +240,9 @@ JS
308
240
  attributes :class_name
309
241
  elements :script
310
242
 
311
- def coffeescript(text)
312
- coffeescript_function('(member)', text)
313
- end
314
-
315
243
  def ruby(*options, &block)
316
- ruby_formatter(options, Java::mondrian.spi.MemberFormatter, 'formatMember', [java.lang.String, Java::mondrian.olap.Member], &block)
244
+ ruby_formatter(options, Java::mondrian.spi.MemberFormatter, 'formatMember',
245
+ [java.lang.String, Java::mondrian.olap.Member], &block)
317
246
  end
318
247
  end
319
248
 
@@ -322,12 +251,9 @@ JS
322
251
  attributes :class_name
323
252
  elements :script
324
253
 
325
- def coffeescript(text)
326
- coffeescript_function('(member,propertyName,propertyValue)', text)
327
- end
328
-
329
254
  def ruby(*options, &block)
330
- ruby_formatter(options, Java::mondrian.spi.PropertyFormatter, 'formatProperty', [java.lang.String, Java::mondrian.olap.Member, java.lang.String, java.lang.Object], &block)
255
+ ruby_formatter(options, Java::mondrian.spi.PropertyFormatter, 'formatProperty',
256
+ [java.lang.String, Java::mondrian.olap.Member, java.lang.String, java.lang.Object], &block)
331
257
  end
332
258
  end
333
259
 
data/lib/mondrian/olap.rb CHANGED
@@ -1,18 +1,22 @@
1
1
  require 'java'
2
2
  require 'nokogiri'
3
3
 
4
+ {
5
+ # Do not register MondrianOlap4jDriver
6
+ "mondrian.olap4j.registerDriver" => false,
7
+ # Do not register log3j2 MBean
8
+ "log4j2.disable.jmx" => true
9
+ }.each do |key, value|
10
+ if java.lang.System.getProperty(key).nil?
11
+ java.lang.System.setProperty(key, value.to_s)
12
+ end
13
+ end
14
+
4
15
  directory = File.expand_path("../jars", __FILE__)
5
16
  Dir["#{directory}/*.jar"].each do |file|
6
17
  require file
7
18
  end
8
19
 
9
- unless java.lang.System.getProperty("log4j.configuration")
10
- file_uri = java.io.File.new("#{directory}/log4j.properties").toURI.to_s
11
- java.lang.System.setProperty("log4j.configuration", file_uri)
12
- end
13
- # register Mondrian olap4j driver
14
- Java::mondrian.olap4j.MondrianOlap4jDriver
15
-
16
20
  %w(error connection query result schema schema_udf cube).each do |file|
17
21
  require "mondrian/olap/#{file}"
18
22
  end
@@ -12,7 +12,7 @@ describe "Connection" do
12
12
  end
13
13
 
14
14
  it "should be successful" do
15
- @olap.connect.should be_true
15
+ @olap.connect.should == true
16
16
  end
17
17
 
18
18
  end
@@ -25,7 +25,7 @@ describe "Connection" do
25
25
  @olap = Mondrian::OLAP::Connection.new(CONNECTION_PARAMS.merge(
26
26
  :catalog_content => @schema_xml
27
27
  ))
28
- @olap.connect.should be_true
28
+ @olap.connect.should == true
29
29
  end
30
30
 
31
31
  end
@@ -49,10 +49,11 @@ describe "Connection" do
49
49
  when 'mysql' then 'mondrian.spi.impl.MySqlDialect'
50
50
  when 'postgresql' then 'mondrian.spi.impl.PostgreSqlDialect'
51
51
  when 'oracle' then 'mondrian.spi.impl.OracleDialect'
52
- when 'mssql' then 'mondrian.spi.impl.MicrosoftSqlServerDialect'
53
52
  when 'sqlserver' then 'mondrian.spi.impl.MicrosoftSqlServerDialect'
54
53
  when 'vertica' then 'mondrian.spi.impl.VerticaDialect'
55
54
  when 'snowflake' then 'mondrian.spi.impl.SnowflakeDialect'
55
+ when 'clickhouse' then 'mondrian.spi.impl.ClickHouseDialect'
56
+ when 'mariadb' then 'mondrian.spi.impl.MariaDBDialect'
56
57
  end
57
58
  end
58
59
 
@@ -89,4 +90,37 @@ describe "Connection" do
89
90
 
90
91
  end
91
92
 
93
+ describe "jdbc_uri" do
94
+ before(:all) { @olap_connection = Mondrian::OLAP::Connection }
95
+
96
+ describe "SQL Server" do
97
+ it "should return a valid JDBC URI" do
98
+ @olap_connection.new(
99
+ driver: 'sqlserver',
100
+ host: 'example.com',
101
+ port: 1234,
102
+ instance: 'MSSQLSERVER',
103
+ database: 'example_db'
104
+ ).jdbc_uri.should == 'jdbc:sqlserver://example.com:1234;databaseName=example_db;instanceName=MSSQLSERVER'
105
+ end
106
+
107
+ it "should return a valid JDBC URI with instance name as property" do
108
+ @olap_connection.new(
109
+ driver: 'sqlserver',
110
+ host: 'example.com',
111
+ properties: {
112
+ instanceName: "MSSQLSERVER"
113
+ }
114
+ ).jdbc_uri.should == 'jdbc:sqlserver://example.com;instanceName=MSSQLSERVER'
115
+ end
116
+
117
+ it "should return a valid JDBC URI with enabled integratedSecurity" do
118
+ @olap_connection.new(
119
+ driver: 'sqlserver',
120
+ host: 'example.com',
121
+ integrated_security: 'true'
122
+ ).jdbc_uri.should == 'jdbc:sqlserver://example.com;integratedSecurity=true'
123
+ end
124
+ end
125
+ end
92
126
  end
@@ -73,7 +73,7 @@ describe "Cube" do
73
73
  end
74
74
 
75
75
  # Do not execute tests on analytical databases with slow individual inserts
76
- describe 'cache', unless: %w(vertica snowflake).include?(MONDRIAN_DRIVER) do
76
+ describe 'cache', unless: %w(vertica snowflake clickhouse mariadb).include?(MONDRIAN_DRIVER) do
77
77
  def qt(name)
78
78
  @connection.quote_table_name(name.to_s)
79
79
  end
@@ -90,7 +90,7 @@ describe "Cube" do
90
90
  case MONDRIAN_DRIVER
91
91
  when 'mysql', 'jdbc_mysql', 'postgresql', 'oracle'
92
92
  @connection.execute 'CREATE TABLE sales_copy AS SELECT * FROM sales'
93
- when 'mssql', 'sqlserver'
93
+ when 'sqlserver'
94
94
  # Use raw_connection.execute to avoid detecting this query as a SELECT query
95
95
  # for which executeQuery JDBC method will fail
96
96
  @connection.raw_connection.execute_update 'SELECT * INTO sales_copy FROM sales'
data/spec/cube_spec.rb CHANGED
@@ -166,6 +166,31 @@ describe "Cube" do
166
166
 
167
167
  end
168
168
 
169
+ describe "cube hierarchies" do
170
+ before(:all) do
171
+ @cube = @olap.cube('Sales')
172
+ @hierarchy_names = %w(Measures Gender Customers Time Time.Weekly)
173
+ end
174
+
175
+ it "should get hierarchy names" do
176
+ @cube.hierarchy_names.should == @hierarchy_names
177
+ end
178
+
179
+ it "should get hierarchy by name" do
180
+ @cube.hierarchy('Gender').name.should == 'Gender'
181
+ end
182
+
183
+ it "should get hierarchy dimension name" do
184
+ hierarchy = @cube.hierarchy('Time.Weekly')
185
+ hierarchy.dimension.name.should == 'Time'
186
+ hierarchy.dimension_name.should == 'Time'
187
+ end
188
+
189
+ it "should return nil when getting dimension with invalid name" do
190
+ @cube.hierarchy('invalid').should be_nil
191
+ end
192
+ end
193
+
169
194
  describe "dimension hierarchies" do
170
195
  before(:all) do
171
196
  @cube = @olap.cube('Sales')
@@ -218,6 +243,15 @@ describe "Cube" do
218
243
  @cube.dimension('Gender').hierarchy.levels.map(&:members_count).should == [1, 2]
219
244
  end
220
245
 
246
+ it "should set and get hierarchy level cardinality" do
247
+ level = @cube.dimension('Gender').hierarchy.levels.last
248
+ level.cardinality.should == Java::JavaLang::Integer::MIN_VALUE
249
+ level.cardinality = 2
250
+ @olap.cube('Sales').dimension('Gender').hierarchy.levels.last.cardinality.should == 2
251
+ level.cardinality = nil
252
+ @olap.cube('Sales').dimension('Gender').hierarchy.levels.last.cardinality.should == Java::JavaLang::Integer::MIN_VALUE
253
+ end
254
+
221
255
  it "should get hierarchy annotations" do
222
256
  @cube.dimension('Customers').hierarchy.annotations.should == {'foo' => 'bar'}
223
257
  end
@@ -238,12 +272,12 @@ describe "Cube" do
238
272
  end
239
273
 
240
274
  it "should get hierarchy all member" do
241
- @cube.dimension('Gender').hierarchy.has_all?.should be_true
275
+ @cube.dimension('Gender').hierarchy.has_all?.should == true
242
276
  @cube.dimension('Gender').hierarchy.all_member_name.should == 'All Genders'
243
277
  end
244
278
 
245
279
  it "should not get all member for hierarchy without all member" do
246
- @cube.dimension('Time').hierarchy.has_all?.should be_false
280
+ @cube.dimension('Time').hierarchy.has_all?.should == false
247
281
  @cube.dimension('Time').hierarchy.all_member_name.should be_nil
248
282
  end
249
283
 
@@ -126,4 +126,44 @@ fullname
126
126
  </CalculatedMember>
127
127
  </Cube>
128
128
 
129
+ <Cube name="Warehouse">
130
+ <Table name="warehouse"/>
131
+ <DimensionUsage name="Time" source="Time" foreignKey="time_id"/>
132
+ <DimensionUsage name="Product" source="Product" foreignKey="product_id"/>
133
+ <Measure aggregator="sum" column="units_shipped" formatString="#,##0" name="Units Shipped"/>
134
+ <Measure aggregator="sum" column="store_invoice" formatString="#,##0.00" name="Store Invoice"/>
135
+ <Measure name="Products with units shipped" aggregator="distinct-count" formatString="#,###">
136
+ <MeasureExpression>
137
+ <SQL dialect="generic">
138
+ CASE WHEN units_shipped IS NOT NULL THEN product_id END
139
+ </SQL>
140
+ </MeasureExpression>
141
+ </Measure>
142
+ </Cube>
143
+
144
+ <VirtualCube name="Sales and Warehouse">
145
+ <VirtualCubeDimension name="Customers" cubeName="Sales"/>
146
+ <VirtualCubeDimension name="Gender" cubeName="Sales"/>
147
+ <VirtualCubeDimension name="Product"/>
148
+ <VirtualCubeDimension name="Time"/>
149
+ <VirtualCubeMeasure cubeName="Sales" name="[Measures].[Unit Sales]"/>
150
+ <VirtualCubeMeasure cubeName="Sales" name="[Measures].[Store Cost]"/>
151
+ <VirtualCubeMeasure cubeName="Sales" name="[Measures].[Store Sales]"/>
152
+ <VirtualCubeMeasure cubeName="Sales" name="[Measures].[Sales Count]"/>
153
+ <VirtualCubeMeasure cubeName="Sales" name="[Measures].[Customer Count]"/>
154
+ <VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Units Shipped]"/>
155
+ <VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Store Invoice]"/>
156
+ <VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Products with units shipped]"/>
157
+ </VirtualCube>
158
+
159
+ <Role name="Canada manager">
160
+ <SchemaGrant access="none">
161
+ <CubeGrant access="all" cube="Sales">
162
+ <HierarchyGrant access="custom" hierarchy="[Customers]">
163
+ <MemberGrant access="all" member="[Customers].[Canada]"/>
164
+ </HierarchyGrant>
165
+ </CubeGrant>
166
+ </SchemaGrant>
167
+ </Role>
168
+
129
169
  </Schema>
@@ -126,4 +126,44 @@ FULLNAME
126
126
  </CalculatedMember>
127
127
  </Cube>
128
128
 
129
+ <Cube name="Warehouse">
130
+ <Table name="WAREHOUSE"/>
131
+ <DimensionUsage name="Time" source="Time" foreignKey="TIME_ID"/>
132
+ <DimensionUsage name="Product" source="Product" foreignKey="PRODUCT_ID"/>
133
+ <Measure aggregator="sum" column="UNITS_SHIPPED" formatString="#,##0" name="Units Shipped"/>
134
+ <Measure aggregator="sum" column="STORE_INVOICE" formatString="#,##0.00" name="Store Invoice"/>
135
+ <Measure name="Products with units shipped" aggregator="distinct-count" formatString="#,###">
136
+ <MeasureExpression>
137
+ <SQL dialect="generic">
138
+ CASE WHEN UNITS_SHIPPED IS NOT NULL THEN PRODUCT_ID END
139
+ </SQL>
140
+ </MeasureExpression>
141
+ </Measure>
142
+ </Cube>
143
+
144
+ <VirtualCube name="Sales and Warehouse">
145
+ <VirtualCubeDimension name="Customers" cubeName="Sales"/>
146
+ <VirtualCubeDimension name="Gender" cubeName="Sales"/>
147
+ <VirtualCubeDimension name="Product"/>
148
+ <VirtualCubeDimension name="Time"/>
149
+ <VirtualCubeMeasure cubeName="Sales" name="[Measures].[Unit Sales]"/>
150
+ <VirtualCubeMeasure cubeName="Sales" name="[Measures].[Store Cost]"/>
151
+ <VirtualCubeMeasure cubeName="Sales" name="[Measures].[Store Sales]"/>
152
+ <VirtualCubeMeasure cubeName="Sales" name="[Measures].[Sales Count]"/>
153
+ <VirtualCubeMeasure cubeName="Sales" name="[Measures].[Customer Count]"/>
154
+ <VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Units Shipped]"/>
155
+ <VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Store Invoice]"/>
156
+ <VirtualCubeMeasure cubeName="Warehouse" name="[Measures].[Products with units shipped]"/>
157
+ </VirtualCube>
158
+
159
+ <Role name="Canada manager">
160
+ <SchemaGrant access="none">
161
+ <CubeGrant access="all" cube="Sales">
162
+ <HierarchyGrant access="custom" hierarchy="[Customers]">
163
+ <MemberGrant access="all" member="[Customers].[Canada]"/>
164
+ </HierarchyGrant>
165
+ </CubeGrant>
166
+ </SchemaGrant>
167
+ </Role>
168
+
129
169
  </Schema>
@@ -39,6 +39,7 @@ describe "Mondrian features" do
39
39
  level 'City', :column => 'city', :unique_members => false
40
40
  level 'Name', :column => 'fullname', :unique_members => true do
41
41
  property 'Related name', :column => 'related_fullname', :type => "String"
42
+ property 'Birthdate', :column => 'birthdate', :type => "String"
42
43
  end
43
44
  end
44
45
  hierarchy 'ID', :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
@@ -54,6 +55,9 @@ describe "Mondrian features" do
54
55
  level 'Year', :column => 'the_year', :type => 'Numeric', :unique_members => true, :level_type => 'TimeYears'
55
56
  level 'Quarter', :column => 'quarter', :unique_members => false, :level_type => 'TimeQuarters'
56
57
  level 'Month', :column => 'month_of_year', :type => 'Numeric', :unique_members => false, :level_type => 'TimeMonths'
58
+ level 'Day', :column => 'day_of_month', :type => 'Numeric', :unique_members => false, :level_type => 'TimeDays' do
59
+ property 'Date', :column => 'the_date', :type => "String"
60
+ end
57
61
  end
58
62
  hierarchy 'Weekly', :has_all => false, :primary_key => 'id' do
59
63
  table 'time'
@@ -64,6 +68,17 @@ describe "Mondrian features" do
64
68
  measure 'Unit Sales', :column => 'unit_sales', :aggregator => 'sum'
65
69
  measure 'Store Sales', :column => 'store_sales', :aggregator => 'sum'
66
70
  end
71
+
72
+ user_defined_function 'IsDirty' do
73
+ ruby do
74
+ returns :scalar
75
+ syntax :function
76
+ def call_with_evaluator(evaluator)
77
+ evaluator.isDirty
78
+ end
79
+ end
80
+ end
81
+
67
82
  end
68
83
  @olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
69
84
  end
@@ -120,4 +135,121 @@ describe "Mondrian features" do
120
135
  expect { @olap.execute mdx }.not_to raise_error
121
136
  end
122
137
 
138
+ # Test for https://jira.pentaho.com/browse/MONDRIAN-2714
139
+ it "should return datetime property as java.sql.Timestamp" do
140
+ full_name = '[2010].[Q1].[1].[1]'
141
+ member = @olap.cube('Sales').member(full_name)
142
+ member.property_value('Date').should be_a(java.sql.Timestamp)
143
+
144
+ result = @olap.from('Sales').
145
+ with_member('[Measures].[date]').as("#{full_name}.Properties('Date')", format_string: 'dd.mm.yyyy').
146
+ columns('[Measures].[date]').execute
147
+ result.values.first.should be_a(java.sql.Timestamp)
148
+ result.formatted_values.first.should == '01.01.2010'
149
+ end
150
+
151
+ it "should return date property as java.sql.Date" do
152
+ expected_date_class =
153
+ case MONDRIAN_DRIVER
154
+ when 'oracle'
155
+ java.sql.Timestamp
156
+ else
157
+ java.sql.Date
158
+ end
159
+
160
+ member = @olap.cube('Sales').hierarchy('Customers').level('Name').members.first
161
+ date_value = member.property_value('Birthdate')
162
+ date_value.should be_a(expected_date_class)
163
+
164
+ result = @olap.from('Sales').
165
+ with_member('[Measures].[date]').as("#{member.full_name}.Properties('Birthdate')", format_string: 'dd.mm.yyyy').
166
+ columns('[Measures].[date]').execute
167
+ result.values.first.should be_a(expected_date_class)
168
+ result.formatted_values.first.should == Date.parse(date_value.to_s).strftime("%d.%m.%Y")
169
+ end
170
+
171
+ describe "optimized Aggregate" do
172
+ def expected_value(crossjoin_members = nil)
173
+ query = @olap.from('Sales').columns('[Measures].[Unit Sales]')
174
+ query = query.crossjoin(crossjoin_members) if crossjoin_members
175
+ query.rows('[Customers].[USA].[CA]', '[Customers].[USA].[OR]').
176
+ execute.values.map(&:first).inject(&:+)
177
+ end
178
+
179
+ it "should aggregate stored members" do
180
+ result = @olap.from('Sales').
181
+ with_member('[Customers].[CA and OR]').as("Aggregate({[Customers].[USA].[CA], [Customers].[USA].[OR]})").
182
+ columns('[Measures].[Unit Sales]').
183
+ rows('[Customers].[CA and OR]').execute
184
+ result.values[0][0].should == expected_value
185
+ end
186
+
187
+ it "should aggregate stored members from several dimensions" do
188
+ result = @olap.from('Sales').
189
+ with_member('[Customers].[CA and OR]').
190
+ as("Aggregate({[Gender].[F]} * {[Customers].[USA].[CA], [Customers].[USA].[OR]})").
191
+ columns('[Measures].[Unit Sales]').
192
+ rows('[Customers].[CA and OR]').execute
193
+ result.values[0][0].should == expected_value('[Gender].[F]')
194
+ end
195
+
196
+ it "should aggregate stored members and a measure" do
197
+ result = @olap.from('Sales').
198
+ with_member('[Measures].[CA and OR]').
199
+ as("Aggregate({[Customers].[USA].[CA], [Customers].[USA].[OR]} * {[Measures].[Unit Sales]})").
200
+ columns('[Measures].[CA and OR]').execute
201
+ result.values[0].should == expected_value
202
+ end
203
+
204
+ it "should aggregate stored members with expression" do
205
+ result = @olap.from('Sales').
206
+ with_member('[Measures].[CA and OR twice]').
207
+ as("Aggregate({[Customers].[USA].[CA], [Customers].[USA].[OR]}, [Measures].[Unit Sales] * 2)").
208
+ columns('[Measures].[CA and OR twice]').execute
209
+ result.values[0].should == expected_value * 2
210
+ end
211
+
212
+ it "should aggregate calculated aggregate members" do
213
+ result = @olap.from('Sales').
214
+ with_member('[Customers].[CA calculated]').as("Aggregate({[Customers].[USA].[CA]})").
215
+ with_member('[Customers].[OR calculated]').as("Aggregate({[Customers].[USA].[OR]})").
216
+ with_member('[Customers].[CA and OR]').as("Aggregate({[Customers].[CA calculated], [Customers].[OR calculated]})").
217
+ columns('[Measures].[Unit Sales]').
218
+ rows('[Customers].[CA and OR]').execute
219
+ result.values[0][0].should == expected_value
220
+ end
221
+ end
222
+
223
+ it "should call evaluator isDirty method" do
224
+ result = @olap.from('Sales').
225
+ with_member('[Measures].[is dirty]').as('IsDirty()').
226
+ columns('[Measures].[is dirty]').execute
227
+ result.values[0].should == false
228
+ end
229
+
230
+ it "should support multiple values IN expression" do
231
+ lambda do
232
+ @olap.from('Sales').
233
+ columns('[Measures].[Unit Sales]').
234
+ where('[Time].[2011].[Q1]', '[Time].[2011].[Q2]').
235
+ execute
236
+ end.should_not raise_error
237
+ end
238
+
239
+ describe "functions with double argument" do
240
+ it "should get Abs with decimal measure" do
241
+ result = @olap.from('Sales').
242
+ with_member('[Measures].[Abs Store Sales]').as('Abs([Measures].[Store Sales])').
243
+ columns('[Measures].[Store Sales]', '[Measures].[Abs Store Sales]').execute
244
+ result.values[0].should == result.values[1]
245
+ end
246
+
247
+ it "should get Round with decimal measure" do
248
+ result = @olap.from('Sales').
249
+ with_member('[Measures].[Round Store Sales]').as('Round([Measures].[Store Sales])').
250
+ columns('[Measures].[Store Sales]', '[Measures].[Round Store Sales]').
251
+ where('[Customers].[USA].[CA]').execute
252
+ result.values[0].round.should == result.values[1]
253
+ end
254
+ end
123
255
  end