mondrian-olap 0.3.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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