mondrian-olap 0.3.0 → 0.5.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.
Files changed (50) hide show
  1. checksums.yaml +7 -0
  2. data/Changelog.md +38 -0
  3. data/LICENSE.txt +1 -1
  4. data/README.md +302 -0
  5. data/VERSION +1 -1
  6. data/lib/mondrian/jars/commons-collections-3.2.jar +0 -0
  7. data/lib/mondrian/jars/commons-logging-1.1.1.jar +0 -0
  8. data/lib/mondrian/jars/commons-math-1.1.jar +0 -0
  9. data/lib/mondrian/jars/eigenbase-properties-1.1.2.jar +0 -0
  10. data/lib/mondrian/jars/eigenbase-resgen-1.3.1.jar +0 -0
  11. data/lib/mondrian/jars/eigenbase-xom-1.3.1.jar +0 -0
  12. data/lib/mondrian/jars/{javacup.jar → javacup-10k.jar} +0 -0
  13. data/lib/mondrian/jars/log4j-1.2.14.jar +0 -0
  14. data/lib/mondrian/jars/mondrian.jar +0 -0
  15. data/lib/mondrian/jars/olap4j-1.0.1.539.jar +0 -0
  16. data/lib/mondrian/olap.rb +2 -1
  17. data/lib/mondrian/olap/connection.rb +163 -32
  18. data/lib/mondrian/olap/cube.rb +163 -24
  19. data/lib/mondrian/olap/error.rb +57 -0
  20. data/lib/mondrian/olap/query.rb +52 -17
  21. data/lib/mondrian/olap/result.rb +298 -6
  22. data/lib/mondrian/olap/schema.rb +220 -29
  23. data/lib/mondrian/olap/schema_element.rb +31 -11
  24. data/lib/mondrian/olap/schema_udf.rb +331 -0
  25. data/lib/mondrian/olap/version.rb +5 -0
  26. data/spec/connection_role_spec.rb +130 -0
  27. data/spec/connection_spec.rb +36 -1
  28. data/spec/cube_spec.rb +137 -7
  29. data/spec/fixtures/MondrianTest.xml +4 -4
  30. data/spec/mondrian_spec.rb +53 -0
  31. data/spec/query_spec.rb +294 -11
  32. data/spec/rake_tasks.rb +8 -8
  33. data/spec/schema_definition_spec.rb +845 -26
  34. data/spec/spec_helper.rb +26 -17
  35. data/spec/support/matchers/be_like.rb +2 -2
  36. metadata +296 -237
  37. data/.rspec +0 -2
  38. data/Gemfile +0 -18
  39. data/README.rdoc +0 -221
  40. data/RUNNING_TESTS.rdoc +0 -66
  41. data/Rakefile +0 -46
  42. data/lib/mondrian/jars/commons-collections-3.1.jar +0 -0
  43. data/lib/mondrian/jars/commons-logging-1.0.4.jar +0 -0
  44. data/lib/mondrian/jars/commons-math-1.0.jar +0 -0
  45. data/lib/mondrian/jars/eigenbase-properties.jar +0 -0
  46. data/lib/mondrian/jars/eigenbase-resgen.jar +0 -0
  47. data/lib/mondrian/jars/eigenbase-xom.jar +0 -0
  48. data/lib/mondrian/jars/log4j-1.2.8.jar +0 -0
  49. data/lib/mondrian/jars/olap4j.jar +0 -0
  50. data/mondrian-olap.gemspec +0 -126
@@ -82,7 +82,7 @@ namespace :db do
82
82
  task :define_models => :require_spec_helper do
83
83
  unless MONDRIAN_DRIVER == 'luciddb'
84
84
  class TimeDimension < ActiveRecord::Base
85
- set_table_name "time"
85
+ self.table_name = "time"
86
86
  validates_presence_of :the_date
87
87
  before_create do
88
88
  self.the_day = the_date.strftime("%A")
@@ -102,7 +102,7 @@ namespace :db do
102
102
  class Customer < ActiveRecord::Base
103
103
  end
104
104
  class Sales < ActiveRecord::Base
105
- set_table_name "sales"
105
+ self.table_name = "sales"
106
106
  belongs_to :time_by_day
107
107
  belongs_to :product
108
108
  belongs_to :customer
@@ -150,7 +150,7 @@ namespace :db do
150
150
  )
151
151
  Product.create!(
152
152
  # LucidDB is not returning inserted ID therefore doing it hard way
153
- :product_class_id => ProductClass.find_all_by_product_category("Category #{i}").first.id,
153
+ :product_class_id => ProductClass.where(:product_category => "Category #{i}").to_a.first.id,
154
154
  :brand_name => "Brand #{i}",
155
155
  :product_name => "Product #{i}"
156
156
  )
@@ -223,9 +223,9 @@ namespace :db do
223
223
  Sales.delete_all
224
224
  count = 100
225
225
  # LucidDB does not support LIMIT therefore select all and limit in Ruby
226
- products = Product.order("id").all[0...count]
227
- times = TimeDimension.order("id").all[0...count]
228
- customers = Customer.order("id").all[0...count]
226
+ products = Product.order("id").to_a[0...count]
227
+ times = TimeDimension.order("id").to_a[0...count]
228
+ customers = Customer.order("id").to_a[0...count]
229
229
  count.times do |i|
230
230
  Sales.create!(
231
231
  :product_id => products[i].id,
@@ -251,9 +251,9 @@ namespace :spec do
251
251
  end
252
252
  end
253
253
 
254
- desc "Run specs with all database drivers"
254
+ desc "Run specs with all primary database drivers"
255
255
  task :all do
256
- %w(mysql postgresql oracle luciddb mssql sqlserver).each do |driver|
256
+ %w(mysql postgresql oracle mssql).each do |driver|
257
257
  Rake::Task["spec:#{driver}"].invoke
258
258
  end
259
259
  end
@@ -1,3 +1,5 @@
1
+ # encoding: utf-8
2
+
1
3
  require "spec_helper"
2
4
 
3
5
  describe "Schema definition" do
@@ -10,25 +12,25 @@ describe "Schema definition" do
10
12
  describe "root element" do
11
13
  it "should render to XML" do
12
14
  @schema.to_xml.should be_like <<-XML
13
- <?xml version="1.0"?>
15
+ <?xml version="1.0" encoding="UTF-8"?>
14
16
  <Schema/>
15
17
  XML
16
18
  end
17
19
 
18
20
  it "should render to XML with attributes" do
19
21
  @schema.define('FoodMart') do
20
- description 'Demo "FoodMart" schema'
22
+ description 'Demo "FoodMart" schema āčē'
21
23
  end
22
24
  @schema.to_xml.should be_like <<-XML
23
- <?xml version="1.0"?>
24
- <Schema description="Demo &quot;FoodMart&quot; schema" name="FoodMart"/>
25
+ <?xml version="1.0" encoding="UTF-8"?>
26
+ <Schema description="Demo &quot;FoodMart&quot; schema āčē" name="FoodMart"/>
25
27
  XML
26
28
  end
27
29
 
28
30
  it "should render to XML using class method" do
29
31
  schema = Mondrian::OLAP::Schema.define('FoodMart')
30
32
  schema.to_xml.should be_like <<-XML
31
- <?xml version="1.0"?>
33
+ <?xml version="1.0" encoding="UTF-8"?>
32
34
  <Schema name="FoodMart"/>
33
35
  XML
34
36
  end
@@ -45,7 +47,7 @@ describe "Schema definition" do
45
47
  end
46
48
  end
47
49
  @schema.to_xml.should be_like <<-XML
48
- <?xml version="1.0"?>
50
+ <?xml version="1.0" encoding="UTF-8"?>
49
51
  <Schema name="default">
50
52
  <Cube cache="false" defaultMeasure="Unit Sales" description="Sales cube" enabled="true" name="Sales"/>
51
53
  </Schema>
@@ -58,7 +60,7 @@ describe "Schema definition" do
58
60
  :description => 'Sales cube', :cache => false, :enabled => true
59
61
  end
60
62
  @schema.to_xml.should be_like <<-XML
61
- <?xml version="1.0"?>
63
+ <?xml version="1.0" encoding="UTF-8"?>
62
64
  <Schema name="default">
63
65
  <Cube cache="false" defaultMeasure="Unit Sales" description="Sales cube" enabled="true" name="Sales"/>
64
66
  </Schema>
@@ -74,7 +76,7 @@ describe "Schema definition" do
74
76
  end
75
77
  end
76
78
  @schema.to_xml.should be_like <<-XML
77
- <?xml version="1.0"?>
79
+ <?xml version="1.0" encoding="UTF-8"?>
78
80
  <Schema name="default">
79
81
  <Cube name="Sales">
80
82
  <Table alias="sales" name="sales_fact"/>
@@ -91,7 +93,7 @@ describe "Schema definition" do
91
93
  end
92
94
  %w(oracle luciddb).each do |driver|
93
95
  @schema.to_xml(:driver => driver).should be_like <<-XML
94
- <?xml version="1.0"?>
96
+ <?xml version="1.0" encoding="UTF-8"?>
95
97
  <Schema name="default">
96
98
  <Cube name="Sales">
97
99
  <Table alias="SALES" name="SALES_FACT" schema="FACTS"/>
@@ -108,7 +110,7 @@ describe "Schema definition" do
108
110
  end
109
111
  end
110
112
  @schema.to_xml.should be_like <<-XML
111
- <?xml version="1.0"?>
113
+ <?xml version="1.0" encoding="UTF-8"?>
112
114
  <Schema name="default">
113
115
  <Cube name="Sales">
114
116
  <Table alias="SALES" name="SALES_FACT" schema="FACTS"/>
@@ -125,7 +127,7 @@ describe "Schema definition" do
125
127
  end
126
128
  %w(oracle luciddb).each do |driver|
127
129
  @schema.to_xml(:driver => driver).should be_like <<-XML
128
- <?xml version="1.0"?>
130
+ <?xml version="1.0" encoding="UTF-8"?>
129
131
  <Schema name="default">
130
132
  <Cube name="Sales">
131
133
  <Table alias="sales" name="sales_fact" schema="facts"/>
@@ -144,7 +146,7 @@ describe "Schema definition" do
144
146
  end
145
147
  end
146
148
  @schema.to_xml.should be_like <<-XML
147
- <?xml version="1.0"?>
149
+ <?xml version="1.0" encoding="UTF-8"?>
148
150
  <Schema name="default">
149
151
  <Cube name="Sales">
150
152
  <Table alias="sales" name="sales_fact">
@@ -166,7 +168,7 @@ describe "Schema definition" do
166
168
  end
167
169
  end
168
170
  @schema.to_xml.should be_like <<-XML
169
- <?xml version="1.0"?>
171
+ <?xml version="1.0" encoding="UTF-8"?>
170
172
  <Schema name="default">
171
173
  <Cube name="Sales">
172
174
  <View alias="sales">
@@ -195,7 +197,7 @@ describe "Schema definition" do
195
197
  end
196
198
  end
197
199
  @schema.to_xml.should be_like <<-XML
198
- <?xml version="1.0"?>
200
+ <?xml version="1.0" encoding="UTF-8"?>
199
201
  <Schema name="default">
200
202
  <Cube name="Sales">
201
203
  <Dimension foreignKey="customer_id" name="Gender">
@@ -226,7 +228,7 @@ describe "Schema definition" do
226
228
  end
227
229
  end
228
230
  @schema.to_xml.should be_like <<-XML
229
- <?xml version="1.0"?>
231
+ <?xml version="1.0" encoding="UTF-8"?>
230
232
  <Schema name="default">
231
233
  <Cube name="Sales">
232
234
  <Dimension foreignKey="time_id" name="Time">
@@ -242,6 +244,39 @@ describe "Schema definition" do
242
244
  XML
243
245
  end
244
246
 
247
+ it "should render dimension with hierarchy and level defaults" do
248
+ @schema.define do
249
+ cube 'Sales' do
250
+ dimension 'Time' do
251
+ foreign_key 'time_id'
252
+ hierarchy do
253
+ all_member_name 'All Times' # should add :has_all => true
254
+ primary_key 'time_id'
255
+ table 'time_by_day'
256
+ level 'Year', :column => 'the_year', :type => 'Numeric' # first level should have default :unique_members => true
257
+ level 'Quarter', :column => 'quarter' # next levels should have default :unique_members => false
258
+ level 'Month', :column => 'month_of_year', :type => 'Numeric'
259
+ end
260
+ end
261
+ end
262
+ end
263
+ @schema.to_xml.should be_like <<-XML
264
+ <?xml version="1.0" encoding="UTF-8"?>
265
+ <Schema name="default">
266
+ <Cube name="Sales">
267
+ <Dimension foreignKey="time_id" name="Time">
268
+ <Hierarchy allMemberName="All Times" hasAll="true" primaryKey="time_id">
269
+ <Table name="time_by_day"/>
270
+ <Level column="the_year" name="Year" type="Numeric" uniqueMembers="true"/>
271
+ <Level column="quarter" name="Quarter" uniqueMembers="false"/>
272
+ <Level column="month_of_year" name="Month" type="Numeric" uniqueMembers="false"/>
273
+ </Hierarchy>
274
+ </Dimension>
275
+ </Cube>
276
+ </Schema>
277
+ XML
278
+ end
279
+
245
280
  it "should render dimension hierarchy with join" do
246
281
  @schema.define do
247
282
  cube 'Sales' do
@@ -260,7 +295,7 @@ describe "Schema definition" do
260
295
  end
261
296
  end
262
297
  @schema.to_xml.should be_like <<-XML
263
- <?xml version="1.0"?>
298
+ <?xml version="1.0" encoding="UTF-8"?>
264
299
  <Schema name="default">
265
300
  <Cube name="Sales">
266
301
  <Dimension foreignKey="product_id" name="Products">
@@ -297,7 +332,7 @@ describe "Schema definition" do
297
332
  end
298
333
  end
299
334
  @schema.to_xml(:driver => 'oracle').should be_like <<-XML
300
- <?xml version="1.0"?>
335
+ <?xml version="1.0" encoding="UTF-8"?>
301
336
  <Schema name="default">
302
337
  <Cube name="Sales">
303
338
  <Dimension foreignKey="PRODUCT_ID" name="Products">
@@ -316,6 +351,113 @@ describe "Schema definition" do
316
351
  XML
317
352
  end
318
353
 
354
+ it "should render dimension hierarchy with nested joins" do
355
+ @schema.define do
356
+ cube 'Sales' do
357
+ dimension 'Products', :foreign_key => 'product_id' do
358
+ hierarchy :has_all => true, :all_member_name => 'All Products',
359
+ :primary_key => 'product_id', :primary_key_table => 'product' do
360
+ join :left_key => 'product_class_id', :right_alias => 'product_class', :right_key => 'product_class_id' do
361
+ table 'product'
362
+ join :left_key => 'product_type_id', :right_key => 'product_type_id' do
363
+ table 'product_class'
364
+ table 'product_type'
365
+ end
366
+ end
367
+ level 'Product Family', :table => 'product_type', :column => 'product_family', :unique_members => true
368
+ level 'Product Category', :table => 'product_class', :column => 'product_category', :unique_members => false
369
+ level 'Brand Name', :table => 'product', :column => 'brand_name', :unique_members => false
370
+ level 'Product Name', :table => 'product', :column => 'product_name', :unique_members => true
371
+ end
372
+ end
373
+ end
374
+ end
375
+ @schema.to_xml.should be_like <<-XML
376
+ <?xml version="1.0" encoding="UTF-8"?>
377
+ <Schema name="default">
378
+ <Cube name="Sales">
379
+ <Dimension foreignKey="product_id" name="Products">
380
+ <Hierarchy allMemberName="All Products" hasAll="true" primaryKey="product_id" primaryKeyTable="product">
381
+ <Join leftKey="product_class_id" rightAlias="product_class" rightKey="product_class_id">
382
+ <Table name="product"/>
383
+ <Join leftKey="product_type_id" rightKey="product_type_id">
384
+ <Table name="product_class"/>
385
+ <Table name="product_type"/>
386
+ </Join>
387
+ </Join>
388
+ <Level column="product_family" name="Product Family" table="product_type" uniqueMembers="true"/>
389
+ <Level column="product_category" name="Product Category" table="product_class" uniqueMembers="false"/>
390
+ <Level column="brand_name" name="Brand Name" table="product" uniqueMembers="false"/>
391
+ <Level column="product_name" name="Product Name" table="product" uniqueMembers="true"/>
392
+ </Hierarchy>
393
+ </Dimension>
394
+ </Cube>
395
+ </Schema>
396
+ XML
397
+ end
398
+
399
+ end
400
+
401
+ describe "Shared dimension" do
402
+ it "should render to XML" do
403
+ @schema.define do
404
+ dimension 'Gender' do
405
+ hierarchy do
406
+ has_all true
407
+ all_member_name 'All Genders'
408
+ primary_key 'customer_id'
409
+ table 'customer'
410
+ level 'Gender', :column => 'gender', :unique_members => true
411
+ end
412
+ end
413
+ cube 'Sales' do
414
+ dimension_usage 'Gender', :foreign_key => 'customer_id' # by default :source => 'Gender' will be added
415
+ end
416
+ end
417
+ @schema.to_xml.should be_like <<-XML
418
+ <?xml version="1.0" encoding="UTF-8"?>
419
+ <Schema name="default">
420
+ <Dimension name="Gender">
421
+ <Hierarchy allMemberName="All Genders" hasAll="true" primaryKey="customer_id">
422
+ <Table name="customer"/>
423
+ <Level column="gender" name="Gender" uniqueMembers="true"/>
424
+ </Hierarchy>
425
+ </Dimension>
426
+ <Cube name="Sales">
427
+ <DimensionUsage foreignKey="customer_id" name="Gender" source="Gender"/>
428
+ </Cube>
429
+ </Schema>
430
+ XML
431
+ end
432
+ end
433
+
434
+ describe "Virtual cube" do
435
+ it "should render to XML" do
436
+ @schema.define do
437
+ virtual_cube 'Warehouse and Sales', :default_measure => 'Store Sales' do
438
+ virtual_cube_dimension 'Customers', :cube_name => 'Sales'
439
+ virtual_cube_dimension 'Product'
440
+ virtual_cube_measure '[Measures].[Store Sales]', :cube_name => 'Sales'
441
+ calculated_member 'Profit' do
442
+ dimension 'Measures'
443
+ formula '[Measures].[Store Sales] - [Measures].[Store Cost]'
444
+ end
445
+ end
446
+ end
447
+ @schema.to_xml.should be_like <<-XML
448
+ <?xml version="1.0" encoding="UTF-8"?>
449
+ <Schema name="default">
450
+ <VirtualCube defaultMeasure="Store Sales" name="Warehouse and Sales">
451
+ <VirtualCubeDimension cubeName="Sales" name="Customers"/>
452
+ <VirtualCubeDimension name="Product"/>
453
+ <VirtualCubeMeasure cubeName="Sales" name="[Measures].[Store Sales]"/>
454
+ <CalculatedMember dimension="Measures" name="Profit">
455
+ <Formula>[Measures].[Store Sales] - [Measures].[Store Cost]</Formula>
456
+ </CalculatedMember>
457
+ </VirtualCube>
458
+ </Schema>
459
+ XML
460
+ end
319
461
  end
320
462
 
321
463
  describe "Measure" do
@@ -326,13 +468,15 @@ describe "Schema definition" do
326
468
  column 'unit_sales'
327
469
  aggregator 'sum'
328
470
  end
471
+ measure 'Store Sales', :column => 'store_sales' # by default should use sum aggregator
329
472
  end
330
473
  end
331
474
  @schema.to_xml.should be_like <<-XML
332
- <?xml version="1.0"?>
475
+ <?xml version="1.0" encoding="UTF-8"?>
333
476
  <Schema name="default">
334
477
  <Cube name="Sales">
335
478
  <Measure aggregator="sum" column="unit_sales" name="Unit Sales"/>
479
+ <Measure aggregator="sum" column="store_sales" name="Store Sales"/>
336
480
  </Cube>
337
481
  </Schema>
338
482
  XML
@@ -348,7 +492,7 @@ describe "Schema definition" do
348
492
  end
349
493
  end
350
494
  @schema.to_xml(:driver => 'oracle').should be_like <<-XML
351
- <?xml version="1.0"?>
495
+ <?xml version="1.0" encoding="UTF-8"?>
352
496
  <Schema name="default">
353
497
  <Cube name="Sales">
354
498
  <Measure aggregator="sum" column="UNIT_SALES" name="Unit Sales"/>
@@ -368,7 +512,7 @@ describe "Schema definition" do
368
512
  end
369
513
  end
370
514
  @schema.to_xml.should be_like <<-XML
371
- <?xml version="1.0"?>
515
+ <?xml version="1.0" encoding="UTF-8"?>
372
516
  <Schema name="default">
373
517
  <Cube name="Sales">
374
518
  <Measure aggregator="sum" name="Double Unit Sales">
@@ -394,7 +538,7 @@ describe "Schema definition" do
394
538
  end
395
539
  end
396
540
  @schema.to_xml.should be_like <<-XML
397
- <?xml version="1.0"?>
541
+ <?xml version="1.0" encoding="UTF-8"?>
398
542
  <Schema name="default">
399
543
  <Cube name="Sales">
400
544
  <CalculatedMember dimension="Measures" formatString="#,##0.00" name="Profit">
@@ -404,6 +548,32 @@ describe "Schema definition" do
404
548
  </Schema>
405
549
  XML
406
550
  end
551
+
552
+ it "should render embedded cube XML defintion before additional calculated member to XML" do
553
+ @schema.define do
554
+ cube 'Sales' do
555
+ xml <<-XML
556
+ <Table name="sales_fact_1997"/>
557
+ XML
558
+ calculated_member 'Profit' do
559
+ dimension 'Measures'
560
+ formula '[Measures].[Store Sales] - [Measures].[Store Cost]'
561
+ format_string '#,##0.00'
562
+ end
563
+ end
564
+ end
565
+ @schema.to_xml.should be_like <<-XML
566
+ <?xml version="1.0" encoding="UTF-8"?>
567
+ <Schema name="default">
568
+ <Cube name="Sales">
569
+ <Table name="sales_fact_1997"/>
570
+ <CalculatedMember dimension="Measures" formatString="#,##0.00" name="Profit">
571
+ <Formula>[Measures].[Store Sales] - [Measures].[Store Cost]</Formula>
572
+ </CalculatedMember>
573
+ </Cube>
574
+ </Schema>
575
+ XML
576
+ end
407
577
  end
408
578
 
409
579
  describe "Aggregates" do
@@ -425,7 +595,7 @@ describe "Schema definition" do
425
595
  end
426
596
  end
427
597
  @schema.to_xml.should be_like <<-XML
428
- <?xml version="1.0"?>
598
+ <?xml version="1.0" encoding="UTF-8"?>
429
599
  <Schema name="default">
430
600
  <Cube name="Sales">
431
601
  <Table name="sales_fact_1997">
@@ -465,7 +635,7 @@ describe "Schema definition" do
465
635
  end
466
636
  end
467
637
  @schema.to_xml.should be_like <<-XML
468
- <?xml version="1.0"?>
638
+ <?xml version="1.0" encoding="UTF-8"?>
469
639
  <Schema name="default">
470
640
  <Cube name="Sales">
471
641
  <Table name="sales_fact_1997">
@@ -519,7 +689,7 @@ describe "Schema definition" do
519
689
  end
520
690
  end
521
691
  @schema.to_xml.should be_like <<-XML
522
- <?xml version="1.0"?>
692
+ <?xml version="1.0" encoding="UTF-8"?>
523
693
  <Schema name="default">
524
694
  <Cube name="Sales">
525
695
  <Table name="sales_fact_1997">
@@ -573,7 +743,7 @@ describe "Schema definition" do
573
743
  end
574
744
  end
575
745
  @schema.to_xml.should be_like <<-XML
576
- <?xml version="1.0"?>
746
+ <?xml version="1.0" encoding="UTF-8"?>
577
747
  <Schema name="default">
578
748
  <Cube name="Sales">
579
749
  <Dimension foreignKey="employee_id" name="Employees">
@@ -594,6 +764,615 @@ describe "Schema definition" do
594
764
  end
595
765
  end
596
766
 
767
+ describe "Element annotations" do
768
+ it "should render XML from block of elements" do
769
+ @schema.define do
770
+ cube 'Sales' do
771
+ annotations do
772
+ annotation 'key1', 'value1'
773
+ annotation 'key2', 'value2'
774
+ end
775
+ measure 'Unit Sales', :column => 'unit_sales' do
776
+ annotations do
777
+ annotation 'key3', 'value3'
778
+ end
779
+ end
780
+ end
781
+ end
782
+ @schema.to_xml.should be_like <<-XML
783
+ <?xml version="1.0" encoding="UTF-8"?>
784
+ <Schema name="default">
785
+ <Cube name="Sales">
786
+ <Annotations>
787
+ <Annotation name="key1">value1</Annotation>
788
+ <Annotation name="key2">value2</Annotation>
789
+ </Annotations>
790
+ <Measure aggregator="sum" column="unit_sales" name="Unit Sales">
791
+ <Annotations>
792
+ <Annotation name="key3">value3</Annotation>
793
+ </Annotations>
794
+ </Measure>
795
+ </Cube>
796
+ </Schema>
797
+ XML
798
+ end
799
+
800
+ it "should render XML from hash options" do
801
+ @schema.define do
802
+ cube 'Sales' do
803
+ annotations :key1 => 'value1', :key2 => 'value2'
804
+ measure 'Unit Sales', :column => 'unit_sales', :annotations => {:key3 => 'value3'}
805
+ end
806
+ end
807
+ @schema.to_xml.should be_like <<-XML
808
+ <?xml version="1.0" encoding="UTF-8"?>
809
+ <Schema name="default">
810
+ <Cube name="Sales">
811
+ <Annotations>
812
+ <Annotation name="key1">value1</Annotation>
813
+ <Annotation name="key2">value2</Annotation>
814
+ </Annotations>
815
+ <Measure aggregator="sum" column="unit_sales" name="Unit Sales">
816
+ <Annotations>
817
+ <Annotation name="key3">value3</Annotation>
818
+ </Annotations>
819
+ </Measure>
820
+ </Cube>
821
+ </Schema>
822
+ XML
823
+ end
824
+ end
825
+
826
+ describe "User defined functions and formatters in JavaScript" do
827
+ before(:each) do
828
+ @schema.define do
829
+ cube 'Sales' do
830
+ table 'sales'
831
+ dimension 'Customers', :foreign_key => 'customer_id' do
832
+ hierarchy :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
833
+ table 'customers'
834
+ level 'Name', :column => 'fullname' do
835
+ member_formatter { javascript "return member.getName().toUpperCase();" }
836
+ property 'City', :column => 'city' do
837
+ property_formatter { javascript "return propertyValue.toUpperCase();" }
838
+ end
839
+ end
840
+ end
841
+ end
842
+ calculated_member 'Factorial' do
843
+ dimension 'Measures'
844
+ formula 'Factorial(6)'
845
+ cell_formatter do
846
+ javascript <<-JS
847
+ var s = value.toString();
848
+ while (s.length < 20) {
849
+ s = "0" + s;
850
+ }
851
+ return s;
852
+ JS
853
+ end
854
+ end
855
+ calculated_member 'City' do
856
+ dimension 'Measures'
857
+ formula "[Customers].CurrentMember.Properties('City')"
858
+ end
859
+ end
860
+ user_defined_function 'Factorial' do
861
+ javascript <<-JS
862
+ function getParameterTypes() {
863
+ return new Array(
864
+ new mondrian.olap.type.NumericType());
865
+ }
866
+ function getReturnType(parameterTypes) {
867
+ return new mondrian.olap.type.NumericType();
868
+ }
869
+ function execute(evaluator, arguments) {
870
+ var n = arguments[0].evaluateScalar(evaluator);
871
+ return factorial(n);
872
+ }
873
+ function factorial(n) {
874
+ return n <= 1 ? 1 : n * factorial(n - 1);
875
+ }
876
+ JS
877
+ end
878
+ end
879
+ @olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
880
+ end
881
+
882
+ it "should render XML" do
883
+ @schema.to_xml.should be_like <<-XML
884
+ <?xml version="1.0" encoding="UTF-8"?>
885
+ <Schema name="default">
886
+ <Cube name="Sales">
887
+ <Table name="sales"/>
888
+ <Dimension foreignKey="customer_id" name="Customers">
889
+ <Hierarchy allMemberName="All Customers" hasAll="true" primaryKey="id">
890
+ <Table name="customers"/>
891
+ <Level column="fullname" name="Name" uniqueMembers="true">
892
+ <MemberFormatter>
893
+ <Script language="JavaScript">return member.getName().toUpperCase();</Script>
894
+ </MemberFormatter>
895
+ <Property column="city" name="City">
896
+ <PropertyFormatter>
897
+ <Script language="JavaScript">return propertyValue.toUpperCase();</Script>
898
+ </PropertyFormatter>
899
+ </Property>
900
+ </Level>
901
+ </Hierarchy>
902
+ </Dimension>
903
+ <CalculatedMember dimension="Measures" name="Factorial">
904
+ <Formula>Factorial(6)</Formula>
905
+ <CellFormatter>
906
+ <Script language="JavaScript">
907
+ var s = value.toString();
908
+ while (s.length &lt; 20) {
909
+ s = "0" + s;
910
+ }
911
+ return s;
912
+ </Script>
913
+ </CellFormatter>
914
+ </CalculatedMember>
915
+ <CalculatedMember dimension="Measures" name="City">
916
+ <Formula>[Customers].CurrentMember.Properties('City')</Formula>
917
+ </CalculatedMember>
918
+ </Cube>
919
+ <UserDefinedFunction name="Factorial">
920
+ <Script language="JavaScript">
921
+ function getParameterTypes() {
922
+ return new Array(
923
+ new mondrian.olap.type.NumericType());
924
+ }
925
+ function getReturnType(parameterTypes) {
926
+ return new mondrian.olap.type.NumericType();
927
+ }
928
+ function execute(evaluator, arguments) {
929
+ var n = arguments[0].evaluateScalar(evaluator);
930
+ return factorial(n);
931
+ }
932
+ function factorial(n) {
933
+ return n &lt;= 1 ? 1 : n * factorial(n - 1);
934
+ }
935
+ </Script>
936
+ </UserDefinedFunction>
937
+ </Schema>
938
+ XML
939
+ end
940
+
941
+ it "should execute user defined function" do
942
+ result = @olap.from('Sales').columns('[Measures].[Factorial]').execute
943
+ value = 1*2*3*4*5*6
944
+ result.values.should == [value]
945
+ result.formatted_values.should == ["%020d" % value]
946
+ end
947
+
948
+ it "should format members and properties" do
949
+ result = @olap.from('Sales').columns('[Measures].[City]').rows('[Customers].[All Customers].Children').execute
950
+ result.row_members.each_with_index do |member, i|
951
+ member.caption.should == member.name.upcase
952
+ city = member.property_value('City')
953
+ result.formatted_values[i].first.should == city
954
+ member.property_formatted_value('City').should == city.upcase
955
+ end
956
+ end
957
+ end
958
+
959
+ describe "User defined functions and formatters in CoffeeScript" do
960
+ before(:each) do
961
+ @schema.define do
962
+ cube 'Sales' do
963
+ table 'sales'
964
+ dimension 'Customers', :foreign_key => 'customer_id' do
965
+ hierarchy :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
966
+ table 'customers'
967
+ level 'Name', :column => 'fullname' do
968
+ member_formatter { coffeescript "member.getName().toUpperCase()" }
969
+ property 'City', :column => 'city' do
970
+ property_formatter { coffeescript "propertyValue.toUpperCase()" }
971
+ end
972
+ end
973
+ end
974
+ end
975
+ calculated_member 'Factorial' do
976
+ dimension 'Measures'
977
+ formula 'Factorial(6)'
978
+ cell_formatter do
979
+ coffeescript <<-JS
980
+ s = value.toString()
981
+ s = "0" + s while s.length < 20
982
+ s
983
+ JS
984
+ end
985
+ end
986
+ calculated_member 'City' do
987
+ dimension 'Measures'
988
+ formula "[Customers].CurrentMember.Properties('City')"
989
+ end
990
+ end
991
+ user_defined_function 'Factorial' do
992
+ coffeescript <<-JS
993
+ parameters: ["Numeric"]
994
+ returns: "Numeric"
995
+ execute: (n) ->
996
+ if n <= 1 then 1 else n * @execute(n - 1)
997
+ JS
998
+ end
999
+ user_defined_function 'UpperName' do
1000
+ coffeescript <<-JS
1001
+ parameters: ["Member"]
1002
+ returns: "String"
1003
+ syntax: "Property"
1004
+ execute: (member) ->
1005
+ member.getName().toUpperCase()
1006
+ JS
1007
+ end
1008
+ user_defined_function 'toUpperName' do
1009
+ coffeescript <<-JS
1010
+ parameters: ["Member", "String"]
1011
+ returns: "String"
1012
+ syntax: "Method"
1013
+ execute: (member, dummy) ->
1014
+ member.getName().toUpperCase()
1015
+ JS
1016
+ end
1017
+ end
1018
+ @olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
1019
+ end
1020
+
1021
+ it "should execute user defined function" do
1022
+ result = @olap.from('Sales').columns('[Measures].[Factorial]').execute
1023
+ value = 1*2*3*4*5*6
1024
+ result.values.should == [value]
1025
+ result.formatted_values.should == ["%020d" % value]
1026
+ end
1027
+
1028
+ it "should format members and properties" do
1029
+ result = @olap.from('Sales').columns('[Measures].[City]').rows('[Customers].[All Customers].Children').execute
1030
+ result.row_members.each_with_index do |member, i|
1031
+ member.caption.should == member.name.upcase
1032
+ city = member.property_value('City')
1033
+ result.formatted_values[i].first.should == city
1034
+ member.property_formatted_value('City').should == city.upcase
1035
+ end
1036
+ end
1037
+
1038
+ it "should execute user defined property on member" do
1039
+ result = @olap.from('Sales').
1040
+ with_member('[Measures].[Upper Name]').as('[Customers].CurrentMember.UpperName').
1041
+ columns('[Measures].[Upper Name]').rows('[Customers].Children').execute
1042
+ result.row_members.each_with_index do |member, i|
1043
+ result.values[i].should == [member.name.upcase]
1044
+ end
1045
+ end
1046
+
1047
+ it "should execute user defined method on member" do
1048
+ result = @olap.from('Sales').
1049
+ with_member('[Measures].[Upper Name]').as("[Customers].CurrentMember.toUpperName('dummy')").
1050
+ columns('[Measures].[Upper Name]').rows('[Customers].Children').execute
1051
+ result.row_members.each_with_index do |member, i|
1052
+ result.values[i].should == [member.name.upcase]
1053
+ end
1054
+ end
1055
+ end
1056
+
1057
+ describe "User defined functions and formatters in Ruby" do
1058
+ before(:each) do
1059
+ @schema.define do
1060
+ cube 'Sales' do
1061
+ table 'sales'
1062
+ dimension 'Customers', :foreign_key => 'customer_id' do
1063
+ hierarchy :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
1064
+ table 'customers'
1065
+ level 'Name', :column => 'fullname' do
1066
+ member_formatter { ruby {|member| member.getName().upcase } }
1067
+ property 'City', :column => 'city' do
1068
+ property_formatter { ruby {|member, property_name, property_value| property_value.upcase} }
1069
+ end
1070
+ end
1071
+ end
1072
+ end
1073
+ calculated_member 'Factorial' do
1074
+ dimension 'Measures'
1075
+ formula 'Factorial(6)'
1076
+ cell_formatter { ruby {|value| "%020d" % value} }
1077
+ end
1078
+ calculated_member 'City' do
1079
+ dimension 'Measures'
1080
+ formula "[Customers].CurrentMember.Properties('City')"
1081
+ end
1082
+ end
1083
+ user_defined_function 'Factorial' do
1084
+ ruby do
1085
+ parameters :numeric
1086
+ returns :numeric
1087
+ def call(n)
1088
+ n <= 1 ? 1 : n * call(n - 1)
1089
+ end
1090
+ end
1091
+ end
1092
+ user_defined_function 'UpperName' do
1093
+ ruby do
1094
+ parameters :member
1095
+ returns :string
1096
+ syntax :property
1097
+ def call(member)
1098
+ member.getName.upcase
1099
+ end
1100
+ end
1101
+ end
1102
+ user_defined_function 'toUpperName' do
1103
+ ruby do
1104
+ parameters :member, :string
1105
+ returns :string
1106
+ syntax :method
1107
+ def call(member, dummy)
1108
+ member.getName.upcase
1109
+ end
1110
+ end
1111
+ end
1112
+ user_defined_function 'firstUpperName' do
1113
+ ruby do
1114
+ parameters :set
1115
+ returns :string
1116
+ syntax :property
1117
+ def call(set)
1118
+ set.first.getName.upcase
1119
+ end
1120
+ end
1121
+ end
1122
+ user_defined_function 'firstToUpperName' do
1123
+ ruby do
1124
+ parameters :set, :string
1125
+ returns :string
1126
+ syntax :method
1127
+ def call(set, dummy)
1128
+ set.first.getName.upcase
1129
+ end
1130
+ end
1131
+ end
1132
+ user_defined_function 'firstChildUpperName' do
1133
+ ruby do
1134
+ parameters :hierarchy
1135
+ returns :string
1136
+ syntax :property
1137
+ def call_with_evaluator(evaluator, hierarchy)
1138
+ evaluator.getSchemaReader.getMemberChildren(hierarchy.getDefaultMember).first.getName.upcase
1139
+ end
1140
+ end
1141
+ end
1142
+ user_defined_function 'firstLevelChildUpperName' do
1143
+ ruby do
1144
+ parameters :level
1145
+ returns :string
1146
+ syntax :property
1147
+ def call_with_evaluator(evaluator, level)
1148
+ evaluator.getSchemaReader.getLevelMembers(level, false).first.getName.upcase
1149
+ end
1150
+ end
1151
+ end
1152
+ end
1153
+ @olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
1154
+ end
1155
+
1156
+ it "should execute user defined function" do
1157
+ result = @olap.from('Sales').columns('[Measures].[Factorial]').execute
1158
+ value = 1*2*3*4*5*6
1159
+ result.values.should == [value]
1160
+ result.formatted_values.should == ["%020d" % value]
1161
+ end
1162
+
1163
+ it "should format members and properties" do
1164
+ result = @olap.from('Sales').columns('[Measures].[City]').rows('[Customers].[All Customers].Children').execute
1165
+ result.row_members.each_with_index do |member, i|
1166
+ member.caption.should == member.name.upcase
1167
+ city = member.property_value('City')
1168
+ result.formatted_values[i].first.should == city
1169
+ member.property_formatted_value('City').should == city.upcase
1170
+ end
1171
+ end
1172
+
1173
+ it "should execute user defined property on member" do
1174
+ result = @olap.from('Sales').
1175
+ with_member('[Measures].[Upper Name]').as('[Customers].CurrentMember.UpperName').
1176
+ columns('[Measures].[Upper Name]').rows('[Customers].Children').execute
1177
+ result.row_members.each_with_index do |member, i|
1178
+ result.values[i].should == [member.name.upcase]
1179
+ end
1180
+ end
1181
+
1182
+ it "should execute user defined method on member" do
1183
+ result = @olap.from('Sales').
1184
+ with_member('[Measures].[Upper Name]').as("[Customers].CurrentMember.toUpperName('dummy')").
1185
+ columns('[Measures].[Upper Name]').rows('[Customers].Children').execute
1186
+ result.row_members.each_with_index do |member, i|
1187
+ result.values[i].should == [member.name.upcase]
1188
+ end
1189
+ end
1190
+
1191
+ it "should execute user defined property on set" do
1192
+ result = @olap.from('Sales').
1193
+ with_member('[Measures].[Upper Name]').as("{[Customers].CurrentMember}.firstUpperName").
1194
+ columns('[Measures].[Upper Name]').rows('[Customers].Children').execute
1195
+ result.row_members.each_with_index do |member, i|
1196
+ result.values[i].should == [member.name.upcase]
1197
+ end
1198
+ end
1199
+
1200
+ it "should execute user defined method on set" do
1201
+ result = @olap.from('Sales').
1202
+ with_member('[Measures].[Upper Name]').as("{[Customers].CurrentMember}.firstToUpperName('dummy')").
1203
+ columns('[Measures].[Upper Name]').rows('[Customers].Children').execute
1204
+ result.row_members.each_with_index do |member, i|
1205
+ result.values[i].should == [member.name.upcase]
1206
+ end
1207
+ end
1208
+
1209
+ it "should execute user defined property on hierarchy" do
1210
+ result = @olap.from('Sales').
1211
+ with_member('[Measures].[Upper Name]').as("[Customers].firstChildUpperName").
1212
+ columns('[Measures].[Upper Name]').rows('[Customers].Children').execute
1213
+ first_member = result.row_members.first
1214
+ result.row_members.each_with_index do |member, i|
1215
+ result.values[i].should == [first_member.name.upcase]
1216
+ end
1217
+ end
1218
+
1219
+ it "should execute user defined property on level" do
1220
+ result = @olap.from('Sales').
1221
+ with_member('[Measures].[Upper Name]').as("[Customers].[Name].firstLevelChildUpperName").
1222
+ columns('[Measures].[Upper Name]').rows('[Customers].Children').execute
1223
+ first_member = result.row_members.first
1224
+ result.row_members.each_with_index do |member, i|
1225
+ result.values[i].should == [first_member.name.upcase]
1226
+ end
1227
+ end
1228
+ end
1229
+
1230
+ describe "Shared user defined functions in Ruby" do
1231
+ before(:each) do
1232
+ shared_schema = Mondrian::OLAP::Schema.define do
1233
+ user_defined_function 'Factorial' do
1234
+ ruby :shared do
1235
+ parameters :numeric
1236
+ returns :numeric
1237
+ def call(n)
1238
+ n <= 1 ? 1 : n * call(n - 1)
1239
+ end
1240
+ end
1241
+ end
1242
+ user_defined_function 'UpperName' do
1243
+ ruby :shared do
1244
+ parameters :member
1245
+ returns :string
1246
+ syntax :property
1247
+ def call(member)
1248
+ member.getName.upcase
1249
+ end
1250
+ end
1251
+ end
1252
+ user_defined_function 'toUpperName' do
1253
+ ruby :shared do
1254
+ parameters :member, :string
1255
+ returns :string
1256
+ syntax :method
1257
+ def call(member, dummy)
1258
+ member.getName.upcase
1259
+ end
1260
+ end
1261
+ end
1262
+ user_defined_cell_formatter 'Integer20Digits' do
1263
+ ruby :shared do |value|
1264
+ "%020d" % value
1265
+ end
1266
+ end
1267
+ end
1268
+
1269
+ @schema.define do
1270
+ include_schema shared_schema
1271
+
1272
+ cube 'Sales' do
1273
+ table 'sales'
1274
+ dimension 'Customers', :foreign_key => 'customer_id' do
1275
+ hierarchy :has_all => true, :all_member_name => 'All Customers', :primary_key => 'id' do
1276
+ table 'customers'
1277
+ level 'Name', :column => 'fullname'
1278
+ end
1279
+ end
1280
+ measure 'Unit Sales', :column => 'unit_sales', :aggregator => 'sum', :format_string => '#,##0'
1281
+ calculated_member 'Factorial' do
1282
+ dimension 'Measures'
1283
+ formula 'Factorial(6)'
1284
+ cell_formatter 'Integer20Digits'
1285
+ end
1286
+ end
1287
+ end
1288
+ @olap = Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
1289
+ end
1290
+
1291
+ it "should render XML" do
1292
+ @schema.to_xml.should be_like <<-XML
1293
+ <?xml version="1.0" encoding="UTF-8"?>
1294
+ <Schema name="default">
1295
+ <Cube name="Sales">
1296
+ <Table name="sales"/>
1297
+ <Dimension foreignKey="customer_id" name="Customers">
1298
+ <Hierarchy allMemberName="All Customers" hasAll="true" primaryKey="id">
1299
+ <Table name="customers"/>
1300
+ <Level column="fullname" name="Name" uniqueMembers="true"/>
1301
+ </Hierarchy>
1302
+ </Dimension>
1303
+ <Measure aggregator="sum" column="unit_sales" formatString="#,##0" name="Unit Sales"/>
1304
+ <CalculatedMember dimension="Measures" name="Factorial">
1305
+ <Formula>Factorial(6)</Formula>
1306
+ <CellFormatter className="rubyobj.Mondrian.OLAP.Schema.CellFormatter.Integer20DigitsUdf"/>
1307
+ </CalculatedMember>
1308
+ </Cube>
1309
+ <UserDefinedFunction className="rubyobj.Mondrian.OLAP.Schema.UserDefinedFunction.FactorialUdf" name="Factorial"/>
1310
+ <UserDefinedFunction className="rubyobj.Mondrian.OLAP.Schema.UserDefinedFunction.UppernameUdf" name="UpperName"/>
1311
+ <UserDefinedFunction className="rubyobj.Mondrian.OLAP.Schema.UserDefinedFunction.TouppernameUdf" name="toUpperName"/>
1312
+ </Schema>
1313
+ XML
1314
+ end
1315
+
1316
+ it "should execute user defined function" do
1317
+ result = @olap.from('Sales').columns('[Measures].[Factorial]').execute
1318
+ value = 1*2*3*4*5*6
1319
+ result.values.should == [value]
1320
+ result.formatted_values.should == ["%020d" % value]
1321
+ end
1322
+
1323
+ it "should get measure cell formatter name" do
1324
+ @olap.cube('Sales').member('[Measures].[Factorial]').cell_formatter_name.should == 'Integer20Digits'
1325
+ end
1326
+
1327
+ it "should not get measure cell formatter name if not specified" do
1328
+ @olap.cube('Sales').member('[Measures].[Unit Sales]').cell_formatter_name.should be_nil
1329
+ end
1330
+
1331
+ it "should get measure format string" do
1332
+ @olap.cube('Sales').member('[Measures].[Unit Sales]').format_string.should == '#,##0'
1333
+ end
1334
+
1335
+ it "should not get measure format string if not specified" do
1336
+ @olap.cube('Sales').member('[Measures].[Factorial]').format_string.should be_nil
1337
+ end
1338
+
1339
+ end
1340
+
1341
+ describe "Roles" do
1342
+ it "should render XML" do
1343
+ @schema.define do
1344
+ role 'California manager' do
1345
+ schema_grant :access => 'none' do
1346
+ cube_grant :cube => 'Sales', :access => 'all' do
1347
+ dimension_grant :dimension => '[Measures]', :access => 'all'
1348
+ hierarchy_grant :hierarchy => '[Customers]', :access => 'custom',
1349
+ :top_level => '[Customers].[State Province]', :bottom_level => '[Customers].[City]' do
1350
+ member_grant :member => '[Customers].[USA].[CA]', :access => 'all'
1351
+ member_grant :member => '[Customers].[USA].[CA].[Los Angeles]', :access => 'none'
1352
+ end
1353
+ end
1354
+ end
1355
+ end
1356
+ end
1357
+ @schema.to_xml.should be_like <<-XML
1358
+ <?xml version="1.0" encoding="UTF-8"?>
1359
+ <Schema name="default">
1360
+ <Role name="California manager">
1361
+ <SchemaGrant access="none">
1362
+ <CubeGrant access="all" cube="Sales">
1363
+ <DimensionGrant access="all" dimension="[Measures]"/>
1364
+ <HierarchyGrant access="custom" bottomLevel="[Customers].[City]" hierarchy="[Customers]" topLevel="[Customers].[State Province]">
1365
+ <MemberGrant access="all" member="[Customers].[USA].[CA]"/>
1366
+ <MemberGrant access="none" member="[Customers].[USA].[CA].[Los Angeles]"/>
1367
+ </HierarchyGrant>
1368
+ </CubeGrant>
1369
+ </SchemaGrant>
1370
+ </Role>
1371
+ </Schema>
1372
+ XML
1373
+ end
1374
+ end
1375
+
597
1376
  end
598
1377
 
599
1378
  describe "connection with schema" do
@@ -636,4 +1415,44 @@ describe "Schema definition" do
636
1415
 
637
1416
  end
638
1417
 
639
- end
1418
+ describe "errors" do
1419
+ before(:each) do
1420
+ @schema = Mondrian::OLAP::Schema.new
1421
+ end
1422
+
1423
+ it "should raise error when invalid schema" do
1424
+ @schema.define do
1425
+ cube 'Sales' do
1426
+ end
1427
+ end
1428
+ expect {
1429
+ Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
1430
+ }.to raise_error {|e|
1431
+ e.should be_kind_of(Mondrian::OLAP::Error)
1432
+ e.message.should == "mondrian.olap.MondrianException: Mondrian Error:Internal error: Must specify fact table of cube 'Sales'"
1433
+ e.root_cause_message.should == "Internal error: Must specify fact table of cube 'Sales'"
1434
+ }
1435
+ end
1436
+
1437
+ it "should raise error when invalid calculated member formula" do
1438
+ @schema.define do
1439
+ cube 'Sales' do
1440
+ table 'sales'
1441
+ calculated_member 'Dummy' do
1442
+ dimension 'Measures'
1443
+ formula 'Dummy(123)'
1444
+ end
1445
+ end
1446
+ end
1447
+ expect {
1448
+ Mondrian::OLAP::Connection.create(CONNECTION_PARAMS.merge :schema => @schema)
1449
+ }.to raise_error {|e|
1450
+ e.should be_kind_of(Mondrian::OLAP::Error)
1451
+ e.message.should == "mondrian.olap.MondrianException: Mondrian Error:Named set in cube 'Sales' has bad formula"
1452
+ e.root_cause_message.should == "No function matches signature 'Dummy(<Numeric Expression>)'"
1453
+ }
1454
+ end
1455
+
1456
+ end
1457
+
1458
+ end